STM32F4+FreeRTOS+LVGL实现嵌入式快速开发(缝合怪)

news2025/4/15 6:00:38

极速进行项目开发,只需要懂一款芯片架构+一个操作系统+一个GUI。各种部件程序全靠抄

,成为究极缝合怪。本文用stm32f407+FreeRTOS+lvgl演示一些demo。

原文链接:STM32F4+FreeRTOS+LVGL实现快速开发(缝合怪)

lvgl官方的音乐播放器demo:

百问网的2048小游戏:

1.STM32F407和FreeRTOS

STM32F407这款芯片就不多介绍了,挺老的MCU,架构为ARM_CM4F。随便一搜就有非常非常多的例程和项目。

会缝合的基础是对芯片架构非常了解,刚入门的同学建议先从基础学起,推荐学习ARM官方的权威指南。

在家中找到一个早之前的开发板,个人还挺喜欢的,只有最小系统,把pin引出来了,没有乱七八糟的外设,还找到一个240*320的LCD屏幕,ILI9341驱动。

至于FreeRTOS,之前讲了FreeRTOS在STM32F4上的移植:STM32F4移植FreeRTOS光是MCU加上FreeRTOS就已经能做很多东西了,github上也有非常多的项目,直接git clone再随便改改就能做出很多东西。

FreeRTOS的系列讲解:FreeRTOS全解析-1.引入与RTOS简介

2.LVGL和FreeRTOS结合

都有显示屏了,当然得显示一下,增加一下逼格,但是自己画肯定不好看,也也没有那个必要,这就需要借助开源图形库了。

嵌入式GUI有非常多,LVGL是其中之一,很低的配置就可以实现非常好的效果。介绍就没必要了,就是一个C语言写的图形库,需要学习的可以去官网:https://lvgl.io/看手册。

官网的一个demo:

我对LVGL了解不多,但通过手册可以知道,LVGL的底层是通过定时器来循环调用lv_tick_inc();函数,以获知系统时间,称为LVGL的心跳。再通过lv_task_handler();(新版的是lv_timer_handler())来调度LVGL的各种任务,包括显示、输入,各种事件。搞明白这个,我们就可以非常容易得将他与FreeRTOS结合了。

2.1下载LVGL源码,并加入keil工程

去官网找到源码,找个需要的版本,我这里用git下了lvgl8.0。

只需要如下几个东西

 

把lv_conf_template.h的文件名改成lv_conf.h。这是官方提供的配置样板,我们要改成适合自己的。开头的if 0改成1,使它生效。

#if 1 /*Set it to "1" to enable content*/

显示屏的颜色,我的是16位的

/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/#define LV_COLOR_DEPTH     16

显示和输入(触屏、按钮等)的周期,按需要改。显示周期20ms就是50帧。

/*Default display refresh period. LVG will redraw changed ares with this period time*/#define LV_DISP_DEF_REFR_PERIOD     20      /*[ms]*//*Input device read period in milliseconds*/#define LV_INDEV_DEF_READ_PERIOD    30      /*[ms]*/

内存和CPU监控开一下,方便看效果,就是我上面demo中左下角和右下角的显示。

/*1: Show CPU usage and FPS count in the right bottom corner*/#define LV_USE_PERF_MONITOR     1/*1: Show the used memory and the memory fragmentation  in the left bottom corner * Requires LV_MEM_CUSTOM = 0*/#define LV_USE_MEM_MONITOR      1

examples文件夹中只需要porting文件夹。

里面的文件名中的template,全部删掉。

以STM32F4移植FreeRTOS中的已经移植好FreeRTOS的keil工程为模板

新建文件夹

LVGL放入src文件,LVGL_PORT放入porting中的文件,LVGL_APP放入example中随便复制的demo

然后在keil中把所有.c文件添加进去,这个过程就比较麻烦,要,需要一个个点。

2.2调整显示接口

lv_port_disp.c为lvgl的显示接口第一步就是把它和lv_port_disp.h头文件中的第一行if 0改为1,使他们生效。

lv_port_disp.h文件中添加两行,表示我LCD的分辨率为240*320

#define MY_DISP_HOR_RES 240#define MY_DISP_VER_RES 320

lv_port_disp.c中初始化函数disp_init中,加上自己的LCD初始化函数。

static void disp_init(void){    /*You code here*/      LCD_Init();}

刷屏函数中加上自己的画点函数,官方中的例子是这样的

static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p){    int32_t x;    int32_t y;    for(y = area->y1; y <= area->y2; y++) {        for(x = area->x1; x <= area->x2; x++) {            /*Put a pixel to the display. For example:*/           /*put_px(x, y, *color_p)*/             color_p++;        }    }    lv_disp_flush_ready(disp_drv);}

/*put_px(x, y, *color_p)*/改成你LCD的画点函数,不过这样很慢,推荐直接使用区域绘制

static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p){    LCD_Color_Fill(area->x1,area->y1,area->x2,area->y2,(u16*)color_p);    lv_disp_flush_ready(disp_drv);}

这样会更快。

我的LCD用FSMC,速度已经够快了,但是我想更快一点,就用DMA(提升不会特别大)。在这个函数中只放一个DMA传输函数。

static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p){  LCD_Start_DMA_Transfer(area->x1,area->y1,area->x2,area->y2,(u16*)color_p);}

在DMA完成中断函数中通知LVGL

void DMA2_Stream3_IRQHandler(void){  if(DMA_GetITStatus(LCD_DMA_Stream,LCD_DMA_IT_TCIFx)!=RESET)  {       DMA_ClearITPendingBit(LCD_DMA_Stream,LCD_DMA_IT_TCIFx);       lv_disp_flush_ready(&disp_drv);   }}

然后就是为LVGL选择一种显存,在这个函数:

void lv_port_disp_init(void)

中,提供了三种显存方案,第一种是十行,第二种是双十行显存,第三种是双全屏显存,stm32f407sram只有192k,开不了全屏显存。我选择第二种,双显存,因为我开启了DMA,传输的时候CPU可以去计算,并存入第二块显存。用不到的代码注释掉就行。如下:

    /* Example for 1) *///    static lv_disp_draw_buf_t draw_buf_dsc_1;//    static lv_color_t buf_1[MY_DISP_HOR_RES * 10];                          /*A buffer for 10 rows*///    lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10);   /*Initialize the display buffer*/    /* Example for 2) */    static lv_disp_draw_buf_t draw_buf_dsc_1;    static lv_color_t buf_2_1[MY_DISP_HOR_RES * 80];                        /*A buffer for 10 rows*/    static lv_color_t buf_2_2[MY_DISP_HOR_RES * 80];                        /*An other buffer for 10 rows*/    lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 80);   /*Initialize the display buffer*///    /* Example for 3) also set disp_drv.full_refresh = 1 below*///    static lv_disp_draw_buf_t draw_buf_dsc_3;//    static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES];            /*A screen sized buffer*///    static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES];            /*An other screen sized buffer*///    lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2, MY_DISP_VER_RES * LV_VER_RES_MAX);   /*Initialize the display buffer*/

同时,比较旧的lvgl在这个函数中,还需要注册一下,新的不用

    /*Set up the functions to access to your display*/    /*Set the resolution of the display*/    disp_drv.hor_res = MY_DISP_HOR_RES;    disp_drv.ver_res = MY_DISP_VER_RES;

其实就是加上分辨率。

2.3调整输入接口

lv_port_indev.c为lvgl的显示接口第一步就是把它和lv_port_indev.h头文件中的第一行if 0改为1,使他们生效。

lvgl支持触摸屏,按键,编码器等输入,我用触摸屏,只需要修改touchpad相关

static void touchpad_init(void){   /*Your code comes here*/  tp_dev.init();}/*Return true is the touchpad is pressed*/static bool touchpad_is_pressed(void){    tp_dev.scan(0);    if(tp_dev.sta&TP_PRES_DOWN)      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)    (*x) = tp_dev.x[0];    (*y) = tp_dev.y[0];}

分别是初始化、扫描确实是否有按下,和获取按下的点的值。

我的触摸屏是电阻屏,驱动从正点原子的触摸屏实验例程中来,该驱动中用到了SysTick定时器来延时微秒,我们的工程是含有FreeRTOS的,会造成冲突,我也懒得优化细改了,用了个比较粗糙简单的方法,直接在延时前把几个寄存器保存一下,延时后再恢复。

void delay_us(u32 nus){      u32 temp;    u32  tickload = SysTick->LOAD;  u32  tickval = SysTick->VAL;  u32  tickctrl = SysTick->CTRL;    SysTick->LOAD=nus*fac_us;         //ʱ¼ä¼ÓÔØ           SysTick->VAL=0x00;                //Çå¿Õ¼ÆÊýÆ÷  SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //¿ªÊ¼µ¹Êý      do  {    temp=SysTick->CTRL;  }while((temp&0x01)&&!(temp&(1<<16)));  //µÈ´ýʱ¼äµ½´ï     SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //¹Ø±Õ¼ÆÊýÆ÷  SysTick->VAL =0X00;               //Çå¿Õ¼ÆÊýÆ÷     SysTick->LOAD  = tickload;  SysTick->VAL  = tickval;  SysTick->CTRL  = tickctrl;}

读数据TP_Read_AD中加入临界区保护,就可以完美使用。

u16 TP_Read_AD(u8 CMD)    {      taskENTER_CRITICAL();  taskEXIT_CRITICAL();       return(Num);   }

2.4main函数

开头说了,将LVGL与FreeRTOS结合的重点其实就是再讲lv_tick_inc()和lv_task_handler()两个函数与FreeRTOS结合起来。

FreeRTOS也有心跳Tick,我们在FreeRTOS的FreeRTOSConfig.h中开启TICK钩子:

#define configUSE_TICK_HOOK        1

每次FreeRTOS发生tick都会去调用钩子函数,在钩子函数中放入LVGL的心跳就可以了:

void vApplicationTickHook(void){  lv_tick_inc(1);}

数字1的意思是告诉LVGL每1ms执行一次。

至于lv_task_handler(),我们开一个任务去执行它

因为LVGL线程不安全,所以调用到LVGL的API时需要加互斥锁。

主函数:

  MutexSemaphore=xSemaphoreCreateMutex();   xTaskCreate(lvgl_handler, "lvgl_handler", 1000, NULL, 4, NULL);

任务:

static void lvgl_handler( void *pvParameters ){  for( ;; )  {        xSemaphoreTake(MutexSemaphore,portMAX_DELAY);      lv_task_handler();    xSemaphoreGive(MutexSemaphore); //    vTaskDelay(pdMS_TO_TICKS(20));  }}

到这了就是完全移植成功了。

现在你需要的就是个应用代码,github上有特别多的LVGL相关例子,官方也有很多,随便一抄,稍微修改,就可以完成一个小项目,这就是缝合的威力。

lvgl官方的压力测试demo:

STM32F4+FreeRTOS+LVGL实现快速开发(缝合怪)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/414765.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序学习笔记

一、Node.js主题 1、npm&#xff1a;node.js包管理工具&#xff0c;有超过60万个JavaScript代码包可供下载 2、Node.js&#xff1a;运行在服务端的JavaScript&#xff0c;基于Chrome JavaScript建立的一个平台&#xff0c;基于Google V8引擎。 3、Nodejs安装教程&#xff1a…

Redis篇之主从复制及哨兵模式

主从复制及哨兵模式 1、概念 主从复制&#xff1a; 是指将一台 Redis 服务器的数据&#xff0c;复制到其他的 Redis 服务器。前者称为主节点&#xff08;Master/Leader&#xff09;,后者称为从节点&#xff08;Slave/Follower&#xff09;&#xff0c; 数据的复制是单向的&a…

毕业设计 基于51单片机的智能水表水流量计流量报警器温度设计

基于STM32的语音IC卡停车管理系统1、快速完成毕设的方法2、项目资料2.1 系统框架2.2 系统功能3、部分电路设计3.1 STC89C52单片机最小系统电路设计3.2 继电器控制电路设计3.3 DS18B20温度检测电路设计3.4 LCD1602液晶显示电路设计4、部分代码展示4.1 LCD1602液晶显示屏引脚初始…

2023年第十四届蓝桥杯 C++ B组参赛经验总结

没错&#xff0c;今年本菜狗又来啦~~ hhh &#xff0c; 文章当时比赛完就写完了&#xff0c; 发的有点晚 比赛成绩 &#xff08;等出来我就写这里&#xff09; 感觉最多省二 估计没省一了555 赛前准备 赛前把蓝桥杯课基本都刷了 &#xff0c; 但是还是感觉有点慌 刷题经验 …

【mysql是怎样运行的】-B+树索引深入理解

文章目录1. 无索引查找方式1.1 在一个页中查找1.2 在多个页中查找2. 索引3. 简易索引方案4. InnoDB 中的索引方案5. **常见索引概念**数据页与记录关系&#xff1a;各个数据页可以组成一个 双向链表&#xff0c;而每个数据页中的记录会按照主键值从小到大的顺序组成一个 单向链…

Linux主机上的用户信息传递(查询用户(w,who,last,lastlog),用户对谈(write,mesg,wall),用户邮箱mail)

文章目录Linux主机上的用户信息传递查询用户&#xff1a;w、who、last、lastlog用户对谈&#xff1a;write、mesg、wall用户邮箱&#xff1a;mail使用案例给自己的QQ邮箱发送一封邮件①获取授权码②使用mailx发送邮件③测试是否可以发送邮件Linux主机上的用户信息传递 想过吗如…

阿里版 ChatGPT 突然上线!

转自:纯洁的微笑 其实早本月初&#xff0c;就传出过不少阿里要推出类ChatGPT的消息。 前几天率先流出的天猫精灵“鸟鸟分鸟”脱口秀版GPT&#xff0c;就是基于大模型的“压缩版”&#xff0c;已经以其惊艳表现吸引了众目光。 如今“原版大菜”上桌&#xff0c;自然一点即着&a…

PHP反序列化魔术方法详细解析及实例公私有属性对比

目录 一、魔术方法利用点分析 <__construct&__destruct> <__toString> <__call> <__get> <__set> <__sleep> <__wakeup> <__isset> <__unset> <__invoke> <总结> 二、对象变量属性及序列化…

Pandas 常用按照查询条件筛选数据

文章目录1. 筛选指定的列2. 按照条件筛选3.1 单条件筛选3.2 多条件组合筛选创建一个DataFrame import pandas as pd data {name:[张三, 李四, 王五, 赵六],age:[20, 21, 22, 23], gender: [0, 1, 1, 1], stature: [165, 189, 178, 160], year: [2000, 2002, 2003, 1993]} df …

Servlet教程

在JavaEE平台上&#xff0c;处理TCP连接&#xff0c;解析HTTP协议这些底层工作统统扔给现成的Web服务器去做&#xff0c;我们只需要把自己的应用程序跑在Web服务器上。为了实现这一目的&#xff0c;JavaEE提供了Servlet API&#xff0c;我们使用Servlet API编写自己的Servlet来…

JavaWeb开发 —— 前端工程化

目录 一、前后端分离开发 二、YApi 三、前端工程化 1. 环境准备&#xff1a;vue-cli 2. Vue项目创建 四、Vue项目开发流程 一、前后端分离开发 ① 最早的前端开发就是实现页面&#xff0c;顶多再写写JS让页面可以有交互的特效。属于前后端未分离的时代。 早期前后端混合开…

Amazon 中国区配置 PingIdentity 身份集成实现 Redshift 数据库群集单点登录

无疑使用单点登录 (SSO)访问组织中的多种应用程序能够提升用户体验 。 如果您负责为 Amazon Redshift 启用 SSO&#xff0c;则可以使用 ADFS、PingIdentity、Okta、Azure AD 或其他基于 SAML 浏览器的身份提供程序设置 SSO 身份验证。 这篇文章向您展示了如何将 PingOne 设置为…

js中 = 等号赋值的问题,Js中对象的引用问题,深浅拷贝

js "" 赋值符号 在js中 “”对于基本数据类型是赋值符号&#xff0c;比较&#xff08; 或 &#xff09;的时候是值&#xff1b;对于引用数据类型-对象来说 是地址引用&#xff0c;比较的时候是比较的地址。 基本数据类型和引用数据类型的比较 let a 3; let b a;c…

Go是一门面向对象编程语言吗

本文首发自「慕课网」&#xff0c;想了解更多IT干货内容&#xff0c;程序员圈内热闻&#xff0c;欢迎关注"慕课网"&#xff01; 作者&#xff1a;tonybai|慕课网讲师 Go语言已经开源13年了&#xff0c;在近期TIOBE发布的2023年3月份的编程语言排行榜中&#xff0c;…

Go面向对象

前言 Go也有面向对象 面向对象引入: 用面向对象好啊 结构体定义 GO中的结构体和其他语言中的class是同一个等级的 这个就懒得写了 , 直接贴一个 内存分析 当实例化一个结构体的时候,分配一份内存空间. 结构体实例的创建 package main import "fmt" type Te…

计组第一章——计算机组成的基本认识

计算机——> 数值计算——> 处理电信号——> 基本单元&#xff08;逻辑元件&#xff09; 电子管——> 晶体管——>中小规模集成电路 ——>大规模&#xff0c;超大规模集成电路 机器字长&#xff1a;计算机一次整数运算所能处理的二进制位数 解析存储器中的程…

Vue——组件 v-model

目录 ​ v-model 的参数​ 多个 v-model 绑定​ 处理 v-model 修饰符​ v-model 可以在组件上使用以实现双向绑定。 首先让我们回忆一下 v-model 在原生元素上的用法&#xff1a; <input v-model"searchText" />在代码背后&#xff0c;模板编译器会对 v-…

工程日记的感悟

我个人很喜欢工程日记&#xff0c;好像一片自己一亩三分地一样&#xff0c;自己想弄些啥&#xff0c;就弄些啥。 人需要这份自由&#xff0c;需要这份能动性&#xff0c;因为人是创造者。 在《从小工到专家》的书中所要求的条目之中&#xff0c;有一条&#xff0c;就是工程日志…

最详细的Ubuntu服务器搭建Stable-Diffusion教程(无显卡,仅用CPU)

1. 首先安装基本工具 # 安装python环境 sudo apt install wget git若已经安装过请忽略 2. 安装miniconda&#xff08;也可以自己下载python&#xff09; 下载最新的安装包 wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh执行安装 ./Minicon…

NumPy 秘籍中文第二版:十二、使用 NumPy 进行探索性和预测性数据分析

原文&#xff1a;NumPy Cookbook - Second Edition 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 在本章中&#xff0c;我们涵盖以下秘籍&#xff1a; 探索气压探索日常气压范围研究年度气压平均值分析最大可见度用自回归模型预测气压使用移动平均模型预测气压研究年…