最近接触到的问题用到编码器,所以还是学了一段时间这方面的知识,如果有问题欢迎各位朋友指教:
所用的的器件为:
STM32F407系列,电机型号为JGB37-520(带有霍尔编码器),电池为飞行电池,尤其要吐槽飞行电池的大电流烧了我几个LM298N电机驱动器,后面改成大功率驱动器。
首先基本东西介绍:
JBGB37-520是AB项的减速电机,每个编码器至少要用两个定时器。(关于ABZ的自行找资料)
给出编码器原理:编码器原理
编码器的分类:
按工作原理:光电式、磁电式和触点电刷式
按码盘的刻孔方式:增量式和绝对式两类
由于博主接触面还不是很广,一共就用过两个种类的编码器,都是属于光电编码器和霍尔编码器:一般由6线差分A+ A- B+ B- VCC 和GND;正交编码器:一般是5根线连接,信号线分别为A B Z VCC和GND
编码器线数: 就是旋转一圈你的A(B)会输出多少个脉冲 ,这里的A B就是上面的输出脉冲信号线,它们转一圈发出的脉冲数一样的,不过存在90°相位差 通常都是360线的 线数越高代表编码器能够反应的位置精度越高
给出我在课题中用的电机参数
线速:线速度??? 基础脉冲11*减速比 我的理解是算出线速度然后除以减速比10,
磁环触发极数: 我的理解是上边有个圆盘,里面有11个磁极,AB有两极所以22对极,参考图中给出的霍尔传感器原理。
电机旋转一圈产生11个脉冲: what ’s the fuck ???是单极产生11个脉冲,还是两极一共产生11个脉冲,单片机的双边极性采集得到22或者44个脉冲!这就让我很迷,问过淘宝客服,他们也说不清白,简直了。刚开始计数计数清零,然后手动采集10多组旋转一圈的脉冲数值,发现脉冲数在214呈现正态分布,所以我最终取旋转一圈产生214个脉冲。
后语:写到这214与22成10倍(减速比为10),这难道是巧合???希望有人解答。
关于F407定时器重装载值的的选取问题,这里我就比较迷,到目前为止看到了三种:
(1)基于光电编码器:这里给出参考(光电编码器历程),之前看过一篇对原理介绍的更详细的博客,没有收藏。 拿360个栅格的光栅(线数)来举例子,一度一个光栅。因此AB两极,且程序通常又是双边采集,因此一个栅格会产生四个脉冲,360个栅格会产生360x4个脉冲,因此通常会写成:360x4-1,这样做的好处对求旋转的角度很方便:即编码器的旋转角度直接等于采集到的脉冲数除以4得到角度,然后微分得到角速度,然后由减速比得到车轮线速度(正比关系)。
(2)基于霍尔编码器:参考这篇(霍尔编码器教程),也谢谢这位博客主给出了思路,我也是按照他的方法进行的测试,首先明确定时器计数器的最大计数值0XFFFF,这样才不会溢出,然后直接给到最大,我也是采用这种方式。(后面,发现其实也用不到那么多计数值)这位博客主给出的“齿数比”我也不知道怎么求,百度感觉就是减速比,但是与他的文章不符合,也问过淘宝客服,结果了无音讯———知道的朋友希望留言告知,哈哈!
给出详细细节,也可以参考给出的博客主:
(3)基于STM32CUEMX:目前用这个软件用来统计端口和查询端口,之前也研究过一段时间,会一点点,不过还是习惯于自己调库。给出这位博客主的博文:STM32CUBEMX的使用介绍,并没有介绍的很详细,只是串口输出的数据对我有启发。
代码部分:
代码属于原来工程的下面的一部分,需要同时对车速度进行控制,所以对四个车轮的属性进行了采集,进行反馈,也利于后面换装麦克纳姆轮。代码有点乱,仅供参考:
#include "encoder.h"
#include "sys.h"
//使用的定时器
//TIM1 3 4 8
void encoder_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);//开启TIM4时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE);//开启GPIOB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//开启TIM3时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//开启GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);//开启TIM3时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);//开启GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8,ENABLE);//开启TIM3时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE);//开启GPIOB时钟
GPIO_PinAFConfig(GPIOD,GPIO_PinSource12,GPIO_AF_TIM4);//PB0引脚复用
GPIO_PinAFConfig(GPIOD,GPIO_PinSource13,GPIO_AF_TIM4);//PB1引脚服用
GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_TIM3);//PB0引脚复用
GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_TIM3);//PB1引脚服用
GPIO_PinAFConfig(GPIOE,GPIO_PinSource9,GPIO_AF_TIM1);//PB0引脚复用
GPIO_PinAFConfig(GPIOE,GPIO_PinSource11,GPIO_AF_TIM1);//PB1引脚服用
GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM8);//PB0引脚复用
GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_TIM8);//PB1引脚服用
/*--------------------------------------------------------------*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13; //GPIOD12,GPIOD13
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_Init(GPIOD,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //GPIOB0,GPIOB1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_11; //GPIOB0,GPIOB1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_Init(GPIOE,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //GPIOB0,GPIOB1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_Init(GPIOC,&GPIO_InitStructure);
/*--------------------------------------------------------------*/
TIM_TimeBaseStructure.TIM_Period = 65535; //设置下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler = 0; //设置用来作为TIMx时钟频率除数的预分频值 不分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period =65535; //设置下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler = 0; //设置用来作为TIMx时钟频率除数的预分频值 不分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period =65535; //设置下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler = 0; //设置用来作为TIMx时钟频率除数的预分频值 不分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = 65535; //设置下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler = 0; //设置用来作为TIMx时钟频率除数的预分频值 不分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure);
/*--------------------------------------------------------------*/
TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12,TIM_ICPolarity_BothEdge,TIM_ICPolarity_BothEdge);
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10; //输入滤波器
TIM_ICInit(TIM4, &TIM_ICInitStructure);
TIM_ClearFlag(TIM4, TIM_FLAG_Update); //清楚所有标志位
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); //允许中断更新
TIM4->CNT = 0;
TIM_Cmd(TIM4, ENABLE); //使能TIM3
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12,TIM_ICPolarity_BothEdge,TIM_ICPolarity_BothEdge);
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;
TIM_Cmd(TIM3, ENABLE); //使能TIM3
TIM_EncoderInterfaceConfig(TIM1, TIM_EncoderMode_TI12,TIM_ICPolarity_BothEdge,TIM_ICPolarity_BothEdge);
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10; //输入滤波器
TIM_ICInit(TIM1, &TIM_ICInitStructure);
TIM_ClearFlag(TIM1, TIM_FLAG_Update); //清楚所有标志位
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); //允许中断更新
TIM1->CNT = 0;
TIM_Cmd(TIM1, ENABLE); //使能TIM3
TIM_EncoderInterfaceConfig(TIM8, TIM_EncoderMode_TI12,TIM_ICPolarity_BothEdge,TIM_ICPolarity_BothEdge);
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10; //输入滤波器
TIM_ICInit(TIM8, &TIM_ICInitStructure);
TIM_ClearFlag(TIM8, TIM_FLAG_Update); //清楚所有标志位
TIM_ITConfig(TIM8, TIM_IT_Update, ENABLE); //允许中断更新
TIM8->CNT = 0;
TIM_Cmd(TIM8, ENABLE); //使能TIM3
}
_EncoderStructure EncoderStructure;
void Read_Encoder(void)
{
int i;
EncoderStructure.Encoder_Value[0]=TIM1 -> CNT; //正数
EncoderStructure.Encoder_Value[1]=TIM3 -> CNT;
EncoderStructure.Encoder_Value[2]=TIM4 -> CNT;
EncoderStructure.Encoder_Value[3]=TIM8 -> CNT;
//清零
TIM1 -> CNT=0;TIM3 -> CNT=0;
TIM4 -> CNT=0;TIM8 -> CNT=0;
for(i=0;i<=3;i++)
{
if(EncoderStructure.Encoder_Value[i]>6000)
{
EncoderStructure.Encoder_Value[i]=EncoderStructure.Encoder_Value[i]-65535; //负数
}
}
// 屏蔽小波动
for(i=0;i<=3;i++)
{
if(EncoderStructure.Encoder_Value[i]<=2&&EncoderStructure.Encoder_Value[i]>=-2)
{EncoderStructure.Encoder_Value[i]=0;
}
}
}
_WheelStructure WheelStructure;
//10ms中断一次然后测速
//计数CNT 单项极对数为11对,产生11个脉冲 ,两个对极,双边检测,因此单旋转一圈产生44个脉冲
//实际测量旋转一圈旋转的脉冲数为
//217 214 216 214 214 215 214 212 212 218 取众数214
#define Dt_10ms 1e-3
void EnANWh_Velocity(void)
{
u8 i;
//读取编码器的值
EncoderStructure.Encoder_Value[0]=TIM1 -> CNT;
EncoderStructure.Encoder_Value[1]=TIM3 -> CNT;
EncoderStructure.Encoder_Value[2]=TIM4 -> CNT;
EncoderStructure.Encoder_Value[3]=TIM8 -> CNT;
//大端取反得负数
for(i=0;i<=3;i++)
{
if(EncoderStructure.Encoder_Value[i]>6000)
{
EncoderStructure.Encoder_Value[i]=EncoderStructure.Encoder_Value[i]-65535;
}
}
//清零
TIM1 -> CNT=0;TIM3 -> CNT=0;
TIM4 -> CNT=0;TIM8 -> CNT=0;
// 屏蔽小波动
for(i=0;i<=3;i++)
{
if(EncoderStructure.Encoder_Value[i]<=2&&EncoderStructure.Encoder_Value[i]>=-2)
{
EncoderStructure.Encoder_Value[i]=0;
}
}
//得到旋转的圈数
for(i=0;i<=3;i++)
{
EncoderStructure.Motor_CylNum[i]=EncoderStructure.Encoder_Value[i]/214;
//得到电机的角速度,车轮的角速度,减速比为10
EncoderStructure.Motor_angVel[i]=EncoderStructure.Motor_CylNum[i]*200*pai; //单位(rad/s)
WheelStructure.Wheel_angVel[i]=EncoderStructure.Motor_angVel[i]/10 ;
//得到车轮的线速度
WheelStructure.Wheel_linVel[i]=WheelStructure.Wheel_angVel[i]*WheelStructure.wheel_R;
//得到车轮的行驶距离
WheelStructure.Wheel_distance[i]=WheelStructure.wheel_perimeter*EncoderStructure.Motor_CylNum[i]/10;
}
}
低通滤波器
//float L_filter(float value,float a)
//{
// static float Last_value;
// value=Last_value*(256-a)/256+a*value/256;
// Last_value=value;
// return value;
//}
#define TIM9_arr 839
#define TIM9_psc 999
//得到快关频率 100hz
//车轮速度专用解算定时器
//10ms中断一次
void TIM9_EnAnWh_Vel(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM9,ENABLE); ///使能TIM2时钟
TIM_TimeBaseInitStructure.TIM_Period = TIM9_arr; //自动重装载值
TIM_TimeBaseInitStructure.TIM_Prescaler=TIM9_psc; //定时器分频
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM9,&TIM_TimeBaseInitStructure);//初始化TIM7
TIM_ITConfig(TIM9,TIM_IT_Update,ENABLE); //允许定时器2更新中断
TIM_Cmd(TIM9,ENABLE); //使能定时器9
NVIC_InitStructure.NVIC_IRQChannel=TIM1_BRK_TIM9_IRQn; //定时器2中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; //抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
s16 TIM9_flag;
void TIM1_BRK_TIM9_IRQHandler(void)
{
if(TIM_GetITStatus(TIM9,TIM_IT_Update)!=RESET)
{
EnANWh_Velocity();
TIM_ClearITPendingBit(TIM9,TIM_IT_Update);
}
}
V12/12