STM32 Cubemax(六) —— STM32利用定时器编码器模式处理带编码器直流电机
前言
电赛延期了,趁有时间再写点东西吧.
编码器电机配置较为繁琐,本文较长,耐心看下去,一定有收获.
本文适合已经对编码器有所了解的同学观看,如果对编码器原理还不太理解,可以看看CSDN中别人讲编码器的,个人觉得已经讲的十分清楚了,这里主要讲解怎么使用Cubemax去使用编码器
一、硬件准备
- ????????本次实验使用的是带增量式AB相霍尔编码器的直流减速电机
?简单介绍一下这款电机,减速比为1:30,即输出轴转一圈,电机内部实际转30圈,霍尔编码器为13位编码器,即电机每转,对于编码器有2的13次方的增量.(简单的说,上面那个霍尔编码器检测的圆盘,转一圈,检测13个脉冲)
?2.? ? ? ? 电机所使用的电机驱动为应该大家都十分熟悉的L298N
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
这里就不过多介绍了,CSDN上也有很多对此介绍十分详细的.
3.? ? ? ? 合适的8-12V的电源
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
电机额定电压为12V,可以选择串联3节锂电池12V,或者两节锂电池8V来进行实验.
二、接线?
电机 | L298N | STM32 | M- | OUT1 | / | 5V | 5V | / | A | / | 编码器模式定时器IO | B? ? ? ? | / | 编码器模式定时器IO | GND | GND | GND | M+ | OUT2 | / | / | ENA | PWM输出IO | / | M1 | 输出IO | / | M2 | 输出IO |
? ? ? ? 其中,这里要说明几点.
? ? ? ? 1.L298N,单片机和电机需要共地
? ? ? ? 2.我这里使用的是单PWM输出IO来控制ENA,而不是和其他博主一样使用两路PWM输出去控制M1,M2,然后一直使能的方法,这样可以节约一个定时器产生PWM.
三、Cubemax配置
1.首先是接线中用来控制M1和M2的两个IO口的输出配置,这里我使用的是PC4和PC5
?2.开启定时器一的编码器模式
这里面修改Counter Period为20000,即代表,编码器计数器范围为0~20000.其他默认即可
?对应的IO口为PE9,PE11.
?3.开启用于控制ENA的PWM输出
这里采用的是定时器5的PWM输出.
本实验使用的是F103的板子,PWM输出频率为100hz
?4.开启每隔10ms对编码器定时器中值的读取的定时器
5.最后配置中断?
这里注意一下,最好编码器的更新中断定时器,要比10ms定时器的优先级高,可以防止在更新中打被打断.
四、代码
首先创建一个文件Motor.c和Motor.h,在此文件中来编写我们有关电机的代码.
首先在Motor.h中来定义一些我们常用的东西
1.Motor.h中定义Motor相关参数
#define RR 30u //电机减速比
#define RELOADVALUE __HAL_TIM_GetAutoreload(&htim1) //获取自动装载值,本例中为20000
#define COUNTERNUM __HAL_TIM_GetCounter(&htim1) //获取编码器定时器中的计数值
#define IN1(state) HAL_GPIO_WritePin(GPIOC,GPIO_PIN_4,(GPIO_PinState)(state)) //M1
#define IN2(state) HAL_GPIO_WritePin(GPIOC,GPIO_PIN_5,(GPIO_PinState)(state)) //M2
//电机结构体
typedef struct _Motor
{
int32_t lastAngle; //上10ms转过的角度
int32_t totalAngle; //总的角度
int16_t loopNum; //防超上限
float speed; //电机输出轴目前转速,单位为RPM
}Motor;
2.Motor初始化
void Motor_Init()
{
HAL_TIM_Encoder_Start(&htim1, TIM_CHANNEL_ALL); //开启编码器定时器
__HAL_TIM_ENABLE_IT(&htim1,TIM_IT_UPDATE); //开启编码器定时器更新中断,防溢出处理
HAL_TIM_Base_Start_IT(&htim6); //开启10ms定时器中断
HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_2); //开启PWM
__HAL_TIM_SET_COUNTER(&htim1, 10000); //编码器定时器初始值设定为10000
motor.loopNum = 0; //防溢出
}
此函数在main中使用即可.
?3.中断处理函数重写
我们需要重写HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)来处理中断
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==htim6.Instance) //10ms中断
{
int16_t pluse = COUNTERNUM - RELOADVALUE/2;
motor.totalAngle = pluse + motor.loopNum * RELOADVALUE/2; //获取出10ms内一共获取的脉冲数
motor.speed = (float)(motor.totalAngle - motor.lastAngle)/(4*13*RR)*6000; //进行速度计算,根据前文所说的,4倍频,编码器13位,减速比30,再乘以6000即为每分钟输出轴多少转
motor.lastAngle = motor.totalAngle; //更新转过的圈数
}
else if(htim->Instance == htim1.Instance) //如果是编码器更新中断,即10ms内,脉冲数超过了计数范围,需要进行处理
{
if(COUNTERNUM < 10000) motor.loopNum++;
else if(COUNTERNUM > 10000) motor.loopNum--;
__HAL_TIM_SetCounter(&htim1, 10000); //重新设定初始值
}
}
这里注意,本文采用了编码器更新中断的方法,防止出现10ms内出现电机转速过快,脉冲计数超过设定值的情况出现,跟其他博主不同,主流在编码器计数时都采用限定值为65535,并没有考虑这个情况,不过多数电机不是转太快的情景,主流的方法都可行.
之后我们可以通过,对IN1,IN2写值,来控制电机正转,反转,然后对PWM写值,来控制电机运动来观察结构体中Motor中speed值的变化.
如果懒的写,就用手去选择一下输出轴或者上面的编码器盘,在debug中观察一下现象.
总结
本文介绍了编码器电机数据的读取,当我们可以读取到编码器电机中的值后,我们就可以在后续进行PID速度和串级PID角度的控制.
我们在下一次再讲讲如何使用PID来控制电机的转速.
|