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

news2024/11/16 11:42:36

系列文章目录

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
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1637983.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Cisco IOS XE Web UI 权限提升漏洞复现(CVE-2023-20198)

0x01 产品简介 Web UI 是一种基于GUI的嵌入式系统管理工具,能够提供系统配置、简化系统部署和可管理性以及增强用户体验。它带有默认映像,因此无需在系统上启用任何内容或安装任何许可证。Web UI 可用于构建配置以及监控系统和排除系统故障,而无需CLI专业知识。 0x02 漏洞…

路由器的构成

一、路由器简介 路由器是互联网中的关键设备: 连接不同的网络路由器是多个输入端口和多个输出端口的专用计算机,其任务是转发分组(转发给下一跳路由器)下一跳路由器也按照这种方法处理分组,直到该分组到达终点为止 …

洛谷 P1377:树的序 ← 笛卡尔树

【题目来源】https://www.luogu.com.cn/problem/P1377【题目描述】 众所周知,二叉查找树的形态和键值的插入顺序密切相关。准确的讲: 1.空树中加入一个键值 k,则变为只有一个结点的二叉查找树,此结点的键值即为 k。 2.在非空树中插…

Vue2基础用法及案例

Vue2基础用法及案例 目录 Vue2基础用法及案例导入响应式布局文本指令v-textv-html 属性指令Vue-set()v-ifv-showv-forv-bindv-model 事件指令v-on函数使用 Class 与 Style 绑定数组绑定对象绑定 案例1:切换图片案例2:过滤查找方法补充 导入 Vue2 cdn &…

如何从 iPhone 恢复已删除或丢失的联系人?

不小心删除了您的 iPhone 联系人?不用担心。我们将向您展示如何从 iPhone或 iPad恢复已删除或丢失的联系人。当您从 iPhone 中删除联系人时,您可能认为无法将其恢复。但事实是,您可以从 iPhone 或 iPad 恢复已删除的联系人,因为它…

04_jvm性能调优_并行收集器介绍

并行收集器(此处也称为吞吐量收集器)是类似于串行收集器的分代收集器。串行和并行收集器之间的主要区别在于并行收集器具有多个线程,用于加速垃圾回收过程。 通过命令行选项-XX:UseParallelGC 可启用并行收集器。默认情况下,使用…

WPF之border标签边框控件、设置弧度、图片

border标签在WPF中承担着边框的角色又称之为边框标签,使用嵌套的方法去给一些标签添加边框,border标签包裹目标标签(border不能有多个子元素)。一般在给标签添加弧度时可以使用border。 常用属性 CornerRadius边框拐角的弧度,当宽高是一样的…

【Mac】Photoshop 2024 for mac最新安装教程

软件介绍 Photoshop 2024是Adobe公司推出的一款图像处理软件,它支持Windows和Mac OS系统。Adobe Photoshop是业界领先的图像编辑和处理软件之一,广泛用于设计、摄影、数字绘画等领域。 Photoshop 2024的功能包括: 1.图像编辑:提…

【16-Ⅰ】Head First Java 学习笔记

HeadFirst Java 本人有C语言基础,通过阅读Java廖雪峰网站,简单速成了java,但对其中一些入门概念有所疏漏,阅读本书以弥补。 第一章 Java入门 第二章 面向对象 第三章 变量 第四章 方法操作实例变量 第五章 程序实战 第六章 Java…

张鸣独到解读:规矩与自信的政治影响

在当今多变的政治舞台上,学者张鸣教授以其犀利而深邃的视角,对规矩与自信提出了新的解读。他的言论不仅引发了公众的广泛关注,也为我们提供了思考社会政治问题的一个新的角度。张教授指出,规矩并非僵化的教条,而应是动…

Linux进程管理与监控

一、相关概念 1、进程的的基本定义 在自身的虚拟地址空间运行的一个独立的程序,从操作系统的角度来看,所有在系统上运行的东西,都可以称为一个进程。 2、进程的分类 系统进程:可以执行内存资源分配和进程切换等管理工作&am…

翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习四

合集 ChatGPT 通过图形化的方式来理解 Transformer 架构 翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习一翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习二翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深…

基于Python的人脸识别系统设计与实现(论文+源码)_kaic

基于Python的人脸识别系统设计与实现 摘 要 随着人工智能的发展,人脸识别系统在我们的生活中越来越被广泛应用。人脸识别系统是指能够从数字图像或视频源中识别人的技术。人脸识别系统可以通过多种方法工作,但是,它们通常是通过将给定图像中的面部特征与…

IDEA基于Maven构建项目

IDEA基于Maven构建项目 一、Maven简介 Apache Maven 是一个软件项目管理和理解工具。基于项目对象模型的概念(POM),Maven 可以从中心信息中管理项目的构建、报告和文档。 Apache Maven 可以用于构建和管理任何基于 Java 的项目。 下载地址…

Axure如何调起浏览器的打印功能

Axure如何调起浏览器的打印功能 答:javascript:window.print(); 不明白的继续往下看 应用场景: 原型设计中,页面上的打印按钮,需要模拟操作演示,需要点击指定的按钮时,唤起浏览器的打印功能&#xff08…

JDBC连接MySQL8 SSL

1.创建用户并指定ssl连接 grant all on . to test% identified by imooc require SSL(X509); 2.查看是否使用ssl SELECT ssl_type From mysql.user Where user"test" 3.配置用户必须使用ssl ALTER USER test% REQUIRE SSL(X509); FLUSH PRIVILEGES; 注意&#xff…

aardio封装库) 微软开源的js引擎(ChakraCore)

前言 做爬虫肯定少不了JavaScript引擎的使用,比如在Python中现在一般用pyexecjs2来执行JavaScript代码,另外还有一些其他执行JavaScript的库: https://github.com/eight04/node_vm2: rpc调用nodejs,需要安装nodehttps://github.…

【Spring 】Spring MVC 入门Ⅱ

Spring MVC 入门Ⅱ 一、接收Cookie / Session 这两者都是用来保存用户信息的,但不同的是: Cookie存在客户端 Session存在服务器 Session产生时会生成一个唯一性的SessionID,这个SessionID可以用于匹配Session和Cookie SessionID可以在Cooki…

java+jsp+Oracle+Tomcat 记账管理系统论文(二)

⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️ ➡️点击免费下载全套资料:源码、数据库、部署教程、论文、答辩ppt一条龙服务 ➡️有部署问题可私信联系 ⬆️⬆️⬆️​​​​​​​⬆️…

Pandas入门篇(二)-------Dataframe篇4(进阶)(Dataframe的进阶用法)(机器学习前置技术栈)

目录 概述一、复合索引(一)创建具有复合索引的 DataFrame1. 使用 set_index 方法:2.在创建 DataFrame 时直接指定索引: (二)使用复合索引进行数据选择和切片(三)重置索引&#xff08…