2.2 STM32电机测速(正交或霍尔编码器)
我们提供左右电机测速代码,在微信官方账号:小白学移动机器人,发送:电机测速,即可获得源代码工程下载链接。
2.2.1 实现工具
STM32单片机、带编码器的直流减速电机、Keil5.调试(蓝牙、串口助手)
2.2.2 编码器原理
(1)什么是编码器?
编码器是一种旋转传感器,将角位移或角速转换为一串电数字脉冲。编码器分为光电编码器和霍尔编码器。
(2)编码器工作原理是什么?
霍尔编码器由霍尔马盘和霍尔元件组成。霍尔马盘在一定直径的圆板上等分布置不同的磁极。霍尔马盘与电机同轴。当电机旋转时,霍尔元件检测并输出多个脉冲信号。为了判断转向,两组方波信号通常有一定的相位差。示意图如下:
(3)带霍尔编码器的直流减速电机接线图
(4)速度测量原理:
在单位时间内,根据脉冲距离计算电机的实际速度,这里使用5ms中断定时器。
(5)数据采集方式:
根据外部中断,通常有两种方法可以直接收集第一种软件技术AB不同的相位差可以判断正负。第二种硬件技术直接采用定时器的编码器模式,这里采用第二种。常说的四倍频,提高测量精度的方法。其实就是把AB上升边和下降边都是收集的,所以1变4。利用外部中断实现更占用资源,因此不建议使用。
(6)速度计算方法:
这里计算的是真实电机轮的物理转速
电机旋转的脉冲数:num1 单位:个
单位时间:t 单位:秒
单位时间内捕获的脉冲变化数:num2 单位:个 (反应电机正反转)
电机轮子半径:r 单位:m
圆周率:pi 单位:无
速度:speed 单位: mm/s
speed = 1000 x num2 x ( 2 x pi x r\num1)\t
2.2.3 共享部分代码
主要流程:
将编码器AB我们根据定时器的编码器模式设置了相引脚TIMx->CNT寄存器数据的变化计算单位时间内脉冲的变化值。然后在定时器中断服务函数中计算速度,然后通过蓝牙发送速度数据PC串口助手,验证数据是否正确。
(1)编码器引脚配置及测速代码
#include "encoder.h"
/**************************************************************************函数功能:把TIM2初始化为编码器接口模式的入口参数 值:无**************************************************************************/
void Encoder_Init_TIM2(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); ///使定时器2的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;///端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; ///浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure);//GPIOA
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0x0; ///预分频器TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; ////设计数器自动重装值TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; ///选择时钟分频:不分频TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM向上计数TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除TIM更新标志位TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
//Reset counterTIM_SetCounter(TIM2,0);
//===============================================TIM2->CNT = 0x7fff;
//===============================================TIM_Cmd(TIM2, ENABLE);
}
/**************************************************************************函数功能:把TIM4初始化为编码器接口模式的入口参数 值:无**************************************************************************/
void Encoder_Init_TIM4(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); ///使定时器4的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7.///端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; ///浮空输入GPIO_Init(GPIOB, &GPIO_InitStructure);//GPIOB
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // 预分频器TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; ////设计数器自动重装值TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; ///选择时钟分频:不分频TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM向上计数TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10;
TIM_ICInit(TIM4, &TIM_ICInitStructure);
TIM_ClearFlag(TIM4, TIM_FLAG_Update); //清除TIM更新标志位TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
//Reset counterTIM_SetCounter(TIM4,0);
//===============================================TIM4->CNT = 0x7fff;
//===============================================TIM_Cmd(TIM4, ENABLE);
}
/**************************************************************************函数功能:读取编码器脉冲差,读取单位时间内的脉冲变化值口参数:TIM_TypeDef * TIMx返回 值:无**************************************************************************/
s16 getTIMx_DetaCnt(TIM_TypeDef * TIMx)
{
s16 cnt;
cnt = TIMx->CNT-0x7fff;
TIMx->CNT = 0x7fff;
return cnt;
}
/**************************************************************************函数功能:计算左右轮速入口参数:int *leftSpeed,int *rightSpeed返回 值:无//计算左右车轮线速度,正向速度为正值 ,反向速度为负值,速度为乘以1000之后的速度 mm/s//一定时间内的编码器变化值*转化率(转化为直线上的距离m)*200s(5ms计算一次) 得到 m/s *1000转化为int数据一圈的脉冲数:左:1560右:1560轮子半径:0.03m轮子周长:2*pi*r一个脉冲的距离:左:0.000120830m右:0.000120830m速度分辨率:左: 0.0240m/s右: 0.0240m/s200 5ms的倒数1000 扩大分辨率**************************************************************************/
void Get_Motor_Speed(int *leftSpeed,int *rightSpeed)
{
//5ms测速 5ms即这里说的单位时间*leftSpeed = getTIMx_DetaCnt(TIM4)*1000*200*0.000120830;
*rightSpeed = getTIMx_DetaCnt(TIM2)*1000*200*0.000120830;
}
(2)main.c
#include "sys.h"
//====================自己加入的头文件===============================#include "delay.h"#include "led.h"#include "encoder.h"#include "usart3.h"#include "timer.h"#include //===================================================================
int leftSpeedNow =0;
int rightSpeedNow =0;
int main(void)
{
GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//禁用JTAG 启用 SWD
MY_NVIC_PriorityGroupConfig(2);//=====设置中断分组
delay_init(); //=====延时函数初始化LED_Init(); //=====LED初始化 程序灯
usart3_init(9600); //=====串口3初始化 蓝牙 发送调试信息
Encoder_Init_TIM2(); //=====初始化编码器1接口Encoder_Init_TIM4(); //=====初始化编码器2接口
TIM3_Int_Init(50-1,7200-1); //=====定时器初始化 5ms一次中断
while(1)
{
printf("L=%d,R=%d\r\n",leftSpeedNow,rightSpeedNow);
delay_ms(15);
}
}
//5ms 定时器中断服务函数
void TIM3_IRQHandler(void) //TIM3中断{
if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除TIMx的中断待处理位:TIM 中断源
Get_Motor_Speed(&leftSpeedNow,&rightSpeedNow);
Led_Flash(100);
}
}
2.2.4 总结
上面的一篇文章,我们实现的STM32单片机对直流减速电机的测速。再加上之前介绍的电机PWM控制,下一篇我们要结合前两篇的内容加上PID实现对电机速度的闭环控制。
系列文章