IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> RT-Thread-学习分析 -> 正文阅读

[嵌入式]RT-Thread-学习分析

RT-Thread简介–>>>下载资料来源于RT-Thread文档中心

在这里插入图片描述
具体包括以下部分:

  • 内核层:RT-Thread 内核,是 RT-Thread的核心部分,包括了内核系统中对象的实现,例如多线程及其调度、信号量、邮箱、消息队列、内存管理、定时器等;libcpu/BSP(芯片移植相关文件
    / 板级支持包)与硬件密切相关,由外设驱动和 CPU 移植构成。
  • 组件与服务层:组件是基于 RT-Thread 内核之上的上层软件,例如虚拟文件系统、FinSH 命令行界面、
  • 网络框架、设备框架等。采用模块化设计,做到组件内部高内聚,组件之间低耦合。

内核

1.内核的组成部分、系统如何启动、内存分布情况以及内核配置方法

 RT-Thread 内核架构图

  • 内核库是为了保证内核能够独立运行的一套小型的类似 C 库的函数实现子集。
  • 实时内核的实现包括:对象管理、线程管理及调度器、线程间通信管理、时钟管理及内存管理等等,内核最小的资源占用情况是 3KB ROM,1.2KB RAM。

线程调度:系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的,包括线程调度器自身。

时钟管理:时钟节拍是 RT-Thread 操作系统中最小的时钟单位。通常使用定时器定时回调函数(即超时函数),完成定时服务。

线程间同步:RT-Thread 采用信号量、互斥量与事件集实现线程间同步。

线程间通信:支持邮箱和消息队列等通信机制。邮件的长度固定为 4 字节大小;消息队列能够接收不固定长度的消息,并把消息缓存在自己的内存空间中。邮箱效率较消息队列更为高效。
内存管理:支持静态内存池管理及动态内存堆管理。
I/O 设备管理:将 PIN、I2C、SPI、USB、UART 等作为外设设备,统一通过设备注册完成。当设备事件触发时,由驱动程序通知给上层的应用程序。

2.RT-Thread 启动流程

在这里插入图片描述
rtthread_startup() 函数是 RT-Thread 规定的统一启动入口。

MDK 的扩展功能 $Sub$$ 和 $Super$$
startup_stm32f103xe.s 开始-> components.c
根据宏跳转到对应函数,如MDK:  int $Sub$$main(void)  ->int rtthread_startup(void)
rtthread_startup:关闭中断 ->板级初始化 -> 打印版本信息-> 定时器初始化->调度器初始化 -> 信号初始化->创建初始化线程(main线程) -> 定时器线程初始化 -> 空闲线程初始化->启动调度器。

启动调度器之前,系统所创建的线程在执行 rt_thread_startup() 后并不会立马运行,它们会处于就绪状态等待系统调度

3.程序内存分布

MCU 包含的存储空间有:片内 Flash 与片内 RAM。Program Size 包含以下几个部分
1)Code:代码段,存放程序的代码部分;
2)RO-data:只读数据段,存放程序中定义的常量;
3)RW-data:读写数据段,存放初始化为非 0 值的全局变量;
4)ZI-data:0 数据段,存放未初始化的全局变量及初始化为 0 的变量
. map 的文件,说明了各个函数占用的尺寸和地址
1)RO Size 包含了 Code 及 RO-data,表示程序占用 Flash 空间的大小;
2)RW Size 包含了 RW-data 及 ZI-data,表示运行时占用的 RAM 的大小;
3)ROM Size 包含了 Code、RO-data 以及 RW-data,表示烧写程序所占用的 Flash 空间的大小;

烧写过程

在这里插入图片描述STM32 在上电启动之后默认从 Flash 启动,启动之后会将 RW 段中的 RW-data(初始化的全局变量)搬运到 RAM 中,但不会搬运 RO 段,即 CPU 的执行代码从 Flash 中读取,另外根据编译器给出的 ZI 地址和大小分配出 ZI 段,并将这块 RAM 区域清零。动态内存堆为未使用的 RAM 空间,应用程序申请和释放的内存块都来自该空间。
一些全局变量则是存放于 RW 段和 ZI 段中,RW 段存放的是具有初始值的全局变量(而常量形式的全局变量则放置在 RO 段中,是只读属性的),ZI 段存放的系统未初始化的全局变量

RT-Thread 自动初始化机制–>此处应更深入了解何处被自动执行

自动初始化机制是指初始化函数不需要被显式调用,只需要在函数定义处通过宏定义的方式进行声明,就会在系统启动过程中被执行
在哪里被调用的呢?
1 INIT_BOARD_EXPORT(fn) 非常早期的初始化,此时调度器还未启动
2 INIT_PREV_EXPORT(fn) 主要是用于纯软件的初始化、没有太多依赖的函数
3 INIT_DEVICE_EXPORT(fn) 外设驱动初始化相关,比如网卡设备
4 INIT_COMPONENT_EXPORT(fn) 组件初始化,比如文件系统或者 LWIP
5 INIT_ENV_EXPORT(fn) 系统环境初始化,比如挂载文件系统
6 INIT_APP_EXPORT(fn) 应用初始化,比如 GUI 应用

4.内核对象模型

RT-Thread 内核采用面向对象的设计思想进行设计,系统级的基础设施都是一种内核对象,例如线程,信号量,互斥量,定时器等。内核对象分为两类:静态内核对象和动态内核对象,静态内核对象通常放在 RW 段和 ZI 段中,在系统启动后在程序中初始化动态内核对象则是从内存堆中创建的,而后手工做初始化。如:静态线程对象thread1 ,线程控制块 thread1 与栈空间 thread1_stack 都是编译时决定的。thread2 是一个动态线程对象运行中用到的空间都是动态分配的
静态对象会占用 RAM 空间,不依赖于内存堆管理器,内存分配时间确定。动态对象则依赖于内存堆管理器,运行时申请 RAM 空间,当对象被删除后,占用的 RAM 空间被释放。

内核对象管理架构

RT-Thread 采用内核对象管理系统来访问 / 管理所有内核对象。不依赖于具体的内存分配方式。
RT-Thread 内核对象包括:线程,信号量,互斥量,事件,邮箱,消息队列和定时器,内存池,设备驱动等。对象容器中包含了每类内核对象的信息,包括对象类型,大小等。对象容器给每类内核对象分配了一个链表,所有的内核对象都被链接到该链表上在这里插入图片描述
RT-Thread 中各类内核对象的派生和继承关系。对于每一种具体内核对象和对象控制块,除了基本结构外,还有自己的扩展属性(私有属性)。每一种具体对象是抽象对象的派生,继承了基本对象的属性并在此基础上扩展了与自己相关的属性。在这里插入图片描述对象管理模块中,定义了通用的数据结构,用来保存各种对象的共同属性,各种具体对象只需要在此基础上加上自己的某些特别的属性,就可以清楚的表示自己的特征。由对象控制块 rt_object 派生出来的有:线程对象、内存池对象、定时器对象、设备对象和 IPC 对象(IPC:进程间通信。此处作用是进行线程间同步与通信);由 IPC 对象派生出信号量、互斥量、事件、邮箱与消息队列、信号等对象。

对象控制块

struct rt_object//内核对象控制块的数据结构:
{
     /* 内核对象名称     */
     char      name[RT_NAME_MAX];
     /* 内核对象类型     */
     rt_uint8_t  type;
     /* 内核对象的参数   */
     rt_uint8_t  flag;
     /* 内核对象管理链表 */
     rt_list_t   list;
};
type ->>>>>  enum rt_object_class_type

如果是静态对象,那么对象类型的最高位将是 1,否则就是动态对象,系统最多能够容纳的对象类别数目是 127 个。

内核对象管理方式

struct rt_object_information
{
     /* 对象类型 */
     enum rt_object_class_type type;
     /* 对象链表 */
     rt_list_t object_list;
     /* 对象大小 */
     rt_size_t object_size;
};

一类对象由一个 rt_object_information 结构体来管理,每一个这类对象的具体实例都通过链表的形式挂接在 object_list 上。而这一类对象的内存块尺寸由 object_size 标识出来,每一类对象的具体实例,他们占有的内存块大小都是相同的

初始化(静态)对象

//在使用一个未初始化的静态对象前必须先对其进行初始化
void rt_object_init(struct  rt_object*  object ,//需要初始化的对象指针,它必须指向具体的对象内存块,而不能是空指针或野指针
                    enum rt_object_class_type  type ,//对象的类型,必须是 rt_object_class_type 枚举类型中列出的除 RT_Object_Class_Static 以外的类型(对于静态对象,或使用 rt_object_init 接口进行初始化的对象,系统会把它标识成 RT_Object_Class_Static 类型)
                    const char* name)//对象的名字。每个对象可以设置一个名字,这个名字的最大长度由 RT_NAME_MAX 指定,并且系统不关心它是否是由’\0’做为终结符
//当调用这个函数进行对象初始化时,系统会把这个对象放置到对象容器中进行管理,即初始化对象的一些参数,然后把这个对象节点插入到对象容器的对象链表中

脱离(静态)对象

调用该接口,可使得一个静态内核对象从内核对象容器中脱离出来,即从内核对象容器链表上删除相应的对象节点。对象脱离后,对象占用的内存并不会被释放。

void rt_object_detach(rt_object_t object);

分配(动态)对象

动态的对象则可以在需要时申请,不需要时释放出内存空间给其他应用使用。

rt_object_t rt_object_allocate(enum  rt_object_class_type type ,//只能是 rt_object_class_type 中除 RT_Object_Class_Static 以外的类型。并且经过这个接口分配出来的对象类型是动态的
                               const  char*  name)//对象的名字
//分配成功:对象句柄 失败:RT_NULL

删除(动态)对象

void rt_object_delete(rt_object_t object);//object	对象的句柄

辨别(静态/动态)对象

rt_err_t rt_object_is_systemobject(rt_object_t object);

RT-Thread 内核配置示例

通过修改工程目录下的 rtconfig.h 文件来进行,在实际应用中,系统配置文件 rtconfig.h 是由配置工具自动生成的,无需手动更改。

-1.RT-Thread 内核部分:

/* 表示内核对象的名称的最大长度,若代码中对象名称的最大长度大于宏定义的长度,多余的部分将被截掉。*/
#define RT_NAME_MAX 8
/* 字节对齐时设定对齐的字节个数。常使用 ALIGN(RT_ALIGN_SIZE) 进行字节对齐。*/
#define RT_ALIGN_SIZE 4
/* 定义系统线程优先级数;通常用 RT_THREAD_PRIORITY_MAX-1 定义空闲线程的优先级 */
#define RT_THREAD_PRIORITY_MAX 32
/* 定义时钟节拍,为 100 时表示 100 个 tick 每秒,一个 tick 就为 10ms */
#define RT_TICK_PER_SECOND 100
/* 检查栈是否溢出,未定义则关闭 */
#define RT_USING_OVERFLOW_CHECK
/* 定义该宏开启 debug 模式,未定义则关闭 */
#define RT_DEBUG
/* 开启 debug 模式时:该宏定义为 0 时表示关闭打印组件初始化信息,定义为 1 时表示启用 */
#define RT_DEBUG_INIT 0
/* 开启 debug 模式时:该宏定义为 0 时表示关闭打印线程切换信息,定义为 1 时表示启用 */
#define RT_DEBUG_THREAD 0
/* 定义该宏表示开启钩子函数的使用,未定义则关闭 */
#define RT_USING_HOOK
/* 定义了空闲线程的栈大小 */
#define IDLE_THREAD_STACK_SIZE 256

-2.线程间同步与通信部分

/* 定义该宏可开启信号量的使用,未定义则关闭 */
#define RT_USING_SEMAPHORE
/* 定义该宏可开启互斥量的使用,未定义则关闭 */
#define RT_USING_MUTEX
/* 定义该宏可开启事件集的使用,未定义则关闭 */
#define RT_USING_EVENT
/* 定义该宏可开启邮箱的使用,未定义则关闭 */
#define RT_USING_MAILBOX
/* 定义该宏可开启消息队列的使用,未定义则关闭 */
#define RT_USING_MESSAGEQUEUE
/* 定义该宏可开启信号的使用,未定义则关闭 */
#define RT_USING_SIGNALS

-3.内存管理部分

/* 开启静态内存池的使用 */
#define RT_USING_MEMPOOL
/* 定义该宏可开启两个或以上内存堆拼接的使用,未定义则关闭 */
#define RT_USING_MEMHEAP
/* 开启小内存管理算法 */
#define RT_USING_SMALL_MEM
/* 关闭 SLAB 内存管理算法 */
/* #define RT_USING_SLAB */
/* 开启堆的使用 */
#define RT_USING_HEAP

-4.内核设备对象

/* 表示开启了系统设备的使用 */
#define RT_USING_DEVICE
/* 定义该宏可开启系统控制台设备的使用,未定义则关闭 */
#define RT_USING_CONSOLE
/* 定义控制台设备的缓冲区大小 */
#define RT_CONSOLEBUF_SIZE 128
/* 控制台设备的名称 */
#define RT_CONSOLE_DEVICE_NAME "uart1"

-5.自动初始化方式

/* 定义该宏开启自动初始化机制,未定义则关闭 */
#define RT_USING_COMPONENTS_INIT
/* 定义该宏开启设置应用入口为 main 函数 */
#define RT_USING_USER_MAIN
/* 定义 main 线程的栈大小 */
#define RT_MAIN_THREAD_STACK_SIZE 2048

-6.FinSH

MSH:指令描述功能

/* 定义该宏可开启系统 FinSH 调试工具的使用,未定义则关闭 */
#define RT_USING_FINSH
/* 开启系统 FinSH 时:将该线程名称定义为 tshell */
#define FINSH_THREAD_NAME "tshell"
/* 开启系统 FinSH 时:使用历史命令 */
#define FINSH_USING_HISTORY
/* 开启系统 FinSH 时:对历史命令行数的定义 */
#define FINSH_HISTORY_LINES 5
/* 开启系统 FinSH 时:定义该宏开启使用 Tab 键,未定义则关闭 */
#define FINSH_USING_SYMTAB
/* 开启系统 FinSH 时:定义该线程的优先级 */
#define FINSH_THREAD_PRIORITY 20
/* 开启系统 FinSH 时:定义该线程的栈大小 */
#define FINSH_THREAD_STACK_SIZE 4096
/* 开启系统 FinSH 时:定义命令字符长度 */
#define FINSH_CMD_SIZE 80
/* 开启系统 FinSH 时:定义该宏开启 MSH 功能 */
#define FINSH_USING_MSH
/* 开启系统 FinSH 时:开启 MSH 功能时,定义该宏默认使用 MSH 功能 */
#define FINSH_USING_MSH_DEFAULT
/* 开启系统 FinSH 时:定义该宏,仅使用 MSH 功能 */
#define FINSH_USING_MSH_ONLY

-7.关于 MCU

/* 定义该工程使用的 MCU 为 STM32F103ZE;系统通过对芯片类型的定义,来定义芯片的管脚 */
#define STM32F103ZE
/* 定义时钟源频率 */
#define RT_HSE_VALUE 8000000
/* 定义该宏开启 UART1 的使用 */
#define RT_USING_UART1

-0.常见宏定义说明

//static 关键字的作用是令函数只能在当前的文件中使用;inline 表示内联,用 static 修饰后在调用函数时会建议编译器进行内联展开。
#define rt_inline                   static __inline
//作用是向编译器说明这段代码有用,即使函数中没有调用也要保留编译。例如 RT-Thread 自动初始化功能使用了自定义的段,使用 RT_USED 会将自定义的代码段保留。
#define RT_USED                     __attribute__((used))
//表示函数或变量可能不使用,这个属性可以避免编译器产生警告信息。
#define RT_UNUSED                   __attribute__((unused))
//常用于定义函数,编译器在链接函数时会优先链接没有该关键字前缀的函数,如果找不到则再链接由 weak 修饰的函数。
#define RT_WEAK                     __weak
//作用是在给某对象分配地址空间时,将其存放的地址按照 n 字节对齐,这里 n 可取 2 的幂次方。
#define ALIGN(n)                    __attribute__((aligned(n)))
//将 size 提升为 align 定义的整数的倍数,例如,RT_ALIGN(13,4) 将返回 16。
#define RT_ALIGN(size, align)      (((size) + (align) - 1) & ~((align) - 1))

5.线程管理

例如让嵌入式系统执行这样的任务,系统通过传感器采集数据,并通过显示屏将数据显示出来,在多线程实时系统中,可以将这个任务分解成两个子任务,如下图所示,一个子任务不间断地读取传感器数据,并将数据写到共享内存中,另外一个子任务周期性的从共享内存中读取数据,并将传感器数据输出到显示屏上。
在这里插入图片描述
线程是实现任务的载体,它是 RT-Thread 中最基本的调度单位,它描述了一个任务执行的运行环境,也描述了这个任务所处的优先等级.
线程运行时,它会认为自己是以独占 CPU 的方式在运行,线程执行时的运行环境称为上下文,具体来说就是各个变量和数据,包括所有的寄存器变量、堆栈、内存信息等。

线程管理的功能特点

线程管理的主要功能是对线程进行管理和调度,系统中总共存在两类线程,分别是系统线程用户线程,系统线程是由 RT-Thread 内核创建的线程,用户线程是由应用程序创建的线程
每个线程都有重要的属性,如线程控制块、线程栈、入口函数等。
在这里插入图片描述
RT-Thread 的线程调度器是抢占式的,当一个运行着的线程使一个比它优先级高的线程满足运行条件,高优先级的线程立刻得到了 CPU 的使用权。
如果是中断服务程序使一个高优先级的线程满足运行条件,中断完成时,被中断的线程挂起,优先级高的线程开始运行。
当调度器调度线程切换时,先将当前线程上下文保存起来,当再切回到这个线程时,线程调度器将该线程的上下文信息恢复。

线程的工作机制

-1.线程控制块

线程控制块由结构体 struct rt_thread 表示,线程控制块是操作系统用于管理线程的一个数据结构,它会存放线程的一些信息,例如优先级、线程名称、线程状态等,也包含线程与线程之间连接用的链表结构,线程等待事件集合等

/* 线程控制块 */
struct rt_thread
{
    /* rt 对象 */
    char        name[RT_NAME_MAX];     /* 线程名称 */
    rt_uint8_t  type;                   /* 对象类型 */
    rt_uint8_t  flags;                  /* 标志位 */
    rt_list_t   list;                   /* 对象列表 */
    rt_list_t   tlist;                  /* 线程列表 */
    /* 栈指针与入口指针 */
    void       *sp;                      /* 栈指针 */
    void       *entry;                   /* 入口函数指针 */
    void       *parameter;              /* 参数 */
    void       *stack_addr;             /* 栈地址指针 */
    rt_uint32_t stack_size;            /* 栈大小 */
    /* 错误代码 */
    rt_err_t    error;                  /* 线程错误代码 */
    rt_uint8_t  stat;                   /* 线程状态 */
    /* 优先级 */
    rt_uint8_t  current_priority;    /* 当前优先级 */
    rt_uint8_t  init_priority;        /* 初始优先级 */
    rt_uint32_t number_mask;
    //......
    rt_ubase_t  init_tick;               /* 线程初始化计数值 */
    rt_ubase_t  remaining_tick;         /* 线程剩余计数值 */
    struct rt_timer thread_timer;      /* 内置线程定时器 */
    void (*cleanup)(struct rt_thread *tid);  /* 线程退出清除函数 */
    rt_uint32_t user_data;                      /* 用户数据 */
};

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-10-18 17:33:13  更:2021-10-18 17:34:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:26:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码