前言
念春时已夏,恋冬雪已融。
总是感叹时光匆匆,便努力在在平凡中挣扎,在平庸中努力,在平淡中积累。奈何时代飞速发展,时间又被工作占用,外加生活中的诱惑又太多了,很多想学、想做、想超越的事,都被抛之一旁,渐渐的跟不上时代了,当年对兴趣爱好的激情,也下降了好多。
上一篇博客还是几年前的,虽然中间进步不少,但是却少了许多当年写博客时的激情飞扬,真是感慨诸多。
按公司要求,以后需要每周交一篇知识分享文档,索性就继续写一下博客,算是总结,也算是对自己的积累。
由于文档允许在大框架内自由发挥,所以决定在我的兴趣点上找一些还说的过去的东西写一写,而不是单纯的复制一些网上随便就能找到的东西。
以后会努力在机械电机、三维重建、图像视觉、深度学习等兴趣范围内做一些比较综合的整理,而不是仅限于嵌入式的方向。当然,他们的实现都离不开底层,所以肯定是包含嵌入式方向的。
一:说明
BL808芯片是三核异构的RISC-V CPU,参数如下:
三核异构RISC-V CPUs:
- RV64GCV 480MHz
- RV32GCP 320MHz
- RV32EMC 160MHz
AI NN 通用硬件加速器 —— BLAI-100 用于视频/音频检测/识别
内置 768KB SRAM + 64MB UHS PSRAM
编解码:
- MJPEG and H264(Baseline/Main)
- 1920x1080@30fps + 640x480@30fps
接口: - 摄像头接口 :DVP 和 MIPI-CSI
- 显示接口:SPI、DBI、DPI(RGB)
无线: - 支持 Wi-Fi 802.11 b/g/n
- 支持 Bluetooth 5.x Dual-mode(BT+BLE)
- 支持 Wi-Fi / 蓝牙 共存
USB 2.0 HS OTG (引出到 USB Type-C 接口)
具体参数可以查看Sipeed科技的 M1s DOCK 开发板 说明页。
可以看到,这款芯片的能力还是很强的,完全可以媲美之前非常火的入门级Linux芯片 F1C100S,另外,它支持FreeRTOS,所以可以当做裸机进行开发,代替单片机。但是可能由于是新出的产品,或是由于其他原因,目前这款产品只有Sipeed科技生产的模组,没有纯芯片售卖。目前体验设备便是Sipeed科技推出的全功能测试开发板:【Sipeed M1s DOCK 开发板】
开始使用体验
1:环境搭建
- --Linux开发环境搭建
参考Sipeed科技的【上手使用】教程的1-4大节,搭建linux使用环境后,使用模拟U盘的方式将bin文件拖拽进去即可体验。如果有问题,请自行查找解决办法。
这里我使用VMware软件运行ubuntu16.04环境进行测试。支持快速内外复制等功能。如果是别的虚拟机环境,自行判断是否支持。
由于购买时间较早,所以需要根据教程的 3.2【串口烧录】 来更新板载BL702固件来实现稳定的下载,在使用图形化界面烧录最新的固件,来实现 3.1【U盘烧录】。后面编译生成的bin文件都可以通过U盘烧录来快速更新。
目前主要测试程序都是基于三个核心中的C906核心的。 - --SDK下载和编译测试
参考【上手使用】教程的第5大节,下载SDK并配置好编译工具链,然后就可以编译并下载程序测试。
可以跟着操作一遍【lvge_demo】的编译和下载操作,熟悉整个流程。
新手小提示:- 1:程序需要两个文件夹,一个是 M1s_BL808_SDK ,另一个是 M1s_BL808_example ,可以用 VSCode 打开这两个文件夹所在的文件夹,就可以使用 【Ctrl + 单击】需要了解的函数就能进行快速追踪跳转了。如果只打开例子文件夹是不会跳转的,或是文本会包含所在文本的文件夹地址。
- 2:修改【lvgl_demo】的示例内容方法如下:在配置头文件 M1s_BL808_SDK/compnets/lvgl/lvgl/lv_conf.h 中,LV_BUILD_EXAMPLES 配置部分进行示例切换,默认是 LV_USE_DEMO_BENCHMARK 例子。将想要的例子后面的0切换为1,其他的切换为0,然后在 M1s_BL808_example/c906_app/lvgl_demo/main.c 的主文件中将之前的 lv_demo_benchmark() 函数屏蔽,更换为开启的demo的函数,比如music例子是 M1s_BL808_SDK/compnets/lvgl/lvgl/demos/lv_demo_music.h中的lv_demo_music()函数,其他几个例子类似,都在M1s_BL808_SDK/compnets/lvgl/lvgl/demos文件夹下对应的文件夹内。
- 3:编译其他几个例子需要开启对应的字体(具体开启那些字体,可以按照报错提示来),字体开关在配置头文件 M1s_BL808_SDK/compnets/lvgl/lvgl/lv_conf.h 中,快速搜索LV_FONT_MONTSERRAT_ 即可跳转到,后面的数字是你需要开启的字体包。
- 4:export BL_SDK_PATH 这一步每次重开都要重新来一次,否则会找不到编译链。有长效修改的方法,可自行搜索尝试。
2:LVGL体验
- LVGL是由来自匈牙利首都布达佩斯的 Gábor Kiss-Vámosi最早于2016年编写并开源的一款可运行于低资源的MCU设备上的开源嵌入式GUI库(轻量级通用型图形库)。
- 其GitHub地址为:【LVGL Git】
- LVGL当时叫 LittlevGL而不是LVGL,后来作者统一修改为 LVGL 甚至连仓库地址都改了。目前最新版本已经发展到了 v9.0.0 版本,在6.0版本时,我还体验过移植和简单使用。当时还没有拖拽式GUI设计软件,所有界面都要手写。如今,LVGL已经有了如SquareLine Studio、GUI-Guider等GUI设计软件,可以直接PC上进行界面设计和仿真运行,得到【所见即所得】的方便效果。而且随着LVGL的发展壮大,其中文教程资源等都有了极大的发展,比如【百问网】就有包含文档和视频教程在内的丰富的教程资源,而且以前【正点原子】就有编写过专门的LVGL教程。
- 现在,首先下载和体验【SquareLine Studio】软件。
SquareLine Studio是LVGL官方提供的GUI设计软件,可以在LVGL官网直接点击到下载页面。目前最新版本是V1.2.1。按照官网的说明,它只有30天的全功能体验时间。
按照教程下载安装后(具体安装步骤可以参考其他网友的教程,很多的),即可新建一个工程进行GUI设计了。
在新建界面,可以看到,SquareLine Studio支持的LVGL版本只有8.3.3和8.3.4两个版本:
【M1s DOCK】开发板的屏幕是 1.69 寸 240x280 电容触摸屏,所以我们需要在新建界面将Resolution设置为 (280,240) ,Color depth 要设置为 (16 bit swap)模式 。设置好名字和要保存的地址后就可以点击 CREATE 新建工程了。其实也可以点击上方的 Example 来查看官方提供的几个比较丰富的demo示例,跑起来很绚丽。不过由于开发板官方提供的LVGL例程所使用的的版本是8.2的,而SquareLine Studio只能选择两个更高的版本,所以下载demo后,程序并不能正常运行。以后有时间再移植成最新的版本再测试吧。
GUI设计界面如图:
(具体含义和操作可以自行搜索教程使用,也可以自己摸索)
我们可以点击一个【Button】到界面。
然后点击左上角的【Export】,选择【Export UI Files】生成代码到指定文件夹。
生成成功后,可以看到指定文件夹下有几个生成的文件:
将这几个文件复制到linux环境中的 M1s_BL808_example/c906_app/lvgl_demo/ 文件夹下(此文件夹下除了原本的 bouffalo.mk 和 main.c,其他的都可以删掉)。
我用的是VMware虚拟机,所以支持win与ubuntu环境的快速复制。如果是别的虚拟机环境,自行处理文件复制问题。
将 ui.h 文件中的 #include "lvgl/lvgl.h" 改为 #include "lvgl.h",不然会报错,据说这个是 SquareLine Studio 的BUG。
将 main.c中的 demo测试相关的代码替换为ui.h中的ui_init()函数。
编译通过后,将开发板配置为U盘下载模式(USB插在OTG口,按住两侧按键再按RST按键),将 M1s_BL808_example/c906_app/build_out/lvgl_demo.bin 文件拖拽到U盘中即可。
可以看到,所见即所得:
LVGL升级体验
完成基础示例后,既可以进行更复杂一些的基础示例了。
首先进行一次页面切换。
点击做下角的【Screen】图标即可增加一个显示界面了。
在界面2上增加一个【Arc】控件。
下面,就应该思考该如何做界面切换了。首先,计划需要实现的功能:点击按钮进入第二页面,右划第二界面回到第一页面。
实现方法如下:
-
选中第一页面的按钮,可以看到右边的属性设置栏,可以对按钮进行各种属性配置。
-
点击最下面的【ADD EVENT】,可以看到,有几个事件类型选项,我们做如下选择:
- 【Trigger】选择 【CLICKED】,即点击事件
- 【Action】选择【CHANGE SCREEN】,即切换屏幕
然后点击【ADD】,增加这个点击事件,然后就可以对此事件进一步配置:
- 【Screen to】选择【Screen2】,即切换到屏幕2(名称可以自行修改,对应到自己的屏幕的名称)
- 其他选项默认(可自行测试功能,或是查看手册查找功能说明)
此时就算配置完成了第一步,实现点击按钮切换屏幕。可以点击【运行】三角号按钮进行模拟测试。
后面可以进行同样的配置,点击第二页的屏幕,选中屏幕,右侧进行配置
* 【Trigger】选择 【GESTURE_RIGHT】,即右划事件
* 【Screen to】选择【Screen1】,即切换到屏幕1
然后点击【ADD】,添加事件,进入到事件配置界面。
* 【Screen to】选择【Screen1】,即切换到屏幕1
此时完成预定目标的配置,实现右划切换回屏幕1,可以点击【运行】三角号按钮进行模拟测试。
点击【Export】->【Export UI Files】生成代码。
复制替换生成的代码到linux对应环境下,然后编译下载,进行实际运行查看,可以看到实现了既定目标,在设备上跑起来了,按钮和滑动也能正常切换。
LVGL操控设备
目前我们已经完成了LVGL的简单绘制,基本事件绑定等体验功能,但具体来说,也只是使用SquareLine Studio本身实现的,不需要编写一行代码就能实现(需要移植好的设备环境),并没有涉及LVGL之外的交互。所以下面尝试一下,将LVGL与硬件交互联系起来,然后就能举一反三,实现完整复杂的逻辑功能了。
-
首先,计划需要实现的功能:将屏幕2上的【Arc】控件于设备背面的LED的亮度绑定,实现拖拽【Arc】的角度就能控制LED的亮度。
-
然后,我们来看一下LED的PWM驱动例子。
打开 M1s_BL808_example/c906_app/pwm_demo 中的main.c,查看PWM的例子。
可以看到,PWM操作背面的LED灯,只需要几个特定的函数。 -
再然后,我们考虑怎么将LVGL的【Arc】拖动事件与PWM调值联系起来。
- 选中屏幕2的【Arc】控件,点击右下角的【ADD EVENT】,增加一个事件。
- 【Trigger】选择 【VALUE_CHANGED】,即值改变事件
- 【Action】选择【CALL_FUNCTION】,即调用函数
- 点击【ADD】,进入配置
- 给调用函数起一个名字,在【Action】->【Function name】文本框中输入起的名字,如[Arc_PWMControl] ,注意,名字要够一定字数
配置完成,开始生成文件。
将生成的文件复制替换到linux环境下,打开文件夹,可以看到 ui_events.c 中有一个函数:
void Arc_PWMControl(lv_event_t * e) { // Your code here }
里面没有任何功能。所以我们需要在本函数中添加回调函数对应的处理,即获取改变后的值,然后设置对应脉宽给PWM,实现调节亮度。
使用如下函数即可获取到【Arc】控件的当前值:
int16_t ArcValue = 0; lv_obj_t* arc = lv_event_get_target(e);//获取目标控件指针 ArcValue = lv_arc_get_value(arc) ;//获取目标值
然后就可以使用这个值,调用 pwm_demo 中的 m1s_xram_pwm_set_duty 函数进行占空比配置。我们可以在main函数中整合一下PWM操作相关的函数,包括初始化和占空比调节,进行调用。在main.c中增加如下两个函数:
``` #include "m1s_c906_xram_pwm.h" #define PWM_PORT (0) #define PWM_PIN (8) #define PWM_FREQ 2000 //PWM频率设置 //PWM驱动LED初始化代码 void LED_PWMInit(int freq,int duty){ m1s_xram_pwm_init(PWM_PORT, PWM_PIN, freq, duty); m1s_xram_pwm_start(PWM_PORT, PWM_PIN); } //设置占空比 void LED_PWMDutySet(int duty){ m1s_xram_pwm_set_duty(PWM_PORT, PWM_PIN, PWM_FREQ, duty); } ```
将LED_PWMInit()函数放置到main函数中,将LED_PWMDutySet()函数使用extern关键字引用到ui_events.c中。
查看pwm_demo中的m1s_xram_pwm_set_duty()函数,可以看到,占空比为0-99,我们的【Arc】范围为0-100,所以需要限制【Arc】的范围。两种方法:
- 1:是在【Arc】属性配置控件中将【Range max】最大值属性设置为99
- 2:在程序中增加判断,大于99的都设置为99
也可以两种都用上,增加保险。
然后在Arc_PWMControl()函数中调用,将ArcValue 值传给m1s_xram_pwm_set_duty()函数即可。
另外,由于LED灯电路上的驱动电路的不同(高电平端控制或是低电平端控制),所以PWM脉宽与LED亮度不一定成正比,可以加上一句 ArcValue = 99-ArcValue; 来换算。
具体程序如下(仅供参考):
main.c:
/* FreeRTOS */ #include <FreeRTOS.h> #include <task.h> /* bl808 c906 std driver */ #include <bl808_glb.h> #include "demos/lv_demos.h" #include "lv_port_disp.h" #include "lv_port_indev.h" #include "lvgl.h" #include "ui.h" #include "m1s_c906_xram_pwm.h" static void lvgl_task(void *param) { while (1) { lv_task_handler(); vTaskDelay(1); } vTaskDelete(NULL); } #define PWM_PORT (0) #define PWM_PIN (8) #define PWM_FREQ 2000 //PWM驱动LED初始化代码 void LED_PWMInit(int freq,int duty){ m1s_xram_pwm_init(PWM_PORT, PWM_PIN, freq, duty); m1s_xram_pwm_start(PWM_PORT, PWM_PIN); } //设置占空比 void LED_PWMDutySet(int duty){ m1s_xram_pwm_set_duty(PWM_PORT, PWM_PIN, PWM_FREQ, duty); } void main() { LED_PWMInit(PWM_FREQ,99);//PWM初始化,默认占空比设为99 lv_init(); lv_port_disp_init(); lv_port_indev_init();//触摸相关 ui_init(); lv_task_handler(); xTaskCreate(lvgl_task, (char *)"lvgl task", 512, NULL, 15, NULL); }
ui_events.c中程序如下:
#include "ui.h" extern void LED_PWMDutySet(int duty); void Arc_PWMControl(lv_event_t * e) { // Your code here int16_t ArcValue = 0; lv_obj_t* arc = lv_event_get_target(e);//获取目标控件指针 ArcValue = lv_arc_get_value(arc) ;//获取目标值 if(ArcValue>99)ArcValue=99; ArcValue = 99-ArcValue;//占空比对应灯的亮度;由于LED驱动方式的不同,亮度与占空比不一定是正比 //这样处理后便将亮度与占空比换算为正比了 LED_PWMDutySet(ArcValue); }
将程序编译,然后将 build_out文件夹下生成的 lvgl_demo.bin 拖拽到模拟U盘中更新程序,可以看到已经实现了既定目标:
- 点击【button】切换到第二页面
- 右划第二页面回到第一页面
- 拖拽【Arc】控件实现控制背面LED的亮度
(LED在背面,此处不再展示)
以上只是一些简单的例程,用于实现LVGL的操作体验和与硬件的交互实现的流程,更多功能便可以很轻松的拓展了。其实还有很多优化空间的,比如【Arc】控件的左右滑动会激发页面滑动效果从而回到第一页面,右下角的参数窗口没去掉,点击指示图标是一个USB图标,SDK的lvgl版本与编辑器的版本不同导致复杂界面卡死等。这些都可以自行深入学习并完善。
关于LVGL深入的学习,还请自行查看相关教程。而且例程是基于freertos嵌入式操作系统的,在main函数中可以看到,lv_task_handler()句柄是在 lvgl_task 任务中循环执行的,所以可以创建其他线程执行lvgl之外的功能,而不是仅仅依靠lvgl控件的回调函数实现。后面有时间会再写一些好玩的东西,比如深入理解一下LVGL的移植,然后实现其他的绘图库的移植,比如之前写过的旋转立方体:
或是基于此算法的旋转时钟:
- 随梦,随心,随愿,恒执念,为梦执战,执战苍天 ----执念执战 (好久没写我这很中二的座右铭了,哈哈)