EC11 参数
脉冲数:20个
带开关:有
操作寿命:50000次
最大额定:10mA 5V DC
轴型:半轴柄/梅花柄
EC11 原理图 EC11 一共有5个引脚:
S1 按钮引脚1
S2 按钮引脚2
A A相引脚
B B 相引脚
C 是 A和B的公共引脚。
3个10K电阻是上拉电阻,默认三个引脚都输出高电平;3个470电阻是输出电阻,也可以 不要。
旋转编码器正转时的时序波形
旋转编码器正反转判断 通过编码器旋转时的波形可以看到,如果 A 下降沿时,判断B的电平高低,如果为高是一个方向如果是低则为另一个方向。
分享一个国外的EC11 硬件消抖方案 博客网址:Strömlinge: Rotary Encoder Debouncer (stroemlinge.blogspot.com)
方案文档下载:EC11 debouncer - Google 云端硬盘
这个方案用到的两个芯片为非门反相器集成电路,一个一路的一个两路的。
由于该方案用到了反相器,所以默认3个IO口输出为低电平。单片机外部中断要设置成上升沿触发。
原理图
分享一个国外的EC11软件消抖解决方案 YouTube 地址: Coding a KY-040 Rotary Encoder on a Raspberry Pi Pico - Detailed Explanation & Step by Step Code
GitHub 地址:gurgleapps/rotary-encoder: Code to drive a rotary encoder in micropython (github.com)
该项目用的硬件是树莓派这里来分析下代码实现:
rotary.py 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 import machineimport utime as timefrom machine import Pinimport micropythonclass Rotary : ROT_CW = 1 ROT_CCW = 2 SW_PRESS = 4 SW_RELEASE = 8 def __init__ (self,dt,clk,sw ): self.dt_pin = Pin(dt, Pin.IN) self.clk_pin = Pin(clk, Pin.IN) self.sw_pin = Pin(sw, Pin.IN) self.last_status = (self.dt_pin.value() << 1 ) | self.clk_pin.value() self.dt_pin.irq(handler=self.rotary_change, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING ) self.clk_pin.irq(handler=self.rotary_change, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING ) self.sw_pin.irq(handler=self.switch_detect, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING ) self.handlers = [] self.last_button_status = self.sw_pin.value() def rotary_change (self, pin ): new_status = (self.dt_pin.value() << 1 ) | self.clk_pin.value() if new_status == self.last_status: return transition = (self.last_status << 2 ) | new_status if transition == 0b1110 : micropython.schedule(self.call_handlers, Rotary.ROT_CW) elif transition == 0b1101 : micropython.schedule(self.call_handlers, Rotary.ROT_CCW) self.last_status = new_status def switch_detect (self,pin ): if self.last_button_status == self.sw_pin.value(): return self.last_button_status = self.sw_pin.value() if self.sw_pin.value(): micropython.schedule(self.call_handlers, Rotary.SW_RELEASE) else : micropython.schedule(self.call_handlers, Rotary.SW_PRESS) def add_handler (self, handler ): self.handlers.append(handler) def call_handlers (self, type ): for handler in self.handlers: handler(type )
main.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from rotary import Rotaryimport utime as timerotary = Rotary(0 ,1 ,2 ) val = 0 def rotary_changed (change ): global val if change == Rotary.ROT_CW: val = val + 1 print (val) elif change == Rotary.ROT_CCW: val = val - 1 print (val) elif change == Rotary.SW_PRESS: print ('PRESS' ) elif change == Rotary.SW_RELEASE: print ('RELEASE' ) rotary.add_handler(rotary_changed) while True : time.sleep(0.1 )
我用 GD32 的实现 1 2 3 4 5 6 7 8 9 10 11 void EXTI0_1_IRQHandler (void ) { if (SET == exti_interrupt_flag_get(ENCODER_CLK_EXTI_LINE)){ ec11_1.clk_flag = 1 ; exti_interrupt_flag_clear(ENCODER_CLK_EXTI_LINE); } }
encoder.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 void encoder_handle (void ) { FlagStatus SW_State = gpio_input_bit_get(ENCODER_SW_GPIO_PORT, ENCODER_SW_PIN); if (ec11_1.sw_down_flag == 1 && ec11_1.sw_down_time > 10 && ec11_1.sw_down_count == 0 ) { if (SW_State == RESET) { ec11_1.sw_down_count = 1 ; } } if (ec11_1.sw_down_count == 1 && ec11_1.sw_down_time > 500 && SW_State == RESET) { ec11_1.sw_mode_flag = 2 ; } if (ec11_1.sw_down_count == 1 && ec11_1.sw_down_time > 500 && SW_State == SET) { ec11_1.sw_down_count = 0 ; ec11_1.sw_down_time = 0 ; ec11_1.sw_down_flag = 0 ; ec11_1.sw_mode_flag = 0 ; } if (ec11_1.sw_down_count == 1 && SW_State == SET && ec11_1.sw_down_time < 500 ) { ec11_1.sw_down_count = 2 ; ec11_1.sw_down_time = 0 ; } if (ec11_1.sw_down_flag == 1 && ec11_1.sw_down_count == 2 && ec11_1.sw_down_time > 100 ) { if (SW_State == RESET) { ec11_1.sw_mode_flag = 3 ; ec11_1.sw_down_time = 0 ; ec11_1.sw_down_flag = 0 ; ec11_1.sw_down_count = 0 ; } else { ec11_1.sw_mode_flag = 1 ; ec11_1.sw_down_time = 0 ; ec11_1.sw_down_flag = 0 ; ec11_1.sw_down_count = 0 ; } } if (ec11_1.sw_mode_flag == 2 ) { if (ec11_1.sw_long_press_time == LED_AUTO_SETP) { test_number = 0 ; ec11_1.sw_long_press_time = 0 ; } FlagStatus sw_state_temp = gpio_input_bit_get(ENCODER_SW_GPIO_PORT, ENCODER_SW_PIN); if (sw_state_temp == SET) { ec11_1.sw_mode_flag = 0 ; } } if (ec11_1.sw_mode_flag == 1 ) { ec11_1.sw_mode_flag = 0 ; } if (ec11_1.sw_mode_flag == 3 ) { ec11_1.sw_mode_flag = 0 ; } if (ec11_1.clk_rotate_flag == 1 ) { FlagStatus clk_value = gpio_input_bit_get(ENCODER_CLK_GPIO_PORT, ENCODER_CLK_PIN); FlagStatus dt_value = gpio_input_bit_get(ENCODER_DT_GPIO_PORT, ENCODER_DT_PIN); uint8_t dt_temp = (uint8_t )dt_value; uint8_t clk_temp = (uint8_t )clk_value; rotary_chanage(dt_temp, clk_temp); if (ec11_1.directron == 1 ) { test_number++; ec11_1.directron = 0 ; ec11_1.clk_rotate_flag = 0 ; } if (ec11_1.directron == 2 ) { test_number--; ec11_1.directron = 0 ; ec11_1.clk_rotate_flag = 0 ; } } } static void rotary_chanage (uint8_t clk, uint8_t dt) { ec11_1.new_status = (dt << 1 ) | (clk); if (ec11_1.new_status == ec11_1.last_status) { return ; } uint8_t transition = (ec11_1.last_status << 2 ) | ec11_1.new_status; if (transition == 0x0e ){ ec11_1.directron = 1 ; } if (transition == 0x0d ){ ec11_1.directron = 2 ; } ec11_1.last_status = ec11_1.new_status; }
一个国内论坛提供的软件消抖方案(推荐) 以上几个方案都不完美,国外那个用硬件(非门)实现的方案我没有经过实践效果还不知怎样;另一个基于树莓派的那个方案经过实践还是有微弱抖动,当没有旋转到位时也会有动作。最后这个国内论坛提供的方案,经过我的测试效果非常完美,特此再次分享给大家。
论坛内的代码例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void interrupt main_int (void ) { if (RBIF) { if (int_nu==0 && KEY_A==0 ) { flag=0 ; if (KEY_B) flag=1 ; int_nu=1 ; } if (int_nu && KEY_A) { if (KEY_B==0 && flag==1 ) --power_m; if (KEY_B && flag==0 ) ++power_m; int_nu=0 ; } RBIF=0 ; }
论坛地址:解决旋转编码抖动的方案 - Arduino - 极客工坊 - Powered by Discuz! (geek-workshop.com)
我的代码 GD32 encoder.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 typedef struct { uint16_t sw_down_time; uint8_t sw_mode_flag; uint8_t sw_down_flag; uint8_t sw_down_count; uint16_t sw_long_press_time; uint8_t clk_flag; uint8_t directron; uint16_t clk_count; } EC11_t; EC11_t ec11_1 = { .sw_down_count = 0 , .sw_down_flag = 0 , .sw_down_time = 0 , .sw_long_press_time = 0 , .sw_mode_flag = 0 , .clk_flag = 0 , .directron = 0 , .clk_count = 0 , };
gd32f1x0_it.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 void EXTI0_1_IRQHandler (void ) { FlagStatus clk_value = gpio_input_bit_get(ENCODER_CLK_GPIO_PORT, ENCODER_CLK_PIN); FlagStatus dt_value = gpio_input_bit_get(ENCODER_DT_GPIO_PORT, ENCODER_DT_PIN); if (SET == exti_interrupt_flag_get(ENCODER_CLK_EXTI_LINE) && clk_value == RESET && ec11_1.clk_count == 0 ) { ec11_1.clk_flag = 0 ; if (dt_value) ec11_1.clk_flag = 1 ; ec11_1.clk_count = 1 ; exti_interrupt_flag_clear(ENCODER_CLK_EXTI_LINE); } if (SET == exti_interrupt_flag_get(ENCODER_CLK_EXTI_LINE) && clk_value == SET && ec11_1.clk_count == 1 ) { if (dt_value ==0 && ec11_1.clk_flag == 1 ) test_number++; if (dt_value && ec11_1.clk_flag == 0 ) { test_number--; } ec11_1.clk_count = 0 ; exti_interrupt_flag_clear(ENCODER_CLK_EXTI_LINE); } }