本文简述了本人对一种智能小车测速代码的原理的理解。如果大家有异议,还请指出。示例代码来自本人购买的智能小车配套的源码。代码如下:
int Read_Encoder(u8 TIMX)
{
int Encoder_TIM;
switch(TIMX)
{
case 2: Encoder_TIM= (short)TIM2 -> CNT; TIM2 -> CNT=0; break;
case 3: Encoder_TIM= (short)TIM3 -> CNT; TIM3 -> CNT=0; break;
case 4: Encoder_TIM= (short)TIM4 -> CNT; TIM4 -> CNT=0; break;
case 8: Encoder_TIM= (short)TIM8 -> CNT; TIM8 -> CNT=0; break;
default: Encoder_TIM=0;
}
return Encoder_TIM;
}
void Get_Velocity_From_Encoder(void)
{
//Retrieves the original data of the encoder
//获取编码器的原始数据
float Encoder_A_pr,Encoder_B_pr;
OriginalEncoder.A=Read_Encoder(Encoder1);
OriginalEncoder.B=Read_Encoder(Encoder2);
//Decide the encoder numerical polarity according to different car models
//根据不同小车型号决定编码器数值极性
switch(Car_Num)
{
case Akm_Car: Encoder_A_pr=OriginalEncoder.A; Encoder_B_pr=-OriginalEncoder.B;break;
case Diff_Car: Encoder_A_pr=OriginalEncoder.A; Encoder_B_pr=-OriginalEncoder.B;break;
case Small_Tank_Car:Encoder_A_pr=OriginalEncoder.A; Encoder_B_pr=-OriginalEncoder.B;break;
case Big_Tank_Car: Encoder_A_pr=OriginalEncoder.A; Encoder_B_pr=-OriginalEncoder.B;break;
}
//The encoder converts the raw data to wheel speed in m/s
//编码器原始数据转换为车轮速度,单位m/s
MotorA.Current_Encoder= Encoder_A_pr*Frequency*Perimeter/1560.0f;
MotorB.Current_Encoder= Encoder_B_pr*Frequency*Perimeter/1560.0f; //1560=4*13*30=2(两路脉冲)*2(上下沿计数)*霍尔编码器13线*电机的减速比
// MotorA.Current_Encoder= Encoder_A_pr*CONTROL_FREQUENCY*Akm_wheelspacing//(4*13*30);
// MotorB.Current_Encoder= Encoder_B_pr*CONTROL_FREQUENCY*Akm_wheelspacing/Encoder_precision;
}
代码流程简单来讲,就是Get_Velocity_From_Encoder调用Read_Encoder读取左右两个电机的编码器脉冲计数值,然后进行处理,最后算出车轮的速度,存放到各自的结构体的成员Current_Encoder中。不过,本文要说的其实是这里的Read_Encoder函数。这个函数一开始看起来觉得怪怪的,因为在我遇到的场景下,当车子向前行驶时,与TIM8相连的电机是在反转的,所以TIM8的计数方向是递减的,在测量周期内,编码器脉冲计数值应该是65536-CNT,而非CNT的值。但是,整个小车的运行看起来却是正常的,这是咋回事?带着这个疑问,我用KEIL的调试功能跑了一下程序,最终发现了其中的奥秘。
这个问题的关键其实就是这个将CNT强制转换成short类型的操作上。CNT是一个无符号32位整型变量。假设CNT的值为0x0000FFF5,那么这个CNT对应的内存区域就存储着0x0000FFF5的补码,即0x0000FFF5. 当其被强制转换成short类型时,C语言会截取这个补码低16位,并将其作为一个short类型变量的补码来处理,于是CNT的值就变成了-11,在计数器处于递减模式时,这个数值的绝对值正好就是编码器脉冲计数值。而且负号也正好表示电机是在反转的,这也符合实际情况。这可以说是对C语言特性的一个巧妙运用。不过这种做法其实也是有局限性的,它要求当计数器处于递减模式时,CNT的最高位要保持为1,当计数器处于递增模式时,CNT的最高位要保持为0,否则就要出错。下面简要说明一下。
假设计数器处于递减模式,然后此时CNT的值是0x00007FF5, 那么,其低16位就是个正数的补码,将其强制转换成short类型后,其数值就是32757。而此时,由于计数器处于递减模式,所以,这个数值的绝对值并不等于编码器的脉冲计数值。计数器处于递增模式时的情况与此类似。这种方法的局限性决定了其测量周期不能太长。