contiki ng與LVGL圖形庫之移植


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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM