RT-Thread学习笔记(二)—— RT-Thread启动代码及入口函数

从本篇开始正式进入RT-Thread的开发学习,参考RT-Thread快速入门实例教程对RT-Thread作以初步了解,探究RT-Thread启动代码及用户入口函数。

1.工程结构

RTT工程文件夹说明
RTT工程文件夹说明

MDK工程结构
MDK工程结构

2.用户入口代码

上一节中,application文件夹中存放main.c文件,所以学习RTT的源码从这个文件入手,由浅到深,打开这个文件后可以看到里面内容很简单:

#include <rtthread.h>

int main(void)
{
    /* user app entry */
    return 0;
}

在这里不禁会有所疑惑,作为一个RTOS,main函数中竟然只有一行注释和return 0,are you kiding me?其实,正如那一句精辟的注释,user app entry,用户应用程序入口,这个main函数并不是我们平常所接触到的main函数,这个main函数其实是由RTT呈现给我们的main函数,我们可以在此,在RTT操作系统之上编写我们的代码,即用户代码。

3.RT-Thread启动代码

了解了rtt的用户入口代码之后,再来看看RT-Thread是如何启动的?最好的办法是使用MDK的软件模拟debug功能,单步执行一遍程序,答案自然知晓。
首先编译工程,然后更改Debug模式为软件模拟模式,然后ctrl+F5进入debug模式,点击左上角复位按钮,将系统置于复位状态,因为RTT使用USART1打印信息,所以需要调出USART1串口便于观察(在View->Serial Windows->UART#1),接下来按F11就可以单步执行程序;


MDK调试界面
MDK调试界面

3.1.汇编启动文件

这个启动文件应该很熟悉了,无论裸机编程还是RTOS编程都是必不可少的,它的作用是构建好单片机上C语言的运行环境(主要是堆栈)

Reset handler
Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  __main
                IMPORT  SystemInit
                LDR     R0, =SystemInit
                BLX     R0         /* 跳转执行系统时钟初始化 */
                LDR     R0, =__main
                BX      R0        /* 跳转执行C语言中的main函数 */
                ENDP

3.2.启动文件跳转到的main函数

在上一步的汇编启动文件中跳转到main函数中,我们可以发现,它跳转到了RT-Thread源码中的"components.c"文件的main函数中,具体代码如下:

#if defined (__CC_ARM)
extern int $Super$$main(void);
/* re-define main function */
int $Sub$$main(void)
{
    /* 关闭中断系统 */
    rt_hw_interrupt_disable();
    /* 启动RT-Thread系统 */
    rtthread_startup();
    return 0;
}

可以看到,这才是整个RTOS系统的核心main函数,依然很简洁,精辟,第一行代码关闭整个中断系统,然后第二行代码启动RT-Thread系统;
这里需要注意一点:在定义main函数的时候采用了<pre>不能识别此Latex公式: Sub</pre><pre>不能识别此Latex公式: main,这是ARM链接器的特殊语法,因为之前用户入口函数是main,此处不能再次main,所以可以采用</pre>Sub<pre>不能识别此Latex公式: </pre>在不修改函数名的情况下对main函数的主体重新定义,在进入 main 程序之前调用另外一个例程完成系统功能初始化,具体说明可以参见ARM官方文档。Linker User Guide
接下来我们继续执行代码:

3.2.1.关闭中断系统

程序跳转到了汇编编写的“ context_rvds.S”文件中,执行以下几行代码,关闭整个中断系统:

    MRS     r0, PRIMASK
    CPSID   I
    BX      LR

3.2.2.启动RT-Thread系统

单步执行后系统再次跳转到"components.c"文件中,rtthread_startup()函数的代码如下,由注释可以看到,中断系统对于RTOS的影响很大,所以在启动之前再次关闭中断系统,然后便是整个RTT的启动流程:

int rtthread_startup(void)
{
    /* 再次关闭中断系统 */
    rt_hw_interrupt_disable();

    /* 板级初始化 */
    rt_hw_board_init();

    /* 打印rt版本号 */
    rt_show_version();

    /* 定时器系统初始化 */
    rt_system_timer_init();

    /* 调度器系统初始化 */
    rt_system_scheduler_init();

#ifdef RT_USING_SIGNALS
    /* 信号量系统初始化 */
    rt_system_signal_init();
#endif

    /* 创建初始化线程 */
    rt_application_init();

    /* 定时器线程初始化 */
    rt_system_timer_thread_init();

    /* 空闲线程初始化 */
    rt_thread_idle_init();

    /* 启动调度器 */
    rt_system_scheduler_start();

    /* 永远不会执行到这儿 */
    return 0;
}
启动流程
启动流程

至此,我们对RTT的启动过程和我们需要的用户入口函数有了大致的认识,接下来我们不再深入RT-Thread的内核底层,而是 在RT-Thread的基础上学习如何应用编程,毕竟原理上的理解是为了更好的应用,在学习完RT-Thread的基本应用之后,我们再回过头来深入底层一探内核究竟,便于我们在一些特殊的应用里对内核进行高度裁剪。

猜你喜欢

转载自www.cnblogs.com/Mculover666/p/9387720.html