最新LVGL的releases版本下载: https://github.com/lvgl/lvgl/releases
值得注意的是,它的lv_examples和lv_drivers都是要另外下载的,没有全部包含在LVGL主源码里面
一、首先我选用芯片是华芯微特的SWM32S,该芯片大该信息如下:
SWM320 是一款基于 ARM○R Cortex TM -M4 的 32 位微控制器。具有高性能、低功耗、代码密度大
等突出特点,适用于工业控制、人机界面、白色家电、电机驱动等诸多应用领域。
SWM320 内嵌 ARM○R Cortex TM -M4 控制器,片上包含精度为 1%以内的 20MHz/40MHz 时钟,可
通过 PLL 倍频到 120MHz 时钟,提供多种内置 FLASH/SRAM 大小可供选择,支持 ISP(在系统编
程)操作及 IAP(在应用编程)。
外设串行总线包括 1 个 CAN 接口,多个 UART 接口、SPI 通信接口(支持主/从选择)及 I2C 接口
(支持主/从选择)。此外还包括 1 个 32 位看门狗定时器,6 组 32 位通用定时器,1 组 32 位专用
脉冲宽度测量定时器,12 通道 16 位的 PWM 发生器,2 个 8 通道 12 位、1MSPS 的逐次逼近型
ADC 模块,1 个 SDIO 接口模块,TFT-LCD 液晶驱动模块以及 RTC 实时时钟、SRAMC、SDRAMC、
NORFLC 接口控制模块,同时提供欠压检测及低电压复位功能。
我们可以看到,上面的SWM32S芯片,有LCD RGB565外设,8MB内置SDRAM动态存储,LQFP64 小体积.带RBG565彩屏驱动外设,又内置SDRAM绝对是市场上少有的芯片。
我已经事先参考官方LVGL612例程,移植好了LVGL v7.8.1放在单片机\STM8 32\国产-华芯微特\LVGL_graphics里面,本人用的是IAR 8.40,该例程实现了所有细节的移植,
利用LVGL的线控功能,在LCD屏上画一个方框,并显示CPU使用率,并具已经上传到华芯微特的QQ技术群里面,如下图所示:
如果要下载的朋友,请移步到官方技术QQ群492524359,进行下载或交流,我在群里面的名字还叫“木子--剑”。
二、裸机的环境与硬件都测试好,接下来我们怎么把LVGL移植OS里面呢?
直接上图吧,因为LVGL是C实现的,简单明了,先在OS增加LVGL文件如下:
说不多说,直接给出测试源码
#include "contiki.h" #include "sys/etimer.h" #include "sys/stimer.h" #include "sys/timer.h" #include "sys/rtimer.h" #include "lvgl.h" // 图形库相关 #include "dev_rgblcd.h" #include "dev_systick.h" PROCESS(timer_process, "ETimer x Timer x STimer Process"); static int counter_etimer; static int counter_timer; static int counter_stimer; static struct timer timer_timer; static struct stimer timer_stimer; static struct ctimer timer_ctimer; static struct rtimer timer_rtimer; /*---------------------------------------------------------------------------*/ static u16_t CTMER=0; void ctimer_callback(void *ptr) { /* rearm the ctimer */ ctimer_reset(&timer_ctimer); UARTprintf("CTimer callback called %d\n",CTMER++); } /*---------------------------------------------------------------------------*/ void //当LCD tick吧,进行测试 rtimer_callback(struct rtimer *timer, void *ptr) { /* Normally avoid printing from rtimer - rather do a process poll */ /* Re-arm rtimer */ swm_inctick(); // LCD的tick时钟,+++++ SysTick_Handler_cb(); // 必须定期调用此函数, 实是lv_tick_inc(x); rtimer_set(&timer_rtimer, RTIMER_SECOND*15, 0, // 重置时间 100us*15 rtimer_callback, NULL); } /*---------------------------------------------------------------------------*/ /* * This Process will count timer expires on one e-timer (that drives the * events to the process), one timer, and one stimer. * */ PROCESS_THREAD(timer_process, ev, data) { static struct etimer timer_etimer; PROCESS_BEGIN(); ctimer_set(&timer_ctimer, CLOCK_SECOND/1, ctimer_callback, NULL); rtimer_set(&timer_rtimer, RTIMER_NOW()+RTIMER_SECOND*10, 0, rtimer_callback, NULL); while(1) { /* set all the timers */ timer_set(&timer_timer, 1 * CLOCK_SECOND); stimer_set(&timer_stimer, 1); etimer_set(&timer_etimer, 1 * CLOCK_SECOND); PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER); counter_etimer++; if(timer_expired(&timer_timer)) { counter_timer++; } if(stimer_expired(&timer_stimer)) { counter_stimer++; } UARTprintf("Timer process: %s\n\n\n", counter_timer == counter_etimer && counter_timer == counter_stimer ? "SUCCESS" : "FAIL"); } PROCESS_END(); } /* 线控件的测试 */ void lvgl_line_set_point_test(void) { /*Create style*/ static lv_style_t style_line; // 新建样式 lv_style_init(&style_line); // 初始化样式 lv_style_set_line_width(&style_line, LV_STATE_DEFAULT, 2); // 线为,默认,宽2 /* 为line创建一个数组,2点1线,3点2线,5点4线依次类推 */ static lv_point_t line_points[] = { {5, 5}, {5, 267},{475, 267}, {475, 5}, {5, 5} }; // X Y座标 lv_obj_t* line1 = lv_line_create(lv_scr_act(), NULL); // 创建行对象 lv_obj_add_style(line1, LV_LINE_PART_MAIN, &style_line); // 将style_line样式加到list中 */ lv_line_set_points(line1, line_points,5); // 这个线有4个接点 lv_obj_align(line1, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0); // 将线的4个接点连起来 } /*---------------------------------------------------------------------------*/ PROCESS(lcd_1, "lcd 1"); PROCESS_THREAD(lcd_1, ev, data)//进程1的功能体 { static struct etimer timeout1; //声明ETMIER事件 PROCESS_BEGIN();//开始 etimer_set(&timeout1,CLOCK_SECOND/1000);//从当前时间起的,为XXX的时间事件 while(1) { // 相当于裸机while(1){lv_task_handler();} PROCESS_WAIT_EVENT();//进行等待 if(ev == PROCESS_EVENT_TIMER) {// 如果时间到了就打印信息,如果事件产生?? lv_task_handler(); // 图形任务,2ms处理一次,应该可以了 }//if etimer_reset(&timeout1);//复位时间,周而复始 }//while(1) PROCESS_END();//结束,与PROCESS_BEGIN()配对! }//PROCESS_THREAD /*---------------------------------------------------------------------------*/ PROCESS(lcd_2, "lcd 2"); PROCESS_THREAD(lcd_2, ev, data)//进程1的功能体 { static struct etimer timeout1; //声明ETMIER事件 PROCESS_BEGIN();//开始 etimer_set(&timeout1,CLOCK_SECOND/5);//从当前时间起的,为XXX的时间事件 lvgl_line_set_point_test(); // 画了一个框 static s8_t cpu=0; static lv_obj_t * perf_label = NULL; // 创建标签perf_label perf_label = lv_label_create(lv_layer_sys(), NULL); //lv_label_set_text_fmt(perf_label, "%d FPS\n%d%% CPU", fps, cpu); // 打印数据 lv_obj_align(perf_label, NULL, LV_ALIGN_IN_TOP_LEFT, 14, 14); // 对齐 x值,y值。 //lv_ex_msgbox_1(); // 以下都是些测试 /*Create a Preloader object*/ lv_obj_t * preload = lv_spinner_create(lv_scr_act(), NULL); lv_obj_set_size(preload, 100, 100); lv_spinner_set_spin_time(preload, 1000); lv_spinner_set_arc_length(preload, 5); lv_obj_align(preload, NULL, LV_ALIGN_IN_TOP_RIGHT, -7, 7); /*Create a Preloader object*/ lv_obj_t * preload1 = lv_spinner_create(lv_scr_act(), NULL); lv_obj_set_size(preload1, 100, 100); lv_spinner_set_spin_time(preload1, 1500); lv_spinner_set_arc_length(preload1, 20); lv_obj_align(preload1, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 7, -7); /*Create a Preloader object*/ lv_obj_t * preload2 = lv_spinner_create(lv_scr_act(), NULL); lv_obj_set_size(preload2, 100, 100); lv_spinner_set_spin_time(preload2, 2000); lv_spinner_set_arc_length(preload2, 20); lv_obj_align(preload2, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, -7); lv_obj_t * preload3 = lv_spinner_create(lv_scr_act(), NULL); lv_obj_set_size(preload3, 100, 100); lv_spinner_set_spin_time(preload3, 2500); lv_spinner_set_arc_length(preload3, 20); lv_obj_align(preload3, NULL, LV_ALIGN_IN_TOP_MID, 0, 7); lv_spinner_set_dir(preload3, LV_SPINNER_DIR_BACKWARD); lv_obj_t * preload4 = lv_spinner_create(lv_scr_act(), NULL); lv_obj_set_size(preload4, 100, 100); lv_spinner_set_spin_time(preload4, 1800); lv_spinner_set_arc_length(preload4, 100); lv_obj_align(preload4, NULL, LV_ALIGN_IN_LEFT_MID, 7, -24); lv_spinner_set_dir(preload4, LV_SPINNER_DIR_BACKWARD); while(1) { PROCESS_WAIT_EVENT();//进行等待 if(ev == PROCESS_EVENT_TIMER) { cpu++; lv_label_set_text_fmt(perf_label, "hello world!\nCounter: %d",cpu); // 打印CPU数据 }//if etimer_reset(&timeout1);//复位时间,周而复始 }//while(1) PROCESS_END();//结束,与PROCESS_BEGIN()配对! }//PROCESS_THREAD /*------------------------所有自启动的测试PROCESSES---------------------------*/ AUTOSTART_PROCESSES(//&demo_timer1, &demo_timer2, &demo_timer3, &demo_timer4,//all timer测试 &timer_process, // 系统所有timer测试 &lcd_1, &lcd_2 );
正常情况下,烧写到32S芯片里面后,在PC串口处会收到如下信息:
在32S驱动下,显示屏上会看到如下图片:
计数器在计数,圈圈在旋转,CPU使用率在显示说明正常了, 如果有朋友要完整的例程,
可以到官方技术群492524359找"木子--剑"索要,这个OS+LVGL的没有上传到Q群。
说说我的自己做法,我是用CONTIKI NG的实时定时器RTIMER,去驱动LVGL的心跳工作的,
当我不用LVGL功能时,我直接不开启RTIMER任务,lv_task_handler()任务也关掉就行了,哈哈
真方便呀。通过简单的测试,发现所有功能都正常。。。加油!
时间:2021-6-13
在CONTIKI NG中,关于进程的启动和LVGL的按排,我个人觉比较好的方法就是:
开启一个自动启动进程,把所有LVGL的功能放在里面进行开启,当全部正常开启后
将该进程直退出并删除。为什么呢?因为在OS中,通常OS权力高于LVGL,LVGL只是
OS的XX任务,该任务优先级高于其他链表任务而已,我们应该是先主后次的按排比较安全。
再说LVGL的图形任务和触摸描述任务,很多人加入OS后还是这样的~~~~~
while (1) { lv_task_handler(); gt911_scan(); HAL_Delay(5); }
或者就是这样~~~~~
while (1) { lv_task_handler(); }
void SysTick_Handler(void) { HAL_IncTick(); lv_tick_inc(1); gt911_scan(); }
这样通常能正常工作,但是感觉不是很安全。我们用OS有个目标就是,要把一大块的程序拆分,进行碎片化管理。
OS内核进行自动任务调度,而不是让一个任务长时间占用CPU,或者有发生资源竟争风险。
LVGL,有几个重要的定时调度。1是IncTick心跳计数器,2是lv_task_handler()图形任务,3是gt911_scan()触摸
任务,4是lv_tick_inc(1)让LCD知道是时间是多少了~~
所以,LGVL加入OS后,不应该将它们一锅端,全都放在一起,是很不安全的;应该拆分,多个定时任务进行处理,
为什么呢?因为OS的时间精度高,又有链表管理,是非常安全、可靠、有效的!好了,就分析这么多了。大伙加油!!