一、旋转编码器介绍
1、编码器工作原理
编码器内部有一个开槽圆盘,连接到公共接地引脚 C。它还具有两个接触针 io A 和io B,旋钮转动会生成下列波形
2、旋转编码器的半码和全码
半码编码器:编码器拧动一次,A相和B相同时由高到低,转动半个周期为半码编码器
全码编码器:编码器拧动一次,A相和B相同时由高到低,再到高,转动一个周期。下图红色部分为一个周期
二、代码实现过程和原理
1、硬件io配置
2、定时器配置
配置循环扫描定时器,扫描二个io的状态,rdec_key_scan_para参数里面有二个io的状态获取函数。
#if TCFG_RDEC_KEY_ENABLE
extern const struct rdec_platform_data rdec_key_data;
extern struct key_driver_para rdec_key_scan_para;
err = rdec_key_init(&rdec_key_data);
if (err == 0) {
if(!rdec_timeid) {
rdec_timeid = sys_s_hi_timer_add((void *)&rdec_key_scan_para, key_driver_scan, rdec_key_scan_para.scan_time); //注册按键扫描定时器
}
}
#endif
3、代码实现原理
1、把a和b的二个io转动过程中的io电平变化记录下来
static u8 arry[4] = {0,0,0,0};
ptr++;
ptr %= 4;
arry[ptr] = now_ph;
s8 c_ptr=ptr;
u8 now_4ph[4];
for(u8 i=0;i<4;i++){
now_4ph[i] = arry[c_ptr];
c_ptr--;
if(c_ptr<0){
c_ptr+=4;
}
}
下面是全码编码器正转和反转的四种io电平状况。10代表a为1,b为0;
u8 io[4]={11,11,00,01};
u8 io[4]={11,10,00,01};
u8 io[4]={11,11,00,10};
u8 io[4]={11,01,00,10};
三、整体代码实现如下
#include "key_driver.h"
#include "gpio.h"
#include "system/event.h"
#include "app_config.h"
#include "audio_config.h"
#include "rdec_key.h"
#define TCFG_USR_RDEC_TIME 1
#define HALF_CODE_RDEC 1 //是否为半码编码器
#define RDEC_PHASE_A_IO TCFG_RDEC0_ECODE1_PORT // 编码器反转调换这两个口即可
#define RDEC_PHASE_B_IO TCFG_RDEC0_ECODE2_PORT
#if TCFG_USR_RDEC_TIME
#define SMARTWEAR_RDEC_KEY_NEW_METHOD_CFG 1
#if SMARTWEAR_RDEC_KEY_NEW_METHOD_CFG
/* #include "SmartWear_includes.h" */
#endif
#if TCFG_RDEC_KEY_ENABLE
static const struct rdec_platform_data *__this = NULL;
u8 rdec_get_key_value(void);
struct key_driver_para rdec_key_scan_para = {
.scan_time = 2, //按键扫描频率, 单位: ms
.last_key = NO_KEY, //上一次get_value按键值, 初始化为NO_KEY;
.filter_time = 0, //按键消抖延时;
.long_time = 75, //按键判定长按数量
.hold_time = (75 + 15), //按键判定HOLD数量
.click_delay_time = 0, //按键被抬起后等待连击延时数量
.key_type = KEY_DRIVER_TYPE_RDEC,
.get_value = rdec_get_key_value,
};
extern s8 get_rdec_rdat(int i);
extern u32 timer_get_ms(void);
#if SMARTWEAR_RDEC_KEY_NEW_METHOD_CFG
#define CAP_DIV 512
#define CAP_CLK 24000000
#define CEC_TMR JL_TIMER3
#define CEC_TMR_IDX IRQ_TIME3_IDX
#define CAP_RISING_EDGE 0b10
#define CAP_FALLING_EDGE 0b11
static u32 cap_mode;
static u32 phase_b_pre_state, phase_b_state;
static volatile int phase_data;
static void rdec_timer()
{
static u32 cnt = 0;
static u8 last_a_io = 255;
static u8 last_b_io = 255;
u8 a_io = gpio_read(RDEC_PHASE_A_IO);
u8 b_io = gpio_read(RDEC_PHASE_B_IO);
if(last_a_io!=a_io) {
last_a_io = a_io;
goto _check;
}
if(last_b_io!=b_io) {
last_b_io = b_io;
goto _check;
}
return;
_check:
wdt_clear();
static u8 last_ph = 0;
static s8 count = 0;
static u8 now_ph =0;
now_ph = a_io+(b_io*10);
last_ph = now_ph;
static u8 ptr=0;
static u8 arry[4] = {0,0,0,0};
ptr++;
ptr %= 4;
arry[ptr] = now_ph;
s8 c_ptr=ptr;
u8 now_4ph[4];
for(u8 i=0;i<4;i++){
now_4ph[i] = arry[c_ptr];
c_ptr--;
if(c_ptr<0){
c_ptr+=4;
}
}
#if HALF_CODE_RDEC
u8 a1[3]={00,10,11};
u8 a2[3]={11,01,00};
u8 b1[4]={11,10,00};
u8 b2[4]={00,01,11};
if(memcmp(now_4ph,a1,3)==0 || memcmp(now_4ph,a2,3)==0)
{
phase_data++;
return ;
}
else if(memcmp(now_4ph,b1,3)==0 || memcmp(now_4ph,b2,3)==0)
{
phase_data--;
return ;
}
#else
u8 a1[4]={11,11,00,01};
u8 a2[4]={11,10,00,01};
u8 b1[4]={11,11,00,10};
u8 b2[4]={11,01,00,10};
if(memcmp(now_4ph,a1,4)==0 || memcmp(now_4ph,a2,4)==0)
{
phase_data++;
return ;
}
if(memcmp(now_4ph,b1,4)==0 || memcmp(now_4ph,b2,4)==0)
{
phase_data--;
return ;
}
#endif
}
static void timer_rdec_port_init(u8 port)
{
gpio_set_pull_down(port, 0);
gpio_set_pull_up(port, 1);
gpio_set_die(port, 1);
gpio_set_direction(port, 1);
}
static void timer_rdec_port_uninit(u8 port)
{
gpio_set_pull_down(port, 1);
gpio_set_pull_up(port, 0);
gpio_set_die(port, 1);
gpio_set_direction(port, 1);
}
int timer_rdec_init(const struct rdec_platform_data *user_data)
{
timer_rdec_port_init(RDEC_PHASE_A_IO);
timer_rdec_port_init(RDEC_PHASE_B_IO);
phase_data = 0;
return 0;
}
#endif
u8 rdec_get_key_value(void)
{
int i;
s8 rdec_data=0;
u8 key_value = 0;
if (!__this->enable) {
return NO_KEY;
}
#if SMARTWEAR_RDEC_KEY_NEW_METHOD_CFG
for (i = 0; i < 1; i++)
#else
for (i = 0; i < 3; i++)
#endif
{
#if SMARTWEAR_RDEC_KEY_NEW_METHOD_CFG
rdec_timer();
rdec_data = phase_data;
phase_data = 0;
if (rdec_data < 0) {
key_value = __this->rdec[i].key_value0;
printf("rdec_data_1 = %d\n", rdec_data);
return key_value;
} else if (rdec_data > 0) {
key_value = __this->rdec[i].key_value1;
printf("rdec_data_2 = %d\n", rdec_data);
return key_value;
}
#else
rdec_data = get_rdec_rdat(i);
#endif
}
return NO_KEY;
}
int rdec_key_init(const struct rdec_platform_data *rdec_key_data)
{
__this = rdec_key_data;
if (!__this) {
return -EINVAL;
}
if (!__this->enable) {
return KEY_NOT_SUPPORT;
}
//printf("rdec_key_init >>>> ");
#if SMARTWEAR_RDEC_KEY_NEW_METHOD_CFG
return timer_rdec_init(rdec_key_data);
#else
return rdec_init(rdec_key_data);
#endif
}
/*------------------------------------------------------*/
// low power
#include "asm/power_interface.h"
#include "asm/power/power_api.h"
#define AT_VOLATILE_RAM_CODE AT(.volatile_ram_code)
AT_VOLATILE_RAM_CODE
extern const struct rdec_platform_data rdec_key_data;
static void rdec_enter_deepsleep(void)
{
// timer_rdec_port_uninit(RDEC_PHASE_A_IO);
// timer_rdec_port_uninit(RDEC_PHASE_B_IO);
}
AT_VOLATILE_RAM_CODE
static void rdec_exit_deepsleep(void)
{
timer_rdec_port_init(RDEC_PHASE_A_IO);
timer_rdec_port_init(RDEC_PHASE_B_IO);
}
DEEPSLEEP_TARGET_REGISTER(rdec) = {
.name = "rdec",
.enter = rdec_enter_deepsleep,
.exit = rdec_exit_deepsleep,
};
/*------------------------------------------------------*/
#endif /* #if TCFG_RDEC_KEY_ENABLE */
#endif