谷动谷力

标题: 避免弯路:教你RT-Thread完美移植 [打印本页]

作者: sunsili    时间: 2021-9-1 17:35
标题: 避免弯路:教你RT-Thread完美移植
本帖最后由 sunsili 于 2021-9-1 17:49 编辑

避免弯路:教你RT-Thread完美移植!


之前我发过多篇移植RT-Thread到新唐单片机的帖子,不过都是能满足要求,但是还不够完美,本次帖子,完美解决之前的不完美。

该帖基于最新版的RT-Thread Nano 3.1.5版本。
为了体现帖子的完美性,这次我从头开始说关键点。

创建MDK工程

第一步,基于RTE创建工程,应选择下图的选项


第二步,工程应至少包含以下4个基本库文件


第三步,工程的配置

因为我们可以不使用printf,所以我们可以不包含MicroLIB,甚至我们不用在工程配置里包含STD标准库的头文件进来。
因为BSP的标准库基于AC5编写,所以我们这里最好选择AC5,如果选择AC6,应在警告设置AC5-like ,否则编译会因为打印几百个警告而变慢。

第四步,rtconfig.h配置


第五步,按照#error设定的4个部分配置时钟和硬件初始化,串口初始化,串口输出,串口输入。



RT-Thread移植正式开始

时钟初始化,我们可以找到一个厂家提供的模板,复制SYS_Init();并在其中完善滴答定时器的启动与配置。
打开board.c添加系统初始化代码
  1. void SYS_Init(void)
  2. {
  3.     /*---------------------------------------------------------------------------------------------------------*/
  4.     /* Init System Clock                                                                                       */
  5.     /*---------------------------------------------------------------------------------------------------------*/
  6.     /* Unlock protected registers */
  7.     SYS_UnlockReg();

  8.     /* Set XT1_OUT(PF.2) and XT1_IN(PF.3) to input mode */
  9.     PF->MODE &= ~(GPIO_MODE_MODE2_Msk | GPIO_MODE_MODE3_Msk);

  10.     /* Enable HIRC clock (Internal RC 48 MHz) */
  11.     CLK_EnableXtalRC(CLK_PWRCTL_HIRCEN_Msk);
  12.     /* Wait for HIRC clock ready */
  13.     CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk);

  14.     /* Set core clock as 51MHz from PLL */
  15.     CLK_SetCoreClock(FREQ_51MHZ);        

  16.                 /* System Tick Configuration */
  17.                 CLK_EnableSysTick(CLK_CLKSEL0_STCLKSEL_HCLK,SystemCoreClock / RT_TICK_PER_SECOND);

  18.     /* Enable UART clock */
  19.     CLK_EnableModuleClock(UART0_MODULE);

  20.     /* Select UART clock source from HIRC */
  21.     CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UART0SEL_HIRC, CLK_CLKDIV0_UART0(1));               

  22.     /* Update System Core Clock */
  23.     /* User can use SystemCoreClockUpdate() to calculate SystemCoreClock. */
  24.     SystemCoreClockUpdate();

  25.     /* Set GPB multi-function pins for UART0 RXD and TXD */
  26.     SYS->GPB_MFPH = (SYS->GPB_MFPH & ~(SYS_GPB_MFPH_PB12MFP_Msk | SYS_GPB_MFPH_PB13MFP_Msk))    |       \
  27.                     (SYS_GPB_MFPH_PB12MFP_UART0_RXD | SYS_GPB_MFPH_PB13MFP_UART0_TXD);

  28.     /* Lock protected registers */
  29.     SYS_LockReg();
  30. }

  31. CLK_EnableSysTick这个滴答时钟中断使能与配置函数,可以设置滴答定时器的时钟源,重载值,并使能其中断。
  32. 然后就要完成滴答定时器中断的内容:

  33. /* systick 中断服务例程 */
  34. void SysTick_Handler(void)
  35. {
  36.         rt_os_tick_callback();
  37. }
复制代码

其中内部的回调函数,rtt的board.c已经帮我们完成,只需要添加以上代码段即可,也可修改回调函数的名字为中断入口。
将系统初始化代码填入#error "TODO 1: OS Tick Configuration."后面,并注释掉该行,确保编译时候不再报错提示该位置。

串口初始化,按如下代码进行初始化
  1. static int uart_init(void)
  2. {
  3. //#error "TODO 2: Enable the hardware uart and config baudrate."
  4.     /* Reset UART0 */
  5.     SYS_ResetModule(UART0_RST);
  6.     /* Configure UART0 and set UART0 baud rate */
  7.     UART_Open(UART0, 115200);        

  8.     return 0;
  9. }

  10. 串口输出功能

  11. 之前我用了printf实现这部分,但是有个缺点就是要启用MicroLIB,这样就增加了代码存储空间,其实可以使用STD标准库函数实现如下所示,串口写入函数一共三个参数,第一个选用的串口名,第二个是要发送的字符串地址,这里因为类型不同进行了转换,也可以不转换而修改该预设函数的参数类型。第三个是要发送的字符串长度,我们这里用rt的库函数计算一下传入的长度,这样就做到了一个函数一行就搞定了串口打印的映射。
  12. void rt_hw_console_output(const char *str)
  13. {
  14.         //#error "TODO 3: Output the string 'str' through the uart."  
  15.         UART_Write(UART0,(uint8_t *)str,rt_strlen(str));     
  16. }
复制代码

串口输入功能

串口输入功能的配置在finsh_port.c
我们完善该弱函数的内容即可,注释掉error提示行。我实现的方法如下:这里是参考了重定向文件里的方法
  1. RT_WEAK char rt_hw_console_getchar(void)
  2. {
  3.     /* Note: the initial value of ch must < 0 */
  4.     int ch = -1;
  5. //#error "TODO 4: Read a char from the uart and assign it to 'ch'."
  6.         if((UART0->FIFOSTS & UART_FIFOSTS_RXEMPTY_Msk) == 0)
  7.         {
  8.                         return (UART0->DAT);
  9.         }
  10.         return ch;
  11. }
  12. 最后给大家晒一下关键的board.c完整内容
  13. /*
  14. * Copyright (c) 2006-2019, RT-Thread Development Team
  15. * SPDX-License-Identifier: Apache-2.0
  16. * Change Logs:
  17. * Date           Author       Notes
  18. * 2021-05-24                  the first version
  19. */

  20. #include <rthw.h>
  21. #include <rtthread.h>
  22. #include <NuMicro.h>
  23. #include <stdio.h>

  24. #if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
  25. /*
  26. * Please modify RT_HEAP_SIZE if you enable RT_USING_HEAP
  27. * the RT_HEAP_SIZE max value = (sram size - ZI size), 1024 means 1024 bytes
  28. */

  29. #define RT_HEAP_SIZE (15*1024)
  30. static rt_uint8_t rt_heap[RT_HEAP_SIZE];

  31. RT_WEAK void *rt_heap_begin_get(void)
  32. {
  33.     return rt_heap;
  34. }

  35. RT_WEAK void *rt_heap_end_get(void)
  36. {
  37.     return rt_heap + RT_HEAP_SIZE;
  38. }

  39. #endif

  40. void rt_os_tick_callback(void)
  41. {
  42.     rt_interrupt_enter();
  43.     rt_tick_increase();
  44.     rt_interrupt_leave();}


  45. /**
  46. * This function will initial your board.
  47. */

  48. void SYS_Init(void)
  49. {
  50.     /*---------------------------------------------------------------------------------------------------------*/
  51.     /* Init System Clock                                                                                       */
  52.     /*---------------------------------------------------------------------------------------------------------*/

  53.     /* Unlock protected registers */
  54.     SYS_UnlockReg();

  55.     /* Set XT1_OUT(PF.2) and XT1_IN(PF.3) to input mode */
  56.     PF->MODE &= ~(GPIO_MODE_MODE2_Msk | GPIO_MODE_MODE3_Msk);

  57.     /* Enable HIRC clock (Internal RC 48 MHz) */
  58.     CLK_EnableXtalRC(CLK_PWRCTL_HIRCEN_Msk);

  59.     /* Wait for HIRC clock ready */
  60.     CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk);

  61.     /* Set core clock as 51MHz from PLL */
  62.     CLK_SetCoreClock(FREQ_51MHZ);        

  63.      /* System Tick Configuration */
  64.      CLK_EnableSysTick(CLK_CLKSEL0_STCLKSEL_HCLK,SystemCoreClock / RT_TICK_PER_SECOND);

  65.     /* Enable UART clock */
  66.     CLK_EnableModuleClock(UART0_MODULE);

  67.     /* Select UART clock source from HIRC */
  68.     CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UART0SEL_HIRC, CLK_CLKDIV0_UART0(1));               

  69.     /* Update System Core Clock */
  70.     /* User can use SystemCoreClockUpdate() to calculate SystemCoreClock. */
  71.     SystemCoreClockUpdate();

  72.     /* Set GPB multi-function pins for UART0 RXD and TXD */
  73.     SYS->GPB_MFPH = (SYS->GPB_MFPH & ~(SYS_GPB_MFPH_PB12MFP_Msk | SYS_GPB_MFPH_PB13MFP_Msk))    |       \
  74.                     (SYS_GPB_MFPH_PB12MFP_UART0_RXD | SYS_GPB_MFPH_PB13MFP_UART0_TXD);

  75.     /* Lock protected registers */
  76.     SYS_LockReg();
  77. }

  78. /* systick 中断服务例程 */
  79. void SysTick_Handler(void)
  80. {
  81.         rt_os_tick_callback();
  82. }


  83. void rt_hw_board_init(void)
  84. {
  85. //#error "TODO 1: OS Tick Configuration."
  86.         SYS_Init();      
  87.     /*
  88.      * TODO 1: OS Tick Configuration
  89.      * Enable the hardware timer and call the rt_os_tick_callback function
  90.      * periodically with the frequency RT_TICK_PER_SECOND.
  91.      */
  92.     /* Call components board initial (use INIT_BOARD_EXPORT()) */
  93. #ifdef RT_USING_COMPONENTS_INIT
  94.     rt_components_board_init();
  95. #endif

  96. #if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
  97.     rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
  98. #endif
  99. }

  100. #ifdef RT_USING_CONSOLE
  101. static int uart_init(void)
  102. {
  103. //#error "TODO 2: Enable the hardware uart and config baudrate."
  104.     /* Reset UART0 */
  105.     SYS_ResetModule(UART0_RST);

  106.     /* Configure UART0 and set UART0 baud rate */
  107.     UART_Open(UART0, 115200);
  108.     return 0;
  109. }

  110. INIT_BOARD_EXPORT(uart_init);
  111. void rt_hw_console_output(const char *str)
  112. {
  113.         //#error "TODO 3: Output the string 'str' through the uart."  
  114.         UART_Write(UART0,(uint8_t *)str,rt_strlen(str));  
  115. }
  116. #endif
复制代码

朋友们,按照这个方法,全部使用BSP提供的库函数就完成了,程序也变的更加友好可读,占用资源更少。
另外注意,在启用了滴答定时器中断后,我们再用CLK_SysTickDelay(1000);延时就会卡住了,这时候系统通过中断接管了滴答定时器的控制权。可以使用rt_thread_mdelay(2000);实现延时。

最后我们测试例子,编写main.c。
  1. #include "stdio.h"
  2. #include <NuMicro.h>
  3. #include <rtthread.h>

  4. //配置优先级,栈大小,时间片,设置不对没法运行的。

  5. #define THREAD_PRIORITY         5
  6. #define THREAD_STACK_SIZE       256
  7. #define THREAD_TIMESLICE        10

  8. void led(void *parameter)
  9. {
  10.     printf("\n\nCPU <a href="home.php?mod=space&uid=72445" target="_blank">@</a> %d Hz\n", SystemCoreClock);
  11.     printf("+-------------------------------------------------+\n");
  12.     printf("|    PB14(Output)  Sample Code     |\n");
  13.     printf("+-------------------------------------------------+\n\n");

  14.     rt_kprintf("Hello RTT_NANO\n");

  15.     while(1)
  16.     {
  17.           PB14=0;
  18.           rt_thread_mdelay(2000);
  19.           printf("\nLED1 is ON\n");

  20.           PB14=1;
  21.           rt_thread_mdelay(2000);
  22.           printf("\nLED1 is OFF\n");  
  23.     }
  24. //return 0;
  25. }

  26. /* 导出到 msh 命令列表中 */

  27. MSH_CMD_EXPORT(led, RT-Thread first led sample);

  28. void led2(void *parameter)
  29. {
  30.         rt_kprintf("Hello RTT_NANO\n");
  31.         while(1)
  32.         {
  33.             PB14=0;
  34.             rt_thread_mdelay(3000);
  35.             printf("\nLED2 is ON\n");
  36.             
  37.             PB14=1;
  38.             rt_thread_mdelay(3000);
  39.             printf("\nLED2 is OFF\n");
  40.        }
  41. //return 0;
  42. }

  43. MSH_CMD_EXPORT(led2, RT-Thread second led sample);

  44. int led_sample(void)
  45. {
  46.     static rt_thread_t tid = RT_NULL;
  47.     static rt_thread_t tid2 = RT_NULL;

  48.     /* 创建线程1 */
  49.     tid = rt_thread_create("thread1",
  50.                             led, RT_NULL,
  51.                             THREAD_STACK_SIZE,
  52.                             THREAD_PRIORITY, THREAD_TIMESLICE);   

  53.     if (tid != RT_NULL)     rt_thread_startup(tid);
  54.         /* 创建线程2 */
  55.     tid2 = rt_thread_create("thread2",
  56.                             led2, RT_NULL,
  57.                             THREAD_STACK_SIZE,
  58.                             THREAD_PRIORITY, THREAD_TIMESLICE);   

  59.     if (tid2 != RT_NULL)    rt_thread_startup(tid2);   
  60.                 //该例子共用PB14端口,所以一并在创建线程时候初始化为输出模式
  61.                 GPIO_SetMode(PB, BIT14, GPIO_MODE_OUTPUT);      
  62.     return 0;
  63. }

  64. MSH_CMD_EXPORT(led_sample, RT-Thread sample);
  65. /*---------------------------------------------------------------------------------------------------------*/
  66. /*  Main Function                                                                                          */
  67. /*---------------------------------------------------------------------------------------------------------*/

  68. int32_t main(void)
  69. {
  70.     led_sample();
  71.     return 0;
  72. }
复制代码

你学会了吗,这样你就不用等RT-Thread Studio支持新唐的芯片了,自己也可以完美移植RTT到新唐单片机了。





欢迎光临 谷动谷力 (http://bbs.sunsili.com/) Powered by Discuz! X3.2