程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)

STM32F103霍尔编码器测速

一、编码器介绍

编码器,是一种用来测量机械旋转或位移的传感器。这种传感器能够测量机械部件在旋转或直线运动时的位移位置或速度等信息, 并将其转换成一系列电信号。

编码器是工业中常用的传感器之一,广泛应用于工业生产当中需要对机械系统进行监视或控制的场景, 包括工业控制、机器人、照相机镜头、雷达平台以及部分计算机输入设备例如轨迹球和鼠标滚轮等等。

编码器可以根据不同的方式分出很多种类型。例如:

  • 根据检测原理,可分为光电编码器和霍尔编码器;
  • 根据内部机械结构的运动方式,可分为线性编码器和旋转编码器;
  • 根据其刻度实现方法及信号输出形式, 又可分为增量式、绝对式以及混合式三种。

线性编码器通常用于测量直线运动,而旋转编码器常被用来测量旋转运动。因此不难看出,电机转速测量应该使用的是旋转编码器。旋转编码器的原理示意图如下图所示;

1.1 按检测原理划分

1.1.1 光电编码器

光电编码器是一种通过光电转换将输出轴上的机械几何位移量转换成脉冲或数字量的传感器,光电编码器内部大都由码盘、光电检测装置和信号处理电路等部分组成。

光电码盘上刻了若干圈线槽, 线槽等距并且可透光,光电码盘与电动机同轴,电动机旋转时,光电码盘就会周期性的透过和遮挡来自光电检测装置的光线,这样检测装置就会周期性的生成若干电信号。 但是这些电信号通常比较微弱,需要加入一套处理电路对信号进行放大和整形,最后把信号整形为脉冲信号并向外输出。

1.1.2 霍尔编码器

霍尔编码器是一种通过磁电转换将输出轴上的机械几何位移量转换成脉冲或数字量的传感器,由霍尔码盘和霍尔元件等部分组成。

霍尔码盘是在一定直径的圆板上等分地布置有不同的磁极。霍尔码盘与电动机同轴,电动机旋转时,霍尔元件检测输出若干脉冲信号,为判断转向,一般输出两组存在一定相位差的方波信号。

1.2 按信号输出形式划分

1.2.1 增量式编码器

增量式旋转编码器是将设备运动时的位移信息变成连续的脉冲信号,脉冲个数表示位移量的大小。

只有当设备运动的时候增量式编码器才会输出信号。 编码器一般会把这些信号分为通道A和通道B两组输出,并且这两组信号间有90°的相位差。同时采集这两组信号就可以知道设备的运动和方向。

除了通道A、通道B以外,很多增量式编码器还会设置一个额外的通道Z输出信号,用来表示编码器特定的参考位置,传感器转一圈Z轴信号才会输出一个脉冲。 增量式编码器只输出设备的位置变化和运动方向,不会输出设备的绝对位置。

1.2.1.1 光电增量式编码器

增量式编码器都有AB两通道信号输出,这是因为增量式编码器的码盘上有两圈线槽, 两圈线槽的之间会错开一定的角度,这个角度会使得光电检测装置输出的两相信号相差1/4 周期(90°)。码盘的具体工作方式如下图所示;

增量式编码器码盘运作方式1 增量式编码器码盘运作方式2 增量式编码器码盘运作方式3

图中黑色代表透光,白色代表遮光。当码盘转动时,内圈和外圈的线槽会依次透过光线,光电检测装置检测到光线通断的变化, 就会相应的输出脉冲信号,因为内外圈遮光和透光时候存在时间差,所以也就有了AB两通道信号的相位差。

这里我们将设A相为外圈,B相为内圈,码盘转动循环输出如下信号:11 -> 01 -> 00 - > 10

根据两相信号变化的先后顺序就可以判断运动方向,记录输出的脉冲个数可以知道位移量的大小,同时通过输出信号的频率就能得到速度;

img

通过判断B相处于上升沿时A相的电平状态,我们就可以知道码盘旋转的方向了;

  • 当码盘正转时,在B相的上升沿,A相恒为高电平;
  • 当码盘反转时,在B相的上升沿,A相恒为低电平。

注意:

  • 正/反转是相对而言的,重点在于区分不同旋转方向时的波形特征;
  • 通过观察A相上升沿时B相的电平亦能判旋转方向。
1.2.1.2 霍尔增量式编码器

霍尔增量式编码器在结构上和光电式几乎相同,只不过检测原理变成了霍尔效应。 内部元件也稍有不同,霍尔编码器的码盘上不是线槽,而是不同的磁极,或者有些直接把电机的旋转磁场当作码盘, 然后检测装置换成了霍尔传感器。输出和光电式相同,仍然是相位差1/4周期的AB两通道信号。

增量式编码器计数起点任意设定,可实现多圈无限累加和测量。需要提高分辨率时,可触发AB两通道信号的上升沿和下降沿对原脉冲数进行倍频。 但是当接收设备停机重启后,增量式编码器需要重新寻找参考零点。

1.2.2 绝对式编码器

绝对式旋转编码器是将设备运动时的位移信息通过二进制编码的方式变成数字量直接输出。 这种编码器与增量式编码器的区别主要在内部的码盘。

绝对式编码器的码盘利用若干透光和不透光的线槽组成一套二进制编码, 这些二进制码与编码器转轴的每一个不同角度是唯一对应的,读取这些二进制码就能知道设备的绝对位置,所以叫它绝对式编码器。 绝对式编码器一般常用自然二进制、格雷码或者BCD码等编码方式。

1.2.3 混合式绝对式编码器

混合式绝对式编码器,它输出两组信息:一组信息用于检测磁极位置,带有绝对信息功能;另一组则和增量式编码器的输出信息完全相同。

1.3 编码器基本参数

编码器基本参数:

  • 分辨率:指编码器能够分辨的最小单位;
    • 对于增量式编码器,其分辨率表示为编码器转轴旋转一圈所产生的脉冲数, 即脉冲数/转(Pulse Per RevolutionPPR);
    • 对于绝对式编码器,内部码盘所用的位数就是它的分辨率,单位是位(bit),具体还分单圈分辨率和多圈分辨率;
  • 精度:首先明确一点,精度与分辨率是两个不同的概念。精度是指编码器每个读数与转轴实际位置间的最大误差,通常用角度、角分或角秒来表示。 例如有些绝对式编码器参数表里会写±20′′,这个就表示编码器输出的读数与转轴实际位置之间存在正负20角秒的误差,精度由码盘刻线加工精度、 转轴同心度、材料的温度特性、电路的响应时间等各方面因素共同决定;
  • 最大响应频率:指编码器每秒输出的脉冲数,单位是Hz。计算公式:最大响应频率 = 分辨率 * 轴转速/60。;
  • 信号输出形式:增量式、绝对式以及混合式;
    • 对于增量式编码器,每个通道的信号独立输出,输出电路形式通常有集电极开路输出、推挽输出、差分输出等;
    • 对于绝对式编码器,由于是直接输出几十位的二进制数,为了确保传输速率和信号质量,一般采用串行输出或总线型输出,也有一部分是并行输出,输出电路形式与增量式编码器相同。

二、霍尔编码器测速

2.1 霍尔编码器

这里我在淘宝购买的霍尔编码器为:霍尔磁性编码器3系5系7系8系直流电机码盘马达测速度测方向传感器

具体型号信息:775/795/895霍尔编码器(整套)。

img
2.1.1 接口定义

输出端子定义,从PCB板正面从左到右依次为123456

序号 颜色 引脚 功能
1 白色 OutB 编码器B相输出
2 黄色 OutA 编码器A相输出
3 蓝色 VCC 编码器电源正(5V)
4 绿色 GND 编码器电源负(地)
5 黑丝 M2 电机电源2(和M1(红线)对调电机可正反转)
6 红色 M1 电机电源1(和M1(红线)对调电机可正反转)
2.1.2 电机安装

电机上安装编码器要求电机必须具有双出轴,这里我们采用标准安装方式;

注意编码器磁栅极在安装的时候需要和线路板之间有点间距,不要贴的太近。

2.1.3 转速计算

假如编码器码盘旋转一周A/B相输出的脉冲数目为N,在时间T内统计到的有效脉冲数目为S(正转脉冲数+ 1,反转脉冲数- 1),那么转速为:

\[n = \frac{S}{NT}*60 \]

使用的编码器码盘旋转一周输出的脉冲个数为16,即分辨率为\(16PPR\),假如1s内统计得到的有效脉冲数目为 500,代入上式:

\[n = \frac{500}{16}*60 ≈ 1875rpm \]

2.1.4 测试

这里我们使用逻辑分析器去测量霍尔传感器AB相的输出,接线如下:

  • 通道1连接A相;
  • 通道2连接B相;
  • 逻辑分析器的地连接编码器电源负(地),注意这个必须连接;

这里我们给电机通电,通过条件PWM占空比调节电机转速,当占空比为13%时查看霍尔编码器AB相波形如下:

可以看到此时波形的频率为284Hz,即1s内统计得到的有效脉冲数目为 284,此时计算转速:

\[n = \frac{284}{16}*60 ≈ 1065rpm \]

2.2 STM32编码器测速原理

在带霍尔传感器的直流电机的条件下,电机转动一圈,通过霍尔传感器的AB两相输出一定数量的脉冲,我们可以根据一定时间内的脉冲数计算出电机的瞬时转速。

记脉冲数有两种方式:

  • 我们通过GPIO引脚的外部中断来检测边沿变化,以此来检测脉冲数,但是会有毛刺,也就是错误的脉冲信号;
  • 我们以STM32芯片作为主控,利用STM32的定时器外设的输入捕获功能,配置相关输入通道为编码器接口模式,就可以进行脉冲数的计数。

在第二种方式中,错误的脉冲信号会被输入滤波器过滤掉,所以更推荐也更常用。

2.2.1 输入捕获模式

我们在《STM32F103 捕获配置》介绍了输入捕获的原理;我们利用STM32的输入捕获功能,可以测量外部信号的频率、周期或脉冲宽度。

其工作原理:

  • 定时器在输入捕获模式下,会捕获外部信号的边沿(上升沿、下降沿或两者);
  • 当捕获事件发生时,定时器的当前计数值(TIMx_CNT)会被记录到捕获寄存器(TIMx_CCR1)中;
  • 通过比较两次捕获事件的计数值,可以计算出信号的周期或脉冲宽度;(当然了,如果脉冲持续时间较长,可能会触发更新(溢出)中断买这种就需要特殊处理)

在这种工作模式下:

  • 定时器的计数器(TIMx_CNT)通常以固定的频率(计数频率是由通预分频寄存器TIMx_PSC)控制的递增或递减;
  • 捕获事件发生时,计数器的值会被保存到捕获寄存器(TIMx_CCRx),但计数器本身不会停止或重置;
2.2.2 编码器模式

STM32芯片内部有专门用来采集增量式编码器方波信号的接口,这些接口实际上是STM32定时器的其中一种功能。 不过编码器接口功能只有高级定时器TIM1TIM8和通用定时器TIM2~TIM5才有,编码器接口用到了定时器的输入捕获部分。

其工作原理:

  • 定时器在编码器模式下,会同时捕获两个相位差为90°的信号(即A 相和B相);
  • 根据A相和B相的边沿变化,定时器会自动判断旋转方向,并递增或递减计数器;
  • 每个脉冲的上升沿和下降沿都会触发计数。

在这种工作模式下:

  • 定时器的计数器(TIMx_CNT)会根据编码器的旋转方向自动递增或递减;
  • 计数器的值直接反映了编码器的位置或脉冲数;

这里我们来介绍一下STM32的编码器模式,这里我们需要结合定时器框图来分析;

假如我们将:

  • 编码器A相输出信号连接到定时器通道1,上图TIMx_CH1
  • 编码器B相输出信号连接到定时器通道2,上图TIMx_CH2

如果要实现编码器工作模式,我们需要需要进行如下配置;

(1) TIMx时钟使能:通过配置RCC_APB1ENR/RCC_APB2ENR寄存器使能TIMx时钟;

(2)GPIO口配置:以TIM4_CH1PB6)、TIM4_CH2PB7)为例;

  • 配置PB6PB7为下拉输入;
  • GPIOB时钟使能;

(3) 配置TIMx时基单元;

  • 配置TIMx_ARR寄存器自动重装载的值(0xFFFF);
  • 配置TIMx_PSC寄存器预分频系数(0);

(4) 配置捕获/比较模式寄存器寄存器TIMx_CCMRx

  • 输入捕获预滤波器(ICxF):配置采样频率和数字滤波器长度,用于消除输入信号的抖动;

  • 输入捕获/比较输出选择(CCxS):设置输入捕获映射关系:

    • TIMx_CH1捕捉到的信号可以连接到IC1

    • TIMx_CH2捕捉到的信号可以连接到IC2

II1TI2经过输入滤波器和边沿检测器产生TI1FP1TI2FP2

(5) 配置从模式控制寄存器TIMx_SMCR

  • 配置从模式选择(SMS):配置为编码器模式3,根据另一个信号的输入电平,计数器在TI1FP1TI2FP2的边沿向上/向下计数;

这个时候TI1FP1TI2FP2信号作为预分频器的输入时钟,因此定时器计数器的值就可以看做是输入信号脉冲的个数。

下表是定时器计数器计数方向与编码器信号的关系:

通常为了提高精度我们会选择在上升沿和下降沿都进行计数,那么对应在一个周期就可以计数四次,计数次数的增加也就意味着精度的提高!

我们将以1-8时刻为例,结合之前的表格对编码器工作原理进行剖析:

时刻 TI1和TI2动作 计数器动作
1 TI2低电平,TI1FP1上升 向上计数
2 TI1高电平,TI2FP2上升 向上计数
3 TI2高电平,TI1FP1下降 向上计数
4 TI1低电平,TI2FP2下降 向上计数
5 TI1低电平,TI2FP2上升 向下计数
6 TI2高电平,TI1FP1上升 向下计数
7 TI1高电平,TI2FP2下降 向下计数
8 TI2低电平,TI1FP1下降 向下计数

(6) 允许TIMx工作:配置TIMx_CR101

2. 3 软件实现

本节我们将定时器TIM4来测量编码器的转速,对于TIM4PB6PB7引脚分别复用为TIM4_CH1TIM4_CH2

硬件连接:

接线的时候有一点需要注意:LN298驱动板、开发板和霍尔编码器如果使用是不同电源,那么GND需要共地。

2.3.1 配置定时器为编码器模式

这里创建一个encoder.c文件,编写编码器初始化函数,在该函数中进行编码器模式配置;

static unsigned int g_last_cnt = 0; // 用于保存上一次的计数器值

unsigned int g_motor_speed = 0; // 电机转速

/*****************************************************************************
 *
 *		 Description:   使用定时器4,通道1和通道2进行编码器测速

********************************************************************************/
void encoder_init(void)
{
  // 1. 使能时钟
  RCC->APB1ENR |= 1 << 2; // 定时器4时钟使能

  // 2. 配置GPIO为浮空输入
  gpio_init(PB6, GPI_UP, HIGH); // 浮空输入
  PBout(6) = 0;

  gpio_init(PB7, GPI_FLOAT, HIGH); // 浮空输入
  PBout(7) = 0;

  // 3. 定时器基础配置
  TIM4->ARR = 0xFFFF; // 设定计数器自动重装值
  TIM4->PSC = 0x0000; // 预分频器不分频

  // 4. 输入通道配置
  TIM4->CCMR1 &= ~(0x03);      // 清空位[1:0],CC1S=0
  TIM4->CCMR1 |= 0x01;         // CC1S=1 CC1配置为输入 IC1映射到TI1上
  TIM4->CCMR1 &= ~(0x0f << 4); // 清空位[7:4],IC1F=0
  TIM4->CCMR1 |= 0x03 << 4;    // IC1F=3 配置滤波器以FCKint采样,8个事件有效(消除抖动)
  TIM4->CCMR1 &= ~(0x03 << 2); // IC1PS=0  不分频
  TIM4->CCER &= ~(1 << 1);     // 清空位[1],CC1P上升沿捕获  通道1

  TIM4->CCMR1 &= ~(0x03 << 8);  // 清空位[9:8],CC2S=0
  TIM4->CCMR1 |= 0x01 << 8;     // CC2S=1 CC2配置为输入 IC2映射到TI2上
  TIM4->CCMR1 &= ~(0x0f << 12); // 清空位[15:12],IC2F=3
  TIM4->CCMR1 |= 0x03 << 12;    // IC2F=3 配置滤波器以FCKint采样,8个事件有效(消除抖动)
  TIM4->CCMR1 &= ~(0x03 << 10); // IC2PS=0  不分频
  TIM4->CCER &= ~(1 << 5);      // 清空位[5],CC2P上升沿捕获  通道2

  // 5. 从编码器模式改为外部时钟模式,单通道测速CH1
  // TIM4->SMCR &= ~(7 << 0); // SMS清零
  // TIM4->SMCR |= 7 << 0;    // SMS=111 (外部时钟模式)
  // TIM4->SMCR &= ~(7 << 4); // TS清零
  // TIM4->SMCR |= 5 << 4;    // TS=101 (外部时钟触发源设置为TI1FP1)

  // 5. 编码器模式设置,双通道测速CH1、CH2
  TIM4->SMCR &= ~(0x07 << 0); // 清空位[2:0],SMS=0
  TIM4->SMCR |= (0x03 << 0);  // SMS = 011 (编码器模式 3,捕获TI1和TI2的边沿,即计数器同时在TI1和TI2边沿计数)

  TIM4->CNT = 0; // 复位计数值

  TIM4->CR1 |= 1 << 0; // 使能计数器 开始计数

  g_last_cnt = TIM4->CNT; // 初始化上一次计数值
}
2.3.2 计算转速

我们统计sampling_period周期开始和结束时定时器计数器的值,然后通过作差等运算计算出电机转速;

/**************************************************************************************************************
 *
 *		 Description:   获取编码器速度
 *       Parameter  :  sampling_period:采样周期,单位ms
 *       Return     :  转速,正负值表示正反转
 *
 **************************************************************************************************************/
int get_encoder_speed(unsigned int sampling_period)
{
  unsigned int resolution = 16;         // 编码器分辨率(每转的脉冲数)
  int current_cnt = TIM4->CNT;          // 获取当前计数值
  int delta = current_cnt - g_last_cnt; // 计算两次采样的差值
  float speed;
  g_last_cnt = current_cnt; // 这里除去4是因为在A、B相的上升沿、下降沿各计数了一次
  delta = delta / 4;

  // 计算转速(单位:转/分钟)
  speed = (delta / (float)resolution) * 1000 / sampling_period * 60.0f; // 转速公式
  return (int)speed;
}

如果转速值为正数表明电机正转,如果转速为负数表明电机反转。

2.3.3 主函数

在主函数主要实现以下几个功能:

  • 我们通过按键KEY0KEY1调节PWM占空比来控制电机的转速;
  • 进行编码器的初始化;
  • 开启定时中断,这里使用的定时器6,定时周期为1s,在定时器溢出中断中计算平均转速;

具体代码如下:

#include "common.h"
#include "stdio.h"

int main()
{
    int duty;
    int speed;
    STM32_Clock_Init(9); // 系统时钟初始化

    // 串口初始化
    STM32_NVIC_Init(2, USART1_IRQn, 0, 1); // 串口中断优先级初始化,其中包括中断使能
    usart_init(USART_1, 115200);           // 串口1初始化,波特率115200 映射到PA9 PA10

    // 按键KEY初始化
    gpio_init(PC5, GPI_UP, HIGH);             // PC5接按键KEY0
    gpio_init(PA15, GPI_UP, HIGH);            // PA15接按键KEY1
    Ex_NVIC_Congig(PC5, FALLING);             // 按键KEY0按下触发 高电平->低电平
    Ex_NVIC_Congig(PA15, FALLING);            // 按键KEY1按下触发 高电平->低电平
    STM32_NVIC_Init(2, EXTI9_5_IRQn, 2, 2);   // EXTI线[9:5]中断优先级初始化,其中包括中断使能
    STM32_NVIC_Init(2, EXTI15_10_IRQn, 2, 2); // EXTI线[15:10]中断优先级初始化,其中包括中断使能

    motor_init(); // 电机初始化,使用的定时器2,PA0/PA1

    encoder_init(); // 编码器初始化,使用的定时器4,PB6/PB7

    STM32_NVIC_Init(2, TIM6_IRQn, 0, 0); // TIM6溢出中端使能
    TIM_Init_MS(TIMER6, 1000);           // TIM6计数到1000ms发生中断,在TIM6_IRQHandler中获取转速,并保存到全局变量g_motor_speed
    while (1)
    {
        duty = get_motor_duty();
        printf("duty: %d\n", duty);
        delay_ms(1000);
        printf("Speed: %d RPM\n", g_motor_speed); // 打印转速
    }
}

/*******************************************************************************
 * Function Name  : TIM6_IRQHandler
 * Description    : This function handles TIM6 global interrupt request.
 * Input          : None
 * Output         : None
 * Return         : None
 *******************************************************************************/
void TIM6_IRQHandler(void)
{
	//**********************自定义用户任务****************************//
	g_motor_speed = get_encoder_speed(1000);

	//*****************************************************************//
	TIM6->SR &= ~(1 << 0); // 清中断标志
}
2.3.4 测试

编译程序并下载测试,我们通过按下KEY0/KEY1调节占空比,可以通过串口查看当前输出的占空比以及对应的转速;

开发板、电机、L298N驱动板接线如下:

在占空比为13%时,可以看到这里输出的转速\(1207rpm\)比上面使用逻辑分析器计算\(1065rpm\)的稍微要大一些,这主要是因为我调节了一下编码器电路板和电机之间的紧密程度,导致摩擦阻力略有下降造成的转速上升。

此外,我也测试了在不同占空比下的转速:

占空比(%) 转速(rpm)
10 397
20 2553
30 4470
40 6290
50 7980
60 9990

三、源码下载

源码下载路径:stm32f103

参考文章

[1] STM32Cube HAL库——霍尔编码器测速(电机转速测量)

[2] 编码器详解

[3] 编码器分类及原理和测速应用(含代码)

[4] 增量式编码器详解

[5] STM32电机测速(正交或者霍尔编码器)

[6] STM32开发之寄存器版】(八)-定时器的编码器接口模式

[7] STM32定时器配置为编码器模式

[8] 4. 编码器的使用

posted @ 2025-02-10 23:36  大奥特曼打小怪兽  阅读(456)  评论(0)    收藏  举报
如果有任何技术小问题,欢迎大家交流沟通,共同进步