实物图
引脚功能
时序图
防抖动
因为是两个金属片来回摩擦触碰, 产生的高低电平信号, 会产生抖动, 如果是使用外部中断触发或者计数器中断触发, 将会面临很大的问题, 就是转动一次将会产生很多的抖动信号. 解决方法有硬件防抖, 还有软件防抖.
硬件防抖动
在clk和dt两根引脚分别串联两个小电容, 实现滤波的功能.
软件防抖
因为购买的模块一般都是没有电容的, 所以需要我们在程序中实现防抖动, 有以下方法
1. 中断中加延时函数: 最简单粗暴的方法, 但是不可以的. 首先在中断里面加延时函数是会有风险的, 随着代码体量增大, 用到的传感器数量增多, 程序容易卡死在中断函数里面. 另一方面, 实际操作下来发现效果并不好, 因为具有了延时, 快速旋转将会体验非常卡顿而且还会出现大量的错误.
2. 双中断: 因为两个引脚分别产生波形, 所以在两根引脚上分别让他产生中断, 当两个中断均触发时, 判断为旋转了一次. 这个思想非常好, 避免了在中断中写延时函数, 而且效果提升了很多, 但是快速旋转, 偶尔也会出现问题. 这时候就有了第三个想法.
3. 双中断+记录3次中断为旋转一次: 简单的纠错程序, 因为连续三次判断错误的概率是比较低的, 基本不会出现问题.
参考代码(基于stm32f407)
//初始化旋转编码器
void encoder_init(void)
{
//配置PA4外部中断,PA3为外部中断
//使能时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_EXTIT, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStruct.GPIO_Speed = GPIO_High_Speed;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStruct);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource4);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource3);
//配置外部中断
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line = EXTI_Line4 | EXTI_Line3;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿有效
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
//配置NVIC, 抢占优先级和响应优先级最高.
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置优先级分组
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = EXTI4_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = EXTI3_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
enum Direction
{
JUMP,
RIGHT,
LIFT,
};
static uint8_t flag = JUMP;
void EXTI3_IRQHandler(void)
{
if(EXTI_GetFlagStatus(EXTI_Line3) == SET)
{
if(PAin(4) == 0 && flag == RIGHT) //右转
{
num++;
flag = JUMP;
}
else if(PAin(4) == 1 && flag == LIFT) //左转
{
num--;
flag = JUMP;
}
EXTI_ClearITPendingBit(EXTI_Line3);//清空中断标志位
}
}
void EXTI4_IRQHandler(void)
{
if(EXTI_GetFlagStatus(EXTI_Line4) == SET)
{
if(PAin(3) == 1) //右转
{
flag = RIGHT;
}
else if(PAin(3) == 0) //左转
{
flag = LIFT;
}
EXTI_ClearITPendingBit(EXTI_Line4);//清空中断标志位
}
}