RTT(RT-Thread)内核启动流程详解

目录

RTT(RT-Thread)内核启动流程详解

前言

RTT内核启动流程

启动流程概述

汇编阶段

C阶段

1、entry入口

 2、进入rtthread_startup函数

3、创建主线程


RTT(RT-Thread)内核启动流程详解

前言

        与STM32裸机启动流程一样,RT-Thread启动流程分为汇编阶段和C阶段。首先在汇编代码里面去创建好C语言执行的环境,然后调用系统初始化函数把我们的系统进行初始化(其中就包括了系统时钟的初始化),初始化完成以后就进入了C语言的main函数入口。不过RTT在进入main函数之前还做了对系统内核一些功能的初始化。

RTT内核启动流程

        RT-Thread 支持多种平台和多种编译器,而 rtthread_startup() 函数是 RT-Thread 规定的统一启动入口。一般执行顺序是:系统先从启动文件开始运行,然后进入 RT-Thread 的启动 rtthread_startup() ,最后进入用户入口 main(),如下图所示:(RT-Thread Studio使用的是GCC编译器)

 

启动流程概述

启动的第一阶段从启动汇编代码开始执行,我们的编译环境一共有三种

分别为MDK(如Keil5)、IAR、GCC(如VScode、CLion等),不同开发工具调用的函数都是不一样的,不过它们最终的结果都是进入到rtthread_startup函数里执行

我们使用的编译软件是RT Thread Stduio,它使用的是GCC编译环境,它执行完启动文件,然后经过入口函数entry()以后,最终调用了rtthread_startup函数

在rtthread_startup函数里首先,执行rt_hw_interrupt_disable,关闭所有的硬件中断;

然后分别执行

rt_hw_board_init:初始化板子相关的硬件外设

rt_show_version:调用显示版本函数

rt_system_timer_init:系统定时器初始化

rt_system_scheduler_init:系统线程调度器初始化

rt_system_signal_init:系统信号初始化

rt_application_init:应用层初始化

rt_system_timer_thread_init:定时器线程初始化

rt_thread_idle_init:空闲线程的初始化

最后执行rt_system_scheduler_start,启动系统线程调度器,执行相关线程

在应用层初始化函数、系统定时器线程初始化函数、空闲线程函数中分别创建了三个线程,然后当我们的线程调度器工作以后就会调度这三个线程去执行(其中idle线程的优先级最低)

在我们的主线程入口,会对组件进行初始化,同时最终会跳转到用户定义的main函数中

汇编阶段

 

Reset_Handler:    //复位

/* Copy the data segment initializersfrom flash to SRAM */
 movs r1, #0
 b LoopCopyDataInit

CopyDataInit:
 ldr r3, =_sidata
 ldr r3, [r3, r1]
 str r3, [r0, r1]
 adds r1, r1, #4

LoopCopyDataInit:
 ldr r0, =_sdata
 ldr r3, =_edata
 adds r2, r0, r1
 cmp r2, r3
 bcc CopyDataInit
 ldr r2, =_sbss
 b LoopFillZerobss
/* Zero fill the bss segment. */
FillZerobss:
 movs r3, #0
 str r3, [r2], #4

LoopFillZerobss:
 ldr r3, = _ebss
 cmp r2, r3
 bcc FillZerobss

/* Call the clock systemintitialization function.*/
   bl  SystemInit
/* Call static constructors */
   /* bl __libc_init_array */
/* Call the application's entrypoint.*/
 bl  entry
 bx lr

主要过程:

  • 从Flash中拷贝数据段到SRAM中

  • 清空BSS段(BSS段清零),创建好C语言的开发环境,由于C语言的全局未初始化的变量是放在BSS段的,因此打印未初始化的全局变量,值是0

  • 初始化系统时钟(SystemInit)

我们首先在board.h中设置好时钟

最终调用SystemInit进行初始化

  • 进入entry入口

C阶段

【1】系统时钟初始化

system_stm32f1xx.c中的系统初始化函数,参考之前章节内容

时钟系统配置文件board.h

使用外部高速时钟,时钟源晶振8MHz,系统时钟72MHz

1、entry入口
int entry(void)
{
    rtthread_startup();
    return 0;
}

 2、进入rtthread_startup函数
int rtthread_startup(void)
{
   rt_hw_interrupt_disable();       //关闭硬件中断

   /* 
    * board level 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();

#ifdef RT_USING_SMP
   rt_hw_spin_lock(&_cpus_lock);
#endif /*RT_USING_SMP*/

   /* start scheduler */
   rt_system_scheduler_start();
   
   return 0;
}

 

主要过程:

  • 初始化系统相关硬件
  • 初始化系统内核对象、例如定时器、调度器、信号
  • 创建主线程、定时器线程、idle线程
  • 启动调度器
3、创建主线程

在应用层初始函数中调用线程创建函数创建主线程,其中通过条件编译,有两种创建线程的方法:如果使用堆,就用动态的方法来创建线程(rt_thread_create());如果不使用堆,就使用静态的方法来创建线程(rt_thread_init())

第二个参数为函数指针

然后通过rt_thread_startup()启动线程

一旦主线程启动,就会执行main_thread_entry函数,最终就会跳转到用户自己定义的main函数中

线程函数入口:main_thread_entry

栈大小:2048

优先级:10

同等优先级时间片轮询时间:20 个OS Tick    rfconfig.h 中配置 :

#define RT_TICK_PER_SECOND 1000   Tick每秒1000次,一次的时间为1ms

//创建线程,线程函数main_thread_entry
tid =rt_thread_create("main", main_thread_entry, RT_NULL,
        RT_MAIN_THREAD_STACK_SIZE,RT_MAIN_THREAD_PRIORITY, 20);
//开启线程 —— 将线程加入到系统的线程队列中,等待系统线程调度器遍历队列调用                
rt_thread_startup(tid);
开启线程调度器
//选择优先级最高的线程开始调度
rt_system_scheduler_start();

void main_thread_entry(void*parameter)
{
   extern int main(void);
   extern int $Super$$main(void);
   
#ifdef RT_USING_COMPONENTS_INIT
   /* RT-Thread components initialization */
   rt_components_init();
#endif    
#ifdef RT_USING_SMP
   rt_hw_secondary_cpu_up();
#endif
   /* invoke system main function */
#if defined(__CC_ARM) ||defined(__CLANG_ARM)
   $Super$$main(); /* for ARMCC. */  //进入用户的main函数入口
#elif defined(__ICCARM__) ||defined(__GNUC__)
   main();
#endif
}

注意:

$Sub$ $foo :定义的新功能函数,在foo()函数之前/后使用$Sub$$foo 可以添加一些新的程序代码。

$Super$ $foo :就是原始的未修补的foo函数,使用这个$Super$ $foo函数将直接跳转到foo()函数。

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

竹烟淮雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值