rt-thread启动流程(最详细教程)

news2024/11/18 12:39:17

资料下载

RT-Thread Simulator 例程

操作流程

  1. 将上面的仿真例程下载并解压,通过MDK打开,编译,调试,并打开串口点击运行,就可以看到如下输出了:
    在这里插入图片描述
  2. 添加自己的 thread:在main()函数中添加即可,如下图:
    在这里插入图片描述

启动流程

  1. 首先是通过startup_stm32f103xe.s启动文件调用SystemInit(),系统初始化完成后,调用C库函数__main(),然后由__main()调用用户的main()函数。但是,由于ARMCC编译器的特性,可以在调用main()函数前插入一个$Sub$$main()函数(其他编译器也有类似特性)。rt-thread就是利用了这个特性,使所有的硬件、系统初始化都在$Sub$$main()函数完成,而不需要用户在main()中调用。
    在这里插入图片描述
  2. 接下来看看 rtthread_startup() 函数:
    在这里插入图片描述
  3. 我们进入到 rt_application_init() 函数去看一下:
    在这里插入图片描述
    在这里插入图片描述
  4. 以上就是系统的启动即初始化流程,以及实现自己的 thread 的操作流程。但是我们明明还有其他的 thread 在运行啊,比如上面图一中的tshelltidle线程,这两个线程又是从哪里启动的呢?
    在这里插入图片描述
  5. 首先,我们通过全局搜索tshell,看下这个这个thread是在哪里创建的:
#define FINSH_THREAD_NAME "tshell"

在这里插入图片描述
原来是在 shell.c 中的finsh_system_init()函数中创建了tshell线程,那理论上是不是就应该全局搜索在哪里调用了finsh_system_init()函数呢?确实是,但搜索后发现,这个函数没有被调用的记录,这又是为啥呢?

  1. 重点就在finsh_system_init()函数后面的一句:
INIT_APP_EXPORT(finsh_system_init);

查看定义如下(C语言中##起连接作用):
在这里插入图片描述
那么INIT_APP_EXPORT(finsh_system_init);这句翻译一下,就是:

INIT_EXPORT(finsh_system_init, "6");

再翻译一下,就是:

RT_USED const init_fn_t __rt_init_finsh_system_init SECTION(".rti_fn.6") = finsh_system_init;

这句就已经很清晰了吧,定义了一个变量__rt_init_finsh_system_init,变量类型为const init_fn_t(其中init_fn_t为函数指针),这个变量的值就是finsh_system_init()这个函数的起始地址,重点是:将这个函数起始地址放在了.rti_fn.6的段中!
分析到这里,就很容易再分析出另外几个宏定义的作用了:

INIT_BOARD_EXPORT(fn) : 将板子初始化函数起始地址放到.rti_fn.1的段中
INIT_PREV_EXPORT(fn) :将预初始化函数起始地址放到.rti_fn.2的段中
INIT_DEVICE_EXPORT(fn) :将设备初始化函数起始地址放到.rti_fn.3的段中
INIT_COMPONENT_EXPORT(fn) :将组件初始化函数起始地址放到.rti_fn.4的段中
INIT_ENV_EXPORT(fn) :将环境初始化函数起始地址放到.rti_fn.5的段中

这里再提醒一下,当在同一个段中放入多个变量时,这些变量会按照时间顺序依次往后排。

  1. 现在我们知道,tshell线程创建函数没有被直接调用,而是放在了一个特色的段中,那么这个段中的函数什么时候被执行的呢?这个我也没找到什么好的定位方法,只能从初始化函数依次看下来,然后发现,有这么一条函数调用链:

$Sub$$main() --> rtthread_startup() --> rt_application_init() --> main_thread_entry() --> rt_components_init()

在这里插入图片描述
通过以上代码,我们看到通过INIT_EXPORT()宏又定义了几个函数并放入了相应的段:

rti_start() --> .rti_fn.0
rti_board_start() --> .rti_fn.0.end
rti_board_end() --> .rti_fn.1.end
rti_end() --> .rti_fn.6.end

根据上面所定义段的命名,猜测一下这些段的地址关系应该是:

.rti_fn.0 --> .rti_fn.0.end --> .rti_fn.1 --> .rti_fn.1.end --> .rti_fn.2 --> .rti_fn.3 --> .rti_fn.4 --> .rti_fn.5 --> .rti_fn.6 --> .rti_fn.6.end

打开map文件看一下各个段的地址验证一下(由于我们例程中并没有使用到2-5这几个段,所以map中没有记录):
在这里插入图片描述

这里有个还有个疑问:各个段的地址是怎么确定的呢?由编译器指定还是由开发人员指定呢?
如果由编译器指定,那编译器又怎么知道哪个段需要放前面呢?如果由开发人员指定,又是在哪里指定的呢?

自问自答一下:我暂时没有找到什么资料,但是通过在代码中添加其他的段名进行各种尝试,发现段地址的由编译器决定的,而段的存放顺序是由段的名字按字母排序决定的!如下图:
在这里插入图片描述

接下来终于可以看看这些初始化函数是怎么调用的了:

/**
 * @brief  RT-Thread Components Initialization.
 */
void rt_components_init(void)
{
    volatile const init_fn_t *fn_ptr;

    for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
    {
        (*fn_ptr)();
    }
}

通过以上函数中的for循环,直接将__rt_init_rti_board_end(即.rti_fn.1.end)__rt_init_rti_end(即.rti_fn.6.end)之间的所有初始化函数执行了一遍。当然,也就包括了在.rti_fn.6段中的finsh_system_init()函数了。

  1. 同理,通过以下的函数调用流程,可以将.rti_fn.1中的初始化函数也执行一遍:

$Sub$$main() --> rtthread_startup() --> rt_hw_board_init() --> rt_components_board_init()

/**
 * @brief  Onboard components initialization. In this function, the board-level
 *         initialization function will be called to complete the initialization
 *         of the on-board peripherals.
 */
void rt_components_board_init(void)
{
    volatile const init_fn_t *fn_ptr;

    for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
    {
        (*fn_ptr)();
    }
}

通过以上函数中的for循环,直接将__rt_init_rti_board_start(即.rti_fn.0.end)__rt_init_rti_board_end(即.rti_fn.1.end)之间的所有初始化函数执行了一遍。而且,在rtthread_startup()函数中,rt_hw_board_init()函数是比rt_application_init()函数更早调用,这就保证了.rti_fn.1段中的函数要早于其他段中函数的执行。

  1. rt-thread的启动流程这就讲完啦,大家现在应该知道为什么要把这些初始化函数放在这么多个段中了吧?

因为在同一个段的函数执行顺序是由编译器决定的,如果我们需要指定顺序,只能通过在不同段中实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/546029.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【5.19】四、性能测试—流程

4.4 性能测试的流程 性能测试也遵循测试需求分析→测试计划制订→测试用例设计→测试执行→编写测试报告的基本过程&#xff0c;但在实现细节上&#xff0c;性能测试有单独一套流程&#xff1a; 1. 分析性能测试需求 在性能测试需求分析阶段&#xff0c;测试人员需要收集有关…

【AI Earth试玩】权限配置与openAPI调用工具库

前言 AI earth是阿里达摩院出的遥感云计算平台&#xff0c;我简单体验下来感觉像是GEE的python版本遥感深度学习计算平台&#xff0c;整体体验还是挺不错的&#xff0c;尤其是多分类的结果还是挺惊艳的。 平台提供工具箱和notebook两种模式&#xff0c;工具箱整个交互简单易用…

DJ 5-4 以太网 Ethernet

目录 一、以太网的物理拓扑结构 二、以太网物理层标准 1、以太网技术&#xff1a;10Base-T 和 100Base-T 2、以太网技术&#xff1a;1000Base 系列 3、曼彻斯特编码* 4、差分曼彻斯特编码机制* 三、以太网链路层控制技术 四、以太网的帧结构 1、前同步码 2、MAC 地址…

Spring Boot 项目【前后端分离】之后端实现加 LambdaQueryWrapper实现源码分析和手动模拟

目录 Spring Boot 项目【前后端分离】 之架子搭建 技术栈 实现功能03-创建Spring Boot 后端项目 需求分析/图解 思路分析 代码实现 1. 创建springboot_furn 项目 2. 修改pom.xml , 引入mybatis-plus 等相关依赖 3. 创建application.yml 配置port & 配置DB 连接信息…

【数据结构】KMP算法:计算next与nextval函数值(图解)

例&#xff1a;计算模式串"abaabcac"的KMP算法中next函数值 由函数定义 n e x t [ j ] { 0 , j 1 M a x { k ∣ 1 < k < j 且 " t 1 t 2 ⋅ ⋅ ⋅ t k − 1 " " t j − k 1 t j − k 2 ⋅ ⋅ ⋅ t j − 1 " } 1 , k 1 next[j]\left…

asp.net高校运动会管理系统的设计与实现

本高校运动会管理系统是针对我院当前运动会工作需要而开发的B/S模式的网络系统&#xff0c;涉及到运动会赛前的报名录入准备与分组编排、赛中的成绩处理、赛后的成绩汇总与团体总分的统计。它将是一个完整统一、技术先进、高效稳定、安全可靠的基于Internet/Intranet的高校运动…

一、Git安装(Git+TortoiseGit图形化)

Git 是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或大的项目。 Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 Git 与常用的版本控制工具 CVS, Subversion 等不同&#xff0c;它采用了分布式版本库的方式…

《计算机网络—自顶向下方法》 Wireshark实验(八):ICMP 协议分析

ICMP&#xff08;Internet Control Message Protocol&#xff09;网络控制报文协议。它是 TCP/IP 协议簇的一个子协议&#xff0c;用于在 IP 主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户…

进程概念

目录 冯诺依曼体系结构 操作系统OS 系统调用和库函数概念 进程 task_struct内容分类 组织进程 初识fork 进程状态 Z(zombie)-僵尸进程 孤儿进程 进程优先级 环境变量 和环境变量相关的命令 环境变量的组织方式 程序地址空间 冯诺依曼体系结构 关于冯诺依曼&…

Linux 防火墙 iptables

iptables概述 Linux 系统的防火墙 &#xff1a;IP信息包过滤系统&#xff0c;它实际上由两个组件netfilter 和 iptables组成。 主要工作在网络层&#xff0c;针对IP数据包。体现在对包内的IP地址、端口、协议等信息的处理上。 iptables是Linux系统防火墙的一种&#xff0c;是Ce…

SpringBoot【开发实用篇】---- 整合第三方技术(消息)

SpringBoot【开发实用篇】---- 整合第三方技术&#xff08;消息&#xff09; 消息的概念Java处理消息的标准规范JMSAMQPMQTTKafka 购物订单发送手机短信案例订单业务短息处理业务 SpringBoot整合ActiveMQ安装整合 SpringBoot整合RabbitMQ安装整合&#xff08;direct模型&#x…

【操作系统复习】第7章 输入/输出系统1

I/O系统管理的主要对象 ➢ I/O设备和对应的设备控制器 I/O系统的主要任务 ➢ 完成用户提出的I/O请求 ➢ 提高I/O速率 ➢ 改善I/O设备的利用率 I/O系统的上、下接口 ➢ I/O系统接口&#xff08;上接口&#xff09; ➢ 软件/硬件接口&#xff08;下接口&#xff09…

实验三 传感器目标识别

【实验目的】 1、了解环境感知传感器目标识别的目的和方法&#xff0c; 掌握MATLAB中的目标检测方法。 2、了解MATLAB的目标检测器和检测函数&#xff0c;掌握车辆识别、行人识别、交通标志识别和道路识别等目标识别方法。 【实验性质】 验证性实验。 【实验要求】 MATLAB 202…

Kubernetes实战入门

文章目录 一、组件介绍&#xff08;一&#xff09;master主控节点&#xff08;二&#xff09;node工作节点 二、k8s核心概念&#xff08;一&#xff09;pod&#xff08;二&#xff09;controller&#xff08;三&#xff09;service 三、搭建k8s集群&#xff08;一&#xff09;基…

6.1 Python面向对象设计及应用

1 类和对象 对象是具有某些特性和功能的具体事物的抽象。每个对象都具有描述其特征的属性及附属于它的行为。如&#xff1a;一个人有姓名、性别、身高、体重等特征描述&#xff0c;也有走路、说话、学习、开车等行为。 每个对象都有一个类&#xff0c;类是创建对象实例的模板&…

基于springboot家具商城系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SpringBoot 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 前言 基于springboot家具商…

js数组排序的两种方法

1. 冒泡排序 原理&#xff1a;一次比较两个相邻的数&#xff0c;如果不符合规则互换位置&#xff0c;一次比较就能够将最大或最小的值放在数组最后一位继续对除【最后一位】之外的所有元素重复上述过程。 let arr [22,1,43,12,75,32]; for(let i 0; i < arr.length - 1;…

MySQL一条查询语句是怎么执行的?MySQL 的架构是什么样子?

先谈谈MySQL的架构&#xff0c;这样自然就搞清楚一条语句是怎么执行的了 首先&#xff0c;MySQL分为客户端&#xff0c;服务端&#xff0c;存储引擎 客户端&#xff1a; ● Java程序啊&#xff0c;可视化连接工具 Navicat啊等等&#xff0c;就是客户端&#xff1b; 服务端&…

Vivado 下 IP核 之ROM 读写

目录 Vivado 下 IP核 之ROM 读写 1、实验简介 2、ROM IP 核简介 3、ROM IP 核配置 3.1、创建 ROM 初始化文件 3.2、单端口 ROM 的配置 3.3、双端口 ROM 的配置 3.4、ROM IP 核的调用 &#xff08;1&#xff09;ROM 顶层模块代码 &#xff08;2&#xff09;ROM IP 核仿…

lua-5.3.6源码安装

参考博客有https://blog.csdn.net/m0_53157173/article/details/124653430和http://blog.chinaunix.net/uid-14824714-id-3125340.html。 https://www.lua.org/download.html下载网址。点击当前网址中的“download”超链接可以下载以前的版本。 cat /etc/redhat-release看一下…