LVGL,STM32U575RIT6+指纹模块

一,LVGL介绍

GUI即图形用户界面,是一种通过图形化的方式呈现操作和数据,可以让我们通过鼠标,键盘等输入设备与计算机设备进行交互的用户界面。

GUI库:它是一种用于开发图形用户界面的软件库,允许用户通过图形化的方式与应用程序进行交互。比如:用户界面的窗口,下拉菜单,对话框以及按键等等。只需要调用GUI库的接口函数,就可以快速绘制所需要的用户界面。LVGL就GUI库里的一种。

在LVGL中,可以创建很多部件,根据自己的屏幕大小,去创建各种部件,在创建时,基于当前主屏幕为父对象去创建。可以去设置部件的样式和属性,当好几个部件需要去关联,就需要给部件去添加事件,在事件中去完成。LVGL是一个开源的嵌入式图形库,它具有轻量级,多功能,灵活和可移植等特点,可以帮助开发者快速和方便地创建美观和功能丰富的GUI。

在官网里LVGL --- Light and Versatile Embedded UI Ecosystem可以更加详细的了解每个部件的样式属性,同时官网还有图片和字体转换器可以使用。

在官网中可以查到每个部件的各种用法还有官方给的一些示例

这里我们要移植LVGL的话,就要得到官网的代码驱动(我选择的是8.3版本),可以从官网进行获取:https://lvgl.io/,或者是从GITHUB上直接获取下载:https://github.com/lvgl/lvgl/tree/release/v8.3

保留要用的文件,其余的可以删除

然后把保留完的文件都拷贝到我们的工程里,后边屏幕显示和驱动都要用到,其中lv_port_disp.c是屏幕显示,lv_port_indev.c是屏幕触摸的

在工程里新建4个目录来管理对应的lvgl中的代码文件,把对应的文件添加到新建的4个目录中,并添加相对应的头文件路径。

1 LVGL_GUI 我们用GUI设计的界面

2 LVGL_Conf LVGL的两个配置文件 lv_conf,h,lvgl.h

3 LVGL_porting LVGL 的接口文件,例如显示,触摸屏,键盘等等

4 LVGL_src LVGL 的所有底层.c文件

二,LVGL 显示接口

对应的文件驱动

首先参考厂家提供的对应的lcd显示的代码:位置百度网盘 请输入提取码,点击进去找到对应的工程

紧接着打开工程找到对应的.c和.h文件,拷贝到我们自己的工程中

原理图部分

查看原理图,对应屏幕的是采用spi通信,核心板上面也标注了,PA4:片选线,PA5:时钟线

PA6:MISO,PA7:MOSI,PA8:lcd的复位引脚。

SPI的时序图

查看数据手册,找到SPI的读写时序图

相关代码

下边是显示驱动文件里提供的一些API函数,有屏幕初始化,清屏,设置光标,设置显示区域等等。

在bsp_illi9341_4line.h文件中重新定义复位引脚和片选引脚的宏定义

//复位对应引脚

#define LCD_RST_GPIO_Port GPIOA

#define LCD_RST_Pin GPIO_PIN_8

//片选对应的引脚

#define LCD_DCX_GPIO_Port GPIOA

#define LCD_DCX_Pin GPIO_PIN_4

在主函数里面调用对应的屏幕的初始化ILI9341_init();

根据官网文档移植,要先进性初始化,然后注册显示驱动,并且在中断中每毫秒调用一次,lv_tick_inc();,这里我是用TIME6定时器,然后在定时中断里去写这个心跳函数

这是对应的画点函数,在.h文件中声明一下,紧接着就把LVGL关联到屏幕上面,在LVGL刷新函数里面去调用,在屏幕刷新函数里去调用画点函数,_HW_DrawPoint(x, y,color_p->full);

这个函数的参数,前两个就是像素点的坐标,最后一个参数是颜色值,要在哪个坐标点(X, Y)画什么颜色(color_p->full), FULL是一个联合体,并且屏幕是 RGB565 格式的,然后在这个屏幕刷新函数里,通过xy的坐标点,把颜色打到屏幕上面,最后告诉LVGL刷新完成了,这样就把显示和LVGL关联上了。

LVGL也提供了三种方法去创建显示缓冲区,这里用到第一种也是常用的单缓冲区,根据芯片的SRAM的大小,用屏幕水平分辨率*10计算用单缓冲区就可以了。

优化屏幕显示

接下来换一种打点方式,现在调用的是单个打点的函数,它是一个像素一个像素的写给屏幕是非常慢的。

就比如LVGL每次给出一小块刷新区域,就会对这个区域内每一个像素调用一次**_HW_DrawPoint();**这个函数内部通常会做这些事情,设置光标或窗口地址到某一个像素点; 发送 "写RAM" 命令;通过SPI发送2字节颜色数据;可能每次都会有一些函数调用,坐标的计算,SPI收发开销。

假设刷新区域是 **100*100 像素,**那就是10000次循环,并且每次至少发送2字节的颜色数据,就是20000字节,这个规模还是有些庞大的,并不是单纯的发送20000字节,中间夹杂了别的命令,地址设置,函数调用和SPI的启动和停止。为了避免这样的情况发生,如何解决呢。

我们去用SPI加DMA的方式去整块区域填充,跳过CPU,不过多的浪费CPU的资源

然后我们封装两个函数,让它使能DMA发送数据,并且设置SPI的中断回调函数,用于通知LVGL刷新,等DMA把数据搬运完以后,进入这个中断回调函数,通知LVGL刷新

紧接着在屏幕刷新函数里面去调用,同时还有一点要注意,这些弄完以后,屏幕会出现花屏的现象

这是为什么呢,因为当前使用的屏幕芯片是(ILI9341/ST7789),它的硬件协议规定 "先发高字节,再发低字节"。SPI屏幕驱动芯片在接收16位颜色数据时,通常期望高字节先收到(大端模式)。然而STM32内存是小端模式,先存低字节,再存高字节。这样的冲突就会导致屏幕收到错误的字节序,就会出现花屏的现象。

如何解决花屏的现象,在工程文件中,找到lv_conf.h文件,再找到LV_COLOR_16_SWAP的宏定义。这个宏的作用就是起高低字节交换的。将LV_COLOR_16_SWAP设为1,进入大端模式。

三,LVGL 触摸接口

对应的文件驱动

同理也是需要在链接中找到对应的触摸屏代码,以及对应的.c和.h文件,都拷贝到我们自己的工程中

链接:百度网盘 请输入提取码

原理图部分

查看原理图,屏幕的触摸是用I2C进行通信的,再看核心板上面对应的接口去配置,PB6:时钟线,PB7:数据线,PB5:中断引脚,PA11:LCD触摸复位的引脚。

I2C的时序图

接下来这是I2C的读写时序图,根据不同的命令去读取数据还是写入数据

相关代码

下边这是I2C的地址,结合上面的读写时序图,来生成读地址和写地址(I2C,7位的从机地址加上一位的读写位)

在main.c中添加对应的触摸初始化FT6336_init();


以及在bsp_ft6336.h中添加对应的宏定义

这是触摸接口的初始化函数,对应得就是配置PA11得引脚去触摸复位。再MAIN函数里去调用这个函数初始化。

这是读取X,Y的坐标的函数由官方提供

同时可以在主函数里调用这个函数,可以得到触摸屏幕上面的具体坐标,当你带点击屏幕会触发这个函数,当然这里是采用轮询的方式去触发的,点击屏幕以后,X,Y对应的值也会发生改变,当你点击屏幕不同的位置

同样也可以在中断方式上报,配置的PA5中断引脚引脚,然后重写中断处理函数HAL_GPIO_EXTI_Rising_Callback(这里是上升沿的回调函数),在这个函数里把FT6336_scan(void)写进去。

cs 复制代码
/*
**********************************************************************
* @fun     :FT6336_scan 
* @brief   :FT6336触摸点读取
* @param   :None
* @return  :触摸点信息
**********************************************************************
*/
FT6336_TouchPointType FT6336_scan(void)
{
    FT6336_TouchPointType touchPoint;
		//
    touchPoint.touch_count = FT6336_read_td_status();
		//
    if (touchPoint.touch_count == 0)
    {
        touchPoint.tp[0].status = release;
        touchPoint.tp[1].status = release;
    }
		//
    uint8_t id1 = FT6336_read_touch1_id(); // id1 = 0 or 1
    touchPoint.tp[id1].status = (touchPoint.tp[id1].status == release) ? touch : stream;
    touchPoint.tp[id1].x = FT6336_read_touch1_x();
    touchPoint.tp[id1].y = FT6336_read_touch1_y();
    touchPoint.tp[~id1 & 0x01].status = release;
		printf("X==%d  Y==%d\n",touchPoint.tp[id1].x,touchPoint.tp[id1].y);
		
    return touchPoint;
}

在lv_port_indev_init( )函数里有5种输入方式的任务注册:触屏,鼠标,键盘,编码器,物理按键;我们只需要保留触摸屏的任务注册,其余的注册任务注释掉就可以

然后添加触摸状态检测函数,在对应的static bool touchpad_is_pressed(void)中添加对应的检测函数

然后添加对应的触摸坐标获取函数,在static void touchpad_get_xy(lv_coord_t *x,lv_coord_t *y)中添加,对应的厂家也提供了这两个函数:FT6336_read_touch1_xFT6336_read_touch1_y

紧接着就是LVGL关联能调用触屏的函数,需要触摸状态检查函数和触摸坐标获取函数;提供对应的这两个API给LVGL就可以去管理对应的触屏。下面就是如何配置这两个函数,这些配置完以后,LVGL和触摸就关联起来了。

最后在main.c文件里,

lv_init(); //LVGL初始化

lv_port_disp_init();//注册LVGL的显示任务

lv_port_indev_init();//注册LVGL的触屏检查任务

四,在FreeRTOS中运行LVGL

心跳和任务刷新

LVGL也要提供对应的心跳和任务刷新函数

第一个时间任务:为LVGL提供准确的心跳,必须间隔精准的调用时基函数,lv_tick_inc(),俗称心跳,让LVGL精准的知道时间的流逝

第二个任务:间隔5ms左右,调用周期性任务函数:lv_timer_handler(),它的作用是检查所有注册任务的时间戳,执行到哪些已经到期的任务,例如屏幕刷新,动画更新,触控,定时器事件等等。

如何添加LVGL心跳函数(时基函数),在CUBEMX里配置TIM6,让它产生1ms的中断,使能它的中断,并且设置它的中断为高优先级,在中断函数里调用LVGL心跳时基。

重写对应的中断回调函数,在中断回调函数里,TIM6产生一次中断就发生一个任务通知,在freertos中,LVGL刷新这个任务就等待这个任务通知到达。然后在主函数里添加对应定时器中断使能函数:HAL_TIM_Base_Start_IT(&htim6);注意要在对应的lvgl初始化后,进行使能

同时在这个中断函数里,让它每产生一次中断就发一个任务通知,这里根据官方的描述,大约5ms左右刷新一次。

如何在freertos实时系统中允许LVGL,首先就要创建一个任务,在任务中去让LVGL显示,每5毫秒调用lv_timer_handler();也就是任务刷新函数,每隔5毫秒就让LVGL刷新一次。心跳函数产生一次中断,然后间隔5ms向这个任务发送一个任务通知,LVGL_task(),等待这个任务通知到达

五,WIFI账号和密码保存到FLASH中

通过LVGL创建两个文本输入框,在文本输入框里输入WIFI账号和密码,获取到这个账号和密码然后保存到FLASH里面,掉电保存,上电板子能够自动获取到FLASH里面的账号密码,进行连接。

首先要知道FLASH的流程,保存的话要,先知道保存的地址,把数据保存到某个位置。保存首先就要解锁,然后把要保存的那一页擦除,开始写入保存,紧接着上锁,这样FLASH的保存流程就结束了。

STM32U575有2MB FLASH,分为两个Bank,每个Bank 1MB,每页 8KB。程序代码都存放在Bank1里(0x08000000 ~ 0x080FFFFF)所以后边擦写BANK2的时候不会影响到程序代码。

而我们要把账号密码存放在Bank2里(0x08100000 ~ 0x081FFFFF),直接存放在它的最后一页。因此我们定义一下FLASH的存储地址,并且STM32U5的FLASH写入有个硬性规定:每次必须写入16字节(128位)

在LVGL创建的输入框事件中,去获取这个账号密码,用户填写完账号密码,然后点击按钮,也就是最后当用户点击按钮,然后把标志位置为1,后序在rtos任务中WIFI连接中去判断这个标志进行连接。

然后再看FLASH的保存函数,如何进行保存,保存到那个地址,解锁,擦除,存入,上锁。用户写入完以后,每次板子通上电就会在主函数里调用读取FLASH的函数,进行连接

这样用户在第一次输入账号和密码后,就开始连接,在GUI的事件回调中处理,同样也是在rtos的任务里去判断标志位进行连接

六,通过esp8266 mqtt控制外设

七,U5和指纹模块通信

FO51原理图部分

看原理图,F051芯片的串口二引脚是和指纹FPM10A进行通讯的,以及和U5板子进行物理连接的串口,都需要使能。

再查看数据手册,MCU和上位机的波特率是57600,传输的数据格式是10位,1位开始位,8位数据,1位停止位,没有校验位,在CUBEMX上面配置串口的波特率,配置错的话可能会传输错误

查看原理图使能对应的引脚PA9,PA10,并且通过SW2开关去拨动,让串口和TXD2,RXD2进行连接,但发现在原理图上,有一处标识错误

通过原理图然后板子上面印出来的引脚,通过这两个引脚和U5的板子连接

看完原理图以后,在看F051和FPM10A之间的通讯协议,看数据手册需要用到录入指纹指令顺序

01H:录指纹图像 02H:图像转特征 05H:特征合成模板 06H:存储模板到FLASH里

04H:搜索指纹

F051和FPM10A之间通信,发的是固定格式的数据包(包头 + 地址 + 命令 + 数据 + 校验和)

下边把用到的每个指令的包格式还有回包例举出来,不同指令的指令包格式以及功能说明,还有对应的应答包格式,回复不同的确认码表示的含义。通过这些指令,就可以让F051和FPM10A进行通信。

再跟据应答包的确认码,来判断结果。

录指纹图像01H

图像生成特征02H

特征合成模板05H

存储模板06H

搜索指纹04H

现在知道发送的指令的固定包格式

|---------|---------|---------------------------|
| 包头 | 2字节 | 固定:0xEF 0x01 |
| 地址 | 4字节 | 默认:0xFF FF FF FF |
| 包标识 | 1字节 | 命令包:0x01 应答包:0x07 |
| 长度 | 2字节 | 数据区 + 校验和 的总字节数(大端) |
| 数据 | N | 命令码 + 参数 |
| 校验和 | 2字节 | 包标识 + 长度 + 数据的累加和(大端) |

代码配置

接下来跟据原理图和通讯协议来配置,在F051MCU上,串口2用来和FPM10A进行通信,串口一用来和U5板子进行通信。下面看代码部分,在.h文件中定义这些命令码和应答确认码也就是FPM10A传感器的二进制通信协议。

定义的数据包结构,包头,地址,包标识,数据长度以及数据加校验和,还有定义的全局变量buffer265组包的缓冲区,checkDatalen参与校验和的数据长度,totalLen当前包的总长度。

attribute((packed))它的作用是告诉编译器不要填充对齐,让结构体里面的成员紧密挨着

这个函数就是组包并且发送,首先把全局buffer强制转为包结构体指针,方便按字段操作,定义一个临时的数据缓冲区

包已经发送,那么FPM10A收到命令以后,会回答一个应答包所以需要封装一个函数来接收这个应答包。这里为什么要分两次收呢,因为不同命令的应答包长度不一样,但是包头 + 地址 + 包标识 + 长度的总字节数是固定的,第二次就根据长度解析出后边的数据。

录入指纹流程的代码如下,先是进行第一次采集,把采集到的特征存储到第一个缓冲区,然后等手指完全松开,在采集第二次,把特征存储到第二个缓存区,然后合并两个特征为模板

这是录入加存储,参数是U5板子分配好的ID,FPM10A把模板存在内部FLASH的对应位置,方便后序查找,因为FPM10A只能存ID不能存姓名,所以U5板子就要把姓名存入,并且分配ID给F051,F051接收到ID以后,就会把采集指纹合成的模板存到对应的ID位置

在指纹库里搜索想匹配的,然后把匹配到的ID返回给你,当你打卡的时候,肯定也要先采集指纹,然后去对比特征

下面就是打卡验证并且回显给U5板子,调用上面那个searchFingerPrintLib()函数

在主函数里需要做的就是接收U5板子发送过来的数据,来判断要执行那个函数。轮询接收U5发送过来的命令,然后判断是否收到一行数据了,检测看有没有换行符,检测到了就直接解析函数。

其中上面说到的handle_cmd()函数,这个函数用来去处理接收到的数据,根据发送过来的命令,去执行是录入指纹,还是打卡验证指纹

这些是F051这边主要代码,先是主函数初始化,等待FPM10A上电成功,轮询接收串口一发送过来的数据,检测到\r,\n就调用handle_cmd()函数来解析数据,看执行哪些命令函数,在fonger_print.c里,组成FPM10A的固定包,发送不同命令,收到对应的应答包。在往下就是基础的功能函数,录入,存入特征,合并特征,将特征存入模板,等等。

U5原理图部分

通过底板原理图查看印出来的串口4引脚,通过物理连接和F051进行数据传输。

UI层运行

在GUI图形化界面显示如下,这是录入指纹的页面,在输入框里输入姓名,点击按钮,会触发事件回调。

在事件回调函数里,去判断当前按钮是否被点击,在判断请求标志的状态,用户输入姓名然后点击按钮,调用FingerAPP_RequestEnroll()函数,请求录入指纹。

FingerAPP_RequestEnroll()函数的内部实现,接下来通过状态机FingerApp_Process()这个函数去处理,这个函数在RTOS里每50ms去调用这个函数检测状态

验证打卡页面,同理也是点击按钮,触发事件回调

在验证页面的事件回调中去处理,当页面按钮被点击时,就会触发事件回调函数

验证指纹和录入指纹都是封装好的功能函数,在FingerApp_RequestVerify()函数里是这样实现的,这样两大主要的功能就可以了,这是UI层点击不同按钮,芯片要做什么。

Finger_App.c文件

功能函数finger_app.c这个文件是指纹模块主要的核心文件,录入指纹,验证指纹的功能函数都在这里实现,以及fingerapp_process()状态机主函数,它用来检测请求标志的状态,是那种状态,是空闲等待请求,还是要给F051发送命令,还是等待F051回显的结果。它在RTOS任务调度里finger_task()任务每50ms调用一次。接下来,一一看当前文件是如何实现的,最后把录入指纹流程和打卡流程例举出来。

这是定义的变量,和创建的枚举用来后边需要判断的状态

来看状态机FingerApp_Process()函数是如何判断哪些状态来处理的,首先从录入指纹状态数据流程开始,用户在UI页面输入name,然后点击按钮,触发事件回调,调用FingerAPP_RequestEnroll(),把请求状态设为,FINGER_REQ_ENROLL。Finger_task循环检测状态机函数,发生以下改变

下面是等待F051回显然后处理数据,向F051发送命令,F051那边就会根据收到的命令去和FPM10A模块进行通信,根据不同命令去组包发送,然后等FPM10A回包解析数据,在回显给U5

finger_user.c文件

首先要知道,FPM10A指纹模块它只能存储ID,并不能存储姓名之类的,所以要创建一个根据ID查找对应name的映射表,方便打卡验证的时候,屏幕上面显示某某某打卡成功,同样。也是掉电不能丢失。接下来看用户数据存储文件。先看.h文件里创建的结构体,唯一要注意的一点就是需要把结构体填充到16的倍数,当前填充完以后是36字节,3600就是16的倍数了,上面已经说到,FLASH存入要16字节的写入。

这个函数在main.c里启动调用一次

保存到FLASH里的函数,依旧是先解锁,然后擦除指定位置,在进行写入,然后上锁。

根据录入指纹的流程,显示分配ID的函数,从0开始循环遍历,然后找到第一个空闲位置

然后再等用户按完手指,等F051数据解析,匹配ID是否对应然后执行UserTable_Add()函数,把用户ID和name存入FLASH里

打卡验证指纹流程是这样的,再发完打卡命令然后F051回显数据成功,会调用UserTable_FindName()这个函数,根据ID查找name,最后显示到屏幕上,显示某某打卡成功

主函数文件

在main.c主函数里,在程序初始化的时候就,先把FLASH加载指纹用户表到sram里

U5和F051是通过串口4进行收发数据的,这里我们用的是中断1字节的接收,在中断里只做拼接,解析数据放在RTOS里任务中处理,后边else分支判断,是如果收到的缓冲区是空的,直接把下表清零(uart4_line_idx),如果缓冲区没写满的话,就留一个直接位置给结束符,然后把收到的字符存入行缓冲区里,下标依旧自增。

FREERTOS文件里

接下来看在rtos里任务是怎么调用的分两个任务处理,一个是LVGL_task(),用来根据状态刷新当前页面要显示什么。还有一个FINGER_task(),这个函数每50ms就调用一次状态机函数FingerApp_Process().

相关推荐
Brilliantwxx1 小时前
【STM32】 HAL库深度入门:从设计思想到 USART 全模式实战
stm32·单片机·嵌入式硬件
恒锐丰科技林技术员1 小时前
EG1192S:高耐压零功耗使能降压 DC‑DC 芯片,拓宽工业电源设计边界
经验分享·嵌入式硬件·硬件工程
深圳市恒锐丰科技杨生2 小时前
EG1192L 零功耗使能集成上管非同步降压 DC‑DC|屹晶 EGmicro
嵌入式硬件·硬件工程
JNX_SEMI2 小时前
HTD8239 5.5V~36V宽压H桥驱动:适配12V/24V主流电机供电系统
单片机·嵌入式硬件
2401_862880822 小时前
51单片机 --- GPIO、中断、定时器、PWM
单片机·嵌入式硬件·51单片机
零依赖极客2 小时前
《30 天手搓 ARM 架构零依赖纯 C 推理引擎》课程介绍与大纲
c语言·开发语言·arm开发·人工智能·嵌入式硬件·ai编程
国科安芯2 小时前
基于ECC加固的抗辐射MCU在卫星载荷数据管理中的应用分析
单片机·嵌入式硬件·mcu·抗辐射·ecc加固·卫星数据管理
深圳市恒锐丰科技杨生2 小时前
EG3033 三相 P‑N MOS 栅极驱动芯片|屹晶 EGmicro
嵌入式硬件·硬件工程
linx2953 小时前
单元三 · 那 C 的底层知识怎么办
c语言·开发语言·数据结构·c++·嵌入式硬件