ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

LVGL,STM32U575RIT6+指纹模块

2026/9/12 17:51:13 拓冰建站 浏览量
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.h3 LVGL_porting LVGL 的接口文件例如显示触摸屏键盘等等4 LVGL_src LVGL 的所有底层.c文件二LVGL 显示接口对应的文件驱动首先参考厂家提供的对应的lcd显示的代码位置百度网盘 请输入提取码点击进去找到对应的工程紧接着打开工程找到对应的.c和.h文件拷贝到我们自己的工程中原理图部分查看原理图对应屏幕的是采用spi通信核心板上面也标注了PA4:片选线PA5时钟线PA6MISOPA7MOSIPA8lcd的复位引脚。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-fullFULL是一个联合体并且屏幕是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中断引脚PA11LCD触摸复位的引脚。I2C的时序图接下来这是I2C的读写时序图根据不同的命令去读取数据还是写入数据相关代码下边这是I2C的地址结合上面的读写时序图来生成读地址和写地址I2C7位的从机地址加上一位的读写位在main.c中添加对应的触摸初始化FT6336_init();以及在bsp_ft6336.h中添加对应的宏定义这是触摸接口的初始化函数对应得就是配置PA11得引脚去触摸复位。再MAIN函数里去调用这个函数初始化。这是读取X,Y的坐标的函数由官方提供同时可以在主函数里调用这个函数可以得到触摸屏幕上面的具体坐标当你带点击屏幕会触发这个函数当然这里是采用轮询的方式去触发的点击屏幕以后X,Y对应的值也会发生改变当你点击屏幕不同的位置同样也可以在中断方式上报配置的PA5中断引脚引脚然后重写中断处理函数HAL_GPIO_EXTI_Rising_Callback这里是上升沿的回调函数在这个函数里把FT6336_scan(void)写进去。/* ********************************************************************** * 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_x和FT6336_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传感器的二进制通信协议。定义的数据包结构包头地址包标识数据长度以及数据加校验和还有定义的全局变量buffer[265]组包的缓冲区checkDatalen参与校验和的数据长度totalLen当前包的总长度。__attribute__((packed))它的作用是告诉编译器不要填充对齐让结构体里面的成员紧密挨着这个函数就是组包并且发送首先把全局buffer强制转为包结构体指针方便按字段操作定义一个临时的数据缓冲区包已经发送那么FPM10A收到命令以后会回答一个应答包所以需要封装一个函数来接收这个应答包。这里为什么要分两次收呢因为不同命令的应答包长度不一样但是包头 地址 包标识 长度的总字节数是固定的第二次就根据长度解析出后边的数据。录入指纹流程的代码如下先是进行第一次采集把采集到的特征存储到第一个缓冲区然后等手指完全松开在采集第二次把特征存储到第二个缓存区然后合并两个特征为模板这是录入加存储参数是U5板子分配好的IDFPM10A把模板存在内部FLASH的对应位置方便后序查找因为FPM10A只能存ID不能存姓名所以U5板子就要把姓名存入并且分配ID给F051F051接收到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回包解析数据在回显给U5finger_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().