一、知识点
1.编码器接口(Encoder Interface)
编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度
例子解释
若初始化之后CNT初始值为0,右转产生8个脉冲后停下来,CNT就从0自增加到8,再让编码器向左转产生5个脉冲,那么CNT就从8自减5至3。
所以编码器接口就相当于一个带有方向控制的外部时钟,它同时控制着CNT的计数时钟和计数方向,CNT的值就表示了编码器的位置
若每隔一段时间取出CNT的值,再把CNT清零,每次取出来的值就表示了编码器的速度
2.资源简介
编码器接口有使用CH1和CH2的输入捕获滤波器和边沿检测,编码器接口没有使用后面的是否交叉、预分频器、CCR寄存器与编码器接口无关
编码器接口的输出部分:相当于从模式控制器,去控制CNT的计数时钟和计数方向
- 每个高级定时器和通用定时器都拥有1个编码器接口
- 两个输入引脚借用了输入捕获的通道1和通道2(通道3和通道4不能用)
- 如果一个定时器配置为编码器接口模式,则该编码器干不了其他的事,C8T6这一类型芯片只有四个定时器,即最多只能接四个编码器。
3.正交信号
像这样一个信号比另一个信号慢90度,则这两个信号被称为正交信号
-
原理
首先把A相和B相的所有边缘,作为计数器的计数时钟,出现边缘信号时就计数自增或自减,增还是减由另一相的高低电平决定 -
好处
a、正交信号精度更高,因为A、B相都可以计次,相当关于计次频率提高了一倍
b、其次正交信号可以以抗噪声,因为正交信号两个信号必须是交替跳变的,可以设计一个抗噪声电路,如果一个信号不变,另一个信号连续跳变,也就是产生了噪声,这时计次值是不会变化的
4.工作模式
在TI1和TI2上计数
a.均不反向
b.TI1反向,TI2不反向
![](https://img-blog.csdnimg.cn/818cd2d29f274909882db47aba37afaf.png)
5.uint16_t和int16_t
想把65535变成-1,则利用补码原则(把uint16_t写成int16_t)
二、实例(编码器接口测速)
1.基本思路
一、RCC开启时钟,开启GPIO和定时器时钟
二、配置GPIO口
三、配置时基单元(预分频器选择不分频,自动重装选65535,CNT只需要计数就可以)
四、配置输入捕获单元(只需要配置滤波器和极性两个参数)
五、配置编码器接口模式
六、开启定时器(TIM_Cmd)
测量编码器的速度和方向:需要每隔一段固定的闸门时间,取出一次CNT,然后再把CNT清零,这是测频法测量速度
2.代码
Encoder.c
#include "stm32f10x.h" // Device header
void Encoder_Init(void)
{
//RCC开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//配置GPIO口
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_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
//后面两个参数为通道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;
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "Encoder.h" // Device header
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) //中断读取Speed
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
{
Speed=Encoder_Get();
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}