stm32电机控制之控制两路直流电机!看完你会了吗
手头上有一个差分驱动的小车,使用两个直流电机驱动,要实现小车的在给定速度下运动,完成直线行驶,转向,加速,刹车等复杂运动。
使用的电机是12v供电的直流电机,带编码器反馈,这样就可以采用闭环速度控制,这里电机使用PWM驱动,速度控制框图如下:
由以上框图可知,STM32通过定时器模块输出PWM波来控制两个直流电机的转动,通过改变PWM占空比的大小可以改变电机的转速,由于我们的控制目标是实现电机运行在速度范围内任意给定的速度,这里就需要采用闭环控制的思想,通过编码器获取电机的实时转速,通过与给定速度做差,将偏差作为PID控制器的输入,通过PID控制改变PWM占空比的大小,从而使电机的速度运行在给定的速度上。
这里使用的电机驱动芯片为TB6612,该芯片可以十分方便的驱动两个直流电机的运行,其驱动逻辑表如下:
AIN1,AIN2的不同组合可以实现电机的正反转和停车,PWMA为PWM的输入引脚,通过输入不同的占空比可以改变电机转速的快慢。BIN1,BIN2,PWMB是控制另一路电机的引脚。
首先我们需要利用STM32的定时器模块输出两路PWM波,这是使电机转起来的第一步。初始化PWM:
//初始化PWM引脚 void motorPWMPin_init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_11 ;//TIM1_Chn_1,TIM1_Chn_2 GPIO_Init(GPIOE,&GPIO_InitStructure); GPIO_PinAFConfig(GPIOE,GPIO_PinSource9,GPIO_AF_TIM1); GPIO_PinAFConfig(GPIOE,GPIO_PinSource11,GPIO_AF_TIM1); } //初始化PWM void motorPWM_init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrecture; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE); TIM_TimeBaseInitStrecture.TIM_Period = 400;/*PWM's frequency is 20KHz*/ TIM_TimeBaseInitStrecture.TIM_Prescaler =21-1;//将TIM1的时钟频率设定为8MHz TIM_TimeBaseInitStrecture.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStrecture.TIM_CounterMode = TIM_CounterMode_Up;//定时器向上计数 TIM_TimeBaseInitStrecture.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStrecture); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High ; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; TIM_OC1Init(TIM1,&TIM_OCInitStructure); TIM_OC2Init(TIM1,&TIM_OCInitStructure); // TIM_Cmd(TIM1,ENABLE); TIM_CtrlPWMOutputs(TIM1,ENABLE); }
然后初始化电机控制引脚,程序如下:
//初始化电机控制引脚 void motorCtrlPin_init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE); //PE7,PE8控制电机A,PE9,PE10控制电机B GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; GPIO_Init(GPIOE, &GPIO_InitStructure); }
需要注意的是设置PWM输出引脚时要讲引脚复用到定时器TIM1,而电机控制引脚只需要设置成简单的推挽输出模式即可。
接着我们需要使用两个定时器的编码器功能用于读取电机的实时转动速度,这里我使用的是定时器3和定时器4.
这里的编码器是精度较低的霍尔感应式编码器,但是基本满足控制精度的要求,驱动代码如下:
void encoderA_init(void) { GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; /*CLOCK Enable*/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); //PC6,PC7 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用引脚模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //100MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //无上下拉 /*Configure PC6,PC7 as encoder A,B Input*/ GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM3); GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_TIM3); GPIO_Init(GPIOC,&GPIO_InitStructure); //initialize PORTC /* Timer configuration in Encoder mode */ /* Enable the TIM3 Update Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; NVIC_InitStructure.NVIC_IRQChannelSubPriority =0x01; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); TIM_TimeBaseStructure.TIM_Prescaler = 0; //不分频 TIM_TimeBaseStructure.TIM_Period = 65535; //设置为最大 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up ; //向上计数 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising , TIM_ICPolarity_Rising );//上升沿计数 TIM_ICStructInit(&TIM_ICInitStructure); TIM_ICInitStructure.TIM_ICFilter = 10;//设置滤波系数 TIM_ICInit(TIM3, &TIM_ICInitStructure); TIM_ClearFlag(TIM3, TIM_FLAG_Update); //清除更新中断 TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); //使能更新中断 TIM3->CNT = 0;//将计数值设为0 TIM_Cmd(TIM3, ENABLE);//enable TIM3 printf("Encoder_A initializztion is OK\n"); }
stm32视频资料
(stm32直流电机驱动)
http://www.makeru.com.cn/live/1392_1218.html?s=45051
(stm32 USART串口应用)
http://www.makeru.com.cn/live/1392_1164.html?s=45051
PWM脉宽调制技术
http://www.makeru.com.cn/live/4034_2146.html?s=45051
最新文章
- Lesson 15 Good news
- swift的后台编码路
- Windows 2008 server IIS 7 中开启CGI, ISAPI
- Codeforce - Street Lamps
- 在SqlServer中使用Try Catch(转)
- if...else语句的应用题
- Android Drawable 关于selector中state_pressed=";true";的位置顺序
- web前端优化-温故知新系列(1)
- OpenCV中的SVM參数优化
- 【Qt开发】修改源码文件的编码格式的小技巧 .
- poj3648
- HTML5须知十件事
- 洛谷 [P1387] 最大正方形
- 01 深入理解JVM的内存区域
- JDK中Unsafe类详解
- MongoExport后的负载均衡问题查询及解决:can't accept new chunks because there are still 2 deletes from previous migration
- Tomcat修改版本号教程(CentOS)
- C#反射的一些经验[转载]
- 个推基于Consul的配置管理
- 【Unity】3.3 用3ds Max 2015制作模型并将其导入到Unity
热门文章
- Docker系列(26)- 发布镜像到阿里云容器服务
- photoshop CS6打不开提示请卸载并重新安装该程序
- 鸿蒙内核源码分析(编译过程篇) | 简单案例窥视GCC编译全过程 | 百篇博客分析OpenHarmony源码| v57.01
- Mybatis-Plus 全局Update更新策略,和insert插入查询策略
- P5056-[模板]插头dp
- 好久没发文了,一篇Vue3的Composition API使用奉上
- Dapr + .NET Core实战(九)本地调试
- Oracle实时数据抽取项目问题总结
- 拥抱开源,共建生态 - 开源生态与效能提升专场 | CIF 精彩看点
- 回归本心QwQ背包问题luogu1776