一、机械式增量式旋转编码器工作原理
KY-040是一种机械式增量编码器,通过CLK和DT两个相位差90°的方波信号判断旋转方向:
-
顺时针旋转:CLK引脚先变化,DT随后变化
-
逆时针旋转:DT引脚先变化,CLK随后变化
-
它有5个引脚:CLK、DT、SW、VCC、GND
-
-
编程思想:通过中断I/O不断轮询的方式:取一个周期2个引脚的波形图,TI1对应CLK,TI2对应DT,把两个脚配置为上升沿触发,当CLK引脚上升沿到来触发中断,此时进入中断服务函数判断DT引脚电平是否为低电平,若为则编码器正转,反之,几乎同时,当DT引脚上升沿到来触发中断,进入中断服务函数判断CLK引脚是否为低电平,若为则编码器反转。(下面提供的代码会有详细的注释,没看懂可以翻到下面源码部分)我这里通过数据的加减来模拟旋转编码器的正反转,并输出到串口上。
-
下面图片参考作者@百里与司空https://blog.youkuaiyun.com/hjlkklk/article/details/142960567?ops_request_misc=&request_id=&biz_id=102&utm_term=STM32%E2%80%94%E6%97%8B%E8%BD%AC%E7%BC%96%E7%A0%81%E5%99%A8&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-2-142960567.142^v101^pc_search_result_base5&spm=1018.2226.3001.4187
-
-
驱动源码encoder.c:
-
#include "encoder.h" #include "stm32f10x.h" #include "Delay.h" /*通过I/O口的中断不断检测来驱动旋转编码器*/ /********************************* @brief: 旋转编码器的初始化 @param: void @retval: void *********************************/ int16_t Encoder_Count; //定义一个变量的值用来模拟旋转编码器的正反转 void Encoder_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(Encoder_GPIO_CLK, ENABLE); //开启GPIOB的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟,外部中断必须开启AFIO的时钟 //CLK和DT和SW三个引脚对应GPIOA的引脚的配置 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD; //上拉输入,因为旋转编码器空闲时为高电平 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(Encoder_GPIO_PORT, &GPIO_InitStructure); /*AFIO选择中断引脚*/ GPIO_EXTILineConfig(Encoder_GPIO_PORT_Source, GPIO_PinSource0);//将外部中断的0号线映射到GPIOB,即选择CLK的PA0为外部中断引脚 GPIO_EXTILineConfig(Encoder_GPIO_PORT_Source, GPIO_PinSource1);//将外部中断的1号线映射到GPIOB,即选择DT的PA1为外部中断引脚 /*EXTI的初始化*/ EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line=EXTI_Line0|EXTI_Line1; //选择配置外部中断的0号线和1号线 EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt; //指定外部中断线为中断模式 EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising; //指定外部中断线为上升沿触发 EXTI_InitStructure.EXTI_LineCmd=ENABLE; //指定外部中断线使能 EXTI_Init(&EXTI_InitStructure); /*NVIC中断分组*/ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //0~4都可以 /*NVIC配置*/ //EXTI0线的配置 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn; //选择配置NVIC的EXTI0线 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; //指定NVIC线路的抢占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority=1; //指定NVIC线路的响应优先级为1 NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //NVIC使能 NVIC_Init(&NVIC_InitStructure); //将配置好的结构体变量的置赋给NVIC_Init,配置NVIC外设 //EXTI1线的配置 NVIC_InitStructure.NVIC_IRQChannel=EXTI1_IRQn; //选择配置NVIC的EXTI1线 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; //指定NVIC线路的抢占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority=2; //指定NVIC线路的响应优先级为2 NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //NVIC使能 NVIC_Init(&NVIC_InitStructure); //将配置好的结构体变量的置赋给NVIC_Init,配置NVIC外设 } /*编写EXTI0的中断服务函数*/ void EXTI0_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line0) == SET) //先判断中断是否发生,即使不需要这步,但也要保持逻辑的严密性 { if(GPIO_ReadInputDataBit(Encoder_GPIO_PORT, Encoder_GPIO_PIN_CLK) == 1) //校验CLK引脚上升沿对应引脚的电平 { if(GPIO_ReadInputDataBit(Encoder_GPIO_PORT, Encoder_GPIO_PIN_DT) == 0) //在判断DT的引脚是否为低电平 { Encoder_Count++; //自加来模拟正转 EXTI_ClearITPendingBit(EXTI_Line0); //注意:自加完后一定清楚中断标志位 } } } } /*编写EXTI1的中断服务函数*/ //与上面的逻辑是一样的 void EXTI1_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line1) == SET) //先判断中断是否发生,即使不需要这步,但也要保持逻辑的严密性 { if(GPIO_ReadInputDataBit(Encoder_GPIO_PORT, Encoder_GPIO_PIN_DT) == 1) //校验DT引脚上升沿对应引脚的电平 { if(GPIO_ReadInputDataBit(Encoder_GPIO_PORT, Encoder_GPIO_PIN_CLK) == 0) //在判断CLK的引脚是否为低电平 { Encoder_Count--; //自减来模拟反转 EXTI_ClearITPendingBit(EXTI_Line1); //注意:自加完后一定清楚中断标志位 } } } } /*获取Encoder_Count的值,和按键清0*/ int16_t Encoder_Get_Count(void) { int16_t temp; temp=Encoder_Count; if(GPIO_ReadInputDataBit(Encoder_GPIO_PORT, Encoder_GPIO_PIN_CLK) == 1) //检测SW引脚按下,则对Encoder_Count进行清0 { Delay_us(10); //延时按键消抖 Encoder_Count=0; } return temp; }
encoder.h头文件:
-
#ifndef __ENCODER_H #define __ENCODER_H #include "stm32f10x.h" //宏定义部分,方便用户修改引脚 #define Encoder_GPIO_CLK RCC_APB2Periph_GPIOA #define Encoder_GPIO_PORT GPIOA #define Encoder_GPIO_PIN_CLK GPIO_Pin_0 #define Encoder_GPIO_PIN_DT GPIO_Pin_1 #define Encoder_GPIO_PIN_SW GPIO_Pin_2 #define Encoder_GPIO_PORT_Source GPIO_PortSourceGPIOA void Encoder_Init(void); int16_t Encoder_Get_Count(void); #endif
-
主函数main:
-
#include "stm32f10x.h" // Device header #include "Delay.h" #include "usart.h" #include "encoder.h" #include <stdio.h> int16_t Value; int main(void) { USART1_Config(); Encoder_Init(); while(1) { printf("\r\ntest\n"); Value=Encoder_Get_Count(); //获取Encoder_Count的加减来模拟旋转编码器正反转 printf("\r\nValue=%d\n",Value); //打印在串口上 } }
最后看效果:
-