历史上的今天
今天是:2025年12月21日(星期日)
2022年12月21日 | stm32电机控制之控制两路直流电机
2022-12-21 来源:zhihu
小车使用的电机是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 OKn");
}
史海拾趣
|
在电路设计中电流测量应用十分普遍,主要领域分为3大类:测量中,电表会用来进行电流的测量;保护中,电流往往与功率形成直接的关系,如果电流过大代表系统中有短路情况出现而需要保护,因此用到电流测量;控制中,如马达控制、电池充放电等都需要 ...… 查看全部问答> |
|
铝合金分几百个牌号,每个牌号都有特定加工性能,我们就简单地谈各位玩家常用的工艺吧 阳极氧化要达到镜面效果的要选用6061牌号的铝合金,6061是属于铝硅镁合金,特点是具有良好的阳极氧化性能和焊接,高强度的抗腐蚀性 ...… 查看全部问答> |
|
电工常用测量仪表有摇表、万用表和钳形电流表,这些仪表在测量时若不注意正确的使用方法或稍有疏忽,不是将表烧坏,就是使被测元件损坏,甚至还危及人身安全,因此,掌握常用电工测量仪表的正确使用方法是非常重要的。 1摇表 摇表又称兆欧表,其 ...… 查看全部问答> |
|
在学校时的学习主要方向是web方面的,java,.net之类,毕业后一年了,现在是在一个用c开发的银行交易系统项目中coding,从接触程序起我对c一直很感兴趣,想以后就做c程序员,现在把c学精点。但是,对c程序员的发展方向有些迷茫,感觉技术也还是很浅 ...… 查看全部问答> |
|
由430的结构图看出,地址总线MAB为16位的,寻址空间最大为64k,有些片子的内部Flash大于64k,高于这个地址的区间是如何访问的,基于什么原理?谢谢各位!… 查看全部问答> |




