按键和发码使用指南
按键和发码指南*
本文主要介绍如何8002如果使用按键操作和用GPIO模拟无线433M射频协议进行通讯。
FlexibleButton 是一个基于标准 C 语言的小巧灵活的按键处理库,支持单击、连击、短按、长按、自动消抖,可以自由设置组合按键,可用于中断和低功耗场景。为了适配8002低功耗场景和组合键的使用对它做了二次封装方便使用。
目录结构*
remote_control
├── app.mk
├── app.name
├── BUTTON.md
├── flexible_button.c
├── flexible_button.h
├── Kconfig
├── lvp_app_remote_control.c
├── lvp_button.c
├── lvp_button.h
└── ReadMe
- 相关文件介绍
1.flexible_button.c, flexible_button.h标准C语言的按键库的源代码。开源地址
2.lvp_button.c, lvp_button.h 是针对Lvp 低功耗下flexible_button按键使用做了二次封装。
3.lvp_app_remote_control.c APP 应用。
按键相关使用接口*
使用lvp_button.c 只需要在APP应用里定义好以下三步即可使用。
第一步:定义好需要使用的GPIO口。
static GX_BUTTON_CONFIG btn_table[] = {
/* 按键对应的GPIO 按键名称 按下有效电平 持续长按 持续更长按 持续最长按*/
{3, "KEY1", 0, 1000, 3000, 7000},
{4, "KEY2", 0, 1000, 3000, 7000},
};
第二步:定义按键事件回调。
static int button_state = 0; // 0 代表无按键, 1 代表KEY1单击,2 代表key2双击,3 代表 key0 key1同时按下, 根据项目,自己重新定义实现
static void common_btn_evt_cb(void *arg)
{
// 该回调是在中断内的,因此不要做或多耗时间的动作
GX_BUTTON_STATE *btn_event = (GX_BUTTON_STATE *)arg;
printf("combination = %d, id_1 = %d, id_2 = %d, event = %s\n", btn_event->combination_button, btn_event->id_1, btn_event->id_2, enum_event_string[btn_event->event]);
// KEY1 发生一次单击事件
if ((btn_event->id_1 == 3) && (btn_event->combination_button == 0) && (btn_event->event == FLEX_BTN_PRESS_CLICK)) {
button_state = 1;
}
// KEY2 发生一次单击事件
if ((btn_event->id_1 == 4) && (btn_event->combination_button == 0) && (btn_event->event == FLEX_BTN_PRESS_DOUBLE_CLICK
)) {
button_state = 2;
}
// KEY1 和 KEY2 同时按下
if ((btn_event->id_1 == 3 && btn_event->id_2 == 4) && (btn_event->combination_button == 1) && (btn_event->event == FLEX_BTN_PRESS_DOWN)) {
button_state = 3;
}
}
第三步:初始化按键。注意:因为用到了定时器,所有每次低功耗下恢复都要调用一次按键初始化。
lvp_button_init(btn_table, ARRAY_SIZE(btn_table), common_btn_evt_cb);
解析下btn_event
每次回调触发都会有相应的按键事件
typedef struct button_state {
int combination_button; // 如果为1,代表是组合按键(目前仅支持两个按键组合)。那么下面的id_1和id_2都有效。如果为0,代表单按键,仅id_1有效
unsigned char id_1;
unsigned char id_2;
unsigned char event; // CLICK, SHORT_START, LONG_START, LONG_HOLD_UP
}GX_BUTTON_STATE;
事件event 表。所有按键事件触发都会产生以下事件,如按下、点击、双击、长按等。
typedef enum
{
FLEX_BTN_PRESS_DOWN = 0, // 按下事件
FLEX_BTN_PRESS_CLICK, // 单击事件
FLEX_BTN_PRESS_DOUBLE_CLICK, // 双击事件
FLEX_BTN_PRESS_REPEAT_CLICK, // 连击事件,使用 flex_button_t 中的 click_cnt 断定连击次数
FLEX_BTN_PRESS_SHORT_START, // 短按开始事件
FLEX_BTN_PRESS_SHORT_UP, // 短按抬起事件
FLEX_BTN_PRESS_LONG_START, // 长按开始事件
FLEX_BTN_PRESS_LONG_UP, // 长按抬起事件
FLEX_BTN_PRESS_LONG_HOLD, // 长按保持事件
FLEX_BTN_PRESS_LONG_HOLD_UP, // 长按保持的抬起事件
FLEX_BTN_PRESS_MAX, // 最大时长
FLEX_BTN_PRESS_NONE,
} flex_button_event_t;
关于RF发码*
发码协议遵循无线433M射频通讯方式
Head: H 4.8ms ; L 1.5ms
4.8ms
________________ ______
_____| |______|
1.5ms
Data0: H 360us ; L 720us
360us
__ ______
_____| |______|
720us
Data1: H 720us ; L 360us
720us
______ ______
_____| |__|
360us
发射器数据组成说明
ALL data: 5bytes ID(4) + CMD(1)
LAST ID LOW 8 Bits IS CHANNEL 0≦ CHANNEL≦ 0xff
编码定义内容
按键定义 | 按键类型 | 键值 | 功能 |
---|---|---|---|
上升 | 单键 | 0x88 | 上升 |
下降 | 单键 | 0xcc | 下降 |
1.发码相关接口
地址码:
由于要避免RF干扰,要求每个地址是唯一的,所以使用8002 Flash获取唯一id,做crc32校验后去取20位作为地址码。
unsigned int rf_address = 0x00AEE471;
void getUniqueId(void)
{
unsigned char buf[16];
int ret_len;
gx_spinor_flash_init();
gx_spinor_flash_getuid(buf, 16 , &ret_len);
for (int j = 0; j < 16;j++) {
printf("%02X ", buf[j]);
}
printf("\n");
rf_address = crc32(0, buf, ret_len);
// return (((temp>>20)^temp)&0x000ffff);
}
2.发码协议实现
注意:常用的延时接口gx_udelay存在固定耗时,随着delay时间增大,误差会逐渐减小;在做精确延时的时候,误差会较大。
如果要用更精确延时可以参考:高精度延时使用指南。
// 发码规则 注意:发码的时候,必现关中断模式下,否则时间误差会很大
// 该函数,请根据具体的项目重新实现
int rf_send_code(unsigned int code)
{
if(code == 0x00) return -1;
LvpAudioInSuspend();
while (gx_snpu_get_state() == GX_SNPU_BUSY);
uint32_t irq_state = gx_lock_irq_save();
gx_gpio_set_direction(RF_GPIO, GX_GPIO_DIRECTION_OUTPUT);
gx_gpio_set_level(RF_GPIO, GX_GPIO_LEVEL_LOW);
unsigned int send_code = code; // 需要发送的地址吗以及数据码
#ifdef DEBUG_PRINTF
printf("rf_address = %#x send_code = %#x\n",rf_address, send_code);
#endif
// rf_address = 0x00AEE471;
for(int j = 0;j < SEND_TIMES;j++){
// 发送引导码
gx_gpio_set_level(RF_GPIO, GX_GPIO_LEVEL_HIGH);
gx_udelay(4720);
gx_gpio_set_level(RF_GPIO, GX_GPIO_LEVEL_LOW);
gx_udelay(1420);
// 发送地址码
for(int i = 31;i >= 0;i--){
if(rf_address & (1 << i)){
gx_gpio_set_level(RF_GPIO, GX_GPIO_LEVEL_HIGH);
gx_udelay(640);
gx_gpio_set_level(RF_GPIO, GX_GPIO_LEVEL_LOW);
gx_udelay(280);
}else{
gx_gpio_set_level(RF_GPIO, GX_GPIO_LEVEL_HIGH);
gx_udelay(280);
gx_gpio_set_level(RF_GPIO, GX_GPIO_LEVEL_LOW);
gx_udelay(640);
}
}
for(int i = 0;i < 8;i++){
if(send_code & (1 << i)){
gx_gpio_set_level(RF_GPIO, GX_GPIO_LEVEL_HIGH);
gx_udelay(640);
gx_gpio_set_level(RF_GPIO, GX_GPIO_LEVEL_LOW);
gx_udelay(280);
}else{
gx_gpio_set_level(RF_GPIO, GX_GPIO_LEVEL_HIGH);
gx_udelay(280);
gx_gpio_set_level(RF_GPIO, GX_GPIO_LEVEL_LOW);
gx_udelay(640);
}
}
// 因为在关中断内,扫描按键的定时器停止,因此,必现根据发码用了多少时间,把扫描按键的动作补回来,否则会在发码的过程按键,导致按键概率丢失
flex_button_scan();
flex_button_scan();
flex_button_scan();
if (button_state != 0) // 表示在发码的过程中,又有新的按键触发了,就退出发码,进行下一轮新的发码
{
printf("break send code irq %d, %d\n", j, button_state);
return 0;
}
}
gx_gpio_set_level(RF_GPIO, GX_GPIO_LEVEL_HIGH);
gx_gpio_set_level(RF_GPIO, GX_GPIO_LEVEL_LOW);
gx_unlock_irq_restore(irq_state);
LvpAudioInResume();
return 0;
}