LittleVGL (LVGL)干货入门教程二之LVGL的输入设备(indev)API对接。_lvgl indev-程序员宅基地

技术标签: stm32  图像处理  LVGL  嵌入式  单片机  

LittleVGL (LVGL)干货入门教程二之LVGL的输入设备(indev)API对接


前言:

阅读前,请确保你拥有以下条件:

  • 你已经完成“显示API”的移植。
  • 你已经实现了一个屏幕的触摸驱动 (如果你使用外部物理按键进行操作,那么请确保你实现了读按键状态的驱动)。

LVGL有三大种需要对接的API

  1. 显示API(教程一已实现, 链接:LittleVGL (LVGL)入门教程一之移植到stm32芯片
  2. 输入设备API(比如触摸屏、按键 等, 此篇教程实现)
  3. 文件系统API(如FatFs等)

这篇文章讲“输入设备API”的移植,默认你已经移植好了“显示API”。

重要) 编译LVGL至少需要c99标准



一、indev输入设备的种类介绍

(一)输入设备的种类

LVGL有5种输入设备接口:

  1. Touchpad      (触摸板,例如电容屏、电阻屏等)
  2. Mouse             (鼠标)
  3. Keypad            (键盘)
  4. Encoder           (编码器)
  5. Button              (外部按键)

我们常用的是“Touchpad、Keypad”,这篇文章基于这两种,其他类似,可以根据目录第三章看你想看的接口API对接。


二、lv_port_indev更改

(一)使能indev的port文件
  1. 方法很简单,在lv_port_indev文件中把“#if 0”改为“#if 1”,c文件和h文件都要改。
  2. 在lv_port_indev.h中添加声明void lv_port_indev_init(void);
(二)优化indev的port文件(重要

提醒,这里的修改很冗长和枯燥,主要原因,原port文件非常混乱,各种indev的API混在一起,如果你不实现就会报错或警告,而实际上我们的项目往往只会用上一两个indev,比如我同时使用键盘和鼠标或同时使用touchpad和button。为了代码的精简和易于管理,这个部分以添加预处理语句为主,以后你要用什么类型的indev,修改预处理语句和宏定义即可。

/************************************************
 * 一、我们在文件"lv_port_indev.c"顶部定义宏定义
 ************************************************/
#define LV_USE_INDEV_TOUCHPAD 	0x1u
#define LV_USE_INDEV_MOUSE	 	0x2u
#define LV_USE_INDEV_KEYPAD 	0x4u
#define LV_USE_INDEV_ENCODER 	0x8u
#define LV_USE_INDEV_BUTTON 	0x10u
#define LV_USE_INDEV  	LV_USE_INDEV_TOUCHPAD	| \
						LV_USE_INDEV_KEYPAD			// 使用Touchpad 和 keypad

/************************************************
 * 二、根据注释 我们找到 STATIC PROTOTYPES,按类型
 * 添加预处理语句
 ************************************************/
#if ( LV_USE_INDEV & LV_USE_INDEV_TOUCHPAD ) == LV_USE_INDEV_TOUCHPAD
static void touchpad_init(void);
static bool touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static bool touchpad_is_pressed(void);
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y);
#endif

#if ( LV_USE_INDEV & LV_USE_INDEV_MOUSE ) == LV_USE_INDEV_MOUSE
static void mouse_init(void);
static bool mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static bool mouse_is_pressed(void);
static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y);
#endif

#if ( LV_USE_INDEV & LV_USE_INDEV_KEYPAD ) == LV_USE_INDEV_KEYPAD
static void keypad_init(void);
static bool keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static uint32_t keypad_get_key(void);
#endif

#if ( LV_USE_INDEV & LV_USE_INDEV_ENCODER ) == LV_USE_INDEV_ENCODER
static void encoder_init(void);
static bool encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static void encoder_handler(void);
#endif

#if ( LV_USE_INDEV & LV_USE_INDEV_BUTTON ) == LV_USE_INDEV_BUTTON
static void button_init(void);
static bool button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static int8_t button_get_pressed_id(void);
static bool button_is_pressed(uint8_t id);
#endif

/************************************************
 * 我们继续往下看是indev的初始化函数实现,官方注释
 * 很贴心,大家看着添加预处理语句就行
 ************************************************/
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
     */

    // 不动
    lv_indev_drv_t indev_drv;

#if ( LV_USE_INDEV & LV_USE_INDEV_TOUCHPAD ) == LV_USE_INDEV_TOUCHPAD
    /*------------------
     * Touchpad
     * -----------------*/

    /*Initialize your touchpad if you have*/
    touchpad_init();

    /*Register a touchpad input device*/
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = touchpad_read;
    indev_touchpad = lv_indev_drv_register(&indev_drv);
#endif
#if ( LV_USE_INDEV & LV_USE_INDEV_MOUSE ) == LV_USE_INDEV_MOUSE
    /*------------------
     * Mouse
     * -----------------*/

    /*Initialize your touchpad if you have*/
    mouse_init();

    /*Register a mouse input device*/
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = mouse_read;
    indev_mouse = lv_indev_drv_register(&indev_drv);

    /*Set cursor. For simplicity set a HOME symbol now.*/
    lv_obj_t * mouse_cursor = lv_img_create(lv_disp_get_scr_act(NULL), NULL);
    lv_img_set_src(mouse_cursor, LV_SYMBOL_HOME);
    lv_indev_set_cursor(indev_mouse, mouse_cursor);
#endif
#if ( LV_USE_INDEV & LV_USE_INDEV_KEYPAD ) == LV_USE_INDEV_KEYPAD
    /*------------------
     * Keypad
     * -----------------*/

    /*Initialize your keypad or keyboard if you have*/
    keypad_init();

    /*Register a keypad input device*/
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_KEYPAD;
    indev_drv.read_cb = keypad_read;
    indev_keypad = lv_indev_drv_register(&indev_drv);

    /* Later you should create group(s) with `lv_group_t * group = lv_group_create()`,
     * add objects to the group with `lv_group_add_obj(group, obj)`
     * and assign this input device to group to navigate in it:
     * `lv_indev_set_group(indev_keypad, group);` */
#endif
#if ( LV_USE_INDEV & LV_USE_INDEV_ENCODER ) == LV_USE_INDEV_ENCODER
    /*------------------
     * Encoder
     * -----------------*/

    /*Initialize your encoder if you have*/
    encoder_init();

    /*Register a encoder input device*/
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_ENCODER;
    indev_drv.read_cb = encoder_read;
    indev_encoder = lv_indev_drv_register(&indev_drv);

    /* Later you should create group(s) with `lv_group_t * group = lv_group_create()`,
     * add objects to the group with `lv_group_add_obj(group, obj)`
     * and assign this input device to group to navigate in it:
     * `lv_indev_set_group(indev_encoder, group);` */
#endif
#if ( LV_USE_INDEV & LV_USE_INDEV_BUTTON ) == LV_USE_INDEV_BUTTON
    /*------------------
     * 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[2] = {
    
            {
    10, 10},   /*Button 0 -> x:10; y:10*/
            {
    40, 100},  /*Button 1 -> x:40; y:100*/
    };
    lv_indev_set_button_points(indev_button, btn_points);
#endif

}

/************************************************
 * 三、根据注释找到 STATIC FUNCTIONS
 * 下面都是很长的函数实现,我不一一列出来了,
 * 按照不同的indev根据上面的模板进行预处理语句添加就行
 * 值得注意的是,我们可能会使用多种indev,所以不宜
 * 使用“#if”和“#elif”“#endif”,宜使用“#if”和“#endif”
 ************************************************/


三、移植输入设备API

相信大家在上文进行预处理语句的添加的过程中应该有注意各个函数的名字,很直白,函数什么功能基本都能从名字看出来,那我们分别以“touchpad”和“keypad”的移植为例,进行API对接。

(一)以touchpad为例:

打开文件"lv_port_indev.c",找到并参考如下改法:

/*------------------
 * Touchpad
 * -----------------*/
 * /*Initialize your touchpad*/
static void touchpad_init(void)
{
    
    /*Your code comes here*/
    /* 你的touch初始化 */
    touch->init();
}

/* Will be called by the library to read the touchpad */
static bool touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    
    static lv_coord_t last_x = 0;
    static lv_coord_t last_y = 0;

    /*Save the pressed coordinates and the state*/
    /* 这里调用了 touchpad_is_pressed,这个实现在下面 */
    /* 从名字可以知道,这个函数对“是否有点被触摸”进行判断 */
    if(touchpad_is_pressed()) {
    
    	/* touchpad_get_xy 在下面实现 */
        touchpad_get_xy(&last_x, &last_y);
        data->state = LV_INDEV_STATE_PR;
    } else {
    
        data->state = LV_INDEV_STATE_REL;
    }

    /*Set the last pressed coordinates*/
    data->point.x = last_x;
    data->point.y = last_y;

    /*Return `false` because we are not buffering and no more data to read*/
    return false;
}

/*Return true is the touchpad is pressed*/
static bool touchpad_is_pressed(void)
{
    
    /*Your code comes here*/
	/* 在这里我们对触摸屏是否被触摸进行判断 */
	/* 例如触摸屏一般有INT引脚,就是中断引脚 */
	/* 我们根据中断引脚是否有信号判断是否被触摸(不过STM32貌似没有电平触发中断,只有边沿触发) */
	/* 边沿触发只能触发一次,容易判断错误,所以我们一般采用轮询读点 */
	/* 我们分别以电容和电阻触摸屏为例 */

	bool res = false;

	/* 电阻屏 */
	// res = res_is_touched();
	// return res;
	
	/* 电容屏 */
	/* 电容屏一般可以读status寄存器是否有值来判断是否有点 */
	uint8_t status = cap_is_touched();
	if ( status )
		return true;
	
    return false;
}

/*Get the x and y coordinates if the touchpad is pressed*/
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{
    
    /*Your code comes here*/
	/* 这里就容易了,按照你的方式读你触摸屏的坐标并赋值就行 */

    (*x) = my_read_x();
    (*y) = my_read_y();
}

那么到这里,你的触摸屏API就移植结束了,在外面调用函数lv_port_indev_init();进行初始化就行。

(二)以keypad为例:
1.移植keypad API

打开文件"lv_port_indev.c",找到并参考如下改法:

/*------------------
 * Keypad
 * -----------------*/

/* Initialize your keypad */
static void keypad_init(void)
{
    
    /*Your code comes here*/
    /* 你的初始化代码 */
    my_key_init();
}

/* Will be called by the library to read the mouse */
static bool keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    
    static uint32_t last_key = 0;

    /*Get the current x and y coordinates*/
    /* 这里官方默认你有鼠标设备,但实际上可能没有,我们注释掉 */
    // mouse_get_xy(&data->point.x, &data->point.y);

    /*Get whether the a key is pressed and save the pressed key*/
    /* 这里告诉你,你读到的点要根据你的需求进行转换 */
    /* 当我通过keypad_get_key()函数获取act_key值后 */
    /* 转换成LVGL的操作符,如LV_KEY_NEXT等 */
    uint32_t act_key = keypad_get_key();
    if(act_key != 0) {
    
        data->state = LV_INDEV_STATE_PR;
        /*Translate the keys to LVGL control characters according to your key definitions*/
        switch(act_key) {
    
        case 1:
            act_key = LV_KEY_NEXT;
            break;
        case 2:
            act_key = LV_KEY_PREV;
            break;
        case 3:
            act_key = LV_KEY_LEFT;
            break;
        case 4:
            act_key = LV_KEY_RIGHT;
            break;
        case 5:
            act_key = LV_KEY_ENTER;
            break;
        /* 这里可以添加更多操作符 */
        }
        last_key = act_key;
    } else {
    
        data->state = LV_INDEV_STATE_REL;
    }

    data->key = last_key;

    /*Return `false` because we are not buffering and no more data to read*/
    return false;
}

/*Get the currently being pressed key.  0 if no key is pressed*/
static uint32_t keypad_get_key(void)
{
    
    /*Your code comes here*/
	/* LVGL有较多操作符,如果你的按键不够用,可以采用组合键的方案 */
	/* 当然,使用外部按键的话你应该考虑“按键抖动”的问题,使用软消抖或硬消抖 */
	/* 假设我有3个按键:“UP”、“SURE”、“DOWN” */

	switch ( my_read_key() ) {
    
	case UP:
		return 1;
	case SURE:
		return 5;
	case DOWN:
		return 2;
	/* 组合键 */
	case (UP | SURE):
		return 3;
	case (DOWN | SURE):
		return 4;
	}

    return 0;
}

LVGL 操作符参考:
在这里插入图片描述

keypad比较特殊,光移植完还不行,要使用的话,需要indev group。

2.使用keypad的方法

根据LVGL注释的原话:

	/* 
	 * Later you should create group(s) with `lv_group_t * group = lv_group_create()`,
     * add objects to the group with `lv_group_add_obj(group, obj)`
     * and assign this input device to group to navigate in it:
     * `lv_indev_set_group(indev_keypad, group);` 
     * */

我们可以知道,我们要使用下面代码创建一个group,以后我们要把UI obj放入group,才可进行操作(触摸屏不用)。

/* 创建group */
lv_group_t * group = lv_group_create();
// 这里的变量“indev_keypad”在文件顶部“STATIC VARIABLES”区有定义
// 这句话要加入void lv_port_indev_init(void);函数对应的keypad的代码里
lv_indev_set_group(indev_keypad, group);

/* 例如我们创建了一个button对象 */
lv_obj_t * btn = lv_btn_create( lv_scr_act(), NULL );
/* 我们要通过keypad控制btn,那么我们需要添加进group */
lv_group_add_obj( group, btn );

那么到这里,你的keypad API就移植结束了,在外面调用函数lv_port_indev_init();进行初始化就行。


四、使用示例

#include <lvgl.h>

#define LVGL_TICK 	5

/* 引入之前创建的group */
extern lv_group_t * group;	// 如果用触摸屏就不用

/************************************************
 * @brief 事件句柄
 * 
 * @param obj       
 * @param event 
 ************************************************/
static void event_handler(lv_obj_t * obj, lv_event_t event)
{
    
    switch (event) {
    
    case LV_EVENT_CLICKED:
        printf( "btn Clicked\n" );
        // lv_obj_del( obj ); // 点击就消失
        break;
    default:
        break;
    }
}

static void my_lvgl_test(void)
{
    
	lv_obj_t * btn = lv_btn_create( lv_scr_act(), NULL );
	lv_obj_set_size( btn, 80, 50 );
	lv_obj_set_event_cb( btn, event_handler );	// 设置事件句柄
	lv_group_add_obj( group, btn );	// 添加进group 如果用触摸屏就不用
	lv_obj_align( btn, NULL, LV_ALIGN_CENTER, 0, 0);

    lv_obj_t * label1 = lv_label_create(lv_scr_act(), NULL);
    lv_label_set_recolor(label1, true);
    lv_label_set_text(label1, "#ff0000 btn#");
    lv_obj_align(label1, btn, LV_ALIGN_CENTER, 0, 0);
}

static void lvgl_init( void ) 
{
    
    lv_init();
    lv_port_disp_init();        // 显示器初始化
    lv_port_indev_init();       // 输入设备初始化
    // lv_port_fs_init();          // 文件系统设备初始化
}

int main()
{
    
	lvgl_init();

	my_lvgl_test();

	while(1) {
    
		// 先调用 lv_tick_inc 再调用 lv_task_handler
		lv_tick_inc(LVGL_TICK);
		lv_task_handler();
		delay_ms(LVGL_TICK);
	}
}

五、启动LVGL

参考我的上一篇文章即可:LittleVGL (LVGL)干货入门教程一之移植到stm32芯片


本篇完


其他:

LittleVGL (LVGL)干货入门教程一之移植到stm32芯片

LittleVGL (LVGL)干货入门教程二之LVGL的输入设备(indev)API对接。

LittleVGL (LVGL)干货入门教程三之LVGL的文件系统(fs)API对接。

LittleVGL (LVGL)干货入门教程四之制作和使用中文汉字字库


下一篇

LittleVGL (LVGL)干货入门教程三之LVGL的文件系统(fs)API对接。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_26106317/article/details/110388197

智能推荐

OpenGL ES 三 opengl 坐标系 ,纹理坐标系_opengl 坐标后面有个f-程序员宅基地

文章浏览阅读2.6k次。一、 常用的坐标系1. Android View 坐标系2. opengl坐标系中采用的是3维坐标: static final float COORD[] = { -1.0f, -1.0f, //1 1.0f, -1.0f, //2 -1.0f, 1.0f, //3 1.0f, 1.0f, //4 };坐标原点默..._opengl 坐标后面有个f

Win10常用命令:定时关机(shutdown命令)_定时关机命令-程序员宅基地

文章浏览阅读10w+次,点赞26次,收藏79次。定时关机:Win+R 输入命令:①倒计时关机: shutdown -s -t 3600:在一小时后关机 ②取消关机命令:shutdown -a_定时关机命令

Unity | Shader基础知识(什么是shader)_unity shader-程序员宅基地

文章浏览阅读5.2k次,点赞17次,收藏46次。什么是shader?unity当中的shader是什么?_unity shader

选购AWS服务时需要注意哪些问题?九河云详细评测-程序员宅基地

文章浏览阅读710次,点赞8次,收藏4次。企业在选择AWS服务前,首先需要明确自身的业务特点和IT需求,包括计算资源、存储空间、网络带宽等。企业需要了解AWS提供的安全机制,如数据加密、访问控制、安全组等,并确保它们满足自身的安全合规要求。可靠的服务质量是企业选择云计算服务的重中之重。企业需要了解AWS的服务等级协议(SLA),包括可用性承诺、响应时间等指标,并评估是否符合自身的业务需求。总之,选购AWS服务需要全方位地考虑企业的业务需求、成本预算、安全合规、服务可靠性以及迁移成本等因素,只有做好这些充分的评估,企业才能选择最佳的AWS解决方案。

JS对table添加删除一行_var row = btn.parentnode.parentnode; r-程序员宅基地

文章浏览阅读1k次。添加一行,并用AJAX提交数据。 function submitForm() { var name = $("#name").val(); var description = $("#description").val(); var url = $("#url").val(); $.ajax({ url: '/admin/ops', type:_var row = btn.parentnode.parentnode; row.parentnode.removechild(row);

HTML学习记录(列表标签&属性)_列表标签的属性-程序员宅基地

文章浏览阅读196次。ol:有序列表标签属性值描述type1,A,a,I,i规定列表顺序类型reversed (HTML5新加)reversed列表倒叙startnumberHTML5不支持,规定列表起始<ol type="A/a/I,i">ul:无序列表标签属性值描述typedisc,square,circle规定列表顺..._列表标签的属性

随便推点

mysql在线模拟器_推荐几款在线 SQL 模拟器-程序员宅基地

文章浏览阅读775次。有时候,我们想去验证一些 SQL,但又懒得去安装数据库环境,那该怎么办呢?当然是用在线 SQL 模拟器呀,SQL 模拟器免安装,可以在网页直接运行 SQL 。我搜罗了几款 SQL 模拟器,放出来供大家选择:接下来给大家简单介绍各个模拟器的用法。SQL FiddleSQL Fiddle 支持 MySQL、Oracle、MS SQL、PostgreSQL 等主流数据库,不需要注册即可使用。如图 1 所..._mysql模拟器在线

Docker Build报错 - “docker build“ requires exactly 1 argument.See ‘docker build --help‘._docker build" requires exactly 1 argument. see 'do-程序员宅基地

文章浏览阅读1.8k次。今天心血来潮闲着看了一下docker的基本用法,看到构建镜像这里一直报错,后来才发现是少了 '.'解决:提示构建完成通过docker images查看构建完成的镜像妥了_docker build" requires exactly 1 argument. see 'docker build --help'. usage

PIC单片机控制DS18B20在proteux中的测试-程序员宅基地

文章浏览阅读1.7k次。单片机使用的是PIC16F877A和PIC12F609,proteux是8.0的版本! 程序编写使用的是MPLAB X IDE 1.70,编译工具:picc-9.83。 us定时器使用的是picc库定时器,_XTAL_FREQ 4000000UL。 由于没有在网上发现proteux和X IDE的联调工具,只能写好这个程序,直接在Proteux中模拟,_proteux

玫瑰花Java代码怎么运行出来_玫瑰花Java web程序-程序员宅基地

文章浏览阅读1.5k次。玫瑰花Java web程序有兴趣的同学可以参考一下Java类RoseControllerpackage com.spring.controller;import java.io.BufferedWriter;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.i...

phpword 利用现有模板文档,填充数据和图片后导出保存_有什么系统输入word模板后线上填写后,再导出-程序员宅基地

文章浏览阅读2.1k次。话不多说,直接说步骤:1、利用composer下载 phpword:composer require phpoffice/phpword2、直接使用即可: public function actionTest() { $phpWord = new PhpWord(); $template = $phpWord->loadTemplate('./html/test.docx');//模板文档 $template->_有什么系统输入word模板后线上填写后,再导出

Oracle中通过存储过程,Function,触发器实现解析时间类型的字段并插入的对应的数据表中...-程序员宅基地

文章浏览阅读71次。摘要:之前在项目中解决了插入字符串类型的数据,今天试着写了一个插入date类型的字段,成功了,现在记录一下,以便以后查看:一:首先建立一个根据xml节点名称获取对应的xml值的Function.sql:CREATE OR REPLACE FUNCTION MIP.GetXmlNodeValue (xmlStr CLOB, nodeName VARCHAR2) RETURN VAR..._oracle sql 触发器中对date类型的字段的处理

推荐文章

热门文章

相关标签