RT_Thread应用3— RT-Thread 的启动流程

2023-05-16

第十六章 RT-Thread 的启动流程

在RTOS 中,主要有两种比较流行的启动方式:

一、万事俱备,只欠东风

在这里插入图片描述

1 int main (void) 
 2 { 
 3     /* 硬件初始化 */ 
 4     HardWare_Init();                                         (1) 
 5  
 6     /* RTOS 系统初始化 */ 
 7     RTOS_Init();                                             (2) 
 8  
 9     /* 创建线程 1,但线程 1 不会执行,因为调度器还没有开启 */         (3) 
10     RTOS_ThreadCreate(Task1); 
11     /* 创建线程 2,但线程 2 不会执行,因为调度器还没有开启 */ 
12     RTOS_ThreadCreate(Task2); 
13  
14     /* ......继续创建各种线程 */ 
15  
16     /* 启动 RTOS,开始调度 */ 
17     RTOS_Start();                                           (4) 
18 } 
19  
20 void Thread1( void *arg )                                   (5) 
21 { 
22     while (1) 
23     { 
24         /* 线程实体,必须有阻塞的情况出现 */ 
25     } 
26 } 
27  
28 void Thread2( void *arg )                                   (6) 
29 { 
30     while (1) 
31     { 
32         /* 线程实体,必须有阻塞的情况出现 */ 
33     } 
34 } 

(1):硬件初始化。硬件初始化这一步还属于裸机的范畴,我们可以把需要使用到的硬件都初始化好而且测试好,确保无误。
(2):RTOS 系统初始化。比如 RTOS 里面的全局变量的初始化,空闲线程的创建等。不同的 RTOS,它们的初始化有细微的差别。
(3):创建各种线程。这里把所有要用到的线程都创建好,但还不会进入调度,因为这个时候 RTOS 的调度器还没有开启。
(4):启动 RTOS 调度器,开始线程调度。这个时候调度器就从刚刚创建好的线程中选择一个优先级最高的线程开始运行。
(5) (6):线程实体通常是一个不带返回值的无限循环的 C 函数,函数体必须有阻塞的情况出现,不然线程(如果优先权恰好是最高)会一直在 while 循环里面执行,导致其它线程没有执行的机会。

二、小心翼翼,十分谨慎

在 main 函数中将硬件和 RTOS 系统先初始化好,然后创建一个启动线程后就启动调度器,然后在启动线程里面创建各种应用线程,当所有线程都创建成功后,启动线程把自己删除。
在这里插入图片描述

1 int main (void) 
 2 { 
 3     /* 硬件初始化 */ 
 4     HardWare_Init();                                     (1) 
 5  
 6     /* RTOS 系统初始化 */ 
 7     RTOS_Init();                                         (2) 
 8  
 9     /* 创建一个线程 */ 
10     RTOS_ThreadCreate(AppThreadStart);                   (3) 
11  
12     /* 启动 RTOS,开始调度 */ 
13     RTOS_Start();                                        (4) 
14 } 
15  
16 /* 起始线程,在里面创建线程 */ 
17 void AppThreadStart( void *arg )                         (5) 
18 { 
19     /* 创建线程 1,然后执行 */ 
20     RTOS_ThreadCreate(Thread1);                          (6) 
21  
22     /* 当线程 1 阻塞时,继续创建线程 2,然后执行 */ 
23     RTOS_ThreadCreate(Thread2); 
24  
25     /* ......继续创建各种线程 */ 
26  
27     /* 当线程创建完成,关闭起始线程 */ 
28     RTOSThreadClose(AppThreadStart);                     (7) 
29 } 
30  
31 void Thread1( void *arg )                                (8) 
32 { 
33     while (1) 
34     { 
35         /* 线程实体,必须有阻塞的情况出现 */ 
36     } 
37 } 
38  
39 void Thread2( void *arg )                                (9) 
40 { 
41     while (1) 
42     { 
43         /* 线程实体,必须有阻塞的情况出现 */ 
44     } 
45 } 

(1):**硬件初始化。来到硬件初始化这一步还属于裸机的范畴,我们可以把需要使用到的硬件都初始化好而且测试好,确保无误。
(2):RTOS 系统初始化。比如 RTOS 里面的全局变量的初始化,空闲线程的创建等。不同的 RTOS,它们的初始化有细微的差别。
(3):创建一个开始线程。然后在这个初始线程里面创建各种应用线程。
(4):启动 RTOS 调度器,开始线程调度。这个时候调度器就去执行刚刚创建好的初始线程。
(5):我们通常说线程是一个不带返回值的无限循环的 C 函数,但是
因为初始线程的特殊性,它不能是无限循环的,只执行一次后就关闭。在初始线程里面我们创建我们需要的各种线程。
(6):创建线程。每创建一个线程后它都将进入就绪态
,系统会进行一次调度,如果新创建的线程的优先级比初始线程的优先级高的话,那将去执行新创建的线程,当新的线程阻塞时再回到初始线程被打断的地方继续执行。反之,则继续往下创建新的线程,直到所有线程创建完成。
(7):各种应用线程创建完成后,初始线程自己关闭自己,使命完成。
(8) (9):线程实体通常是一个不带返回值的无限循环的 C 函数,函数体必须有阻塞的情况出现,不然线程(如果优先权恰好是最高)会一直在 while 循环里面
执行,其它线程没有执行的机会。
在这里插入图片描述
*

ucos第一种和第二种都可以使用,由用户选择,freertos 和 RT-Thread 则默认使用第二种。

三、RT-Thread 的启动流程

只能在 main 函数里面看到创建线程和启动线程的代码,硬件初始化,系统初始化,启动调度器等信息都看不到。
——原因:因为 RT-Thread 拓展了 main 函数,在 main 函数之前把这些工作都做好了。
系统上电小流程:
在这里插入图片描述

**Reset_Handler 函数** 
1 Reset_Handler   PROC 
 2                 EXPORT  Reset_Handler             [WEAK] 
 3                 IMPORT  SystemInit 
 4                 IMPORT  __main 
 5  
 6                 CPSID   I               ; 关中断 
 7                 LDR     R0, =0xE000ED08 
 8                 LDR     R1, =__Vectors 
 9                 STR     R1, [R0] 
10                 LDR     R2, [R1] 
11                 MSR     MSP, R2 
12                 LDR     R0, =SystemInit 
13                 BLX     R0 
14                 CPSIE   i               ; 开中断 
15                 LDR     R0, =__main             
16                 BX      R0 
17                 ENDP 

在这里插入图片描述
若当需要扩展的函数不是 main 的时候,只需要将 main 换成你要扩展的函数名即可,即 S u b Sub Sub f u n c t i o n 和 function和 functionSuper$$function

 **$Sub$$和$Super$$的使用方法** 
1 extern void ExtraFunc(void);     /* 用户自己实现的外部函数*/ 
 2  
 3  
 4 void $Sub$$function(void) 
 5 { 
 6     ExtraFunc();                 /* 做一些其它的设置工作 */ 
 7     $Super$$function();          /* 回到原始的 function 函数 */ 
 8 } 
 9  
10 /* 在执行 function 函数执行会先执行 function 的扩展函数 
11 $Sub$$function,在扩展函数里面执行一些扩展的操作, 
12 当扩展操作完成后,最后必须调用$Super$$function 函数 
13 通过它回到我们原始的 function 函数 */ 
14 void function(void) 
15 { 
16     /* 函数实体 */ 
17 } 
1、$Sub$$main 函数
1 int $Sub$$main(void) 
 2 { 
  3     rt_hw_interrupt_disable();   //关闭中断                     (1) 
 4     rtthread_startup();                              (2) //rtthread_startup()函数  
 5     return 0; 
 6 } 
2、rtthread_startup()函数
1 int rtthread_startup(void) 
 2 { 
 3     /* 关闭中断 */ 
 4     rt_hw_interrupt_disable();                               (1) 
 5  
 6     /* 板级硬件初始化 
 7      * 注意: 在板级硬件初始化函数中把要堆初始化好(前提是使用动态内存) 
 8      */ 
 9     rt_hw_board_init();                                      (2) 
10  
11     /* 打印 RT-Thread 版本号 */ 
12     rt_show_version();                                       (3) 
13  
14     /* 定时器初始化 */ 
15     rt_system_timer_init();                                  (4) 
16  
17     /* 调度器初始化 */ 
18     rt_system_scheduler_init();                              (5) 
19  
20 #ifdef RT_USING_SIGNALS 
21     /* 信号量初始化 */ 
22     rt_system_signal_init();                                 (6) 
23 #endif 
24  
25     /* 创建初始线程 */ 
26     rt_application_init();                                   (7) 
27  
28     /* 定时器线程初始化 */ 
29     rt_system_timer_thread_init();                           (8) 
30  
31     /* 空闲线程初始化 */ 
32     rt_thread_idle_init();                                   (9) 
33  
34     /* 启动调度器 */ 
35     rt_system_scheduler_start();                             (10) 
36  
37     /* 绝对不会回到这里 */ 
38     return 0;                                                (11) 
39 } 

(1):关中断。在硬件初始化之前把中断关闭是一个很好的选择,如果没有关闭中断,在接下来的硬件初始化中如果某些外设开启了中断,那么它就有可能会响应,可是后面的 RTOS 系统初始化,调度器初始化这些都还没有完成,显然这些中断我们是不希望响应的。
(2):板级硬件初始化。RT-Thread 把板级硬件相关的初始化都放在rt_hw_board_int()函数里面完成 ,该函数需要用户在 board.c 实现。
(3):打印 RT-Thread 的版本号,该函数在 kservice.c 中实现
(4):定时器初始化,实际上就是初始化一个全局的定时器列表,列表里面存放的是处于延时状态的线程。
(5):调度器初始化。
(6):信号初始化,
(7):创建初始线程

3、rt_application_init()函数
**创建初始线程** 
1 /* 使用动态内存时需要用到的宏:rt_config.h 中定义 */                 (2) 
 2 #define RT_USING_USER_MAIN 
 3 #define RT_MAIN_THREAD_STACK_SIZE     256 
 4 #define RT_THREAD_PRIORITY_MAX        32 
 5  
 6 /* 使用静态内存时需要用到的宏和变量:在 component.c 定义 */            (4) 
 7 #ifdef RT_USING_USER_MAIN 
 8 #ifndef RT_MAIN_THREAD_STACK_SIZE 
 9 #define RT_MAIN_THREAD_STACK_SIZE     2048 
10 #endif 
11 #endif 
12  
13 #ifndef RT_USING_HEAP 
14 ALIGN(8) 
15 static rt_uint8_t main_stack[RT_MAIN_THREAD_STACK_SIZE]; 
16 struct rt_thread main_thread; 
17 #endif 
18  
19 void rt_application_init(void) 
20 { 
21     rt_thread_t tid; 
22  
23 #ifdef RT_USING_HEAP 
24     /* 使用动态内存 */                                           (1) 
25     tid = 
26         rt_thread_create("main", 
27                          main_thread_entry, 
28                          RT_NULL, 
29                          RT_MAIN_THREAD_STACK_SIZE, 
30                          RT_THREAD_PRIORITY_MAX / 3,     (初始线程优先级) 
31                          20); 
32     RT_ASSERT(tid != RT_NULL); 
33 #else 
34     /* 使用静态内存 */                                           (3) 
35     rt_err_t result; 
36  
37     tid = &main_thread; 
38     result = 
39         rt_thread_init(tid, 
40                        "main", 
41                        main_thread_entry, 
42                        RT_NULL, 
43                        main_stack, 
44                        sizeof(main_stack), 
45                        RT_THREAD_PRIORITY_MAX / 3,      (初始线程优先级) 
46                        20); 
47     RT_ASSERT(result == RT_EOK); 
48     (void)result; 
49 #endif 
50  
51     /* 启动线程 */ 
52     rt_thread_startup(tid);                                  (6) 
53 } 
54  
55  
56 /* main 线程 */ 
57 void main_thread_entry(void *parameter)                      (5) 
58 { 
59     extern int main(void); 
60     extern int $Super$$main(void); 
61  
62     /* RT-Thread 组件初始化 */ 
63     rt_components_init(); 
64  
65     /* 调用$Super$$main()函数,去到 main */ 
66     $Super$$main(); 
67 } 

(1):创建初始线程的时候,分使用动态内存和静态内存两种情况,这是使用动态内存,有关动态内存需要用到的宏定义具体见代码清单 (2)。
(3):创建初始线程的时候,分使用动态内存和静态内存两种情况,这里是使用静态内存,有关静态内存需要用到的宏定义具体见代码清单 (4)。

4、$Super$$main()函数

(5):初始线程入口
(6):启动初始线程
(初始线程优先级):初始线程的优先级默认配置为最大优先级/3。控制最大优先级的宏 RT_THREAD_PRIORITY_MAX 在 rt_config.h 中定义,目前配置为 32 ,那初始线程的优先级即是 10
在这里插入图片描述在这里插入图片描述

5、main()函数

main 函数里面只是创建并启动一些线程

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

RT_Thread应用3— RT-Thread 的启动流程 的相关文章

随机推荐

  • 蒙特卡洛树搜索 MCTS 入门

    引言 你如果是第一次听到蒙特卡洛 xff0c 可能会认为这是一个人名 那么你就大错特错 xff0c 蒙特卡洛不是一个人名 xff0c 而是一个地方 xff0c 还一个赌场名 xff01 xff01 xff01 但是这不是我们的重点 我们今天
  • 76-高斯核函数

    高斯核函数 上一篇博客详细的介绍了什么是核函数 xff0c 并且主要以多项式核函数为例 这篇博客主要学习一种特殊的核函数 xff0c 而且它也是 SVM 算法使用最多的一种核函数 xff1a 高斯核函数 核函数我们通常表示成 xff1a 那
  • 数仓实践:总线矩阵架构设计

    如何设计一套切实可行的数据仓库呢 xff1f 我们要明白 xff0c 对于数据仓库的设计是不能完全依赖于业务的需求 xff0c 但往往又必须要服务于业务的价值 因此 xff0c 在构建数据仓库前 xff0c 我们往往会通过总线矩阵设计 xf
  • 05 反向传播

    反向传播 上一篇博客介绍了从输入 X 样本开始 xff0c 通过一组 w w w 参数 xff0c 得到了一个得分值 xff0c 然后又将得分值经过 y 61
  • 07 神经网络整体架构

    神经网络整体架构 我们先看看神经网络是什么样子的 xff0c 如下图 可以说神经网络是一个层次的结构 xff0c 有一个输入层 xff0c 隐层 1 xff0c 隐层 2 和输出层 可以说是由多个层组成了一个完整的神经网络 输入层相当于输入
  • 地震勘探原理(一)之地震波的基本概念

    绪论 一 石油勘探的主要方法 地质法 岩石露头物探法 覆盖区 连续测量 间接钻井法 一点 直接勘探 二 地球物理勘探方法 重力法 岩石密度差异磁法 演示磁性差异电法 岩石电性差异地震勘探方法 岩石弹性差异 xff08 用得最多 xff0c
  • 地震勘探原理(二)之时距曲线

    文章目录 什么是时距曲线 xff1f 直达波的时距曲线水平界面的共炮点反射波时距曲线方程 xff08 一个分界面 xff09 倾斜界面的共炮点反射波时距曲线正常时差倾角时差 xff08 dip moveout xff09 时局曲面和时间场的
  • 地震勘探原理(四)之频谱分析概述

    文章目录 一 频谱的基本概念二 频谱的主要特征 振幅谱和相位谱三 获取频谱的方法四 傅里叶展式的重要性质五 地震波频谱特征及其应用六 线性时不变系统的滤波方程七 频率滤波参数的选择 一 频谱的基本概念 频谱 xff08 Spectrum S
  • 模糊C均值聚类算法

    学习了一下模糊聚类中的模糊 C 均值聚类算法 Fuzzy C Means Clustering Fuzzy 意为模糊 xff0c 其中包括几种模糊的方式 xff0c 这里使用的是最简单的方式 xff0c 它是基于概率的概念 我们把每一个点属
  • 数据建模之查文献找数据以及数据预处理

    1 查文献 知网 xff1a 先看硕博士论文谷歌学术镜像 xff1a http scholar scqylaw com Open Access Library xff1a https www oalib com 2 找数据 优先 xff1a
  • 数学建模之论文

    一篇完整的数模论文 包括摘要 最重要 问题重述 模型假设和符号说明 模型建立与求解 最长 模型的优缺点与改进方法 参考文献和附录 1 摘要 最重要 论文研究的问题 43 使用的方法 43 得到的结果 43 每一部分的大致步骤 2 问题重述
  • Deformable Convolution 可变形卷积

    可变形卷积概念出自2017年论文 xff1a Deformable Convolutional Networks 顾名思义 xff0c 可变形卷积的是相对于标准卷积的概念而来 a 一个经典的 3 3 3 times3 3 3
  • 模块化

    模块化 遵守固定的规则 xff0c 把一个 大文件 拆成 独立并互相依赖 的 多个小模块 优点 xff1a 提高了代码的 复用性 提高了代码的 可维护性 可以实现 按需加载 模块化规范 xff1a 降低沟通成本 xff0c 方便模块间的相互
  • 栈的应用:左右符号匹配

    说明 xff1a 在编译器中 xff0c 都有这么一个左右符号匹配的功能 xff0c 这里通过栈来模拟实现这一功能 xff1b 这里采用了代码复用的方法 xff0c 即使用了LinkStack链栈 xff0c 详见 LinkStack链栈
  • windows安装gcc

    完整报错 xff1a RuntimeError Error building extension 39 fused 39 1 3 C Program Files NVIDIA GPU Computing Toolkit CUDA v11 4
  • openstack-mitaka(一) 架构简介

    官网 xff1a OpenStack Docs 概况 1 openstack概况 OpenStack是一个云操作系统 xff0c 它控制整个数据中心的计算 存储和网络资源的大型池 OpenStack通过各种补充服务提供基础设施即服务 Inf
  • ITK和VTK读取DICOM图像文件

    ITK和VTK读取DICOM图像文件 ITK读取DICOM图像 相比于VTK类库中vtkDICOMImageReader类读取DICOM序列图像 xff0c 借助ITK类库实现对DICOM序列图像的读取要复杂许多 但是 xff0c 使用IT
  • 一招完美解决vscode安装go插件失败问题

    vscode 安装go插件 前置用vscode新建一个go文件使用go mod 代理来安装 前置 从https studygolang com dl下载go1 14 6 windows amd64 msi安装即可 xff0c 安装路径选择默
  • 在C中使用string类输入与输出

    问题描述 xff1a 由于在考虑的C语言输入输出的效率较C 43 43 高 xff0c 但string类在C 43 43 语言中使用具有较好的效果 xff0c 所以在开发中就希望在使用C语言的输入输出string类型 xff0c 但是C语言
  • RT_Thread应用3— RT-Thread 的启动流程

    第十六章 RT Thread 的启动流程 在RTOS 中 xff0c 主要有两种比较流行的启动方式 xff1a 一 万事俱备 xff0c 只欠东风 span class token number 1 span span class toke