概要
CMS32L051使用外部中断的方式识别旋转编码器的方向。
- 选取其中一个信号A进行外部中断触发,由于信号A空闲时处于高电平,因此初始化时外部中断使用下降沿触发;
- 触发第一个下降沿后,判断当前是否已经触发了上升沿,如果已经触发上升沿,则需要判断当前与触发上升沿的时间间隔是否大于1ms,如果小于则不往下执行。大于1ms则清除上升沿触发标志,置位下降沿触发标志,记录当前触发的时间戳,改用上升沿触发。
- 如果触发第一个下降沿后,没有触发上升沿则置位下降沿触发标志,记录当前触发的时间戳,改用上升沿触发。
- 触发上升沿后,判断当前时间戳和第一个下降沿的时间戳差值,如果大于2ms则根据另一个信号B的电平状态判断旋钮的方向,低电平则是逆时针,高电平则是顺时针。清除下升沿触发标志,置位上降沿触发标志,记录当前触发的时间戳,改用上升沿触发。
- 如果小于2ms则不往下执行。加入触发间隔判断是为了消抖。消抖结果如下图所示,绿色的是使用GPIO在有效下降沿和上升沿进行的翻转,黄色是信号A。
代码
- bsp_knob.c
#include "UserConfigure.h"
typedef struct
{
uint8_t a_first_falling;
uint8_t a_first_rising;
uint8_t knob_value;
uint8_t last_knob_value;
uint32_t a_first_falling_tick;
uint32_t a_first_rising_tick;
} knob_mgr_t;
/* Private variables ---------------------------------------------------------*/
static knob_mgr_t knob_mgr = {
0
};
void bsp_knob_set_exti_rising(void)
{
INTM->EGN0 &= ~(1 << 2);
INTM->EGP0 &= ~(1 << 2);
INTM->EGN0 |= 0 << 2;
INTM->EGP0 |= 1 << 2;
}
void bsp_knob_set_exti_falling(void)
{
INTM->EGN0 &= ~(1 << 2);
INTM->EGP0 &= ~(1 << 2);
INTM->EGN0 |= 1 << 2;
INTM->EGP0 |= 0 << 2;
}
/********************************************************************************
*******************************************************************************/
void ENCODER_A_EXT_ISR(void)
{
uint32_t diff_tick = 0;
if (!knob_mgr.a_first_falling) {
if (knob_mgr.a_first_rising) {
diff_tick = TICK_DIFF(HAL_GetTick(), knob_mgr.a_first_rising_tick);
if (diff_tick >= 1) { // 大于1ms
knob_mgr.a_first_rising = 0;
knob_mgr.a_first_falling = 1;
bsp_knob_set_exti_rising(); // 上升沿
knob_mgr.a_first_falling_tick = HAL_GetTick();
DBG_OUT_L();
}
} else {
knob_mgr.a_first_falling = 1;
bsp_knob_set_exti_rising(); // 上升沿
knob_mgr.a_first_falling_tick = HAL_GetTick();
DBG_OUT_L();
}
} else {
diff_tick = TICK_DIFF(HAL_GetTick(), knob_mgr.a_first_falling_tick);
if (diff_tick >= 2) { // 大于2ms
if (Bit_RESET == GPIO_ReadInputDataBit(GPIO_PORT1, GPIO_Pin_3)) {
if (knob_mgr.knob_value) {
knob_mgr.knob_value--;
}
} else {
if (knob_mgr.knob_value < 0xff) {
knob_mgr.knob_value++;
}
}
DBG_OUT_H();
// printf("%u\r",diff_tick);
knob_mgr.a_first_falling = 0;
knob_mgr.a_first_rising = 1;
bsp_knob_set_exti_falling();
knob_mgr.a_first_rising_tick = HAL_GetTick();
}
}
INTC_ClearPendingIRQ(INTP2_IRQn);
}
/**************************************************************************************
***************************************************************************************/
void bsp_knob_init(void)
{
INTP_InitTypeDef INTP_InitStructure;
INTP_InitStructure.INTP_Select = INTP2 ; // 选择外部中断INTP0
INTP_InitStructure.EXTI_Trigger = Trigger_Falling; // 设置外部中断,下降沿触发
INTP_Init(&INTP_InitStructure);
ISR_Register(INTP2_IRQn, ENCODER_A_EXT_ISR); // 中断服务路径注册
NVIC_SetPriority(INTP2_IRQn, 1);
INTP_Start(INTP2); // Enable INTP2 Interrupt
}
void bsp_knop_poll(void)
{
if (knob_mgr.last_knob_value != knob_mgr.knob_value) {
printf("%u\r", knob_mgr.knob_value);
bsp_ws2812b_fill_solid_rgb(bsp_ws2812b_set_rgb(0, 0, knob_mgr.knob_value));
bsp_ws2812b_send();
knob_mgr.last_knob_value = knob_mgr.knob_value;
}
}
/***********************END OF FILE***********************/
小结
- 当前使用的时间计数是在定时器1ms中断中的,计时可能不是很准,不过对于旋钮的信号是够用的,如果需要使用精准定时,可以单独使用一个定时器进行计时。