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