一、机械式增量式旋转编码器工作原理
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://share.weiyun.com/zlszrbGj
下面图片参考作者@百里与司空https://blog.csdn.net/hjlkklk/ar ... ult-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); //打印在串口上
}
}
最后看效果:
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/id2658/article/details/145460918
|