龙邱512正交编码器

发布时间 2023-08-06 18:55:33作者: 夜泽大大

基本概念

龙邱的512线正交编码器,工作电压在3.3v-5v
我们只需要关注该款编码器的LSB及DIR引脚。

LSP:该引脚在编码器转动时,会输出步进脉冲,在不同的转速下,步进脉冲的数量是不同的。
所以我们可以设置一个定时器,把定时器的时钟输入通道改为外部引脚输入,这样我们就可以把单片机时钟的GPIO引脚接到编码器的LSP端口处。然后读取定时器的计数数量,就能获取到当前编码器当前的脉冲数量。
一次脉冲定时器就会计数一次。
另外一种方案是设置引脚上升沿或者下降沿中断来记录脉冲次数。
本文使用的是定时器计数

DIR:该引脚在编码器正转反转时输出的电平是不一样的,可以人为的界定。
可以设为Dir为高电平为正转,Dir为低电平为反转。该引脚连接的GPIO端口只需要设置为输入模式读取高低电平就可以了

注意部分

  • 要注意每次读取的时间间隔要是一定的。比如定时10ms读取一次。否则数值会与实际相差很大。
  • 由于会有误差,可以采用一阶线性滤波,这样就可以是数据更加的稳定,本文已经提供

程序实现部分 (基于STC32)

.H文件

/**
  ******************************************************************************
  * @file           : encoder.h
  * @author         : 宋明泽
  * @brief          : None
  * @attention      : None
  * @date           : 2023/5/2
  ******************************************************************************
  */

#ifndef INC_0_TEST_ENCODER_H
#define INC_0_TEST_ENCODER_H
#include "STC32G_Timer.h"

#define my_TIM3                 Timer3
#define my_TIM4                 Timer4

#define my_TIM_Mode             TIM_16BitAutoReload //16位自动重装载
#define my_TIM_CLOCK_Source     TIM_CLOCK_Ext //外部引脚输入
#define my_TIM_CLOCK_Out        DISABLE
#define my_TIM_Value            0
#define my_TIM_Run              ENABLE

// DIR方向引脚
#define Left1_Port              GPIO_P0
#define Left1_Pin               GPIO_Pin_4
#define Left2_Port              GPIO_P0
#define Left2_Pin               GPIO_Pin_5
#define Left_Direction          P05
#define Right1_Port             GPIO_P0
#define Right1_Pin              GPIO_Pin_6
#define Right2_Port             GPIO_P0
#define Right2_Pin              GPIO_Pin_7
#define Right_Direction         P07
//a的取值决定了算法的灵敏度,a越大,新采集的值占的权重越大,算法越灵敏,但平顺性差;相反,a越小,新采集的值占的权重越小,灵敏度差,但平顺性好。
#define A_R                     0.853f  //右电机A参数
#define A_L                     0.88f   //左电机A参数

void Encoder_Init(void);
/**
  * 函数说明: 读取编码器的值
  * 参数:    左右编码器 'L'跟'R'
  * 返回值:   float 一阶线性滤波值
*/
float Read_Encoder(u8 encno);
/**
  * 函数说明: 一阶滤波  本次滤波值 = a*本次采样值 + (1-a)*上次的滤波输出值
  * 参数:    编码器测得值
  * 返回值:   滤波后的值
*/
float First_Order_Filtering(int32 value, float A);
#endif //INC_0_TEST_ENCODER_H

.C文件

//a的取值决定了算法的灵敏度,a越大,新采集的值占的权重越大,算法越灵敏,但平顺性差;相反,a越小,新采集的值占的权重越小,灵敏度差,但平顺性好。
#define A_R                     0.853f  //右电机A参数
#define A_L                     0.88f   //左电机A参数

float last_Value;

void Encoder_Init(void)
{
    TIM_InitTypeDef my_tim_init;
    GPIO_InitTypeDef gpioInitTypeDef;


    my_tim_init.TIM_Mode = my_TIM_Mode;
    my_tim_init.TIM_ClkSource = my_TIM_CLOCK_Source; 
    my_tim_init.TIM_ClkOut = my_TIM_CLOCK_Out;
    my_tim_init.TIM_Value = 0;
    my_tim_init.TIM_Run = my_TIM_Run;

    Timer_Inilize(my_TIM3, &my_tim_init);
    Timer_Inilize(my_TIM4, &my_tim_init);

    gpioInitTypeDef.Mode = GPIO_PullUp;
    gpioInitTypeDef.Pin = Left1_Pin;
    GPIO_Inilize(Left1_Port,&gpioInitTypeDef);
    gpioInitTypeDef.Pin = Left2_Pin;
    GPIO_Inilize(Left2_Port,&gpioInitTypeDef);
    gpioInitTypeDef.Pin = Right1_Pin;
    GPIO_Inilize(Right1_Port,&gpioInitTypeDef);
    gpioInitTypeDef.Pin = Right2_Pin;
    GPIO_Inilize(Right2_Port,&gpioInitTypeDef);

    NVIC_Timer3_Init(DISABLE,Priority_2);// 中断优先级2
    NVIC_Timer4_Init(DISABLE,Priority_2);// 中断优先级2
}

/**
  * 函数说明: 读取编码器的值
  * 参数:    左右编码器 'L'跟'R'
  * 返回值:   float 一阶线性滤波值
*/
float Read_Encoder(u8 encno)
{
    int32 tm=0;
    float value;
    if(encno=='R')
    {
        T4T3M &= ~(1<<7); //暂停定时器4
        tm = T4H;
        if(Right_Direction)//编码器2计数A:P06  DIR:P07
            tm=0-((tm<<8)|T4L);
        else
            tm=(tm<<8)|T4L;
        T4H = 0;
        T4L = 0;
        T4T3M |= (1<<7); //启动定时器4

        value = First_Order_Filtering(tm,A_R);
    }
    else if(encno=='L')
    {
        T4T3M &= ~(1<<3); //暂停定时器3
        tm = T3H;
        if(Left_Direction)//编码器2计数A:P04  DIR:P05
            tm=0-((tm<<8)|T3L);
        else
            tm=(tm<<8)|T3L;
        T3H = 0;
        T3L = 0;
        T4T3M |= (1<<3);//启动定时器3

        value = First_Order_Filtering(tm,A_L);
    }
    return value;
}

/**
  * 函数说明: 一阶滤波 本次滤波值 = a*本次采样值 + (1-a)*上次的滤波输出值
  * 参数:    编码器测得值
  * 返回值:   滤波后的值
*/
float First_Order_Filtering(int32 value, float A)
{
    float out_value;
    out_value = A*(float)value - (1-A)*last_Value;
    last_Value = out_value;
    return out_value;
}