跳转至

按键和发码使用指南

按键和发码指南*

本文主要介绍如何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. 相关文件介绍

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;
}