单片机学习小组
直播中

赵淑洁

7年用户 970经验值
私信 关注

如何读取编码器的脉冲信号?

如何读取编码器的脉冲信号?

回帖(1)

孟佳

2022-1-21 10:20:24
一.编码器简介

        编码器是一种安装在直流电机同心轴上的将电机转子的角位移或者角速度转换成一连串数字脉冲的旋转式传感器. 因为编码器输出的是标准的方波, 我们可以使用MCU,单片机等直接读取.
        从编码器输出数据类型上分, 可以分为增量式编码器和绝对式编码器。
        从编码器检测原理上来分, 还可以分为光学式、 磁式、 感应式、 电容式。 常见的是光电编码器(光学式) 和霍尔编码器(磁式) 。为判断转动转向, 一般输出两组存在一定相位差的方波信号.
        下图为光电编码器和MCU连接使用示意图:
二. 编码器脉冲信号读取

        如图所示: 编码器除正负电源连接线外,还有A,B两路数字脉冲输出信号, 把这两路信号线对接MCU的两个GPIO输入引脚上.
      对于编码器脉冲信号读取,主要包括电机旋转方向电机转速.
     一般使用 M 法测速确定电机的转速, 即通过测量单位时间内 A 相输出的脉冲数来得到速度信息。而对于B相的输入信号测不用处理.

1.对于不带编码计数器功能的单片机或使用STM32但timer不够用的情况


        对于两个驱动电机的情况,我们可以通过配置外部GPIO中断读取, 比如把左轮的编码器 A 相输出接到处理器的外部中断输入口, 并通过跳变沿触发中断;把右轮的编码器 A 相输出接到处理器的另一外部中断输入口, 并通过跳变沿触发中断 (注左右轮的B相不用配置中断,仅配置GPIO输入模式即可) . 然后分别在左右轮分A相对应的外部中断服务函数里面, 通过读取 B 相引脚的电平来确定正反转。 如当 A 相来一个跳变沿的时候, 如果 B 相是高电平就认为是正转, 低电平就认为是反转。 并依靠正反转信息对编码器脉冲次数进行累加统计,进而按照一定周期读取此编码器脉冲次数统计值,按照周期时间计算出左右轮的瞬时速度.


        一个大致的代码如下(相关寄存器的配置代码略):


extern uint8_t volatile LeftEncoderCount;
extern int32_t volatile LeftEncoderTotal;
extern int8_t  volatile LeftEncoderDirection;
extern uint8_t volatile  RightEncoderCount;
extern int32_t volatile RightEncoderTotal;
extern int8_t volatile   RightEncoderDirection;

//左轮编码器A相信号中断处理程序, 右轮类似
void EXTIx_IRQHandler(void)  //EXTIx表某中断, 用以处理左轮电机A相脉冲信号
{
    //统计左轮周期内转速,单位周期内读取完毕后务必清0.    右轮类似
    LeftEncoderCount++;
   
    //读取左轮编码器B相相位电平值
    if( GPIOD->IDR & GPIO_Pin_x ){ //若为高电平.   GPIO_Pin_x表某引脚
        LeftEncoderDirection = -1; //标记当前为反向转动,周期性读取后务必清0
        LeftEncoderTotal--;//总编码器个数减1
    }else{
        LeftEncoderDirection = 1; //标记当前为正向转动,周期性读取后务必清0
        LeftEncoderTotal++;//总编码器个数加1
    }

   
    EXTI_ClearITPendingBit(EXTI_Linex);//EXTI_Linex表某中断引脚
}
2. 对于自带编码器计数接口或利用Timer可实现计数的MCU,如STM32


        对于自带编码器计数接口的MCU,可以直接使用硬件计数器,对编码器脉冲信号进行计数,根据需要确定是否再读取完毕后对计数器进行清0。


        例如下面一个例子,就是对一个Timer进行配置,使其对左轮的编码器A相脉冲方波信号进行计数.(同样因为寄存器差别过大,相关寄存器的配置代码略):


//单位周期内读取编码器计数值. 参数指定读取哪个轮的转速,与定时器一一对应
int Read_Encoder(u8 timerX)
{
    int EncoderCount;   
    switch(timerX)
    {
            case 2:  
            EncoderCount = (short)TIM2 -> CNT;  //对应左轮编码器计数器值,正负代表旋转方向
            TIM3 -> CNT=0; //因为是统计转速,也为了避免计数器溢出,读取完毕立即清0
            break;       
                case 3:  
            EncoderCount = (short)TIM3 -> CNT;  //对应又轮编码器计数器值,正负代表旋转方向
            TIM4 -> CNT=0;  //同上,读取完毕立即清0
            break;       
                default:  
            EncoderCount = 0;
    }
       
    return EncoderCount;
}
        由于一般MCU的Timer计数寄存器都有长度限制,比如16位,最大计数值为65535,超过会自动清零重装. 所以一般不要使用计数器累计求总编码器值.


      


        综上,有了左右轮的转动方向,编码器脉冲瞬时计数个数,或累计编码器总脉冲个数,还要根据小车的物理结构,做编码器脉冲个数(单位:个)和实际小车位移(单位:m)之间的换算,以及编码器脉冲计数速度(个/秒)和车速(米/秒)之间的换算.  到此, 我们就可以把目标车速和编码器读取值传给PID控制器,对小车的驱动轮进行精准的速度和位移控制.
举报

更多回帖

发帖
×
20
完善资料,
赚取积分