STM-32:TIM编码器接口-编码器接口测速

目录

  • 一、编码器接口简介
  • 二、正交编码器
  • 三、编码器接口电路结构
  • 四、编码器接口基本结构
  • 五、抗噪声
  • 六、编码器接口测速程序
    • 6.1接线图
    • 6.2程序代码

一、编码器接口简介

STM-32:TIM编码器接口-编码器接口测速_第1张图片
编码器有两个输出,一个A相,一个B相,然后接入STM32的定时器的编码器接口,编码器接口自动控制定时器时基单元中的CNT计数器进行自增或自减,比如初始化之后,CNT初始值为0,然后编码器右转,CNT就++,右转产生一个脉冲,CNT就加一次,比如右转产生10个脉冲之后,停下来,那么这个过程CNT就由0自增到10,停下来,编码器左转,CNT–,左转产生一个脉冲,CNT减一次,比如编码器再左转产生5个脉冲,那就在原来10的基础上自减5,停下来。
这个编码器接口,其实就相当于是一个带有方向控制的外部时钟,它同时控制这CNT的计数时钟和计数方向,这样的话,CNT的值就表示了编码器的位置,如果我们每隔一段时间取一次CNT的值,再把CNT清零,是不是每次取出来的值就表示了,编码器的速度,由上一篇笔记中的测频法和测周法的知识,这个编码器就是测频法测正交脉冲的频率,CNT计次然后每隔一段时间去一次计数值,只不过这个编码器接口更加高级,它能根据旋转方向,不仅可以自增计次,还可以自减计次,是一个带方向的测速
编码器接口资源比较紧张,如果一个定时器被配置成编码器接口模式,那它基本上就干不了其他的活了(如STM32F103C8T6只有4个定时器,所以最多只能接4个编码器),如果编码器不够用的话就要考虑一下资源够不够用,也可通过使用EXTI外部中断这就是用软件资源弥补硬件资源
每个定时器的CH3和CH4不能接编码器

二、正交编码器

STM-32:TIM编码器接口-编码器接口测速_第2张图片
正交编码器一般可以测量位置,或者带有方向的速度值,它一般有两个信号输出引脚,一个是A相,一个是B相,转的越快,A/B相的频率越高,所以方波的频率就代表了速度,我们取出任一 一相的频率来测频率就能知道旋转速度了,为了测量旋转方向,采取A/B相的方法,当正转时,A相提前B相90度,反转时,A相滞后B相90度。
上图右侧两个表格是总结的正反转AB相的电平特征,即可以通过这些特征来判断正反转

三、编码器接口电路结构

STM-32:TIM编码器接口-编码器接口测速_第3张图片
编码器接口有两个输入端分别要接到编码器的A相和B相,从上图可以看出,这个编码器接口的两个引脚借用了输入捕获单元的前两个通道,所以最终编码器的输入引脚就是定时器的CH1和CH2这两个引脚,信号的通路如下图
STM-32:TIM编码器接口-编码器接口测速_第4张图片
其中CH1和CH2的输入捕获滤波器和边沿检测编码器也有使用,但是后面的是否交叉、预分频器和CCR寄存器,与编码器接口无关,这就是输入部分

编码器接口的输出部分其实就相当于从模式控制器,这里的执行流程是按照我们之前总结的那个表,如果出现了边沿信号,并且对应另一相的状态为正转则控制CNT自增,否则控制CNT自减
注:在这里,我们并不会使用72M内部时钟和在时基单元初始化设置的计数方向,因为此时计数时钟和计数方向都处于编码器接口托管的状态,计数器的自增和自减手编码器控制

四、编码器接口基本结构

STM-32:TIM编码器接口-编码器接口测速_第5张图片
STM-32:TIM编码器接口-编码器接口测速_第6张图片
注:1、正转的状态都向上计数,反转的状态都向下计数

    2、一般选择第三个模式(在TI1和TI2上计数),精度高

五、抗噪声

一个引脚不变,另一个引脚多次变化的毛刺信号,计数值还是原来那个数
STM-32:TIM编码器接口-编码器接口测速_第7张图片
TI1反相,在分析前先将TI1的时序图翻转
TI1反相,是修改将CH1或2通道内的极性选择,在编码器模式下是高低电平的极性翻转,而不是输入捕获模式下的边沿翻转
用途:当计数相反的时候可以修改极性
STM-32:TIM编码器接口-编码器接口测速_第8张图片

六、编码器接口测速程序

6.1接线图

STM-32:TIM编码器接口-编码器接口测速_第9张图片
初始化:
RCC开启时钟,开启GPIO和定时器的时钟
配置GPIO,把两个通道配置成输入模式
配置时基单元,这里预分频器一般选择不分频,自动重装,一般给最大65535,只需要个CNT负责计数即可
配置输入捕获单元
配置编码器接口模式,这个直接调用一个库函数即可
最后调用TIM_Cmd,启动定时器

6.2程序代码

Encoder.c

#include "stm32f10x.h"                  // Device header
 
void Encoder_Init(void)
{
	// 1. RCC开启时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	// 2. 配置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);
	
	// 3. 配置时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	// 这里计数模式失效
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
	
	// 4. 配置输入捕获单元
	TIM_ICInitTypeDef TIM_ICInitStruct;
	TIM_ICStructInit(&TIM_ICInitStruct);							// 给结构体赋一个初始值防止未知问题
	
	// 配置CH1
	TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;					
	TIM_ICInitStruct.TIM_ICFilter = 0xF;							
//	TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;		
//  这里的上升沿并不代表上升沿有效,这里代表高低电平极性不翻转
//	它与TIM_EncoderInterfaceConfig中的参数操作的是同一个寄存器,属于重复配置
	
	TIM_ICInit(TIM3, &TIM_ICInitStruct);
	
	// 配置CH2
	TIM_ICInitStruct.TIM_Channel = TIM_Channel_2;					
	TIM_ICInitStruct.TIM_ICFilter = 0xF;							
//	TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;		
	
	TIM_ICInit(TIM3, &TIM_ICInitStruct);
	
	// 5. 配置编码器接口模式
	TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
	
	// 6. 启动定时器
	TIM_Cmd(TIM3, ENABLE);
}
 
int16_t Encoder_Get(void)
{
	int16_t Temp;
	Temp = TIM_GetCounter(TIM3);	// 这里TIM_GetCounter返回一个uint16_t类型的值,利用补码很方便地将正数变为负数,例如65535变为-1
	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"
 
int16_t Speed;
 
int main()
{
	OLED_Init();
	Timer_Init();
	Encoder_Init();
	
	OLED_ShowString(1, 1, "Speed:");
 
	while(1)
	{
		// 一般不建议以下做法,容易阻塞主程序
//		OLED_ShowSignedNum(1, 5, Encoder_Get(), 5);
//		Delay_ms(1000);
		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);
	}
}
 

你可能感兴趣的:(STM-32学习,单片机,stm32)