HamsterBear lradc按键驱动的适配
平台
- F1C200sLinux版本
- 5.17.2ADC按键
- 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
按1
跳转到选项位置,修改选项为*
后保存退出
查看设备树Binding,并修改添加设备树节点
示例如下
examples:
- |
lradc: lradc@1c22800 {
compatible = "allwinner,sun4i-a10-lradc-keys";
reg = <0x01c22800 0x100>;
interrupts = <31>;
vref-supply = <®_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 = <®_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