Littlevgl键盘和编码器驱动

前言

Littlevgl是近两年刚刚兴起的嵌入式GUI系统,网上资源特别少,自己在使用的过程中也遇到比较多的问题,仅仅只能通过github论坛来查找遇到的问题。这篇帖子写的关于编码器的配置和使用,本人小白,刚刚接触Littlevgl,如果有写错的地方,请各位大牛指导。

Littlevgl的官方网站

Littlevgl官方网站
Littlevgl官方github源代码
很全面的官方半中文文档
控件展示

用于按键接口的类型

键盘 LV_INDEV_TYPE_KEYPAD
编码器 LV_INDEV_TYPE_ENCODER
littlevgl本身是应用于触屏屏,但也提供了按键控制界面的接口方式。
接口方式有三种:
1.键盘 LV_INDEV_TYPE_KEYPAD
2.编码器 LV_INDEV_TYPE_ENCODER
3.按钮 LV_INDEV_TYPE_BUTTON
第三种方式一般不常使用,因为是将硬件按键固定于屏幕上指定的区域,非常不便利。

键盘和编码器的介绍

键盘的键
LV_KEY_NEXT 专注于下一个对象

LV_KEY_PREV 专注于上一个对象

LV_KEY_ENTER 触发器LV_EVENT_PRESSED/CLICKED/LONG_PRESSED等事件

LV_KEY_UP 增加值或向上移动

LV_KEY_DOWN 减小值或向下移动

LV_KEY_RIGHT 增加值或向右移动

LV_KEY_LEFT 减小值或向左移动

LV_KEY_ESC 关闭或退出(例如,关闭下拉列表)

LV_KEY_DEL 删除(例如,“ 文本”区域中右侧的字符)

LV_KEY_BACKSPACE 删除左侧的字符(例如,文本区域中的字符)

LV_KEY_HOME 转到开头/顶部(例如,在“ 文本”区域中)

LV_KEY_END 转到末尾(例如,在“ 文本”区域中)

编码器的键
LV_KEY_NEXT 专注于下一个对象

LV_KEY_PREV 专注于上一个对象

LV_KEY_ENTER 触发器LV_EVENT_PRESSED/CLICKED/LONG_PRESSED等事件

LV_KEY_RIGHT 增加值或向右移动

LV_KEY_LEFT 减小值或向左移动

键盘和编码器的区别
键盘接口类型
可用于按键比较多的产品制作,每一个按键都可以对应一个按键功能,通过不同的按键来操作gui界面的跳转,返回,上移,下移,选取。
编码器接口类型
可用于按键比较少的产品,三个按键便可以实现跟键盘接口一样的多功能控制界面,两个键用于界面控件的选取,一个键用于按键的触发。
再通过更改组的模式来达到控制多个控件和单个控件的效果

编辑模式
void lv_group_set_editing(lv_group_t *group, bool edit) edit:true
控制单个控件的上下跳转,列表下拉等

导航模式
void lv_group_set_editing(lv_group_t *group, bool edit) edit:false
在多个控件上移动,移动选取控件的焦点

官方例程配置

键盘

#include "lv_port_indev_templ.h"

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);

lv_indev_t * indev_keypad;

void lv_port_indev_init(void)
{
	lv_indev_drv_t indev_drv;
	
	/*------------------
     * 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);
}

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

/* Initialize your keypad */
static void keypad_init(void)
{
    /*Your code comes here*/
}

/* 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*/
    uint32_t act_key = keypad_get_key();
    if(act_key != 0) {
        data->state = LV_INDEV_STATE_PR;

        /*Translate the keys to LittlevGL 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*/

    return 0;
}

编码器

#include "lv_port_indev_templ.h"

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);

lv_indev_t * indev_encoder;
static int32_t encoder_diff;
static lv_indev_state_t encoder_state;

void lv_port_indev_init(void)
{
	lv_indev_drv_t indev_drv;
	
	/*------------------
     * 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_KEYPAD;
    indev_drv.read_cb = encoder_read;
    indev_encoder = lv_indev_drv_register(&indev_drv);
}

/*------------------
 * Encoder
 * -----------------*/

/* Initialize your keypad */
static void encoder_init(void)
{
    /*Your code comes here*/
}

/* Will be called by the library to read the encoder */
static bool encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{

    data->enc_diff = encoder_diff;
    data->state = encoder_state;

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

/*Call this function in an interrupt to process encoder events (turn, press)*/
static void encoder_handler(void)
{
    /*Your code comes here*/

    encoder_diff += 0;
    encoder_state = LV_INDEV_STATE_REL;
}

自定义配置

官方源代码键盘接口例程还好,编码器接口例程看起来不清不楚,这也是我会写这篇文章的原因。

官方代码大概框架已经有所了解,那么应该怎么去配置呢?
首先配置输入设备接口,需要有硬件按键扫描函数,按键驱动在网上到处都有,像正点原子、野火还有安富莱都有提供按键驱动大家可以自行查找,但我下面也会贴出我自己用的按键扫描函数,是搬运于正点原子的。
key.h

#ifndef __KEY_H
#define __KEY_H	 
#include "sys.h"

#define KEY_OK      GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_1)//读取按键ok
#define KEY_LEFT    GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_2)//读取按键left
#define KEY_RIGHT   GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_3)//读取按键right

#define KEY_OK_PRES	        1		//KEY_OK  
#define KEY_LEFT_PRES	    2		//KEY_LEFT 
#define KEY_RIGHT_LEFT_PRES	3		//KEY_RIGHT 

void KEY_Init(void);//按键初始化
u8 KEY_Scan(u8 mode); //按键扫描函数				    
#endif

key.c

#include "key.h"
#include "delay.h"
 
void KEY_Init(void)
{
	
	GPIO_InitTypeDef GPIO_InitStructure;

 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
 	GPIO_Init(GPIOC, &GPIO_InitStructure);
 	
} 

//按键处理函数
//返回按键值
//mode:0,不支持长按;1,支持长按;
//返回值:
//0,没有按键按下
//KEY_OK_PRES,KEY_OK 按下
//KEY_LEFT_PRES,KEY_LEFT 按下
//KEY_RIGHT_PRES,KEY_RIGHT 按下
u8 KEY_Scan(u8 mode)
{	 
	static u8 key_up=1;//按键松开标志
	if(mode)key_up=1;  //支持长按	  
	if(key_up&&(KEY_OK==0||KEY_LEFT==0||KEY_RIGHT==0))
	{
		delay_ms(20);//去抖动
		key_up=0;
		if(KEY_OK==0)return KEY_OK_PRES;
		else if(KEY_LEFT==0)return KEY_LEFT_PRES;
		else if(KEY_RIGHT==0)return KEY_RIGHT_LEFT_PRES; 
	}else if(KEY_OK==1&&KEY_LEFT==1&&KEY_RIGHT==1)key_up=1; 	     
	return 0;// 没有按键按下
}

这样扫描按键驱动就编写完成了,然后将此驱动函数放入设备读取
以下是按键接口驱动

#include "lv_port_indev.h"

static bool keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static lv_indev_t * indev_keypad;
static lv_group_t *g;

void lv_port_indev_init(void)
{
	lv_indev_drv_t indev_drv;
    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);
    g = lv_group_create();	//创建组
    lv_indev_set_group(indev_keypad, g);	//将组绑定到输入设备
}

static bool keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    static uint32_t last_key = 0;

    /*Get whether the a key is pressed and save the pressed key*/
    uint32_t act_key = KEY_Scan(1);
    if(act_key != 0) {
        data->state = LV_INDEV_STATE_PR;

        /*Translate the keys to LittlevGL control characters according to your key definitions*/
        switch(act_key) {
        case 1:
            act_key = LV_KEY_ENTER;
            break;
        case 2:
            act_key = LV_KEY_PREV;
            break;
        case 3:
            act_key = LV_KEY_NEXT;
            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;
}

/**
** 为了方便获得组和输入设备可以在这个编写返回函数
**/
lv_indev_t * Get_indev(void)
{
    return indev_keypad;
}
lv_group_t *Get_group(void)
{
    return g;
}

上面是键盘接口的编写,由于我只有三个键我就不演示其他按键,这个比较简单,根据官方例程可以轻松写出来,要使用的时候,只需要创建对象,加入组,就可以通过按键控制界面控件,缺点就是需要有两个键来移动控件之间的焦点,另外两个键来编辑控件的滑动和列表下拉。

编码器接口驱动

#include "lv_port_indev.h"

static bool encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);


static int32_t encoder_diff;
static lv_indev_state_t encoder_state;

static lv_indev_t * indev_encoder;
static lv_group_t *g;

void lv_port_indev_init(void)
{
	lv_indev_drv_t indev_drv;
    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);
    g = lv_group_create();
    lv_indev_set_group(indev_encoder, g);
}

static bool encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    static uint32_t last_key = 0;
    uint32_t act_key = KEY_Scan(0);
    if(act_key != 0) {
        switch(act_key) {
            case 1:
            act_key = LV_KEY_ENTER;
            encoder_state = LV_INDEV_STATE_PR;	
            break;
        case 2:
            act_key = LV_KEY_LEFT;
            encoder_diff = -1;
            encoder_state = LV_INDEV_STATE_REL;
            break;
        case 3:
            act_key = LV_KEY_RIGHT;
            encoder_state = LV_INDEV_STATE_REL;
            encoder_diff = 1;
            break;
        }
        last_key = act_key;
    }
    else {
        encoder_diff = 0;
        encoder_state = LV_INDEV_STATE_REL;
    }
    data->key = last_key;
    data->enc_diff = encoder_diff;
    data->state = encoder_state;
    /*Return `false` because we are not buffering and no more data to read*/
    return false;
}

/**
** 为了方便获得组和输入设备可以在这个编写返回函数
**/
lv_indev_t * Get_indev(void)
{
    return indev_encoder;
}
lv_group_t *Get_group(void)
{
    return g;
}

默认编辑模式,可通过调用void lv_group_set_editing(lv_group_t *group, bool edit)
来更改模式 edit–> true:编辑模式 false:导航模式

调用

void test()
{
	lv_theme_t *th = lv_theme_night_init(184, NULL);
    lv_theme_set_current(th);	//设置夜间的主题模式
	lv_obj_t * scr = lv_scr_act();	//获取活动屏幕
	lv_obj_set_style(scr, &lv_style_scr);	//设置默认的SCR主题
	//调用此函数记得在输入设备驱动的头文件申明函数
	lv_obj_t *group = Get_group();	//获取设备关联的组
	
	lv_obj_t *cont = lv_cont_create(scr,NULL);//创建一个容器
	lv_obj_set_size(cont,320,240);
	
	lv_obj_t *btn1 = lv_btn_create(cont,NULL);//创建按钮1
	lv_obj_set_size(btn1,50,30);
	lv_obj_set_pos(btn1,0,0);
	
	lv_obj_t *btn2 = lv_btn_create(cont,btn1);//创建按钮2
	lv_obj_set_pos(btn2,0,60);

	lv_obj_t *btn3 = lv_btn_create(cont,btn1);//创建按钮3
	lv_obj_set_pos(btn3,0,120);

	lv_obj_t *btn4 = lv_btn_create(cont,btn1);//创建按钮4
	lv_obj_set_pos(btn4,0,180);

	lv_obj_t *roller = lv_roller_create(cont,NULL);//创建滚轮控件
	lv_roller_set_options(roller,"1\n2\n3\n4\n5\n6",LV_ROLLER_MODE_INIFINITE);
	lv_obj_set_pos(roller,100,100);
	
	lv_group_add_obj(group ,btn1);
	lv_group_add_obj(group ,btn2);
	lv_group_add_obj(group ,btn3);
	lv_group_add_obj(group ,btn4);
	lv_group_add_obj(group ,roller);
	lv_group_set_editing(group,false);//导航模式
}

int main()
{
	//时钟初始化
	//LCD初始化
	//延时初始化
	/*****省略一些初始化*****/
	KEY_Init();	//按键初始化
	lv_init();				//lvgl系统初始化
	lv_port_disp_init();	//lvgl显示接口初始化,放在lv_init()的后面
    lv_port_encoder_init();	//输入设备接口初始化
    test();
    while(1)
	{
		lv_task_handler();
        delay_ms(5);
	}
}

总结

很久没写过博客,这是第二篇博客,可能描述起来没什么经验有点乱,但目前还没有人对lvgl的编码器编写过博客,基于这次在这个地方卡的比较久,所以写这篇博客,给刚接触的小白参考一下。
后续会继续更新littlevgl这方面的知识,也是博主自己在制作产品的过程中慢慢摸索出来的,虽然经验不是很够,但希望能对其他人有帮助。也希望其他在使用lvgl的大牛,能多发点关于这方面的知识。

  • 16
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值