stm32103ZET6使用编码器(磁电增量式)

  • Post author:
  • Post category:其他


正点原子的电机例程(原例程用的是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来获取一次电机的转速值,又因为用了一阶低通滤波(因为电子产品本身就会存在误差,所以用这个算法)。



版权声明:本文为m0_57678852原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。