STM32定时器中的编码器接口详解

系列文章目录

STM32单片机系列专栏

C语言术语和结构总结专栏


文章目录

1. 编码器接口简介

2. 旋转编码器简介

3. 正交编码器工作模式

4. 基本结构

5. 编码器工作模式示例

6. 代码示例

6.1 Encoder.c

6.2 Encoder.h

6.3 main.c


1. 编码器接口简介

在STM32中,编码器接口(Encoder Interface)允许通过连接到定时器的输入引脚,来直接与旋转编码器进行接口,利用定时器的计数器(CNT)来跟踪编码器的相位变化,从而确定位置、速度和方向。

  • 编码器接口模式:允许定时器捕获从编码器接收到的相位A和相位B的信号,也就是接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度。
  • 自动计数功能:在编码器模式下,定时器的计数器(CNT)会根据接收到的信号自动增加或减少。例如,相位A和相位B的每一个状态变化都可以被配置为导致计数器的增加或减少,这依赖于编码器的旋转方向和连接方式。
  • 对于每个编码器的状态变化,计数器都有相应的响应:例如,在正常的四相编码器中,每个完整的状态循环(由相位A和相位B的四个状态变化组成)可能导致计数器增加或减少4次,从而提供更高的测量精度和更好的反应速度。
  • 每个高级定时器和通用定时器都拥有一个编码器接口
  • 两个输入引脚借用了输入捕获的通道1和通道2(CH1和CH2)

2. 旋转编码器简介

旋转锁编码器允许用户在输入旋转命令时设置一个“锁”,使得在旋转到特定位置后,输出的信号或电路状态被“锁定”,不会因继续旋转而改变。这通常用于需要精确控制到特定位置并保持该状态的场合,如高精度的调节或选择操作。

  • 步进控制:编码器通过内部的机械结构或电子控制来实现步进控制。每次旋转到一个设定的点,编码器会产生一个停止信号,阻止进一步的旋转或保持当前的输出状态。
  • 位置锁定:旋转到特定位置后,通过物理锁定机制或电子信号锁定,确保输出状态不会因旋转器继续旋转而变化。这种锁定通常用于防止误操作或提供一种用户反馈,确认操作正确执行。

3. 正交编码器工作模式

图片中展示了一个旋转编码器产生的A相和B相信号的两种主要模式:正转和反转。这种编码器通常用于确定机械设备如电机的位置、速度和方向。通过状态变化,可以精确地监测和控制编码器的旋转方向和角度。

波形图

  • A相和B相信号:编码器的输出信号通常包含两个90度相位差的脉冲序列,即A相和B相。
  • 正转:当编码器向正方向转动时,A相信号先于B相信号变化(上升或下降)。
  • 反转:当编码器向反方向转动时,B相信号先于A相信号变化。

状态变化和方向判断:

  • 正转时的状态变化

    • 当A相从低到高(上升沿)时,如果B相处于低状态,表示编码器正在进行正转。
    • 当A相从高到低(下降沿)时,如果B相处于高状态,也表示编码器正在进行正转。
    • 当B相从低到高(上升沿)时,如果A相处于高状态,同样表示正转。
    • 当B相从高到低(下降沿)时,如果A相处于低状态,同样表示正转。
  • 反转时的状态变化

    • 当A相从低到高(上升沿)时,如果B相处于高状态,表示编码器正在进行反转。
    • 当A相从高到低(下降沿)时,如果B相处于低状态,也表示编码器正在进行反转。
    • 当B相从低到高(上升沿)时,如果A相处于低状态,同样表示反转。
    • 当B相从高到低(下降沿)时,如果A相处于高状态,同样表示反转。
有效边沿相对信号的电平TI1FP1信号(A相)TI2FP2信号(B相)
上升下降上升下降
仅在TI1计数向下计数向上计数不计数不计数
向上计数向下计数不计数不计数
仅在TI2计数不计数不计数向上计数向下计数
不计数不计数向下计数向上计数
在TI1和TI2上计数向下计数向上计数向上计数向下计数
向上计数向下计数向下计数向上计数

4. 基本结构

 首先输入捕获的前两个通道,通过GPIO口接入编码器的A、B相,然后通过滤波器和边沿检测极性选择,产生TI1FP1和TI2FP2,通向编码器接口,这里的边沿检测极性选择用来处理要不要加一个非门,反转一下极性。接着编码器接口通过预分频器控制CNT计数器的时钟。同时,编码器接口还根据编码器的旋转方向,控制CNT的计数方向。

编码器正转时,CNT自增,编码器反转时,CNT自减。这里ARR一般也是设置65535。

5. 编码器工作模式示例

在图中,展示了向上和向下计数的工作模式所对应的电平状态,这里是TI1和TI2都不反相,也就是经过边沿检测极性选择时不使用非门。其中毛刺部分体现了正交编码器抗噪的原理,这里TI2没有变化,但是TI1却跳变了,因为正交信号是两个输出交替变化,所以不符合正交编码器的信号规律,所以查看表格中的状态就可以看出,这里计数器就会在加和减之间来回跳动。

下面的示例是TI1反相,TI2不反相,也就是TI1经过边沿检测极性选择时使用非门.

6. 代码示例

代码实现的功能为使用旋转编码器实现计数功能,也就是数值的增加和减少,并在oled上显示。

这里前面的设置方式和其他定时器的示例类似,下面的文章代码示例中可以找到。

STM32的TIM输入捕获和PWMI详解

6.1 Encoder.c

#include "stm32f10x.h"     

int16_t Encoder_Count;					//全局变量,用于计数旋转编码器的增量值

//旋转编码器初始化
void Encoder_Init(void)
{
	//开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);		//开启AFIO的时钟,外部中断必须开启AFIO的时钟
	
	//GPIO初始化
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);						//将PB0和PB1引脚初始化为上拉输入
	
	//AFIO选择中断引脚
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//将外部中断的0号线映射到GPIOB,即选择PB0为外部中断引脚
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);//将外部中断的1号线映射到GPIOB,即选择PB1为外部中断引脚
	
	//EXTI初始化
	EXTI_InitTypeDef EXTI_InitStructure;						//定义结构体变量
	EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;		//选择配置外部中断的0号线和1号线
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;					//指定外部中断线使能
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;			//指定外部中断线为中断模式
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;		//指定外部中断线为下降沿触发
	EXTI_Init(&EXTI_InitStructure);								//将结构体变量交给EXTI_Init,配置EXTI外设
	
	//NVIC中断分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
																//即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅需调用一次
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
	
	//NVIC配置
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;			//选择配置NVIC的EXTI0线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设

	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;			//选择配置NVIC的EXTI1线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;			//指定NVIC线路的响应优先级为2
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
}

//旋转编码器获取增量值
int16_t Encoder_Get(void)
{
	/*使用Temp变量作为中继,目的是返回Encoder_Count后将其清零*/
	/*在这里,也可以直接返回Encoder_Count
	  但这样就不是获取增量值的操作方法了
	  也可以实现功能,只是思路不一样*/
	int16_t Temp;
	Temp = Encoder_Count;
	Encoder_Count = 0;
	return Temp;
}

//EXTI0外部中断函数
void EXTI0_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line0) == SET)		//判断是否是外部中断0号线触发的中断
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
		{
			if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)		//PB0的下降沿触发中断,此时检测另一相PB1的电平,目的是判断旋转方向
			{
				Encoder_Count --;					//此方向定义为反转,计数变量自减
			}
		}
		EXTI_ClearITPendingBit(EXTI_Line0);			//清除外部中断0号线的中断标志位
													//中断标志位必须清除
													//否则中断将连续不断地触发,导致主程序卡死
	}
}

//EXTI1外部中断函数
void EXTI1_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line1) == SET)		//判断是否是外部中断1号线触发的中断
	{
		//如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
		{
			if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)		//PB1的下降沿触发中断,此时检测另一相PB0的电平,目的是判断旋转方向
			{
				Encoder_Count ++;					//此方向定义为正转,计数变量自增
			}
		}
		EXTI_ClearITPendingBit(EXTI_Line1);			//清除外部中断1号线的中断标志位
													//中断标志位必须清除
													//否则中断将连续不断地触发,导致主程序卡死
	}
}

6.2 Encoder.h

接着是Encoder.h文件,这部分引用声明一下即可

#ifndef __ENCODER_H
#define __ENCODER_H

void Encoder_Init(void);
int16_t Encoder_Get(void);

#endif

6.3 main.c

#include "stm32f10x.h" 
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"

int16_t Num;			//定义待被旋转编码器调节的变量

int main(void)
{
	//模块初始化
	OLED_Init();		//OLED初始化
	Encoder_Init();		//旋转编码器初始化
	
	//显示静态字符串
	OLED_ShowString(1, 1, "Num:");			//1行1列显示字符串Num:
	
	while (1)
	{
		Num += Encoder_Get();				//获取自上此调用此函数后,旋转编码器的增量值,并将增量值加到Num上
		OLED_ShowSignedNum(1, 5, Num, 5);	//显示Num
	}
}

 

7. 完整工程文件

STM32通过编码器接口测速

  • 39
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TENET-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值