本帖最后由 Prtemly 于 2025-2-13 22:18 编辑

本文讲述如何在FR3068X-C上移植RT-Thread nano操作系统。

1、准备工作
首先,准备最基本的能跑的FR3068x-C工程,本例以SDK中的GPIO_Demo项目为基础。
Snipaste_2025-02-13_20-52-46.png
项目的文件结构如上图。

2、获取RT-Thread操纵系统源码
RT-Thread Nano是Master的精简版,去掉了一些组件和各种开发板的BSP,保留了OS的核心功能,但足够我们使用了。
下载Nano的源码:https://github.com/RT-Thread/rtthread-nano/archive/refs/heads/master.zip
源码的目录结构如下:
Snipaste_2025-02-13_21-00-36.png
我们移植的时候只需要上图红圈中的文件,将其拷贝出来,放入我们的工程文件夹中,如下图,Rtthread文件夹中即为我们刚刚拷贝出来的nano源代码文件,为方便阅览,将工程文件夹稍微改造一下,如下图,sdk为FR3068x-C的sdk中的components文件夹改名而来,删除example文件夹,其他App文件夹放置用户app代码,Core文件夹放置防止rtthread的启动文件及配置文件,Project文件夹防止MDK项目文件。
Snipaste_2025-02-13_21-02-26.png

3、配置MDK项目
按如下图配置项目文件:
Snipaste_2025-02-13_21-15-36.png
按上图将Nano源码的Src文件夹中所有文件放入项目中Rtt/Src文件夹中,由于FR3068x-C为Cortex-M33核心,将Nano源码中libcpu文件选择arm下的cortex-m33放入项目中Rtt/Port文件夹中,其他文件夹参照上图配置,主要为FR3068x-C的相关源码。

项目配置和GPIO_Demo基本一致,需要注意的是Include Paths需要根据项目实际情况修改,具体如下:
Snipaste_2025-02-13_21-26-00.png


4、修改代码
需要修改的地方主要有3点:
1、修改gpio_demo.c文件,需要修改为rhtthread的task函数形式,修改无难度,代码如下:
<pre>#include "rtthread.h"
  • #include "gpio_demo.h"

  • void gpio_demo(void *paraments)
  • {
  •     GPIO_InitTypeDef GPIO_Handle;

  •     /* init GPIO CLOCK */  
  •     __SYSTEM_GPIOB_CLK_ENABLE();
  •     __SYSTEM_GPIOD_CLK_ENABLE();
  •     __SYSTEM_GPIO_CLK_SELECT_COREH();
  •    
  •     printf("gpio clock:%d\r\n", system_get_peripheral_clock( PER_CLK_GPIOx));

  •     GPIO_Handle.Pin  = GPIO_PIN_14|GPIO_PIN_15;
  •     GPIO_Handle.Mode = GPIO_MODE_OUTPUT_PP;
  •     GPIO_Handle.Pull = GPIO_PULLUP;
  •     gpio_init(GPIOD, &GPIO_Handle);

  •     while(1)
  •     {
  •         rt_kprintf("led on ...\r\n");
  •         gpio_write_pin(GPIOD, GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_SET);
  •         rt_thread_mdelay(RT_TICK_PER_SECOND);
  •         
  •         rt_kprintf("led off ...\r\n");
  •         gpio_write_pin(GPIOD, GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_CLEAR);
  •         rt_thread_mdelay(RT_TICK_PER_SECOND);
  •     }
  • }</pre>
  • 复制代码

    2、修改main文件,将源main文件中时钟和串口初始化部分移除至新的init.c文件,即将硬件初始化部分独立出来。
    ini.c文件如下:
    <pre>#include "fr30xx.h"

  • int fputc(int ch, FILE *stream)
  • {
  •     while(__UART_IS_TxFIFO_FULL(UART3));
  •     __UART_WRITE_FIFO(UART3, ch);
  •    
  •     return ch;
  • }

  • void console_uart_init(void)
  • {
  •     GPIO_InitTypeDef    GPIO_Handle;
  •     UART_HandleTypeDef  Uart3_handle;

  •     /* Uart3 IO init */
  •     GPIO_Handle.Pin       = GPIO_PIN_4|GPIO_PIN_5;
  •     GPIO_Handle.Mode      = GPIO_MODE_AF_PP;
  •     GPIO_Handle.Pull      = GPIO_PULLUP;
  •     GPIO_Handle.Alternate = GPIO_FUNCTION_1;
  •     gpio_init(GPIOB, &GPIO_Handle);

  •     __SYSTEM_UART_CLK_SELECT_COREH();   
  •     Uart3_handle.UARTx = UART3;
  •     Uart3_handle.Init.BaudRate   = 115200;
  •     Uart3_handle.Init.DataLength = UART_DATA_LENGTH_8BIT;
  •     Uart3_handle.Init.StopBits   = UART_STOPBITS_1;
  •     Uart3_handle.Init.Parity     = UART_PARITY_NONE;
  •     Uart3_handle.Init.FIFO_Mode  = UART_FIFO_ENABLE;
  •     uart_init(&Uart3_handle);

  •     printf("SystemCoreClock:%d\r\n", system_get_CoreClock());
  •     printf("SystemDSPClock:%d\r\n", system_get_DSPClock());
  •     printf("System_CORE_HSCLK:%d\r\n", system_get_CORE_HSCLK());
  •     printf("System_SPLLCLK:%d\r\n", system_get_SPLLCLK());
  •     printf("System_AUPLLCLK:%d\r\n", system_get_AUPLLCLK());
  • }

  • void system_clock_config(void)
  • {
  •     System_ClkConfig_t ClkConfig;
  •     pmu_init();

  •     /* CORE HSCLK Config */
  •     ClkConfig.CORE_HSCLK_CFG.CORE_HSCLK_Source = CORE_HSCLK_SEL_HES;
  •     /* PLL clock = HSE_VALUE*N + (HSE_VALUE/65535)*M */
  •     /* SPLL CLK Config */
  •     ClkConfig.SPLL_CFG.PowerEn = PLL_POWER_ENABLE;
  •     ClkConfig.SPLL_CFG.PLL_N = 8;
  •     ClkConfig.SPLL_CFG.PLL_M = 0;
  •     /* PLL clock = HSE_VALUE*N + (HSE_VALUE/65535)*M */
  •     /* AUPLL CLK Config */
  •     ClkConfig.AUPLL_CFG.PowerEn = PLL_POWER_DISABLE;
  •     ClkConfig.AUPLL_CFG.PLL_N = 8;
  •     ClkConfig.AUPLL_CFG.PLL_K = 0;
  •     ClkConfig.AUPLL_CFG.PLL_D = 0;   

  •     System_CORE_HSCLK_config(&ClkConfig.CORE_HSCLK_CFG);
  •     if (System_SPLL_config(&ClkConfig.SPLL_CFG,200) == -1)
  •         while(1);   
  •     if (System_AUPLL_config(&ClkConfig.AUPLL_CFG,200) == -1)
  •         while(1);

  •     ClkConfig.MCU_Clock_Source = MCU_CLK_SEL_CORE_HSCLK;
  •     ClkConfig.SOC_DIV  = 1;    /* This parameter is valid when MCU_Clock_Source == MCU_CLK_SEL_SPLL_CLK */
  •     ClkConfig.MCU_DIV  = 1;
  •     ClkConfig.APB0_DIV = 1;
  •     ClkConfig.APB1_DIV = 1;
  •     ClkConfig.APB2_DIV = 1;

  •     System_MCU_clock_Config(&ClkConfig);
  • }</pre>
  • 复制代码

    main.c文件修改为task启动代码:
    <pre>#include "rtthread.h"

  • #include "gpio_demo.h"

  • // 启动线程
  • static void StartUpThread(  rt_thread_t *handle,
  •                             const char *name,
  •                             void (*entry)(void *parameter),
  •                             void *parameter,
  •                             rt_uint32_t stack_size,
  •                             rt_uint8_t priority,
  •                             rt_uint32_t tick)
  • {
  •     *handle = rt_thread_create(name, entry, parameter, stack_size, priority, tick);
  •     if (*handle != RT_NULL)
  •     {
  •         rt_thread_startup(*handle);
  •     }
  •     rt_thread_mdelay(10);
  • }

  • rt_thread_t _task_gpio = RT_NULL;

  • int main(void)
  • {
  •     StartUpThread(&_task_gpio, "gpio", gpio_demo,   RT_NULL, 512, 1, 10);

  •     return 0;
  • }</pre>
  • 复制代码
    3、修改board文件
    board文件需要配置heap空间,配置硬件启动代码,并初始化systick时钟,这部分相对复杂一些。代码如下:
    <pre>#include <rthw.h>
  • #include <rtthread.h>

  • #include "fr30xx.h"

  • extern void system_init(void);
  • extern uint32_t system_get_CORE_HSCLK(void);
  • extern void main_entry_point(void);

  • #if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
  • #ifdef __ICCARM__
  • // Use *.icf ram symbal, to avoid hardcode.
  • extern char __ICFEDIT_region_IRAM1_end__;
  • #define MCU_SRAM_END            &__ICFEDIT_region_IRAM1_end__
  • #else
  • // 根据自己的MCU不同修改
  • #define MCU_SRAM_SIZE           128
  • // 根据自己的MCU不同修改
  • #define MCU_SRAM_END            (0x20000000 + MCU_SRAM_SIZE * 1024)
  • #endif

  • #ifdef __ICCARM__
  • #pragma section="HEAP"
  • #define HEAP_BEGIN    (__segment_end("HEAP"))
  • #else
  • extern int Image$RW_IRAM1$ZI$Limit;
  • #define HEAP_BEGIN    (&Image$RW_IRAM1$ZI$Limit)
  • #endif
  • #define HEAP_END                MCU_SRAM_END

  • RT_WEAK void *rt_heap_begin_get(void)
  • {
  •     return (void*)HEAP_BEGIN;
  • }

  • RT_WEAK void *rt_heap_end_get(void)
  • {
  •     return (void *)HEAP_END;
  • }
  • #endif

  • void rt_os_tick_callback(void)
  • {
  •     rt_interrupt_enter();
  •    
  •     rt_tick_increase();

  •     rt_interrupt_leave();
  • }

  • void SysTick_Handler(void)
  • {
  •     rt_os_tick_callback();
  • }

  • /**
  • * This function will initial your board.
  • */
  • void rt_hw_board_init(void)
  • {
  •     main_entry_point();
  •     system_init();

  •     SysTick_Config(system_get_CORE_HSCLK() / RT_TICK_PER_SECOND);
  • // #error "TODO 1: OS Tick Configuration."
  •     /*
  •      * TODO 1: OS Tick Configuration
  •      * Enable the hardware timer and call the rt_os_tick_callback function
  •      * periodically with the frequency RT_TICK_PER_SECOND.
  •      */

  •     /* Call components board initial (use INIT_BOARD_EXPORT()) */
  • #ifdef RT_USING_COMPONENTS_INIT
  •     rt_components_board_init();
  • #endif

  • #if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
  •     rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
  • #endif
  • }</rtthread.h></rthw.h></pre>
  • 复制代码
    需要注意的是,在FR3068x-C的SDK中,system_fr30xx.c中,采用$Sub$$main的形式,将main_entry_point函数插入到main函数之前,看代码可知,主要是为了在main函数启动之前,初始化flash和trim模块。而RT-Thread操作系统同样采用了类似方式在main函数之前插入操作系统启动代码,所以如果不修改的话,编译时会报错,提示main函数重复定义
    所以这里,我们首先删除system_fr30xx.c中如下部分代码:
    <pre>extern int $Super$main(void);
  • int $Sub$main(void)
  • {
  •     main_entry_point();
  •     $Super$main();
  •    
  •     return 0;
  • }</pre>
  • 复制代码
    然后将main_entry_point函数的调用,放入到board.c文件中的rt_hw_board_init函数中。由RT-Thread在main函数之前统一调用。

    到此,所有代码方面的改动全部完成,编译并烧录测试。
    Snipaste_2025-02-13_22-09-50.png
    如上图所示,程序正常运行,task中断点已生效,同样,串口日志也正常输出:
    Snipaste_2025-02-13_22-08-56.png
    OK,移植完毕。