STM32 LVGL Input device with GPIO

기존 개발 코드는 Input device를 GPIO로 받아서 처리하였는데, LVGL input device와 연결하지 않고 GPIO를 직접 컨트롤하여 key pressed, key released 및 repeat key 처리를 구현하였다. 이렇게 직접 코딩한 이유가 있겠지만, 키처리 코드는 매우 복잡하며 메뉴하고 연결한 부분은 완전 블랙홀에 가까워 LVGL input device에 GPIO를 연결하여 키 처리하는 방법을 찾아 블로그에 정리한다.

 

우선 개발보드에는 버튼을 위해 아래와 같이 5개의 GPIO input을 할당하였으며, 5개의 GPIO 버튼을 LVGL 키패드로 연결하여 사용하려고 한다.

uint8_t gpio_get_key_state(uint8_t key_code)
{
    uint8_t value = 0;

    /**
     * 
     * KEY Pressed  : 0
     * KEY Released : 1
     * 
     */

    if(key_code >= MAX_KEY_NUM)
        return 0xFF;

    switch(key_code)
    {
        case KEY_UP:   value = HAL_GPIO_ReadPin(UP_KEY_GPIO_Port,      UP_KEY_Pin);      break;
        case KEY_DOWN: value = HAL_GPIO_ReadPin(DOWN_KEY_GPIO_Port,    DOWN_KEY_Pin);    break;
        case KEY_MENU: value = HAL_GPIO_ReadPin(MENU_KEY_GPIO_Port,    MENU_KEY_Pin);    break;
        case KEY_OK:   value = HAL_GPIO_ReadPin(OK_KEY_GPIO_Port,      OK_KEY_Pin);      break;
        case KEY_ESC:  value = HAL_GPIO_ReadPin(RESERVE_KEY_GPIO_Port, RESERVE_KEY_Pin); break;

        default:
            printf("ERROR :: %s() : key_code = %d\r\n", __func__, key_code);
            return 0xFF;
    }
    return value;
}

위 함수는 GPIO 상태를 LVGL의 KeyPad로 변환하는 함수이며, LVGL Input device을 아래와 같이 설정하도록 한다. LVGL input device를 사용할 경우 repeat key 등과 같은 처리는 물론 쉽게 LVGL 위젯과 연결하여 사용이 가능하다.

void lvgl_init(void)
{
    ...
    
    memset(prev_key, 0x01, sizeof(prev_key));
    lv_group_t * g = lv_group_create();
    lv_group_set_default(g);

    lv_indev_drv_init(&indev_drv);
    indev_drv.type    = LV_INDEV_TYPE_KEYPAD;
    indev_drv.read_cb = gpio_read_button;
    lv_indev_t *indev = lv_indev_drv_register(&indev_drv);
    lv_indev_set_group(indev, g);
    
    ...
}

LVGL Input device에 대한 자세한 설명은 아래 문서를 참고하도록 하며, 이 포스트는 LVGL Input device를 KeyPad로 설정하여 사용하였다.

 

Input device interface — LVGL documentation

To use an Encoder (similarly to the Keypads) the objects should be added to groups. Using buttons with Encoder logic In addition to standard encoder behavior, you can also utilize its logic to navigate(focus) and edit widgets using buttons. This is especia

docs.lvgl.io

여기서 중요한 설정은 default group 및 input device group을 설정해야 한다. 이 설정을 생략할 경우 키 이벤트 처리에서 생각과 다르게 동작하는 것을 확인할 수 있을 것이다. gpio_read_button 콜백함수에서 GPIO 입력을 KeyPad로 변환하는 작업을 아래와 같이 해야 한다.

static void gpio_read_button(lv_indev_drv_t * indev_driver, lv_indev_data_t * data)
{
    for(int i = 0 ; i < MAX_KEY_NUM ; i++)
    {
        uint8_t state = gpio_get_key_state(i);
        if( state == 0 /* for repeat */ || prev_key[i] != state)
        {
            switch(i)
            {
                case KEY_UP:   data->key = LV_KEY_UP;    break;
                case KEY_DOWN: data->key = LV_KEY_DOWN;  break;
                case KEY_MENU: data->key = LV_KEY_HOME;  break;
                case KEY_OK:   data->key = LV_KEY_ENTER; break;
                case KEY_ESC:  data->key = LV_KEY_ESC;   break;
            }

            if(state == 0) data->state = LV_INDEV_STATE_PRESSED;
            else           data->state = LV_INDEV_STATE_RELEASED;

            prev_key[i] = state;
        }
    }
}

아래 영상은 위 코드를 사용하여 LVGL 8.0에서 제공하는 Grid Layout으로 간단한 메뉴를 구성하여 GPIO 버튼으로 메뉴 아이템 이동 및 Repeat key 테스트를 한 것이다.

LVGL Input device를 사용하면 메뉴 항목 이동을 위한 키이벤트 처리를 간단히 할 수 있는데, 이것을 쌩으로 코딩하였는지 알수는 없지만, LVGL Input device를 사용하여 기존 코드보다 간결하게 구성을 할 수 있을 것 같다.