RT-Thread学习笔记——移植RT-Thread到STM32(三)

RT-Thread学习笔记——移植RT-Thread到STM32(三)_第1张图片

RT-Thread启动过程(MDK)

RT-Thread学习笔记——移植RT-Thread到STM32(三)_第2张图片

(1)这是主函数中的全部代码,主要是创建一个led线程并启动。

(2)那么问题来了,要点个灯,怎么也要进行led的硬件初始化吧?

(3)但是,在主函数中并没有发现有相关的初始化操作。

(4)那么其在哪进行初始化呢?按照我们以往的习惯,主函数就是用户程序的入口。

(5)难道还有其他入口?还真的有其他入口!这就涉及到RT-Thread的启动过程。

我们可以借助仿真器进行硬件单步调试就可以知道程序的执行流程了。

首先,进入调试界面,并点击复位按钮光标就可以跳到程序开始运行的地方。如:

RT-Thread学习笔记——移植RT-Thread到STM32(三)_第3张图片

再次,一直点击单步运行按钮,直至光标运行到

RT-Thread学习笔记——移植RT-Thread到STM32(三)_第4张图片

这一行代码。此时,再点击单步运行按钮,并不会跳转到main.c中的main函数,

                       而是会跳到component.c中的$Sub$$main函数,该函数如下所示:

RT-Thread学习笔记——移植RT-Thread到STM32(三)_第5张图片

$Sub$$main函数里主要是系统中断失能及调用系统启动函数(系统初始化)。

$Sub$$$Super$$这两个符号是什么意思呢?

《RT-Thread内核实现与应用开发实战指南》这本书中有解释到:

简单来说,$Sub$$和$Super$$具有补丁功能,可以给一些函数打上补丁,如RT-Thread的内核文件component.c中就给我们的用户主函数main打上了”补丁“。

rtthread_startup函数中:

(1)主要实现了板级初始化(如led的初始化,串口初始化就是在这里边调用的);

(2)打印RT-Thread的logo和版本信息;

(3)初始化系统定时器;

(4)初始化调度器;

(5)创建application初始化线程(这里将用户main函数作为一个线程,用户main里面是空的);

(6)初始化软件定时器;

(7)创建空闲线程;

(8)启动系统调度(启用调度后,main函数就会参与调度开始运行)如:


int rtthread_startup(void)
{
    rt_hw_interrupt_disable();

    /* board level initalization
     * NOTE: please initialize heap inside board initialization.
     */
    rt_hw_board_init();

    /* show RT-Thread version */
    rt_show_version();

    /* timer system initialization */
    rt_system_timer_init();

    /* scheduler system initialization */
    rt_system_scheduler_init();

#ifdef RT_USING_SIGNALS
    /* signal system initialization */
    rt_system_signal_init();
#endif

    /* create init_thread */
    rt_application_init();

    /* timer thread initialization */
    rt_system_timer_thread_init();

    /* idle thread initialization */
    rt_thread_idle_init();

    /* start scheduler */
    rt_system_scheduler_start();

    /* never reach here */
    return 0;
}

rt_application_init函数中创建了一个main线程

void rt_application_init(void)
{
    rt_thread_t tid;

#ifdef RT_USING_HEAP
    tid = rt_thread_create("main", main_thread_entry, RT_NULL,
                           RT_MAIN_THREAD_STACK_SIZE, RT_MAIN_THREAD_PRIORITY, 20);
    RT_ASSERT(tid != RT_NULL);
#else
    rt_err_t result;

    tid = &main_thread;
    result = rt_thread_init(tid, "main", main_thread_entry, RT_NULL,
                            main_stack, sizeof(main_stack), RT_MAIN_THREAD_PRIORITY, 20);
    RT_ASSERT(result == RT_EOK);
	
    /* if not define RT_USING_HEAP, using to eliminate the warning */
    (void)result;
#endif

    rt_thread_startup(tid);
}

main线程的线程函数为:

/* the system main thread */
void main_thread_entry(void *parameter)
{
    extern int main(void);
    extern int $Super$$main(void);

    /* RT-Thread components initialization */
    rt_components_init();

    /* invoke system main function */
#if defined (__CC_ARM)
    $Super$$main(); /* for ARMCC. */      //这一步之后跳转到真正的main函数
#elif defined(__ICCARM__) || defined(__GNUC__)
    main();
#endif
}

总结:$Sub$$和$Super$$是成对使用的。可以使用如下结构给函数进行扩展(打补丁):

extern void ExtraFunc(void); /* 用户自己实现的外部函数*/
void $Sub$function(void)
{
 ExtraFunc(); /* 做一些其它的设置工作 */
 $Super$function(); /* 回到原始的 function 函数 */
}

void function(void)
{
/* 函数实体 */
}

在执行 function 函数会先执行 function 的扩展函数$Sub$$function,在扩展函数里面执行一些扩展的操作,当扩展操作完成后,最后必须调用$Super$$function 函数通过它回到我们原始的 function 函数。

你可能感兴趣的:(RT-Thread,嵌入式操作系统,stm32,arm,linux,vs2019,harmonyos)