最新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的時間精度高,又有鏈表管理,是非常安全、可靠、有效的!好了,就分析這么多了。大伙加油!!