8、TIM编码器接口

一、知识点

1、Encoder Interface 编码器接口的工作流程

编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度

每个高级定时器和通用定时器都拥有1个编码器接口

两个输入引脚借用了输入捕获的通道1和通道2

img

2、编码器接口举例子讲解

若初始化之后CNT初始值为0,右转产生10个脉冲后停下来,CNT就由0自增到10停下来
再让编码器在左转产生5个脉冲,那CNT就在原来10的基础上自减5停下来
编码器接口:相当于一个带有方向控制的外部时钟,它同时控制着CNT的计数时钟和计数方向,CNT的值就表示了编码器的位置
若每隔一段时间取是CNT的值,再把CNT清零,每次取出来的值就表示了编码器的速度

3、编码器测速(带方向的测速)实质

实质:测频法测正交脉冲的频率

CNT计次,然后每隔一段时间去一次计次,其实是测频法的思路

4、其他知识点

每个高级定时器和通用定时器都拥有1个编码器接口
两个输入引脚借用了输入捕获的通道1和通道2(通道3和通道4不能用)
如果一个定时器配置成了编码机接口模式,则基本干不了其他活,C816芯片只有(TIM1、TIM2、TIM3、TIM4,4个定时器,故最多只能接4个编码器,接完后就没定时器可以用了

5、正交编码器

img

(1)方波的频率代表速度
(2)用正交信号的好处:
a、正交信号精度更高,因为A、B相都可以计次,相当关于计次频率提高了一倍

b、其次正交信号可以以抗噪声,因为正交信号两个信号必须是交替跳变的,可以设计一个抗噪声电路,如果一个信号不变,另一个信号连续跳变,也就是产生了噪声,这时计次值是不会变化的

(3)编码器接口的设计逻辑:
首先把A相和B相的所有边缘,作为计数器的计数时钟,出现边缘信号时就计数自增或自减,增还是减由另一相的高低电平决定

(4)定时器中的编码器

img

编码器接口有使用CH1和CH2的输入捕获滤波器和边沿检测,编码器接口没有使用后面的是否交叉、预分频器、CCR寄存器与编码器接口无关

编码器接口的输出部分:相当于从模式控制器,去控制CNT的计数时钟和计数方向

6、编码器接口基本结构

img

7、工作模式

img

8、实例

(1)TI1和TI2都不反向均T不反相

img

(2)TI1反相,TI2不反相

img

若发现正转自减、反转自增,则应该把某个极性反相,就能反转计数方向,或者TI1\TI2调换

9、uint16_t写成int16_t

想把65535——>-1,则把uint16_t写成int16_t(借助补码的方式)

二、编码器接口测速实验

1、功能:

每隔一段时间去计数值,就能得到编码器旋转的速度
向右旋转:计数为正,想左旋转,计数为负,大小均为速度

    现在:通过定时器的编码器接口,自动计次(节约软件资源)

    之前:触发外部中断,在中断函数中自动计次

2、原理图

(A相接在PA6、B相接在PA7)

img

3、 步骤

  • 第一步:RCC开启时钟,开启GPIO和定时器的时钟
  • 第二步:配置GPIO,PA6和PA7配置成输入模式

  • 第三步:配置时基单元,预分频器选择不分频,自动重装,一般给最大65535,只需要给CNT执行计数即可

  • 第四步:配置输入捕获单元(只有滤波器和极性两个参数)

  • 第五步:配置编码器接口模式(调用库函数)

  • 最后,调用TIM_Cmd开启定时器

电路初始化后,CNT会随着编码器旋转而自增自减
若想测量编码器的位置:直接读出CNT即可
测量编码器的速度和方向:需要每隔一段固定的闸门时间,取出一次CNT,然后再把CNT清零,这是测频法测量速度

4、代码:

(1)Encoder.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include "stm32f10x.h"                  // Device header

void Encoder_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

GPIO_InitTypeDef GPIO_InitStructure;
//空闲默认高电平,选择上拉输入,空闲默认低电平,选择下拉输入
//浮空输入:没有上拉电阻和下拉电阻去影响外部信号,
//缺点:当引脚浮空时,没有默认的电平,易受到噪声的影响,来回不断跳变
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

//编码器接口的实质是一个带方向控制的外部时钟,故内部时钟没用
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //ARR计数范围最大,且方便换算为负数
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; //PSC不分频
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);

//第四步:配置输入捕获单元
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICStructInit(&TIM_ICInitStructure);//赋一个初始值
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInit(TIM3, &TIM_ICInitStructure);//调用后就写入硬件寄存器,故结构体可以继续使用
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInit(TIM3, &TIM_ICInitStructure);

//编码器模式:TIM_EncoderMode_TI12,TI1和TI2都计数
//后面两个参数为通道1和通道2的电平特性,上升沿:高低电平不反转(编码器上升沿和下降沿都有效)
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);

TIM_Cmd(TIM3, ENABLE);
}

int16_t Encoder_Get(void)
{
int16_t Temp;
Temp = TIM_GetCounter(TIM3);
TIM_SetCounter(TIM3, 0);
return Temp;
}

(2)main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "Encoder.h"

int16_t Speed;

int main(void)
{
OLED_Init();
Timer_Init();
Encoder_Init();

OLED_ShowString(1, 1, "Speed:");

while (1)
{
OLED_ShowSignedNum(1, 7, Speed, 5);
}
}

void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
Speed = Encoder_Get();
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}