IMX8QXP内部M4移植rt-thread

最近研究了一段时间的rt-thread,相对来说还是算比较有追求的设计。从风格上讲,与Linux类似,很多设计思想也是借鉴Linux,比如设备驱动;从设计上来说,精简,高效易扩展,做了很多不错的中间件。

当然最主要的工作还是集中于IMX8QXP,A核就不多说了,都是Linux的传统内容,开发也都搞定了。对其内部的M核比较感兴趣,就想着尝试移植一下rt-thread,一来挑战一下,二来检验一下学习成果。虽然之前在STM32上移植过rt-thread,不过太过easy,感觉没什么意思。

移植前,首先看一下IMX8内部的架构图

其中有两个M4,左下角的称之为SCU,用于资源及clock管理,不对外开放。上面的一个M4才是我们所能用的。

无论A核还是M4,都必须通过SCU来进行资源及clock管理。SCU的SDK不开放,只提供相关API。

内部的M4默认运行的系统为FreeRTOS,个人对其极不喜欢,也是我移植rt-thread的一个原因,O(∩_∩)O哈哈~

首先看一下FreeRTOS的SDK代码结构:

原始SDK中提供了很多裸机example以及包含FreeRtos的demo,移植思路就从这些demo开始。

首先我们需要将rt-thread的源码拷贝进rtos下并为其新建目录rt-thread,同时还需要修改编译配置文件,加入编译过程。

将hello也拷贝一份,相关FreeRTOS重命名为rt-thread。

原始SDK使用的Cmake编译,入口为相关的裸机demo以及rtos demo,我们选择rtos_examples下的hello为例,移植后只要能见到“hello world”,世界从此就光明了。当然见到光明之前,必定先经历一片漆黑。

hello下的armgcc文件夹即为编译配置,对应Cmakelists.txt,在其中加入rt-thread相关源码以及修改hello相关文件。

编译问题不多说,遇到问题修复问题即可。基本都是文件路径以及先后问题,没什么难度。

下面看一下入口main函数,原始代码如下:

/*!
 * @brief Application entry point.
 */
int main(void)
{
    /* Init board hardware. */
    sc_ipc_t ipc;

    ipc = BOARD_InitRpc();
    BOARD_InitPins(ipc);
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();
    BOARD_InitMemory();
    if (xTaskCreate(hello_task, "Hello_task", configMINIMAL_STACK_SIZE + 100, NULL, hello_task_PRIORITY, NULL) !=
        pdPASS)
    {
        PRINTF("Task creation failed!.\r\n");
        while (1)
            ;
    }
    vTaskStartScheduler();
    for (;;)
        ;
}

可见BOARD_XX开头的5个函数已经将硬件环境准备好了,后面直接创建task即可。基于此,我们的适配工作好像很简单。貌似只需要初始化systick即可。由于FreeRTOS systick初始化行为在其源码中,rt-thread需要在外部进行。因此我们得自行实现systick初始化,有两个选择,第一SDK中已有API可调用,第二借用FreeRTOS初始化代码。有systick中断,必定有中断处理好函数,rt-thread已经有标准的实现参考,通常我们放到board.c文件

void SysTick_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    rt_tick_increase();

    /* leave interrupt */
    rt_interrupt_leave();
}

建议移植时,分小步进行,先确认systick是否运行OK,然后再继续。可在Systick_handler中打印log检测,同时屏蔽rtos API。

一般来说systick基本不会有什么问题。

接着往下,有了systick就有任务切换,代码位于:rtos/rt-thread/kernel/libcpu/arm/cortex-m4/context_gcc.S,也不需要变动。

rt-thread初始化过程中有一个重要的板级初始化过程rt_hw_board_init

//for rt-thread
void rt_hw_board_init()
{
    main_scu_init();

    /* 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(heap, sizeof(heap));
#endif
}

前面提到的5个硬件初始化函数BOARD_XX就是在这里进行

void main_scu_init(void)
{
    /* Init board hardware. */
    sc_ipc_t ipc;

    ipc = BOARD_InitRpc();
    BOARD_InitPins(ipc);
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();
    BOARD_InitMemory();

    PRINTF("Hello world. main_scu_init\r\n");
}

在rt-thread初始化流程之中,有一个组件的初始化过程rt_components_board_init,其相关符号位于一些特殊的段中,因此需要修改链接,加入一些段

有了板级初始化,有了systick,调度不用变,加入了特有段,至此应该可以正常跑起来,至少能进入main,哪怕没有调度,至少main能够运行。

关于main入口函数,armgcc与gnugcc入口不同,注意我使用的gnu gcc,入口为entry,在CFLAGS中指定即可-eentry

rt-rthread代码中也有注释说明

需要注意的几个地方:

1. 链接加入特定的段

2. gnu gcc指定入口函数

3. Cmake加入新的或改动的源文件并注意编译顺序

4. Systick中断以及PendSV中断的优先级

5. main task的栈不可过小

6. 精简rt-thread编译配置,尽量最小化

7. 注意宏定义FSL_RTOS_FREE_RTOS,原始SDK中有很多此宏定义,参与编译的 其包含的内容 需要修改为rt-thread头文件

8. 注意rt-thread配置的栈大小:

STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x0400;

最后,“hello world”展示图:

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值