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

2023-05-16

极速进行项目开发,只需要懂一款芯片架构+一个操作系统+一个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实现快速开发(缝合怪)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32F4+FreeRTOS+LVGL实现嵌入式快速开发(缝合怪) 的相关文章

  • 开篇词 | 程序员的成长课

    大家好 xff0c 我是安晓辉 xff0c 做过开发工程师 研发经理 技术总监等岗位 xff0c 现在自由职业 xff0c 专注写作和开发者生涯咨询 出版过 程序员的成长课 Qt Quick 核心编程 你好哇 xff0c 程序员 解忧程序员
  • const与define的区别

    1 define是预编译指令 xff0c const是普通变量的定义 xff0c define定义的宏是在预处理阶段展开的 xff0c 而const定义的只读变量是在编译运行阶段使用的 2 const定义的是变量 xff0c 而define
  • 如何摆脱CRUD等打杂状态,从事更高价值工作

    每个月都会有十来个来询者向我抱怨工作低端 xff0c 程序员说自己每天CRUD xff0c 重复 枯燥 没技术含量 xff0c 销售助理说自己天天搜集客户信息 打印资料 帮老大带饭 xff0c 繁琐 无聊 不重要 xff0c 他们都说自己整
  • Windows下Qt 5.2 for Android开发入门

    Qt 5 2 发布了 xff0c 支持 Android 平台 xff0c 太好了 之前公司项目 xff0c 为了移植一个依赖 Qt 的程序到安卓平台上 xff0c 我自己交叉编译了 Qt Embedded 4 5 2 xff0c 费了老大劲
  • Qt Quick 之 QML 与 C++ 混合编程详解

    Qt Quick 技术的引入 xff0c 使得你能够快速构建 UI xff0c 具有动画 各种绚丽效果的 UI 都不在话下 但它不是万能的 xff0c 也有很多局限性 xff0c 原来 Qt 的一些技术 xff0c 比如低阶的网络编程如 Q
  • 漫谈程序员系列:一张图道尽程序员的出路

    推背图 相传由唐太宗时期的司天监李淳风和袁天罡合著 xff08 此两人其实是超级武学高手 xff0c 参见小椴的 开唐 xff09 xff0c 推算大唐以后中国两千多年的国运盛衰 xff0c 在中国七大预言书中居首 xff0c 是当之无愧的
  • 漫谈程序员系列:咦,你也在混日子啊

    戳你一下 xff0c 疼吗 xff1f 混日子的定义 来自百度百科的定义 xff1a 生活等方面过得不怎么好 xff0c 无目标 xff0c 混混沌沌 混日子 xff1a 即没有理想 xff0c 没有抱负 xff0c 糊里糊涂地生活 也指工
  • QtAndroid详解(1):QAndroidJniObject

    Qt 5 3之后 xff0c 新增了 QtAndroid 名字空间 xff0c 内有下列四个方法 xff1a QAndroidJniObject AndroidActivity int androidSdkVersion void star
  • freeSWITCH安装、配置与局域网测试

    这次来说说 freeSWITCH 的安装和配置 1 安装 freeSWITCH 下载页面 xff1a https freeswitch org confluence display FREESWITCH Installation 我们 Wi
  • 就 3 点,提升工作效率

    要想提高工作效率 xff0c 不论你看什么书 xff0c 看什么文章 xff0c 用什么工具 xff0c 只有下面这三点最重要 xff1a 动力剖析自己 xff0c 找到改善的切入点付诸行动并且坚持 目标驱动 有目标才能高效 我们爬山 xf
  • Python3 下 ROS 的使用 cv_bridge

    Python 3 下 ROSmsg 转 cv2 项目中用到的 Tensorflow2 4 的环境 xff0c 该环境只支持python3 版本 xff0c 项目中遇到不少需要和 ROS 交互的地方 xff0c 所以不断探索 python3
  • 深度图和RGB图对齐

    深度图 canny RGB canny Alignment xff1a code span class token function import span cv2 span class token function import span
  • 认识romfs文件系统

    1 1 什么是romfs romfs 是一个只读文件系统 xff0c 主要用在 mainly for initial RAM disks of installation disks 使用romfs 文件系统可以构造出一个最小的内核 xff0
  • Livox Lidar 特征提取方式总结

    传统的 旋转式雷达 xff0c 激光固定在雷达的旋转部件上 xff0c 依靠转子的转动而获得360的旋转视野 xff0c 由于旋转部件的存在 xff0c 为了获得更加精准的数据 xff0c 需要跟多的人工校准和更复杂的设计 xff0c 也因
  • C++ 菜鸟之路 (四) boost::thread 多线程全解析

    boost thread 的一般用法 boost thread的几个函数 锁 lock 函数多线程函数的限制 官方解释 xff1a http www cplusplus com reference thread thread joinabl
  • ROS 之 advertise 详解

    在学习过程中接触到如下的一段话 span class hljs comment ROS handles span ros span class hljs tag NodeHandle span node tf span class hljs
  • Linux 下 openMP 效率并未提升的解决方案

    OpenMP 正确观察计算时间OpenMP 经验总结 xff08 1 xff09 openmp 线程使用范围 xff08 2 xff09 openmp 多层嵌套的问题 OpenMP 正确观察计算时间 在使用 openmp的过程中 xff0c
  • C++ Yaml文件解析安装及使用 yaml-cpp

    C 43 43 Yaml文件解析安装及使用 安装 yaml cpp克隆官方库编译 yaml cpp 示例代码robot cpprobot yaml编译 robot cpp运行结果 难点分析与总结什么是 a 与 so 文件静态链接库 a 与动
  • 点云数据格式解析 sensor_msgs::PointCloud2

    在使用多线激光的时候需要总是会碰到点云数据 xff0c 这里简单的接受一下点云数据 xff0c 并堆数据结构进行分析 xff0c 方便自己后期对点云特征数据进行处理 文章目录 Rviz中的点云数据点云数据结构分析点云数据 python 解析
  • Arduino 读取GPS 数据发送解析并发布ROS topic(二)

    Arduino 读取GPS 数据发送解析并发布ROS topic 一 https blog csdn net Fourier Legend article details 84107494 概述 本部分将主要讲将串口接受到的数据 xff0c

随机推荐

  • LOAM进行点云地图创建

    3D激光点云数据处理入门 xff08 一 xff09 使用LOAM进行点云地图创建 LOAM 原理简述topic关系算法分析算法伪代码 LOAM 建图实践创建你的 ROS Workspace下载LOAM Package下载数据包运行 LOA
  • ROS - teb_local_planner 参数总结

    参考官方教程 http wiki ros org teb local planner Tutorials 全英文看着有点累 在此总结一下调试的过程和小小的经验 安装 teb local planner sudo apt get instal
  • 路径规划算法(2) - A*寻路算法 python实现及解析

    代码 span class token comment coding 61 utf 8 span span class token keyword import span math span class token comment 启发距离
  • 使用FSMC驱动LCD以及数据线偏移的问题

    FSMC的理解 使用FSMC功能将8080接口的LCD当外部RAM来使用 xff08 数据传给LCD时没经过内部SRAM xff0c 所以一帧图片很大也可以直接传 xff09 xff0c 根据STM的地址分配图可以看出外部RAM的地址由0x
  • C++调用HTTP实现方式

    转自 xff1a http blog 163 com lyz sea blog static 11558670720118245052189 Http访问有两种方式 xff0c GET和POST xff0c 就编程来说GET方式相对简单点
  • 跟我学c++中级篇——STL智能指针再述

    一 智能指针 xff08 smart pointer xff09 在前面的文章分析中对智能指针分析的还是比较多的 xff0c 这里把一具体的遗漏以及一些新的感悟再总结一下 xff0c 以之为鉴 什么是智能指针 xff1f 在C C 43 4
  • 从CAN到CANOpen——准入门大全(二)

    第二节 关于CAN ID xff1a 一个标准帧的ID共11位 xff0c 一个扩展帧的ID共29位 扩展帧的ID分11位和18位的两段 xff0c 如下图所示 RTR显性 xff1a 数据帧 xff1b 隐性 xff1a 远程帧 SRR隐
  • vscode使用clang-format格式化C++代码

    1 安装c c 43 43 插件 2 在首选项 设置中搜索format xff0c 设置Editor Default Formatter为ms vscode cpptools 3 在扩展C C 43 43 中设置 Clang format
  • EC20 GPS RMC格式数据转化

    文章目录 目录 前言 一 RMC是什么 xff1f 二 EC20 输出的RMC解析 1 EC20返回的RMC报文 2 RMC报文解析 3 NMEA数据ddmm mmmm转换成dd ddddd 4 RMC UTC时间转化成北京时间 总结 前言
  • C++ 第三方常用网络库

    From xff1a https www cnblogs com aitantianderuangutou p 11416902 html 1 ACE 庞大 复杂 xff0c 适合大型项目 开源 免费 xff0c 不依赖第三方库 xff0c
  • Postman 使用方法详解

    From xff1a https zhuanlan zhihu com p 534078123 Postman V9 16 绿色版汉化 xff1a https www cr173 com soft 1497202 html Postman是
  • 串口波形分析

    本文使用逻辑分析仪 xff0c 抓取串口波形 xff0c 进而分析串口数据 串口配置为115200波特率 xff0c 8个数据位 xff0c 1个停止位 xff0c 无校验方式 字符1的波形如下图 xff1a 从图中可以看到8个数据位 xf
  • 电影资源 BT PT下载的电影命名 规则 资源 详解

    初识 一般来说 xff0c 正规压制组压制的电影 xff0c 都采用 0day 命名方式 xff0c 即 xff1a 英文名称 版本说明 年份 片源 分辨率 视频编码 音频格式 压制小组 例如文件名 xff1a Jumanji The Ne
  • 对于51单片机的RAM内存分配(包含栈的分配)

    对于51单片机的RAM内存分配 xff08 包含栈的分配 xff09 我使用的是SH79F3283 xff0c 内部RAM有256字节 xff0c 由常规寄存器 静态存储区和堆栈组成的 xff0c 创建一个新的程序默认占用9个字节RAM x
  • 忘记windows密码解决办法(用户密码或SYSKEY)

    64 TOC 还有朋友问第一层的BIOS密码怎么解 xff1a 拔电池就能解 xff0c 断电 xff0c 打开电脑主板 xff0c 找到一个纽扣电池 xff0c 拔掉 xff0c 长按电源键 xff0c 再插电 xff0c 第一层就没了
  • 【数据结构与算法】-哈夫曼树(Huffman Tree)与哈夫曼编码

    超详细讲解哈夫曼树 Huffman Tree 以及哈夫曼编码的构造原理 方法 xff0c 并用代码实现 1哈夫曼树基本概念 路径 从树中一个结点到另一个结点之间的分支构成这两个结点间的路径 结点的路径长度 两结点间路径上的分支数 树的路径长
  • Makefile命令参数、调用其他Makefile、嵌套

    更多单片机 嵌入式内容及参考资料 xff1a 大叔的嵌入式小站 xff1a Makefile学习 3 make命令参数 调用其他makefile 嵌套 一 Make命令运行参数 参数 参数的作用 C dir读入指定目录下的makefile
  • ARM学习-ARM指令集详解

    目录 1 ARM 存储器访问指令 1 1 LDR 和STR 加载 存储字和无符号字节指令 1 2 LDM和STM 批量加载 存储指令 1 3 SWP 寄存器和存储器交换指令 2 ARM 数据处理指令 2 1数据传送指令 2 1 1 MOV
  • IIC(I2C)协议详解

    1 简介 IIC 即I C xff0c 全称 Inter Integrated Circuit xff0c 字面上的意思是集成电路之间 xff0c 它其实是I C Bus简称 xff0c 所以中文应该叫 集成电路总线 xff0c 它是一种串
  • STM32F4+FreeRTOS+LVGL实现嵌入式快速开发(缝合怪)

    极速进行项目开发 xff0c 只需要懂一款芯片架构 43 一个操作系统 43 一个GUI 各种部件程序全靠抄 xff0c 成为究极缝合怪 本文用stm32f407 43 FreeRTOS 43 lvgl演示一些demo 原文链接 xff1a