本帖最后由 aywork 于 2020-2-3 12:57 编辑





一,背景介绍:










    自上次评测GD开发板之后就一直忙公司的事情,幸好主办方延期评测时间,这才能在假期潜心研究一下。众所周知今年的新型冠状病毒肆虐,正好拿这款开发板打发一下时间,本来想移植最近流行的LiitevGl图形系统,但是看评测团队里面已经有人移植过了,只好选个其他的东西玩玩,思来想去就移植一下咱中国自产自销的嵌入式实时操作系统RT-Thread,然后把两者结合起来,看看这个板子能否跑得动。











二、准备开发环境













  • 首先用芯来的IDE建立一个工程,并且让成功跑起来。






  • 官网下载LittevGL






  • 先移植LittevGL,其实官网已经给出了很详细的移植步骤,按部就班的就可以一直完成了,但是可能有些初学者还是又很多地方比较迷惑,下面我就重点说一下需要注意的几个步骤。

  • 官网下载RT-Thread


















  • 备用,等LittevGL移植成功之后在移植。








三、移植LittvGL(v6.1.1)













  • 下载完成之后解压备用,选取<lvgl>文件夹里面的所有文件复制到工程目录下,在解压后的文件中找到如下文件复制到工程目录。





    lvgl目录.jpg

  • 配置文件包含路径,即头文件和lvgl文件所在位置,基础配置步骤请参考我第一篇评测又详细说明。






  • 剩余的移植步骤可以参考。

    官方移植文档

    移植评测文章











  • 重点注意步骤,在<lv_port_indev.c>中获取触摸的函数如下。
    1. static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
    2. {
    3. /*Your code comes here*/
    4. *x = 240 - touch_coordinate_x_get(touch_ad_x);
    5. printf("x = %d \r\n", *x);
    6. *y = 320 - touch_coordinate_y_get(touch_ad_y);
    7. printf("y = %d \r\n", *y);
    8. }







  • 在<lv_port_disp.c>中的显示函数如下。
    1. static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
    2. {
    3. /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
    4. int32_t x;
    5. int32_t y;
    6. for(y = area->y1; y <= area->y2; y++) {
    7. for(x = area->x1; x <= area->x2; x++) {
    8. /* Put a pixel to the display. For example: */
    9. /* put_px(x, y, *color_p)*/
    10. lcd_point_set(x,y,(color_p->full));
    11. color_p++;
    12. }
    13. }
    14. /* IMPORTANT!!!
    15. * Inform the graphics library that you are ready with the flushing*/
    16. lv_disp_flush_ready(disp_drv);
    17. }











  • 在工程<main>函数中,添加如下初始化函数,然后编译运行整个工程就能跑起来了。
    1. int lcdshow_init(){
    2. exmc_lcd_init(); //初始化EXMC
    3. touch_panel_gpio_configure(); //初始化4线触摸
    4. rt_thread_mdelay(50);
    5. lcd_init();
    6. lv_init();
    7. lv_port_disp_init();
    8. lv_port_indev_init();
    9. return 0;
    10. }


























四、移植RT-Thread(v3.1.3)













  • 下载解压之后删除与RISCV无关的文件,将剩余的文件复制到工程目录,目录结构如下。                                       




    rttos目录.jpg






  • 修改<start.S>文件。
    1. /* argc = argv = 0 */
    2. li a0, 0
    3. li a1, 0
    4. call
    5. entry
    6. tail exit







  • 更具实际情况配置<rtconfig.h>文件,一定要配置合适堆栈大小,太小的话能编译通过但是程序无法运行。
    1. #ifndef RT_CONFIG_H__
    2. #define RT_CONFIG_H__
    3. /* Automatically generated file; DO NOT EDIT. */
    4. /* RootMenu */
    5. /* RT-Thread Kernel */
    6. /* Base Configuration */
    7. #define RT_NAME_MAX 8
    8. #define RT_ALIGN_SIZE 4
    9. #define RT_THREAD_PRIORITY_MAX 8
    10. #define RT_TICK_PER_SECOND 1000
    11. #define RT_USING_TIMER_SOFT
    12. #define RT_TIMER_THREAD_PRIO 4
    13. #define RT_TIMER_THREAD_STACK_SIZE 512
    14. #define RT_USING_COMPONENTS_INIT
    15. #define RT_USING_USER_MAIN
    16. #define RT_MAIN_THREAD_STACK_SIZE 1024
    17. /* Debug Configuration */
    18. #define RT_DEBUG
    19. /* InterThread Communication */
    20. #define RT_USING_SEMAPHORE
    21. /* Memory Management */
    22. #define RT_USING_MEMPOOL
    23. #define RT_USING_HEAP
    24. #define RT_USING_SMALL_MEM
    25. #define RT_USING_TINY_SIZE
    26. /* FinSH Shell */
    27. #define RT_USING_CONSOLE
    28. #define RT_CONSOLEBUF_SIZE 128
    29. #endif







  • 注意事项,应为gcc编译有优化,所以RTTOS中的自动初始化函数编译完之后会被丢弃,所以一定要乖乖自己手动添加初始化函数,这个大坑让我迷茫了好久。






  • 剩余的移植步骤可以参考。

    官方移植文档


























  • 做完以上的功课整个RTTOS就算移植成功了,没错就是这么简单。

























五、两者结合













  • 两者结合也比较简单,先起两个线程,注意一定要设置合适的堆栈大小,不然程序能编译,但是不能运行。
    1. /*
    2. * lcdshow_thread.c
    3. *
    4. * Created on: 2020年1月31日
    5. * Author: master
    6. */
    7. #include "lcdshow_thread.h"
    8. #include "rtthread.h"
    9. #include "gd32vf103v_lcd_eval.h"
    10. #include "touch_panel.h"
    11. #include "lv_conf.h"
    12. #include "lv_port_disp.h"
    13. #include "lv_port_indev.h"
    14. static rt_thread_t lv_tick_tid = RT_NULL;
    15. static void lv_tick_thread_entry(){
    16. while(1){
    17. rt_thread_mdelay(5);
    18. lv_tick_inc(5);
    19. }
    20. }
    21. ALIGN(RT_ALIGN_SIZE)
    22. static rt_uint8_t lv_task_thread_stack[1024];
    23. static struct rt_thread lv_task_thread;
    24. static void lv_task_thread_entry(void *param){
    25. while(1){
    26. lv_task_handler();
    27. rt_thread_mdelay(5);
    28. }
    29. }
    30. int lcdshow_thread_run(void){
    31. /* dynamic thread*/
    32. lv_tick_tid = rt_thread_create("lv_tick",
    33. lv_tick_thread_entry,
    34. RT_NULL,
    35. THREAD_STACK_SIZE,
    36. THREAD_PRIORITY,
    37. THREAD_TIMESLICE);
    38. if(lv_tick_tid != RT_NULL){
    39. rt_thread_startup(lv_tick_tid);
    40. }
    41. /* static thread*/
    42. rt_thread_init(&lv_task_thread,
    43. "lv_task",
    44. lv_task_thread_entry,
    45. RT_NULL,
    46. lv_task_thread_stack,
    47. sizeof(lv_task_thread_stack),
    48. THREAD_PRIORITY-1,
    49. THREAD_TIMESLICE);
    50. rt_thread_startup(&lv_task_thread);
    51. return 0;
    52. }








  • 在初始化函数中手动初始化LCD相关函数。
    1. /**
    2. * RT-Thread Components Initialization
    3. */
    4. void rt_components_init(void)
    5. {
    6. #if RT_DEBUG_INIT
    7. int result;
    8. const struct rt_init_desc *desc;
    9. rt_kprintf("do components initialization.\n");
    10. for (desc = &__rt_init_desc_rti_board_end; desc < &__rt_init_desc_rti_end; desc ++)
    11. {
    12. rt_kprintf("initialize %s", desc->fn_name);
    13. result = desc->fn();
    14. rt_kprintf(":%d done\n", result);
    15. }
    16. #else
    17. const init_fn_t *fn_ptr;
    18. for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
    19. {
    20. (*fn_ptr)();
    21. }
    22. lcdshow_init();
    23. #endif
    24. }








  • 在main线程中启动LittevGL线程。
    1. extern int lcdshow_thread_run(void);
    2. int main(void)
    3. {
    4. int i = 0;
    5. gd_eval_led_init(LED1);
    6. lcdshow_thread_run();
    7. while(1){
    8. gd_eval_led_on(LED1);
    9. rt_thread_mdelay(200);
    10. gd_eval_led_off(LED1);
    11. rt_thread_mdelay(200);
    12. }
    13. return 0;
    14. }




  • 完成上述工作,两者就结合在一起了,一个运行在操作系统上的图形界面就完成了。



  • 不过两者结合程序占用有点大,把优化开到-O2。Flashz占用126K,RAM占用31K。这样的占用率后续的应用程序也写不了多少。






  • 重点提示,两者结合之后一定要给littevGL函数加互斥锁,因为它不是线程安全的。

六、总结



















  • 移植这两个东西走了不少的弯路,跳了不少的坑,大多数都是因为gcc编译优化之后的坑。不优化的话程序体积太大。






  • 官方给的调试器太慢了,还是Jlink给力。






  • 芯来的IED不够友好,没有任何的代码提示,严重影响工作效率,感觉不太好用,可能是我用MDK的时间长了的错觉。






  • 移植这两个大家伙下来占用的Flash巨大,这款芯片感觉有点吃不消,好在主频够高,外设够全,性价比够高,还是一款很值得推荐的芯片。






  • 还有好多外设没有用到,后续有时间再带大家体验。                                                                                          




    20200201194034.jpg