本帖最后由 酒酿大丸子 于 2024-3-10 16:13 编辑

【简介】
本文实现了移植FreeRTOS与lvgl,并完成一个综合界面的开发。


【演示视频】



【FreeRTOS简介】
FreeRTOS是一个迷你的实时操作系统内核。作为一个轻量级的操作系统,功能包括:任务管理、时间管理、信号量、消息队列、内存管理、记录功能、软件定时器、协程等,可基本满足较小系统的需要。由于RTOS需占用一定的系统资源(尤其是RAM资源),只有μC/OS-II、embOS、salvo、FreeRTOS等少数实时操作系统能在小RAM单片机上运行。相对μC/OS-II、embOS等商业操作系统,FreeRTOS操作系统是完全免费的操作系统,具有源码公开、可移植、可裁减、调度策略灵活的特点,可以方便地移植到各种单片机上运行。


【LVGL简介】
LVGL(轻量级多功能图形库)是一个免费的开源图形库,提供创建嵌入式 GUI 所需的一切,具有易于使用的图形元素、精美的视觉效果和低内存占用。
主要特点:
  • 强大的构建块,例如按钮、图表、列表、滑块、图像等。
  • 具有动画、抗锯齿、不透明度、平滑滚动的高级图形
  • 触摸板、鼠标、键盘、编码器等各种输入设备。
  • 支持 UTF-8 编码的多语言
  • 多显示器支持,即同时使用多个 TFT、单色显示器
  • 完全可定制的图形元素,具有类似CSS的样式
  • 独立于硬件:与任何微控制器或显示器一起使用
  • 可扩展:能够在很少的内存下运行(64 kB 闪存,16 kB RAM)
  • 支持操作系统、外部存储器和 GPU,但不是必需的
  • 单帧缓冲操作,即使具有高级图形效果
  • 用 C 语言编写以实现最大兼容性(C++ 兼容)
  • 模拟器,用于在没有嵌入式硬件的 PC 上开始嵌入式 GUI 设计
  • 绑定到 MicroPython


【显示功能实现】
GD为我们提供了很多例子,我们可以直接在提供的例子上进行修改就能得到期望的结果。
显示部分没啥好说的,主要注意一下显存地址就可以了。

初始化参考,这里我只使用了TLI的层0。
void app_lcd_init(void)
  • {
  •     /* LCD configure and TLI enable */
  •     lcd_config();
  •     tli_layer_enable(LAYER0);
  • //    tli_layer_enable(LAYER1);
  •     tli_reload_config(TLI_REQUEST_RELOAD_EN);
  •     tli_enable();
  • }
  • 复制代码


    【FreeRTOS移植】
    这个移植算是比较简单的了,基本复制过来加上文件就能用,有几个主要的点:
    为了兼容官方原有的延时函数,需要对SysTick_Handler中断进行一下简单的配置。
    /*!
  •     \brief      this function handles SysTick exception
  •     \param[in]  none
  •     \param[out] none
  •     \retval     none
  • */
  • void SysTick_Handler(void)
  • {
  •     delay_decrement();
  •     if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
  •         xPortSysTickHandler();
  • }
  • 复制代码

    我使用的是AC6编译器,使用armclang编译器,需要调整一下port.c文件,使用下图框出来的位置
    f78ab42bbcf71d4d42010db6fea697c.jpg


    【LVGL移植】
    需要注意几个接口的实现。
    触摸接口
    /* Will be called by the library to read the touchpad */
  • static void DEMO_ReadTouch(lv_indev_drv_t *drv, lv_indev_data_t *data)
  • {
  •     int16_t touch_x = 0;
  •     int16_t touch_y = 0;

  •     data->state = LV_INDEV_STATE_REL;

  •     if(Read_ADS2(&touch_x,&touch_y) == 1)
  •     {
  •         data->state = LV_INDEV_STATE_PR;
  •     }

  •     /*Set the last pressed coordinates*/
  •     data->point.y = touch_y;
  •     data->point.x = touch_x;
  • }
  • 复制代码

    显示接口
    static void DEMO_FlushDisplay(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
  • {
  •     lv_coord_t x1 = area->x1;
  •     lv_coord_t y1 = area->y1;
  •     lv_coord_t x2 = area->x2;
  •     lv_coord_t y2 = area->y2;

  •     int32_t length = (x2 - x1 + 1) * (y2 - y1 + 1) * LCD_FB_BYTE_PER_PIXEL;

  •     int32_t x,y,i = 0;
  •     for(y = y1; y <= y2; y++)
  •     {
  •         for(x = x1; x <= x2; x++)
  •         {

  •             *(uint16_t *)(SDRAM_LCD_BUFF_ADDR + 2 * ((480 * y) + x)) = *(uint16_t *)(color_p + i);
  •             i++;
  •         }
  •     }
  •     /* IMPORTANT!!!
  •      * Inform the graphics library that you are ready with the flushing*/
  •     lv_disp_flush_ready(disp_drv);
  • }
  • 复制代码

    定时调用lv_tick_inc函数
    /*!
  • * @brief FreeRTOS tick hook.
  • */
  • void vApplicationTickHook(void)
  • {
  •     if (s_lvgl_initialized)
  •     {
  •         lv_tick_inc(1);
  •     }
  • }
  • 复制代码

    初始化显示任务
        if (pdPASS != xTaskCreate(AppTask, "lvgl", configMINIMAL_STACK_SIZE + 800, NULL, tskIDLE_PRIORITY + 2, NULL))
  •     {
  •         printf("Failed to create lvgl task");
  •         while (1)
  •             ;
  •     }

  •     vTaskStartScheduler();
  • 复制代码


    【界面开发】
    设计车机控制界面的时候,我使用了设计器,还是很方便的。
    16e9d740176df25e295969272521643.jpg
    878dd5ea46b9f10998faa1c1046b99c.jpg
    fed3ed1a8f87c2bd752083a9a15c238.jpg
    c0550ccefd4e886cbed03c114b8ab21.jpg


    【演示】
    运行Widgets Demo
    cadda348a45019be9b89df45bda3cf0.jpg
    车机电池界面
    f4a5f4e725edfa680b66b2db3d667ff.jpg
    车机拨号界面
    1c50f04496fd3c7637f216cab307e4c.jpg
    车机呼叫界面
    2c28042f1d83fb1c07226bc730f6aac.jpg
    车机A/C控制界面
    b435af091835809402c678aa7a57e32.jpg