Skip to content

Button and Code Transmission User Guide

Button and RF Code Transmission Guide*

This document mainly introduces how to use button operations on the 8002 and how to use GPIO to simulate the wireless 433M RF protocol for communication.

FlexibleButton is a compact and flexible button processing library based on standard C language. It supports single click, repeated click, short press, long press, automatic debouncing, and allows free configuration of combination buttons. It can be used in interrupt and low-power scenarios. To adapt to the 8002 low-power scenario and the use of combination keys, it has been re-wrapped for ease of use.

Directory Structure*

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. Related File Introduction

  2. flexible_button.c, flexible_button.h are the source code of the standard C language button library. Open Source Address

  3. lvp_button.c, lvp_button.h are the re-wrapped implementations for using the flexible_button under Lvp low-power scenarios.

  4. lvp_app_remote_control.c is the APP application.

To use lvp_button.c, simply define the following three steps in the APP application.

Step 1: Define the required GPIO pins.

static GX_BUTTON_CONFIG btn_table[] = {
/*  Button GPIO   Button Name  Active Level  Long Press  Longer Press  Longest Press */
    {3,             "KEY1",    0,       1000,    3000,     7000},
    {4,             "KEY2",    0,       1000,    3000,     7000},
};

Step 2: Define the button event callback.

static int button_state = 0; // 0 means no button pressed, 1 means KEY1 single click, 2 means KEY2 double click, 3 means KEY1 and KEY2 pressed simultaneously. Redefine according to the project.
static void common_btn_evt_cb(void *arg)
{
    // This callback is within an interrupt, so do not perform time-consuming actions
    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 single click event
    if ((btn_event->id_1 == 3) && (btn_event->combination_button == 0) && (btn_event->event == FLEX_BTN_PRESS_CLICK)) {
        button_state = 1;
    }

     // KEY2 double click event
    if ((btn_event->id_1 == 4) && (btn_event->combination_button == 0) && (btn_event->event == FLEX_BTN_PRESS_DOUBLE_CLICK
)) {
        button_state = 2;
    }

    // KEY1 and KEY2 pressed simultaneously
    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;
    }
}

Step 3: Initialize the buttons. Note: Since a timer is used, the button initialization must be called every time after recovering from low-power mode.

lvp_button_init(btn_table, ARRAY_SIZE(btn_table), common_btn_evt_cb);

Explanation of btn_event

Each callback trigger will have a corresponding button event.

typedef struct button_state {
    int combination_button; // If 1, it represents a combination button (currently only supports two-button combinations). Then both id_1 and id_2 below are valid. If 0, it represents a single button, only id_1 is valid.
    unsigned char id_1;
    unsigned char id_2;
    unsigned char event; // CLICK, SHORT_START, LONG_START, LONG_HOLD_UP
}GX_BUTTON_STATE;

Event table. All button event triggers will generate the following events, such as press, click, double click, long press, etc.

typedef enum
{
    FLEX_BTN_PRESS_DOWN = 0,        // Press down event
    FLEX_BTN_PRESS_CLICK,           // Single click event
    FLEX_BTN_PRESS_DOUBLE_CLICK,    // Double click event
    FLEX_BTN_PRESS_REPEAT_CLICK,    // Repeat click event, use click_cnt in flex_button_t to determine the number of clicks
    FLEX_BTN_PRESS_SHORT_START,     // Short press start event
    FLEX_BTN_PRESS_SHORT_UP,        // Short press release event
    FLEX_BTN_PRESS_LONG_START,      // Long press start event
    FLEX_BTN_PRESS_LONG_UP,         // Long press release event
    FLEX_BTN_PRESS_LONG_HOLD,       // Long press hold event
    FLEX_BTN_PRESS_LONG_HOLD_UP,    // Long press hold release event
    FLEX_BTN_PRESS_MAX,             // Maximum duration
    FLEX_BTN_PRESS_NONE,
} flex_button_event_t;

About RF Code Transmission*

The code transmission protocol follows the wireless 433M RF communication method.

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

Transmitter Data Composition Description

        ALL data:  5bytes   ID(4) + CMD(1)
        LAST  ID  LOW 8  Bits  IS  CHANNEL    0≦ CHANNEL≦ 0xff

Encoding Definition Content

Button Definition Button Type Key Value Function
Up Single Key 0x88 Up
Down Single Key 0xcc Down
  1. Code Transmission Related Interfaces

Address Code:

To avoid RF interference, each address is required to be unique. Therefore, the unique ID from the 8002 Flash is used, and after CRC32 validation, 20 bits are taken as the address code.

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);
}
  1. Code Transmission Protocol Implementation

Note: The commonly used delay interface gx_udelay has a fixed overhead time. As the delay time increases, the error gradually decreases; however, for precise delays, the error can be relatively large.

For more precise delays, please refer to: High-Precision Delay Usage Guide.

// Code transmission rules. Note: When transmitting, interrupts must be disabled, otherwise the timing error will be significant.
// This function should be re-implemented according to the specific project.
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;                              // The address and data code to be sent

#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++){

        // Send the preamble code
        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);

        // Send the address code
        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);
            }
        }

        // Since interrupts are disabled, the button scanning timer is stopped. Therefore, the button scanning actions must be compensated based on the time spent transmitting, otherwise button presses during transmission may be lost.
        flex_button_scan();
        flex_button_scan();
        flex_button_scan();


        if (button_state != 0) // Indicates that a new button was triggered during transmission, so exit the current transmission and proceed to the next round.
        {
             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;
}