这里写目录标题
- 磁电增量式编码器介绍
- TIM定时器(编码器接口模式)
- 一些用到的算法
- 均值滤波
- 冒泡排序(从小到大)
- 一阶低通滤波
 
- 编码器测数代码
- 编码器接口HAL库函数
 
正点原子的电机例程(原例程用的是stm32f407,我这里改成用stm32f103zet6)
磁电增量式编码器介绍
 编码器基本参数:
编码器基本参数:
 ① 分辨率:编码器每个计数单位之间产生的距离,它是编码器可以测量到的最小的距离。对于增量式编码器,分辨率表示为编码器的转轴每旋转一圈所输出的脉冲数(PPR),也称为多少线,直流有刷电机教程中所使用的编码器是 11 线的。
 ② 精度:编码器分辨率和精度是两个独立的概念,精度是指编码器输出的信号数据与
 实际位置之间的误差,常用角分′、角秒″表示。
 ③ 最大响应频率:编码器每秒能输出的最大脉冲数,单位 Hz,也称为 PPS。
 ④ 最大转速:指编码器机械系统所能承受的最高转速。
TIM定时器(编码器接口模式)

 可以看到
 当编码器正转时,A波形(高电平)在前,B波形在后
 当编码器反转时,A波形(高电平)在后,B波形在前
根据下图判断计数方向与编码器信号的关系
 下图有三种模式
 1.仅在TI1计数(2倍频)
 2.仅在TI2计数(2倍频)
 3.在TI1和TI2上计数(4倍频)
注意:1、选择仅在 TI1 或者 TI2 处计数,就相当于对脉冲信号进行了 2 倍频(两个边沿),
 此时如果编码器输出 10 个脉冲信号,那么就会计数 20 次。2、选择的是在 TI1 和 TI2 处均计
 数,就相当于对脉冲信号进行了 4 倍频,此时如果编码器输出 10 个脉冲信号,那么就会计数
 40 次。因此,我们通过计数次数来计算电机速度的时候,需要除以相应的倍频系数。
结合上图,可以看出:(此文章只研究1和2模式)
 当编码器正转时,不论是在模式1或者模式2,计数值都是在增加
 当编码器反转时,不论是在模式1或者模式2,计数值都是在减小

 接下来我们就可以通过
 一分钟内计数的变化量来计算电机的速度,具体公式如下:
电机转速 = 一分钟内计数变化量 / 倍频系数 / 编码器线数 / 减速比
这里我用的电机的参数如下:
 
一些用到的算法
均值滤波
例子:
 十次平均值
 uint long average_value()
 {
 for(i=0;i<=9;i++)
 {
 value += getvalue();
 }
 value /=10;
 }
冒泡排序(从小到大)
例子:
 if(k == 10)
 {
 for(i=10;i<=1;i–)
 {
 for(j=0;j<i-1;j++) 
 {
 if(speed_arr[j]>speed_arr[j+1]) /* 数值比较 /
 {
 temp = speed_arr[j]; / 数值换位 */
 speed_arr[j] = speed_arr[j+1] ;
 speed_arr[j+1] = temp;
 } 
 } 
 }
 }
一阶低通滤波
- 公式为:Y(n)= qX(n) + (1-q)Y(n-1),其中
- X(n)为本次采样值;
- Y(n-1)为上次滤波输出值;
- Y(n)为本次滤波输值,
- q为滤波系数,
- q值越小则上一次输出对本次输出影响越大,整体曲线越平稳,但是对于速度变化的响应也会越慢
- 例子:
long Res_value;//先定义一个全局变量,用来存放结果
 float Lv_Bo=00001;//定义一个滤波系数
 long get_shuzhi()//返回一个64位的变量
 {
 float last_value;
 float current_value;
last_value       =  Res_value;//记录上一次的值
current_value =  getvalue();//获取当前值
if(current_value !=0)//读到正确值
{
	Res_value = last_value*Lv_Bo + (1-Lv_Bo)*current_value;
}
return Res_value ;
}

 结论:
 q越大,响应越快,但曲线不平滑。
 q越小,响应越慢,但曲线更平滑。
编码器测数代码
编码器接口HAL库函数

 代码:
 bsp_motor.c(测速代码)
/*************************************    第三部分    编码器测速    ****************************************************/
Motor_TypeDef g_motor_data;  /*电机参数变量*/
ENCODE_TypeDef g_encode;     /*编码器参数变量*/
/**
 * @brief       电机速度计算
 * @param       encode_now:当前编码器总的计数值
 *              ms:计算速度的间隔,中断1ms进入一次,例如ms = 5即5ms计算一次速度
 * @retval      无
 */
void speed_computer(int32_t encode_now, uint8_t ms)
{
    uint8_t i = 0, j = 0;
    float temp = 0.0;
    static uint8_t sp_count = 0, k = 0; //ms(参数)ms计算一次,进行k要等于的次数的均值滤波(看231行),这个例程50ms计算一次,进行10次均值滤波
    static float speed_arr[10] = {0.0};                     /* 存储速度进行滤波运算 */
    if (sp_count == ms)                                     /* 计算一次速度 */
    {
        /* 计算电机转速 
           第一步 :计算ms毫秒内计数变化量
           第二步 ;计算1min内计数变化量:g_encode.speed * ((1000 / ms) * 60 ,
           第三步 :除以编码器旋转一圈的计数次数(倍频倍数 * 编码器分辨率)
           第四步 :除以减速比即可得出电机转速
        */
        g_encode.encode_now = encode_now;                                /* 取出编码器当前计数值 */
        g_encode.speed = (g_encode.encode_now - g_encode.encode_old);    /* 计算编码器计数值的变化量 */
        
        speed_arr[k++] = (float)(g_encode.speed * ((1000 / ms) * 60.0) / REDUCTION_RATIO / ROTO_RATIO );    /* 保存电机转速 */
        
        g_encode.encode_old = g_encode.encode_now;          /* 保存当前编码器的值 */
        /* 累计10次速度值,后续进行滤波*/
        if (k == 10)
        {
            for (i = 10; i >= 1; i--)                       /* 冒泡排序*/
            {
                for (j = 0; j < (i - 1); j++) 
                {
                    if (speed_arr[j] > speed_arr[j + 1])    /* 数值比较 */
                    { 
                        temp = speed_arr[j];                /* 数值换位 */
                        speed_arr[j] = speed_arr[j + 1];
                        speed_arr[j + 1] = temp;
                    }
                }
            }
            
            temp = 0.0;
            
            for (i = 2; i < 8; i++)                         /* 去除两边高低数据 */
            {
                temp += speed_arr[i];                       /* 将中间数值累加 */
            }
            
            temp = (float)(temp / 6);                       /*求速度平均值*/
            
            /* 一阶低通滤波
             * 公式为:Y(n)= qX(n) + (1-q)Y(n-1)
             * 其中X(n)为本次采样值;Y(n-1)为上次滤波输出值;Y(n)为本次滤波输出值,q为滤波系数
             * q值越小则上一次输出对本次输出影响越大,整体曲线越平稳,但是对于速度变化的响应也会越慢
             */
            g_motor_data.speed = (float)( ((float)0.48 * temp) + (g_motor_data.speed * (float)0.52) );
//						g_motor_data.speed = temp;
            k = 0;
        }
        sp_count = 0;
    }
    sp_count ++;
}
bsp_tim.h
/******************************** 3 公用部分 编码器程序 ************************************/
volatile int g_timx_encode_count = 0;                                   /* 溢出次数 */
int Encode_now = 0;
/**
 * @brief       定时器更新中断回调函数
 * @param        htim:定时器句柄指针
 * @note        此函数会被定时器中断函数共同调用的
 * @retval      无
 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM4)
    {
        if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&htim4))   /* 判断CR1的DIR位 */
        {
            g_timx_encode_count--;                                      /* DIR位为1,也就是递减计数 */
        }
        else
        {
            g_timx_encode_count++;                                      /* DIR位为0,也就是递增计数 */
        }
    }
    else if (htim->Instance == TIM6)
    {
         Encode_now = gtim_get_encode();                             /* 获取编码器值,用于计算速度 */
        speed_computer(Encode_now, 50);                                 /* 中位平均值滤除编码器抖动数据,50ms计算一次速度*/
    }
}
/**
 * @brief       获取编码器的值(当前编码器积累的值)
 * @param       无
 * @retval      编码器值
 */
int gtim_get_encode(void)
{
    return ( int32_t )__HAL_TIM_GET_COUNTER(&htim4) + g_timx_encode_count * 65536;       /* 当前计数值+之前累计编码器的值=总的编码器值 */
}
初始化的函数我就不放出来了,把我测速部分用到的HAL库的配置部分给大家看,
 首先时钟树配置
 
定时器4来配置定时器编码器模式,用来计数编码器的脉冲数。
 
 Encoder Mode 模式为Encoder Mode T1 and T12相当于4分频。
 
开启更新中断。
定时器6配置成基础定时器更新中断,中断时间为1ms.
 计算公式:(999+1)*(71+1)/72M = 1ms
 

 开启定时器6的中断
这里来介绍下此例程的流程:
 首先初始化定时器4(编码器模式)和定时器6(基础更新中断),然后在初始化函数里开启定时器运行与开启中断。
 每1ms发送一次中断(由定时器6产生),定时器4的中断回调函数里执行判断电机正转还是反转,正转就++,反转就–,50ms来计算一次电机的转速,然后
 进行10次滤波,所以一共500ms来获取一次电机的转速值,又因为用了一阶低通滤波(因为电子产品本身就会存在误差,所以用这个算法)。



















