ESP32S3-输入设备Keypad

说明

硬件:ESP32S3N16R8开发板 + 2.8寸TFT显示屏(ST7789) + 拨轮
软件:Visual Studio Code + PlatformIO + Squareline Studio
代码:百度网盘:ESP32S3_KEYPAD(提取码:levi)

UI界面

使用Squareline Studio设计如下简单界面

SQ工程

设置工程导出路径,然后导出文件

工程设置

导出文件结构如下

导出

PlatforIO工程

复制工程

复制前面写好的触摸屏工程

删除原工程中的UI文件,将新UI复制过来

创建工程

优化结构

VS Code打开工程

为了让main.cpp更简洁,这里将屏幕初始化的内容写到单独的screen.cpp文件中,然后在main.cpp中引入screen.h头文件

修改后screen.h文件内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef _SCREEN_H
#define _SCREEN_H
#include <Arduino.h>
#include <bb_captouch.h>
#include <TFT_eSPI.h>
#include <SPI.h>
// UI
#include "./lvgl_gui/ui.h"
void touch_init(void);
void my_disp_flush( lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p );
void my_touchpad_read( lv_indev_drv_t * indev_driver, lv_indev_data_t * data );
void screen_init(void);

#endif

screen.cpp内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include "screen.h"
// GT911引脚
#define TOUCH_SDA 18
#define TOUCH_SCL 8
#define TOUCH_INT 45
#define TOUCH_RST 21
BBCapTouch bbct;
const char *szNames[] = {"Unknown", "FT6x36", "GT911", "CST820"};
// 屏幕大小
static const uint16_t screenWidth = 320;
static const uint16_t screenHeight = 240;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[ screenWidth * 10 ];
TFT_eSPI tft = TFT_eSPI(screenWidth, screenHeight);
/**
* @brief 触摸初始化
*/
void touch_init(void)
{
bbct.init(TOUCH_SDA, TOUCH_SCL, TOUCH_RST, TOUCH_INT); // 触摸初始化
int iType = bbct.sensorType(); // 触摸芯片
Serial.printf("Sensor type = %s\n", szNames[iType]);
}
/**
* @brief 触摸读取
*/
void my_touchpad_read( lv_indev_drv_t * indev_driver, lv_indev_data_t * data )
{
TOUCHINFO ti;
if(bbct.getSamples(&ti))
{
data->state = LV_INDEV_STATE_PR;
/*Set the coordinates*/
data->point.x = ti.y[0];
data->point.y = screenHeight - ti.x[0];
}
else
{
data->state = LV_INDEV_STATE_REL;
}
}
/**
* @brief 显示刷新
*/
void my_disp_flush( lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p )
{
uint32_t w = ( area->x2 - area->x1 + 1 );
uint32_t h = ( area->y2 - area->y1 + 1 );
tft.startWrite();
tft.setAddrWindow( area->x1, area->y1, w, h );
tft.pushColors( ( uint16_t * )&color_p->full, w * h, true );
tft.endWrite();
lv_disp_flush_ready( disp );
}
/**
* @brief 屏幕初始化
*/
void screen_init(void)
{
tft.begin(); // tft初始化
tft.setRotation( 1 ); // 屏幕方向
tft.fillScreen(TFT_BLACK);
lv_disp_draw_buf_init( &draw_buf, buf, NULL, screenWidth * 10 );
/*初始化显示*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init( &disp_drv );
/*Change the following line to your display resolution*/
disp_drv.hor_res = screenWidth;
disp_drv.ver_res = screenHeight;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register( &disp_drv );
/*Initialize the (dummy) input device driver*/
static lv_indev_drv_t indev_drv;
lv_indev_drv_init( &indev_drv );
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = my_touchpad_read;
lv_indev_drv_register( &indev_drv );
}

输入设备

百问网:Input devices(输入设备)

打开如下目录文件

port indev

打开lv_port_indev_template.c文件后,可以看到一共有五种输入方式

1
2
3
4
5
lv_indev_t * indev_touchpad;
lv_indev_t * indev_mouse;
lv_indev_t * indev_keypad;
lv_indev_t * indev_encoder;
lv_indev_t * indev_button;

分别是触摸,鼠标,键盘,编码器,按键,具体内容请查看上方百问网lvgl中文文档链接

本次使用的是keypad来控制屏幕上的控件

Keypad设置

lv_port_indev_template.c中关于keypad一共有这几个函数

1
2
3
static void keypad_init(void);
static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static uint32_t keypad_get_key(void);

分别是键盘初始化,键盘回调,键值获取

screen.h

lv_port_indev_template.c中这三个函数声明复制到screen.h中,修改如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef _SCREEN_H
#define _SCREEN_H
#include <Arduino.h>
#include <TFT_eSPI.h>
#include <SPI.h>
// UI
#include "./lvgl_gui/ui.h"
extern lv_indev_t * indev_keypad1;
extern lv_group_t * group1;
void my_disp_flush( lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p );
void screen_init(void);
static void keypad_init(void);
static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static uint32_t keypad_get_key(void);
#endif

screen.cpp

lv_port_indev_template.c中三个函数的定义复制到screen.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/*------------------
* Keypad
* -----------------*/
/*Initialize your keypad*/
static void keypad_init(void)
{
/*Your code comes here*/
}
/*Will be called by the library to read the mouse*/
static void 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 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;
}
/*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;
}

在我自制的ESP32S3N16R8开发板中使用的是如下图所示的多功能开关

多功能开关

原理图如下,三个按键被下拉,IO为低电平,当按键按下时IO将检测到高电平

原理图

screen.cpp中最开始定义如下

1
2
3
4
5
6
7
static lv_indev_drv_t indev_drv;
lv_indev_t * indev_keypad1;
lv_group_t * group1;
// 按键引脚
#define btn1 5
#define btn2 6
#define btn3 7

keypad_init内容如下

1
2
3
4
5
6
7
static void keypad_init(void)
{
/*Your code comes here*/
pinMode(btn1, INPUT); //左
pinMode(btn2, INPUT); //确定
pinMode(btn3, INPUT); //右
}

keypad_read内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
static void 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 = 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;
}

keypad_get_key内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static uint32_t keypad_get_key(void)
{
/*Your code comes here*/
if(digitalRead(btn1) == 1)
{
return 1; // 下一个
}
else if(digitalRead(btn2) == 1)
{
return 2; // 上一个
}
else if(digitalRead(btn3) == 1)
{
return 5; // 确定
}
return 0;
}

下一步修改screen_init内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void screen_init(void)
{
tft.begin(); // tft初始化
tft.setRotation( 1 ); // 屏幕方向
lv_disp_draw_buf_init( &draw_buf, buf, NULL, screenWidth * 10 );
/*初始化显示*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init( &disp_drv );
/*Change the following line to your display resolution*/
disp_drv.hor_res = screenWidth;
disp_drv.ver_res = screenHeight;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register( &disp_drv );
/*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_keypad1 = 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_keypad1, group);`*/
group1 = lv_group_create();
lv_indev_set_group(indev_keypad1, group1);
}

主要修改如下

  • indev_drv.type输入设备类型改为LV_INDEV_TYPE_KEYPAD

  • indev_drv.read_cb回调函数改为keypad_read

  • lv_indev_drv_register(&indev_drv)赋值给最开始定义的indev_keypad1

  • 创建组group1 = lv_group_create()lv_group_create()

  • 将组与输入设备关联lv_indev_set_group(indev_keypad1, group1)

添加对象

只有将控件对象添加到组中,才能通过keypad控制

打开如下目录界面文件lvgl_gui/screens/ui_mainpage.c

可以看到在设计界面时一共添加了四个控件

1
2
3
4
ui_Button2
ui_Switch2
ui_Switch1
ui_Dropdown2

#include "../ui.h"下添加如下代码

1
extern lv_group_t * group1;

ui_mainpage_screen_init函数中最后添加如下代码

1
2
3
4
5
lv_group_add_obj(group1, ui_Button2);
lv_group_add_obj(group1, ui_Switch2);
lv_group_add_obj(group1, ui_Switch1);
lv_group_add_obj(group1, ui_Dropdown2);
lv_group_focus_obj(ui_Button2);

main.cpp

因为前面优化了代码结构,这里还需要在main.cpp中引入screen.h

修改代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <Arduino.h>
#include <lvgl.h>
#include <freertos/FreeRTOS.h>
// SCREEN
#include "./screen.h"
// UI
#include "./lvgl_gui/ui.h"
// 任务
void lvgl_task(void *pt);
void setup()
{
Serial.begin( 115200 ); // 串口初始化
lv_init(); // lvgl初始化
screen_init(); // 屏幕初始化
ui_init(); // UI初始化
xTaskCreatePinnedToCore(lvgl_task, "lvgl display", 1024 * 15, NULL, 2, NULL, 1);
}
void loop()
{
}
void lvgl_task(void *pt)
{
while(1)
{
lv_timer_handler(); /* let the GUI do its work */
vTaskDelay(5);
}
}

效果

修改程序后,编译烧录

拨轮

显示

切换

选中

END

2024 Levi5