旋转编码器模块(软件消抖)

news2024/9/23 19:15:40

目录

旋转编码器简介

模块接线

正交编码器控制原理 

模块代码

Encoder.h

Encoder.c 

main.c 

消抖代码 

Encoder.c


旋转编码器简介

旋转编码器是一种将旋转角位移转换为一连串数字脉冲信号的旋转式电位器。当编码器的旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的正交方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度,位置,旋转方向

正交信号为两个一样的波形但相位差为90°,这提高输出信号的质量和稳定性,从而消除了干扰。

本博客使用的是EC11型号360度旋转编码器,旋转编码器一圈为20格,一格一完整脉冲,在静止的时候,AB两线输出都是高电平 

旋转编码器旋转的输出动作,由于编码器的不同,A、B端输出脉冲不同,我这一种就是转一格,A、B对C端输出一个完整脉冲。 

更多详细见:

EC11旋转编码器 | 立创开发板技术文档中心 (lckfb.com)

我的旋转编码器不能使用按键,本博客就不涉及按键检测了


模块接线

                                   旋转编码器电路

按键电路内部没接,C相内部已经已接入GND  

旋转编码器STM32F103C8T6
VCC3.3V
GNDGND
APB0
BPB1

正交编码器控制原理 

正交编码器正反转由自己定义

 A、B两相空闲状态都为高电平,编码器旋转时都输出方波,但两个信号有90°相位差

注:下面图形中的波形不是从空闲状态开始的

正转

当是顺时针旋转时,B相超前A相90度,即A相为下降沿时,B相为高电平;A相为上升沿时,B相为低电平  

反转

当是逆时针旋转时,A相超前B相90度,即A相为下降沿时,B相为低电平;A相为上升沿时,B相为高电平。  

编码器正交波形动态展示视频时间段为00:50至1:57 旋转编码器|从零开始认识各种传感器_哔哩哔哩_bilibili

模块代码

思路一:A相下降沿进入中断函数,判断B相的高低电平确定编码器旋转方向。

思路二:A相和B相下降沿都进入中断函数,判断另一相的高低电平确定编码器旋转方向。

思路三:A相的上升沿和下降沿都进入中断函数,在A相下降沿时记录B相电平,A相上升沿时再记录B相电平,判断B相电平在这期间是上升沿还是下降沿以确定编码器旋转方向。(推荐此法

Encoder.h

#ifndef __ENCODER_H
#define __ENCODER_H
 
void Encoder_Init(void);
int16_t Encoder_Get(void);
 
#endif

Encoder.c 

该代码实现思路二,消抖效果不佳,请见思路三消抖代码

#include "stm32f10x.h"                  // Device header

int16_t Encoder_Count;

void Encoder_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    
    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);
    
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
    
    EXTI_InitTypeDef EXTI_InitStructure;
    EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_Init(&EXTI_InitStructure);
    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_Init(&NVIC_InitStructure);
}

int16_t Encoder_Get(void)
{
    int16_t Temp = Encoder_Count;
    Encoder_Count = 0;
    return Temp;
}

void EXTI0_IRQHandler(void)
{
    if (EXTI_GetITStatus(EXTI_Line0) == SET)
    {
        /*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
        if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
        {
            if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
            {
                Encoder_Count --;
            }
        }
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}

void EXTI1_IRQHandler(void)
{
    if (EXTI_GetITStatus(EXTI_Line1) == SET)
    {
        /*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
        if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
        {
            if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
            {
                Encoder_Count ++;
            }
        }
        EXTI_ClearITPendingBit(EXTI_Line1);
    }
}
	

main.c 

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"

int16_t Num;

int main(void)
{
    OLED_Init();
    Encoder_Init();
    
    OLED_ShowString(1, 1, "Num:");
    
    while (1)
    {
        Num += Encoder_Get();
        OLED_ShowSignedNum(1, 5, Num, 5);
    }
}

消抖代码 

A相下降沿消抖:A相和B相默认输出高电平,先检测A相下降沿,若发生抖动,不断进入上升沿和下降沿的if判断,但是这时(A相下降沿时),B相电平不会发生变化,不会执行下面的代码。这样A相下降沿时A相电平的抖动就过滤了。

		//判断A相低电平时,B相边沿变化则确定编码器转向,若不变化则过滤计数,用来消除A相和B相边沿变化的抖动
		if(B_level==1 && BOUT == 0) //B相为下降沿
		{
			Encoder_Count++;//顺时针转
		}
		if(B_level==0 && BOUT == 1)//B相为上升沿
		{
			Encoder_Count--;//逆时针转
		}	

B相电平变化的抖动:检测到A下降沿后,若B相下降沿则正转,上升沿则反转。 B相电平变化也会有抖动。但并不影响程序对B相电平检测,也不阻塞程序。A相下降沿时,if判断会将A相下降沿时最后的B相电平存储,A相上升沿时,if会检查B相电平并判断B相电平变化确定编码器计数方向。

A相上升沿抖动A相下降沿消抖类似。A相上升沿时,B相电平不会发生变化,不会执行上面的if语句,不计数

Encoder.c

该代码实现思路三,消抖效果好

#include "stm32f10x.h"

int16_t Encoder_Count;//计数次数


#define AOUT GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0)//编码器A相输出
#define BOUT GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1)//编码器B相输出

void Encoder_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	
	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);
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);	
	
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line = EXTI_Line0;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
	EXTI_Init(&EXTI_InitStructure);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
}

int16_t Encoder_Get(void)
{
	int16_t Temp = Encoder_Count;
	Encoder_Count = 0;
	return Temp;
}

//检测到A相信号上升沿和下降沿进入中断函数
void EXTI0_IRQHandler(void)
{
	static uint8_t B_level;
	//B_level:A相下降沿时B相电平,范围0~1;
	
	//A相下降沿判断下面逻辑
	if(AOUT == 0)//A相下降沿触发中断
	{                                                          
		B_level=0;//默认读取B相电平为0,记录A相下降沿时B相电平
		if(BOUT == 1)//若为B相高电平则B_level置1
		{
			B_level=1;
		}
	}
	
	//A相上升沿时判断下面逻辑
	if(AOUT == 1)
	{
		//判断A相低电平时,B相边沿变化则确定编码器转向,若不变化则视为抖动,过滤计数
		if(B_level==1 && BOUT == 0) //B相为下降沿
		{
			Encoder_Count++;//顺时针转
		}
		if(B_level==0 && BOUT == 1)//B相为上升沿
		{
			Encoder_Count--;//逆时针转
		}	 
	}	
	EXTI_ClearITPendingBit(EXTI_Line0);	
}

参考博客

江协科技STM32——旋转编码器计次(软件消抖)_旋转编码器消抖-CSDN博客

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

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

相关文章

C语言 strlen求字符串长度

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; C 标准库 - <string.h…

OpenCV中的颜色映射函数applyColorMap的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 OpenCV 中应用类似于 GNU Octave 或 MATLAB 中的颜色映射&#xff0c;虽然 OpenCV 中的颜色映射类型与 GNU Octave 或 MATLAB 中的颜色映射类型名…

Windows Server 安装MySQL教程(图文)

本篇教程&#xff0c;在服务器Windows Server 2016 &#xff08;中文版&#xff09;上安装MySQL8.0&#xff0c;并记录详细的安装步骤。 1、下载安装包 在mysql官网上下载安装包 下载地址&#xff1a;https://dev.mysql.com/downloads/installer/ 2、安装步骤 下载之后&#xf…

常见概念 -- WSS光层环回

光层环回&#xff0c;即业务收发经过同一块WSS单板&#xff0c;在WSS单板的DM/AM层面或内部端口层面进行环回&#xff0c;用于定位问题。 目前&#xff0c;光层环回有两种实现方式。 方式一&#xff1a;需要人工进行物理连纤&#xff0c;将WSS单板的DM和AM接口用光纤环回&…

3.2 寻址方式

&#x1f393; 微机原理考点专栏&#xff08;通篇免费&#xff09; 欢迎来到我的微机原理专栏&#xff01;我将帮助你在最短时间内掌握微机原理的核心内容&#xff0c;为你的考研或期末考试保驾护航。 为什么选择我的视频&#xff1f; 全程考点讲解&#xff1a;每一节视频都…

Datawhale x李宏毅苹果书AI夏令营深度学习详解入门 Task3

在深度学习中&#xff0c;模型偏差、优化问题和过拟合是我们经常会遇到的挑战。理解这些问题并找到合适的解决方法对于提高模型的性能至关重要。 第一章&#xff1a;模型偏差 1.1 模型过于简单可能导致模型偏差 在应用机器学习算法时&#xff0c;如果模型过于简单&#xff0c;就…

前端三件套配合MarsCode实现钉钉官网动画 # 豆包MarsCode

文章目录 如何固定动画区域创建项目MarsCode 设置样式MarsCode 优点1MarsCode 缺点MarsCode 优点2 js实现动画实现获取动画曲线的函数为什么实现这个函数&#xff1f;根据当前滚动位置&#xff0c;计算每一个元素不同的数值更新 dom 的 style更新 animationMapgetDomAnimation …

| AutoDL租服务器 |AutoDL租服务器保姆级教程

&#x1f411; | AutoDL租服务器 |AutoDL租服务器保姆级教程 &#x1f411; 文章目录 &#x1f411; | AutoDL租服务器 |AutoDL租服务器保姆级教程 &#x1f411;&#x1f411; 前言&#x1f411;&#x1f411; 实例创建&#x1f411;&#x1f411; 环境配置&#x1f411;&am…

C# DLL已定义类或方法,但是编译报错未定义

现有应用程序1个&#xff0c;动态链接库3个分别称为A、B、C。 应用程序输出在目录P1&#xff0c;动态链接库输出在目录P2。 应用程序引用A、B、C动态链接库&#xff0c;动态链接库A引用B&#xff0c;B引用C。 此时修改动态链接库C&#xff0c;在VS中开发应用程序时可以识别到…

MQ专题:消息积压相关问题和解决思路

一、如何发现消息出现了堆积 二、常见的原因及解决方案 三、消息出现了大量堆积&#xff0c;如何解决 四、建议

2024年软考-信息安全工程师考试介绍及考纲

2024年软考-信息安全工程师考试介绍及考纲 2024年软考-信息安全工程师 考试介绍及考纲 一、考试信息 考试时间&#xff1a;预计2024年11月9日-12日。 考试科目&#xff1a; 科目一&#xff1a;基础知识 客观题&#xff1a;75空 满分&#xff1a;75分 科目二&#xff1a;应用…

C++ | Leetcode C++题解之第391题完美矩形

题目&#xff1a; 题解&#xff1a; class Solution { public:bool isSubsequence(string s, string t) {int n s.size(), m t.size();vector<vector<int> > f(m 1, vector<int>(26, 0));for (int i 0; i < 26; i) {f[m][i] m;}for (int i m - 1; …

linux中最简单方式使用crontab打印当前时间

因特殊需求&#xff0c;需要在linux的某个文件中每分钟打印出当前时间。 先手动试一下命令&#xff1a; echo $(date) 打印出&#xff1a; Mon Sep 1 09:28:06 AM CST 2024 而我需要达到的效果是&#xff1a; 2024-09-01 09:28:06 于是命令改成了&#xff1a; echo $(date &quo…

Unified-IoU:用于高质量对象检测

摘要 https://arxiv.org/pdf/2408.06636 目标检测是计算机视觉领域的重要部分&#xff0c;而目标检测的效果直接由预测框的回归精度决定。作为模型训练的关键&#xff0c;交并比&#xff08;IoU&#xff0c;Intersection over Union&#xff09;极大地展示了当前预测框与真实框…

GCViT实战:使用GCViT实现图像分类任务(二)

文章目录 训练部分导入项目使用的库设置随机因子设置全局参数图像预处理与增强读取数据设置Loss设置模型设置优化器和学习率调整策略设置混合精度&#xff0c;DP多卡&#xff0c;EMA定义训练和验证函数训练函数验证函数调用训练和验证方法 运行以及结果查看测试完整的代码 在上…

Android kotlin使用Netty网络框架实践(客户端、服务端)

开发工具&#xff1a;Android studio 语言:kotlin 设计原理&#xff1a;通讯协议&#xff1a;头类型长度数据尾&#xff0c;自定义编解码器&#xff0c;解析和包装发送数据流&#xff0c;以下贴出部分关键代码 说明&#xff1a;代码中封装了client和server端&#xff0c;可…

记录一下idea的一些使用技巧和遇到的异常(持续更新)

技巧 自己的模板——live template 有些代码在项目中通常会被用到或会被重复使用&#xff0c;可以自己写一个模板存起来&#xff0c;要用的时候用快捷键生成就可以了。 在这里选择生效范围 现在&#xff0c;就有我们自己的模板了&#xff0c;一回车就自动生成 idea的全局配置…

AD7606工作原理以及FPGA控制验证(串行和并行模式)

文章目录 一、AD7606介绍二、AD7606采集原理2.1 AD7606功能框图2.2 AD7606管脚说明 三、AD7606并行模式时序分析以及实现3.1 并行模式时序图3.2 并行模式时序要求3.3 代码编写3.4 仿真观察 四、AD7606串行模式时序分析以及实现4.1 串行模式时序图4.2 串行模式时序要求4.3 代码编…

WWDG—窗口看门狗

WWDG—窗口看门狗 窗口看门狗跟独立看门狗一样&#xff0c;也是一个递减计数器不断的往下递减计数&#xff0c;当减到一个固定值 0X40时还不喂狗的话&#xff0c;产生复位&#xff0c;这个值叫窗口的下限&#xff0c;是固定的值&#xff0c;不能改变。这个是跟独立看门狗类似的…

C练手题--Two Oldest Ages 【7 kyu】

一、原题 链接&#xff1a;Training on Two Oldest Ages | Codewars The two oldest ages function/method needs to be completed. It should take an array of numbers as its argument and return the two highest numbers within the array. The returned value should b…