HamsterBear Linux Low Res ADC按鍵驅動的適配 + LVGL button移植

jensenhua發表於2022-05-10

HamsterBear lradc按鍵驅動的適配

  • 平臺 - F1C200s
  • Linux版本 - 5.17.2
  • ADC按鍵 - 4 KEY tablet

驅動程式位於主線核心:

  • drivers/input/keyboard/sun4i-lradc-keys.c

裝置樹binding

  • Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml

適配流程

開啟驅動程式編譯開關

進入kernel目錄,執行make menuconfig

輸入/後搜尋KEYBOARD_SUN4I_LRADC
image

1跳轉到選項位置,修改選項為*後儲存退出
image

檢視裝置樹Binding,並修改新增裝置樹節點

示例如下

examples:
  - |
    lradc: lradc@1c22800 {
        compatible = "allwinner,sun4i-a10-lradc-keys";
        reg = <0x01c22800 0x100>;
        interrupts = <31>;
        vref-supply = <&reg_vcc3v0>;

        button-191 {
            label = "Volume Up";
            linux,code = <115>;
            channel = <0>;
            voltage = <191274>;
        };

        button-392 {
            label = "Volume Down";
            linux,code = <114>;
            channel = <0>;
            voltage = <392644>;
        };
    };

修改後的lradc節點
底板有4個按鍵,linux,code對應input evnet按鍵code值

        lradc: lradc@1c23400 {
                compatible = "allwinner,sun4i-a10-lradc-keys";
                reg = <0x01c23400 0x100>;
                interrupts = <22>;
                vref-supply = <&reg_vcc3v3>;

                button-132 {
                    label = "PRE";
                    linux,code = <105>;
                    channel = <0>;
                    voltage = <174603>;
                };

                button-196 {
                    label = "NEXT";
                    linux,code = <106>;
                    channel = <0>;
                    voltage = <419047>;
                };

                button-233 {
                    label = "OK";
                    linux,code = <28>;
                    channel = <0>;
                    voltage = <698412>;
                };

                button-271 {
                    label = "BACK";
                    linux,code = <14>;
                    channel = <0>;
                    voltage = <803174>;
                };
        };

裝置註冊到/dev/input/event0
驅動程式上報的資料

[21037.576786] adckey val: 5, voltage: 174603
0000000 522d 0000 cecc 0008 0001 0069 0001 0000
0000010 522d 0000 cecc 0008 0000 0000 0000 0000
0000020 522d 0000 0bd6 000c 0001 0069 0000 0000
0000030 522d 0000 0bd6 000c 0000 0000 0000 0000
[21038.829430] adckey val: 12, voltage: 419047
0000040 522e 0000 aa05 000c 0001 006a 0001 0000
0000050 522e 0000 aa05 000c 0000 0000 0000 0000
0000060 522f 0000 f228 0000 0001 006a 0000 0000
0000070 522f 0000 f228 0000 0000 0000 0000 0000
[21041.763838] adckey val: 19, voltage: 663492
0000080 5231 0000 a9cd 000b 0001 001c 0001 0000
0000090 5231 0000 a9cd 000b 0000 0000 0000 0000
00000a0 5232 0000 4117 0000 0001 001c 0000 0000
00000b0 5232 0000 4117 0000 0000 0000 0000 0000
[21042.978050] adckey val: 25, voltage: 873015
00000c0 5232 0000 ee8e 000e 0001 000e 0001 0000
00000d0 5232 0000 ee8e 000e 0000 0000 0000 0000
00000e0 5233 0000 5964 0003 0001 000e 0000 0000
00000f0 5233 0000 5964 0003 0000 0000 0000 0000

LVGL的適配

修改官方移植模板檔案lv_port_indev_template.c

/* lv_port_indev_linux.c */
void lv_port_indev_init(void)
{
    /**
     * Here you will find example implementation of input devices supported by LittelvGL:
     *  - Touchpad
     *  - Mouse (with cursor support)
     *  - Keypad (supports GUI usage only with key)
     *  - Encoder (supports GUI usage only with: left, right, push)
     *  - Button (external buttons to press points on the screen)
     *
     *  The `..._read()` function are only examples.
     *  You should shape them according to your hardware
     */

    static lv_indev_drv_t indev_drv;

	...
	
	...
	
    /*------------------
     * Button
     * -----------------*/

    /*Initialize your button if you have*/
    button_init();

    /*Register a button input device*/
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_BUTTON;
    indev_drv.read_cb = button_read;
    indev_button = lv_indev_drv_register(&indev_drv);

    /*Assign buttons to points on the screen*/
    static const lv_point_t btn_points[4] = {
        {102, 215},   /* Button 0 -> x:102; y:215 */
        {180, 216},   /* Button 1 -> x:180; y:216 */
        {140, 120},
        {142, 215},
    };
    lv_indev_set_button_points(indev_button, btn_points);
}

static int button_fd;
static struct input_event events[2];
static struct input_event event;
/*Initialize your buttons*/
static void *button_input_thread_function(void *privdata)
{
    while(1){
        if(read(button_fd, &events, 2*sizeof(struct input_event)) > 0){
                // printf("type : %d, code : %d, value : %d\n", events[0].type, events[0].code, events[0].value);
        }
        event = events[0];
        // pthread_mutex_lock(&g_mutex);
        // pthread_cond_signal(&g_cond);
        // pthread_mutex_unlock(&g_mutex);
    }

}

static void button_init(void)
{
    /*Your code comes here*/
    pthread_t tid;

    button_fd = open("/dev/input/event0", O_RDONLY);

    pthread_create(&tid, NULL, button_input_thread_function, NULL);
}

/*Will be called by the library to read the button*/
static void button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{

    static uint8_t last_btn = 0;

    /*Get the pressed button's ID*/
    int8_t btn_act = button_get_pressed_id();
    
    if(btn_act >= 0) {
        data->state = LV_INDEV_STATE_PR;
        last_btn = btn_act;
    }
    else {
        data->state = LV_INDEV_STATE_REL;
    }

    /*Save the last pressed button's ID*/
    data->btn_id = last_btn;
}

/*Get ID  (0, 1, 2 ..) of the pressed button*/
static int8_t button_get_pressed_id(void)
{
    uint8_t key = -1;

    /*Check to buttons see which is being pressed (assume there are 2 buttons)*/
    // pthread_mutex_lock(&g_mutex);
    // pthread_cond_wait(&g_cond, &g_mutex);
    // pthread_mutex_unlock(&g_mutex);
    switch(event.code){
    case KEY_LEFT:
        key = 0;
        break;
    case KEY_RIGHT:
        key = 1;
        break;
    case KEY_ENTER:
        key = 2;
        break;
    case KEY_BACKSPACE:
        key = 3;
        break;
    default:
        key = -1;
        break;
    }

    return (key + event.value)>key?key:-1;
    /*No button pressed*/
}

/*Test if `id` button is pressed or not*/
static bool button_is_pressed(uint8_t id)
{
    /*Your code comes here*/

    return false;
}

簡單解釋下程式碼中的一些操作:

在主init函式中設定的這個陣列

static const lv_point_t btn_points[4] = {
	{102, 215},   /* Button 0 -> x:102; y:215 */
	{180, 216},   /* Button 1 -> x:180; y:216 */
	{140, 120},
	{142, 215},
};

是用來模擬點選lvgl螢幕的某個x,y位置,設定完該陣列後,需要將其與indev_drv關聯起來

//lv_indev_t * indev_button;

lv_indev_set_button_points(indev_button, btn_points);

為什麼要一次讀兩個event?

static struct input_event events[2];
static struct input_event event;
/*Initialize your buttons*/
static void *button_input_thread_function(void *privdata)
{
    while(1){
        if(read(button_fd, &events, 2*sizeof(struct input_event)) > 0){

因為按下和鬆開都算一次event,只讀一次會讀到鬆開的value。

這個return是什麼意思?

/*Get ID  (0, 1, 2 ..) of the pressed button*/
static int8_t button_get_pressed_id(void)
{
    uint8_t key = -1;

	...

    return (key + event.value)>key?key:-1;
}

過濾掉default的情況,event.value 值為 1 或 0
返回正確的按鍵id,這個id用來在上面提到的陣列中確定是哪一組座標。

button_is_pressed函式沒用到,所以留空了。

最後,在button_read函式中btn_act就是剛才return的id,在
if中暫存,最後通過data->btn_id記錄進indev_drv

相關文章