一、前言
1.1 编码器接口原理
编码器模式主要用于检测旋转编码器的转动方向和转动速度。旋转编码器一般输出两路相位相差90度的脉冲信号(称为A相和B相),通过这两路信号,定时器可以判断编码器的旋转方向,并计数转动的脉冲数。
1.2 本次DEMO目标
本次DEMO将使用STM32F103ZET6的PA6(TIM3_CH1)和PA7(TIM3_CH2)的编码器模式,测量电机AB相编码器的正转、反转以及速度信息。并通过串口1打印在上位机XCOM上。
1.3 实验环境介绍
除了之前几篇文章一直使用到的正点原子STM32F103ZET6精英板,本文还是用到了MG513P30_12V直流减速电机。如下图所示:
MG513P30_12V型电机的参数如下表所示:
额定电压(V) | 额定电流(A) | 堵转电流(A) | 减速后空载转速(rpm) | 减速后额定转速(rpm) | 额定扭矩(kg.cm) | 堵转扭矩(kg.cm) | 功率(W) |
---|---|---|---|---|---|---|---|
12 | 0.36 | 3.2 | 366±26 | 293±21 | 1 | 4.5 | 4 |
本文使用的MG513P30_12V型电机自带霍尔编码器,其参数如下所示:
型号 | 编码器线数 | 类型 | 供电范围 |
---|---|---|---|
霍尔编码器 | 13ppr | 磁感应 | 3.3-5V |
本次DEMO的硬件接线方式如下:
开发板 | 带编码器的电机 |
---|---|
5V | 5V |
GND | GND |
PA6 | A相 |
PA7 | B相 |
二、STM32处理AB相编码器原理
选择编码器接口模式的方法是:如果计数器只在TI2的边沿计数,则置TIMx_SMCR寄存器中的SMS=001;如果只在TI1边沿计数,则置SMS=010;如果计数器同时在TI1和TI2边沿计数,则置SMS=011。本文选择计数器同时在TI1和TI2边沿计数。
通过设置TIMx_CCER寄存器中的CC1P和CC2P位,可以选择TI1和TI2极性;如果需要,还可以对输入滤波器编程。
两个输入TI1和TI2被用来作为增量编码器的接口。假定计数器已经启动(TIMx_CR1寄存器中的CEN=’1’),计数器由每次在TI1FP1或TI2FP2上的有效跳变驱动。TI1FP1和TI2FP2是TI1和TI2在通过输入滤波器和极性控制后的信号;如果没有滤波和变相,则TI1FP1=TI1,TI2FP2=TI2。根据两个输入信号的跳变顺序,产生了计数脉冲和方向信号。依据两个输入信号的跳变顺序,计数器向上或向下计数,同时硬件对TIMx_CR1寄存器的DIR位进行相应的设置。不管计数器是依靠TI1计数、依靠TI2计数或者同时依靠TI1和TI2计数。在任一输入端(TI1或者TI2)的跳变都会重新计算DIR位。本文将TIM3_CH1映射到TI1,将TIM3_CH2映射到TI2。
编码器接口模式基本上相当于使用了一个带有方向选择的外部时钟。这意味着计数器只在0到TIMx_ARR寄存器的自动装载值之间连续计数(根据方向,或是0到ARR计数,或是ARR到0计数)。所以在开始计数之前必须配置TIMx_ARR;同样,捕获器、比较器、预分频器、触发输出特性等仍工作如常。
在这个模式下,计数器依照增量编码器的速度和方向被自动的修改,因此计数器的内容始终指示着编码器的位置。计数方向与相连的传感器旋转的方向对应。下表列出了所有可能的组合,假设TI1和TI2不同时变换。
由于本文选择在TI1和TI2上计数,故仅需关注上表的最后两行。下图是一个计数器操作的实例,显示了计数信号的产生和方向控制。它还显示了当选择了双边沿时,输入抖动是如何被抑制的;抖动可能会在传感器的位置靠近一个转换点时产生。在这个例子中,我们假定配置如下:
● CC1S=’01’ (TIMx_CCMR1寄存器,IC1FP1映射到TI1)
● CC2S=’01’ (TIMx_CCMR2寄存器,IC2FP2映射到TI2)
● CC1P=’0’ (TIMx_CCER寄存器,IC1FP1不反相,IC1FP1=TI1)
● CC2P=’0’ (TIMx_CCER寄存器,IC2FP2不反相,IC2FP2=TI2)
● SMS=’011’ (TIMx_SMCR寄存器,所有的输入均在上升沿和下降沿有效).
● CEN=’1’ (TIMx_CR1寄存器,计数器使能)
我们将以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下降 | 向下计数 |
三、时钟树分析
对于时钟树的分析与【STM32开发之寄存器版】(六)-通用定时器中断 相同,供给TIM3的时钟TIM3CLK=72MHz,具体的时钟树配置如下图所示:
四、寄存器介绍
为实现定时器的编码器接口模式,需要控制以下几组寄存器:
寄存器 | 作用 |
---|---|
RCC_APB1ENR | APB1外设时钟使能寄存器 |
RCC_APB2ENR | APB2外设时钟使能寄存器 |
GPIOx_CRL | 端口配置低寄存器 |
TIMx_ARR | 自动重装载寄存器 |
TIMx_PSC | 预分频器 |
TIMx_CCMR1 | 捕获/比较模式寄存器1 |
TIMx_CCER | 捕获/比较使能寄存器 |
TIMx_SMCR | 从模式控制寄存器 |
TIMx_CNT | 计数器 |
TIMx_CR1 | 控制寄存器1 |
下面将对这些寄存器进行一一介绍。
4.1 APB1/2 外设时钟使能寄存器
对该寄存器的描述详见【STM32开发之寄存器版】(二)-USART,需要对RCC_APB1ENR的第1位置1,使能定时器3时钟;对RCC_APB2ENR的第2位置1,使能并口A时钟。
4.2 GPIOx_CRL 端口配置低寄存器
对该寄存器的描述详见【STM32开发之寄存器版】(一)-GPIO,将PA6和PA7设置为浮空输入。
4.3 TIMx_ARR 自动重装载寄存器
对该寄存器的描述详见【STM32开发之寄存器版】(六)-通用定时器中断,本文将其设为0XFFFF。
4.4 TIMx_PSC 预分频器
对该寄存器的描述详见【STM32开发之寄存器版】(六)-通用定时器中断,本文将其设置为0,即不分频。
4.5 TIMx_CCMR1 捕获/比较模式寄存器1
《STM32中文参考手册》对TIMx_CCMR1寄存器的描述如下:
这里我们重点关注IC2F、CC2S、IC1F和CC1S。
IC1F和IC2F分别为输入捕获1和输入捕获2的滤波器,我们在这里设置为0110,即数字滤波器长度为6.
CC1S和CC2S为捕获/比较选择,我们将其设置为01,即CC1和CC2通道被配置为输入,IC1映射在TI1上,IC2映射在TI2上;
4.6 TIMx_CCER 捕获/比较使能寄存器
《STM32中文参考手册》对TIMx_CCER寄存器的描述如下:
这里我们重点关注CC1P和CC2P,这两位设置为0,当我们将CC1和CC2通道配置为输入后,信号不反相。
4.7 TIMx_SMCR 从模式控制寄存器
《STM32中文参考手册》对TIMx_SMCR寄存器的描述如下:
这里我们重点关注SMS从模式选择,我们将其设置为011,即编码器模式3,根据另一个信号的输入电平,计数器在TI1FP1和TI2FP2的边沿向上/下计数。
4.8 TIMx_CNT 计数器
《STM32中文参考手册》对TIMx_CNT寄存器的描述如下:
可以直接从CNT中读取编码器计数值, 也可以通过写的方式清除编码器计数值。
4.9 TIMx_CR1 控制寄存器1
对该寄存器的描述详见【STM32开发之寄存器版】(六)-通用定时器中断,第0位为计数器的使能位,该位必须置1,才能让定时器开始计数。
五、程序设计
本DEMO中的程序主要分为TIM3编码器模式初始化、编码器速度读取以及轮询主函数。完整版代码见【STM32开发之寄存器版】(八-附)-定时器的编码器接口模式
5.1 TIM3编码器模式初始化
本部分代码在HARDWARE/encoder.c/TIM3_Encoder_Init();该函数的作用是按照第四部分的寄存器介绍,将TIM3初始化为编码器模式。具体代码如下所示:
void TIM3_Encoder_Init(void){
RCC->APB1ENR|=1<<1; //TIM3时钟使能
RCC->APB2ENR|=1<<2; //使能PORTA时钟
GPIOA->CRL &= 0X00FFFFFF;//清除PA7和PA6的控制位
GPIOA->CRL |= 0X44000000;//将PA7和PA6设置为浮空输入
TIM3->ARR = 0xFFFF; //设定计数器自动重装值
TIM3->PSC = 0x0000; //预分频器不分频
TIM3->CR1 &= 0XFCFF; //清除TIM3_CR1的[9:8]时钟分频因子
TIM3->CR1 |= 0X0000; //设置时钟分频因子为0,即定时器时钟tCK_INT(72MHz)与数字滤波器采样频率tDTS相等。
TIM3->CCMR1 &= 0XFF0F; //清除TIM3_CCMR1的[7:4]IC1F
TIM3->CCMR1 |= 0X0060; //设置采样频率为fDTS/4,N=6
TIM3->CCMR1 &= 0X0FFF; //清除TIM3_CCMR1的[15:12]IC2F
TIM3->CCMR1 |= 0X6000; //设置采样频率为fDTS/4,N=6
TIM3->CCER &= 0XFFDD; //清除TIM3_CCER的[5]和[1];
TIM3->CCER |= 0X0000; //将CC1和CC2通道设置为输入捕获,不反相。
TIM3->SMCR &= 0XFFF8; //清除TIM3_SMCR的[2:0]SMS从模式选择位
TIM3->SMCR |= 0X0003; //设置为编码器模式3 – 根据另一个信号的输入电平,计数器在TI1FP1和TI2FP2的边沿向上/下计数。
TIM3->CCMR1 &= 0XFCFC; //清除TIM3_CCMR1的[9:8]CC2S和[1:0]CC1S
TIM3->CCMR1 |= 0X0101; //在输入捕获模式下,CC2通道被配置为输入,IC2映射在TI2上;CC1通道被配置为输入,IC1映射在TI1上
TIM3->CNT = 0; //TIM3计数器清零
TIM3->CR1 |= 0x01; //使能定时器3
}
5.2 编码器速度读取
本部分代码在HARDWARE/encoder.c/GetMotorPulse();该函数的作用是读取编码器数值,并对电机旋转方向进行判定,最后将编码器数值清零,具体代码如下所示:
void GetMotorPulse(void)
{
decoderCNT = TIM3->CNT; //获得编码器计数值
if(decoderCNT > 0X7FFF){
decoderCNT = decoderCNT-0XFFFF; //电机反向判定,临界值0X7FFF;
}
TIM3->CNT = 0; //计数器清零
}
5.3 轮询主函数
本部分代码在USER/test.c;该函数的作用是读取并打印编码器速度,具体代码如下所示:
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "encoder.h"
int main(void)
{
Stm32_Clock_Init(9); //系统时钟设置
uart_init(72,115200); //串口初始化为115200
delay_init(72); //延时初始化
TIM3_Encoder_Init(); //初始化编码器
while(1)
{
GetMotorPulse();
printf("电机转速为:%d\r\n",decoderCNT);
delay_ms(20);
}
}
六、上机实验
6.1 编码器原信号测量
将示波器CH1(黄色线)接编码器A相,CH2(绿色线)接编码器B相,电机正转时,编码器信号值如下所示,A相领先于B相若干相位。
电机正转时,编码器信号值如下所示,B相领先于A相若干相位。
当电机速度加快时,信号频率会增大,单位时间内计数器中的值就会越大。
6.2 电机转速测试
对电机进行正转和反转,查看XCOM串口信息,可以看到电机转速值:
至此定时器3编码器模式测试成功!