二轮平衡小车1:舵机与电机的基本控制

今日开始使用STM32F103 C8T6尝试做一个二轮平衡小车,从电机舵机控制开始,小车也是在三个小时的自主设计下框架结构与引脚安排都做好了。

本文主要贴代码,之前的文章都有原理,代码中相应初始化驱动部分也有注释~~

文章提供源码,解释以及工程下载,测试效果视频。

目录

电机与舵机控制基础原理:

初始化TIM1为通用定时器:

初始化定时器TIM2为PWM输出控制电机:

TB6612电机正反转控制引脚的初始化:

编写函数封装电机控制:

初始化定时器TIM4为舵机控制:

如此以后我们将这些动作加入主函数

测试效果:

测试工程下载:


电机与舵机控制基础原理:

原理方面其余文章都有讲到:

使用模块分别为:TB6612带稳压模块、MG996R舵机、JGB-520电机 。

STM32 F103C8T6学习笔记5:定时器输出不同占空比PWM驱动舵机旋转角度_NULL指向我的博客-CSDN博客

【MSP432电机驱动学习—上篇】TB6612带稳压电机驱动模块、MG310电机、霍尔编码器_tb6612fng电机驱动模块_NULL指向我的博客-CSDN博客

初始化TIM1为通用定时器:

这里我选择了TIM1为通用定时器,因为它与定时器TIM2、3、4不同,

1、它是高级定时器,拥有带死区控制,但我们不需要,

2、它的部分初始化内容也与TIM2、3、4不同,因此这里初始化一下TIM1,巩固一下,

3、它占用的引脚有俩个是串口1的PA9\PA10,为了使得串口1能正常使用,因此不使用TIM1的通道,来控制舵机电机了

直接贴代码,这里初始化TIM1为通用定时器,都是周期1ms:

void TIM1_init(void)
{// 定时器初始化结构体定义TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;   // 定时器中断向量配置NVIC_InitTypeDef NVIC_InitStructure;    // 使能定时器1时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);    // 定时器时钟分频系数设置为72-1TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1;    // 定时器重载值设置为1000-1,即定时器溢出时间为1msTIM_TimeBaseStructure.TIM_Period = 1000 - 1;    // 定时器计数模式设置为向上计数TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;    // 定时器时钟分频因子设置为1TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1;  //高级计数器需要,不需要用到的直接给0就好TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;// 应用定时器初始化配置TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);    // 清除定时器中断标志位TIM_ClearFlag(TIM1, TIM_FLAG_Update);   // 使能定时器中断TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);    // 中断优先级设置为最低NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;// 中断子优先级设置为最低NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;    // 使能中断NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  // 应用中断配置NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;NVIC_Init(&NVIC_InitStructure);// 启动定时器1TIM_Cmd(TIM1, ENABLE);
}

 中断服务函数:

//通用定时器 定时器1 中断服务函数
void TIM1_UP_IRQHandler(void)
{if (TIM_GetITStatus(TIM1, TIM_IT_Update) == SET){	
//		if(++t==1000)		
//		{
//			T++;t=0;
//			printf("%d\r\n",T);
//		}TIM_ClearITPendingBit(TIM1, TIM_IT_Update);//清出中断寄存器标志位,用于退出中断}
}

初始化定时器TIM2为PWM输出控制电机:

TIM2_init();              //定时器2初始化为电机PWM (频率 18K HZ,重载值 1000)void TIM2_init(void)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//定义初始化参数结构体TIM_OCInitTypeDef TIM_OCInitStructure;//结构体变量需要赋值GPIO_InitTypeDef GPIO_Initstructure;  //引脚结构体RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//开启TIM2的时钟TIM_InternalClockConfig(TIM2);//开启定时器2的时钟源作为内部时钟源TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim,这里是不分频TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//TIM向上计数模式TIM_TimeBaseInitStructure.TIM_Period=1000- 1;       //ARRTIM_TimeBaseInitStructure.TIM_Prescaler=4 - 1;      //PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;;//定时器的倍率,如果定时1秒,参数是1,那就是1+1=2倍TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位//	TIM_ClearFlag(TIM2,TIM_FLAG_Update);//中断清除TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//开启中断TIM_OCStructInit(&TIM_OCInitStructure);//结构体赋初始值的函数TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//输出比较模式TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//输出比较的极性TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//输出比较的使能TIM_OCInitStructure.TIM_Pulse =0;//设置CCR的TIM_OC1Init(TIM2,&TIM_OCInitStructure);//初始化TIM2_CH1TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);//使能预装载寄存器TIM_OC2Init(TIM2,&TIM_OCInitStructure);//初始化TIM2_CH2TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);//使能预装载寄存器//	用结构体初始化输出比较单元,不同函数不同的GPIO(A0)RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//这里的注释部分是展示开启定时器相应通道管脚的重映射//RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启AFIO的时钟//GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);//部分重映射//GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//关闭调试端口的复用GPIO_Initstructure.GPIO_Mode= GPIO_Mode_AF_PP;//复用推挽输出GPIO_Initstructure.GPIO_Pin= GPIO_Pin_0;GPIO_Initstructure.GPIO_Speed= GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_Initstructure);GPIO_Initstructure.GPIO_Mode= GPIO_Mode_AF_PP;//复用推挽输出GPIO_Initstructure.GPIO_Pin= GPIO_Pin_1;GPIO_Initstructure.GPIO_Speed= GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_Initstructure);//	GPIO_SetBits(GPIOA,GPIO_Pin_0);TIM_Cmd(TIM2,ENABLE);}//void PWM_SetCompare1(uint16_t Compare1)
//{
//	TIM_SetCompare1(TIM2,Compare1);//TIM2通道1,改变比较值CCRx,达到不同的占空比效果
//}

TB6612电机正反转控制引脚的初始化:

控制速度的PWM在上面已经初始化了,现在还需要初始化启用几个类似于点灯的,可以输出1和0逻辑的几个引脚,用于控制电机正反转:

//电机正反转控制引脚初始化:
void TB6612_init(void)
{//初始化BIN2 (PB9) 和 BIN1 (PB10)/*定义一个GPIO_InitTypeDef类型的结构体*/GPIO_InitTypeDef GPIO_InitStructure0;	/*开启LED相关的GPIO外设时钟*/RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB,ENABLE);/*选择要控制的GPIO引脚*/GPIO_InitStructure0.GPIO_Pin = GPIO_Pin_9;	/*设置引脚模式为通用推挽输出*/GPIO_InitStructure0.GPIO_Mode = GPIO_Mode_Out_PP;   /*设置引脚速率为50MHz */   GPIO_InitStructure0.GPIO_Speed = GPIO_Speed_50MHz; /*调用库函数,初始化GPIO*/GPIO_Init(GPIOB, &GPIO_InitStructure0);		/*开启LED相关的GPIO外设时钟*/RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB,ENABLE);/*选择要控制的GPIO引脚*/GPIO_InitStructure0.GPIO_Pin = GPIO_Pin_10;	/*设置引脚模式为通用推挽输出*/GPIO_InitStructure0.GPIO_Mode = GPIO_Mode_Out_PP;   /*设置引脚速率为50MHz */   GPIO_InitStructure0.GPIO_Speed = GPIO_Speed_50MHz; /*调用库函数,初始化GPIO*/GPIO_Init(GPIOB, &GPIO_InitStructure0);			//初始化AIN2 (PA8) 和 AIN1 (PA11)		/*开启LED相关的GPIO外设时钟*/RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA,ENABLE);	/*选择要控制的GPIO引脚*/GPIO_InitStructure0.GPIO_Pin = GPIO_Pin_8;	/*设置引脚模式为通用推挽输出*/GPIO_InitStructure0.GPIO_Mode = GPIO_Mode_Out_PP;   /*设置引脚速率为50MHz */   GPIO_InitStructure0.GPIO_Speed = GPIO_Speed_50MHz; /*调用库函数,初始化GPIO*/GPIO_Init(GPIOA, &GPIO_InitStructure0);			/*开启LED相关的GPIO外设时钟*/RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA,ENABLE);	/*选择要控制的GPIO引脚*/GPIO_InitStructure0.GPIO_Pin = GPIO_Pin_4;	/*设置引脚模式为通用推挽输出*/GPIO_InitStructure0.GPIO_Mode = GPIO_Mode_Out_PP;   /*设置引脚速率为50MHz */   GPIO_InitStructure0.GPIO_Speed = GPIO_Speed_50MHz; /*调用库函数,初始化GPIO*/GPIO_Init(GPIOA, &GPIO_InitStructure0);			//初始化完先都置0,不转GPIO_ResetBits(GPIOA,GPIO_Pin_8);  //AIN1GPIO_ResetBits(GPIOA,GPIO_Pin_4); //AIN2GPIO_ResetBits(GPIOB,GPIO_Pin_10); //BIN1 GPIO_ResetBits(GPIOB,GPIO_Pin_9);	 //BIN2	 
} 

编写函数封装电机控制:

//设置轮子速度,方向
//PWMA PWMB分别设置 左PWMA 右PWMB 电机 
void set_wheels(uint16_t PWMA,uint16_t PWMB,uint16_t DIRA,uint16_t DIRB)
{TIM_SetCompare1(TIM2,PWMB);//TIM2通道1,改变比较值CCRx,达到不同的占空比效果TIM_SetCompare2(TIM2,PWMA);//TIM2通道1,改变比较值CCRx,达到不同的占空比效果if(DIRA==0)  //反转{GPIO_SetBits(GPIOA,GPIO_Pin_8);  //AIN1GPIO_ResetBits(GPIOA,GPIO_Pin_4);//AIN2}else if(DIRA==1)  //正转{GPIO_SetBits(GPIOA,GPIO_Pin_4);  //AIN1GPIO_ResetBits(GPIOA,GPIO_Pin_8);//AIN2		}if(DIRB==0)  //反转{GPIO_SetBits(GPIOB,GPIO_Pin_10); //BIN1 GPIO_ResetBits(GPIOB,GPIO_Pin_9);//BIN2	 }else if(DIRB==1)  //正转{GPIO_SetBits(GPIOB,GPIO_Pin_9);   //BIN1 GPIO_ResetBits(GPIOB,GPIO_Pin_10);//BIN2	 		}
}

初始化定时器TIM4为舵机控制:

void TIM4_init(void)
{GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;TIM_OCInitTypeDef  TIM_OCInitStructure;	//   //开时钟	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);   //  //PWM输出管脚配置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);	TIM_TimeBaseStructure.TIM_Period = 3000; TIM_TimeBaseStructure.TIM_Prescaler =71; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); 	//	//PWM模式配置TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;TIM_OC3Init(TIM4,&TIM_OCInitStructure); TIM_OC3PreloadConfig(TIM4,TIM_OCPreload_Enable); TIM_Cmd(TIM4,ENABLE); 	//TIM_SetCompare3(TIM4,psc);	   //设置舵机占空比,控制舵机转动	
}
//	TIM_SetCompare3(TIM4,350); // 0度
//	TIM_SetCompare3(TIM4,2600);  //180度
//	TIM_SetCompare3(TIM4,1475);  //90度	

如此以后我们将这些动作加入主函数

uint16_t T,t;int main(void)
{	init_ALL();     //初始化所有函数TIM_SetCompare3(TIM4,350); // 0度delay_ms(5000);set_wheels(555,555,1,1);delay_ms(1000);set_wheels(0,0,0,0);delay_ms(1000);set_wheels(555,555,0,0);delay_ms(1000);set_wheels(0,0,0,0);while(1){	}
}//初始化所有函数:
void init_ALL(void)
{SysTick_Init(72);         //初始化滴答计时器Usart1_Init(115200);      //串口1初始化
//	i2c_GPIO_Config();	      //IIC初始化
//	Usart2_Init(115200);      //串口2初始化
//	Usart3_Init(115200);      //串口3初始化
//	OLED_Init();              //初始化OLED屏幕
//	OLED_Clear();             //清空屏幕数据
//	RTC_init();               //初始化RTC实时时钟TIM1_init();              //定时器1初始化为通用定时器 (周期1ms)TIM2_init();              //定时器2初始化为电机PWM (频率 18K HZ,重载值 1000)TIM4_init();              //定时器4初始化为舵机PWM (频率333 hz ,重载值3000)TB6612_init();  	        //电机正反转控制引脚初始化:printf("HELLO");            //开机测试 串口1//	TIM_SetCompare3(TIM4,350); // 0度
//	TIM_SetCompare3(TIM4,2600);  //180度
//	TIM_SetCompare3(TIM4,1475);  //90度	
}

测试效果:

今日就做到这吧,明日在接着做下面的东西......

二轮平衡小车:舵机与电机的基本控制与编码器信号捕获

测试工程下载:

https://download.csdn.net/download/qq_64257614/88286409?spm=1001.2014.3001.5503

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/63315.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CSS判断手机暗黑模式

手机有个功能到了晚上会自动变成深色也就是暗黑模式.这种情况下网页会自动变颜色.如果想自由控制暗黑模式下的html样式的话,可以用如下方式: media (prefers-color-scheme: dark) {/*html, body {*//*filter: invert(1) hue-rotate(180deg);*//*}*/.maill{margin-left: 0;marg…

【ES系列】(一)简介与安装

首发博客地址 首发博客地址[1] 系列文章地址[2] 教学视频[3] 为什么要学习 ES? 强大的全文搜索和检索功能:Elasticsearch 是一个开源的分布式搜索和分析引擎,使用倒排索引和分布式计算等技术,提供了强大的全文搜索和检索功能。学习 ES 可以掌…

【FreeRTOS】互斥量的使用与逐步实现

在FreeRTOS中,互斥量是一种用于保护共享资源的同步机制。它通过二进制信号量的方式,确保在任意时刻只有一个任务可以获取互斥量并访问共享资源,其他任务将被阻塞。使用互斥量的基本步骤包括创建互斥量、获取互斥量、访问共享资源和释放互斥量…

玩客云 线刷Armbian 搭配Alist 阿里云盘 Jellyfin NovaVideoPlayer搞电视墙

啰嗦的背景 喜欢看电影,买了个投影仪,是这一切折腾的开端。 投影仪虽然有当贝系统,但是想看的电影总是需要**电视会员,那我肯定是不用的。因为有爱腾优的会员,最开始都是使用手机投屏,当呗的投影仪好就好…

vue 基于element-plus el-button封装按钮组件

封装组件的原则是&#xff1a;组件只是数据流通的一个管道&#xff0c;不要糅合太多的逻辑在里面&#xff0c;是一个纯组件&#xff0c;还要根据自己项目的业务场景做具体的处理。 // MyButton.vue // 基于element-plus中el-button来封装按钮 <template><el-button c…

【Vue3 知识第二讲】Vue3新特性、vue-devtools 调试工具、脚手架搭建

文章目录 一、Vue3 新特性1.1 重写双向数据绑定1.1.1 Vue2 基于Object.defineProperty() 实现1.1.2 Vue3 基于Proxy 实现 1.2 优化 虚拟DOM1.3 Fragments1.4 Tree shaking1.5 Composition API 二、 vue-devtools 调试工具三、环境配置四、脚手架目录介绍五、SFC 语法规范解析附…

Android Studio 的github 工程克隆

上文介绍了Android Studio 里的"Git 建立和简单操作。本文介绍从github 上的工程fork 和clone到本地&#xff0c;然后学习和改进。 本文参考 https://learntodroid.com/how-to-use-git-and-github-in-android-studio/ 克隆clone Github 仓库&#xff1a; 先 Fork 你选择…

实战黑马苍穹外卖项目8.1-10.1

文章目录 软件开发的基本流程用户层网关层应用层数据层工具 数据库设计导入准备好的前端和后端工程基础工程代码分析完成员工功能完成菜品功能入门Redis实现店铺营业HttpClient微信小程序开发缓存Spring Cache实现地址功能用户下单实现订单推送状态apache对应的工具使用项目用到…

无涯教程-Flutter - 数据库

SQLite" class"css-1occaib">SQLite数据库是基于事实和标准SQL的嵌入式数据库引擎&#xff0c;它是小型且经过时间考验的数据库引擎&#xff0c;sqflite软件包提供了许多函数&#xff0c;可以有效地与SQLite数据库一起使用&#xff0c;它提供了操作SQLite数据…

ExpressLRS开源代码之框架结构

ExpressLRS开源代码之框架结构 1. 源由2. Arduino应用框架3. ExpressLRS应用框架4. 硬件设计框架4.1 单天线4.2 双天线单PA4.3 双天线双PA 5. 应用软件设计6. 参考资料 1. 源由 最近为了理解《ExpressLRS开源之基本调试数据含义》&#xff0c;做了一些源代码的研读。 概念、文…

基于costas环的载波同步系统matlab性能仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ............................................................................ I_Dataroun…

kubernetes进阶 (一) 环境搭建

我是基于一台centos7.6的腾讯云主机进行操作的&#xff0c;配置为4C8G&#xff0c;之前的文档自己试着搭建发现有问题了&#xff0c;这里重新整理下笔记&#xff0c;集群版本选择1.22.2&#xff08;一年前搭的&#xff09;用的还不错 清理环境 之前我的环境可能装过docker或者什…

Ansible学习笔记4

file模块&#xff1a; file模块用于对文件相关的操作&#xff08;创建、删除、属性修改、软链接等&#xff09;touch是创建。 [rootlocalhost ~]# ansible group1 -m file -a "path/tmp/111 statetouch" 192.168.17.105 | CHANGED > {"ansible_facts"…

[SpringBoot3]视图技术Thymeleaf

七、视图技术Thymeleaf Thymeleaf是一个表现层的模板引擎&#xff0c;一般被使用在Web环境中&#xff0c;它可以处理HTML、XML、JS等文档&#xff0c;简单来说&#xff0c;它可以将JSP作为Java Web应用的表现层&#xff0c;有能力展示与处理数据。这样&#xff0c;同一个模板文…

android 12 第三方apk系统签名

需求&#xff1a;客户有两个供应商&#xff0c;我们是其中之一&#xff0c;然后客户想将我们的apk 用 另一家供应商的系统签名&#xff0c;安装到另一家供应商的设备上&#xff0c;另一家供应商提供了系统签名文件 用之前的方法 &#xff08;platform.x509.pem platform.pk8客户…

1. 安装Zookeeper

​ 1.下载 点击下载Zookeeper 单机版安装 安装Zookeeper前需要先安装jdk上传安装包rz解压安装包:tar -zxvf apache-zookeeper-3.6.0-bin.tar.gz -C /opt/app/zookeeper zookeeper目录结构:a. bin: 放置运行脚本和工具脚本b. conf: zookeeper 默认读取配置的目录,里面会有…

docker安装gitlab

安装gitlab sudo docker run --detach \--hostname gitlab \--publish 543:443 --publish 90:80 --publish 222:22 \ --name gitlab \--restart always \--volume $GITLAB_HOME/config:/etc/gitlab \--volume $GITLAB_HOME/logs:/var/log/gitlab \--volume $GITLAB_HOME/data:…

plotlyjs 当dragmode为false时,如何获得选框正在改变的事件,并得到选框的坐标和范围

在Plotly.js中&#xff0c;当dragmode设置为false时&#xff0c;可以使用plotly_selecting事件来监听选框的变化。该事件在用户点击并开始拖动选框时触发&#xff0c;并在选框大小和位置发生变化时持续触发。 可以通过监听plotly_selecting事件来获取选框的坐标和范围&#xf…

Android 蓝牙开发( 四 )

前言 上一篇文章给大家分享了Kotlin版的Android蓝牙的基础知识和基础用法&#xff0c;不过上一篇都是一些零散碎片化的程序&#xff0c;&#xff0c;这一篇给大家分享Android蓝牙开发实战项目KotlinCompose的初步使用 效果演示 : Android Compose 蓝牙开发 Android蓝牙实战开发…

基于Laravel通用型内容建站企业官网系统源码 可免费商用

是一个基于 Laravel 企业内容建站系统。模块市场拥有丰富的功能应用&#xff0c;支持后台一键快速安装&#xff0c;让开发者能快的实现业务功能开发。 系统完全开源&#xff0c;免费且不限制商业使用 2023年08月23日增加了以下12个特性&#xff1a; [新功能] 手机端Banner支持…