目录

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(如 Keil 5)、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 函数中

汇编阶段

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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_stm 32 f 1 xx. c 中的系统初始化函数,参考之前章节内容

时钟系统配置文件 board.h

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

1、entry 入口
1
2
3
4
5
int entry(void)
{
rtthread_startup();
return 0;
}

2、进入 rtthread_startup 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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 线程

  • 启动调度器

其中:

  1. rt_hw_board_init:初始化向量表,初始化时钟,始化板级硬件,初始化 console,初始化堆栈
  2. rt_show_version:打印 RT-Thread 版本信息
  3. rt_system_timer_init:定时器链表初始化
  4. rt_system_scheduler_init:初始化调度器,即线程优先级链表
  5. rt_application_init:创建开始 main 进程 (优先级 10,tick 10)
  6. rt_system_timer_thread_init:创建开始定时器进程(优先级 4,tick 10)

定时器线程代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
static void _timer_thread_entry(void *parameter)
{
rt_tick_t next_timeout;

while (1)
{
/* 若不存在定时器,则当前线程挂起并调度其他线程 */
if (_timer_list_next_timeout(_soft_timer_list, &next_timeout) != RT_EOK)
{
/* no software timer exist, suspend self. */
rt_thread_suspend(rt_thread_self());
rt_schedule();
}
else
{
rt_tick_t current_tick;

/* get current tick */
current_tick = rt_tick_get();

if ((next_timeout - current_tick) < RT_TICK_MAX / 2)
{
/* get the delta timeout tick */
next_timeout = next_timeout - current_tick;
rt_thread_delay(next_timeout);
}
}

/* check software timer */
rt_soft_timer_check();
}
}
  1. rt_thread_idle_init:创建开始空闲进程(最低优先级,tick 32)
  2. rt_system_scheduler_start:启动调度器,开始按照线程优先级链表执行调度
3、创建主线程

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

第二个参数为函数指针

然后通过 rt_thread_startup ()启动线程

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

线程函数入口:main_thread_entry

栈大小:2048

优先级:10

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

1
#define RT_TICK_PER_SECOND 1000   

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//创建线程,线程函数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
}

在主线程启动过程中,通过 rt_components_init 实现组件外设的[[自动初始化]],完成后跳转到用户 main 函数

#todo
#启动流程
#初始化