|
【RT-Thread读书笔记】12. RT-Thread 学习21-23章读后感
[复制链接]
本帖最后由 传媒学子 于 2019-5-11 23:04 编辑
事件集合用32位无符号整型变量来表示,每一位代表一个事件,线程通过“逻辑与”或“逻辑或”与一个或多个事件建立关联,形成一个事件集。事件的“逻辑或”也称作是独立型同步,指的是线程感兴趣的所有事件任一件发生即可被唤醒;事件“逻辑与”也称为是关联型同步,指的是线程感兴趣的若干事件都发生时才被唤醒。
这个很好理解,需要注意的是RT-Thread的事件仅用于同步,不提供数据传输功能。在RT-Thread实现中,每个线程都拥有一个事件信息标记,它有三个属性,分别是RT_EVENT_FLAG_AND(逻辑与),RT_EVENT_FLAG_OR(逻辑或)以及RT_EVENT_FLAG_CLEAR(清除标记)。当线程等待事件同步时,可以通过32个事件标志和这个事件信息标记来判断当前接收的事件是否满足同步条件。
应用场景
事件可使用于多种场合,它能够在一定程度上替代信号量,用于线程间同步。
事件控制块
- /*
- * event structure
- */
- struct rt_event
- {
- struct rt_ipc_object parent; /**< inherit from ipc_object */
-
- rt_uint32_t set; /**< event set */
- };
- typedef struct rt_event *rt_event_t;
复制代码
事件属于内核对象,也会在自身结构体里面包含一个内核对象类型的成员,通过这个成员可以将事件挂到系统对象容器里面。rt_event对象从rt_ipc_object中派生,由IPC容器管理。
事件函数接口有:rt_event_create(), rt_event_delete(), rt_event_send(), rt_event_recv().
基本概念
定时器有硬件定时器和软件定时器之分:
硬件定时器是芯片本身提供的定时功能。一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断。硬件定时器的精度一般很高,可以达到纳秒级别,并且是中断触发方式。
软件定时器,软件定时器是由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受硬件定时器资源限制的定时器服务,它实现的功能与硬件定时器也是类似的。
RT-Thread操作系统提供软件定时器功能,软件定时器的使用相当于扩展了定时器的数量,允许创建更多的定时业务。
RT-Thread提供的软件定时器支持单次模式和周期模式,单次模式和周期模式的定时时间到之后都会调用定时器的超时函数,用户可以在超时函数中加入要执行的工程代码。
单次模式:当用户创建了定时器并启动了定时器后,定时时间到了,只执行一次超时函数之后就将该定时器删除,不再重新执行。
周期模式:这个定时器会按照设置的定时时间循环执行超时函数,直到用户将定时器删除。
RT-Thread中在rtdef.h中定义了相关的宏定义来选择定时器的工作模式:
RT_TIMER_FLAG_HARD_TIMER 为硬件定时器。
RT_TIMER_FLAG_SOFT_TIMER为软件定时器。
应用场景
硬件定时器数量有限,因此有时需要采用软件定时器,软件定时器的精度不高。但需要注意的是软件定时器的精度是无法和硬件定时器相比的,因为在软件定时器的定时过程中是极有可能被其它的线程所打断,因为软件定时器的线程优先级是RT_TIMER_THREAD_PRIO,默认为4。所以,软件定时器更适用于对时间精度要求不高的线程,一些辅助型的线程。
运行机制
软件定时器是系统资源,在创建定时器的时候会分配一块内存空间。当用户创建并启动一个软件定时器时, RT-Thread会根据当前系统rt_tick时间及用户设置的定时确定该定时器唤醒时间timeout,并将该定时器控制块挂入软件定时器列表rt_soft_timer_list。
在RT-Thread定时器模块中维护着两个重要的全局变量:
1. rt_tick,它是一个32位无符号的变量,用于记录当前系统经过的tick时间,当硬件定时器中断来临时,它将自动增加1。
2.软件定时器列表rt_soft_timer_list。系统新创建并激活的定时器都会以超时时间升序的方式插入到rt_soft_timer_list列表中。系统在定时器线程中扫描rt_soft_timer_list中的第一个定时器,看是否已超时,若已经超时了则调用软件定时器超时函数。 否则出软件定时器线程,因为定时时间是升序插入软件定时器列表的,列表中第一个定时器的定时时间都还没到的话,那后面的定时器定时时间自然没到。
软件定时器的相关函数
rt_timer_create();
- rt_timer_t rt_timer_create(const char *name,
- void (*timeout)(void *parameter),
- void *parameter,
- rt_tick_t time,
- rt_uint8_t flag)
- {
- struct rt_timer *timer;
-
- /* allocate a object */
- timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name);
- if (timer == RT_NULL)
- {
- return RT_NULL;
- }
-
- _rt_timer_init(timer, timeout, parameter, time, flag);
-
- return timer;
- }
复制代码
IPC(Inter-Process Communication,进程间通信)邮箱相比于信号量与消息队列来说,其开销更低,效率更高,所以常用来做线程与线程、中断与线程间的通信。
邮箱中的每一封邮件只能容纳固定的4字节内容(STM32是32位处理系统,一个指针的大小即为4个字节,所以一封邮件恰好能够容纳一个指针),当需要在线程间传递比较大的消息时,可以把指向一个缓冲区的指针作为邮件发送到邮箱中。
通过邮箱,线程或中断服务函数可以将一个或多个邮件放入邮箱中。同样,一个或多个线程可以从邮箱中获得邮件消息。当有多个邮件发送到邮箱时,通常应将先进入邮箱的邮件先传给线程,也就是说,线程先得到的是最先进入邮箱的消息,即先进先出原则(FIFO),同时RT-Thread中的邮箱支持优先级,也就是说在所有等待邮件的线程中优先级最高的会先获得邮件。
邮箱中的每一封邮件只能容纳固定的4字节内容(可以存放地址)。
运作机制
创建邮箱对象时会先创建一个邮箱对象控制块,然后给邮箱分配一块内存空间用来存放邮件,这块内存的大小等于邮件大小(4字节)与邮箱容量的乘积,接着初始化接收邮件和发送邮件在邮箱中的偏移量,接着再初始化消息队列,此时消息队列为空。
RT-Thread操作系统的邮箱对象由多个元素组成,当邮箱被创建时,它就被分配了邮箱控制块:邮箱名称、邮箱缓冲区起始地址、邮箱大小等。同时每个邮箱对象中包含着多个邮件框,每个邮件框可以存放一封邮件;所有邮箱中的邮件框总数即是邮箱的大小,这个大小可在邮箱创建时指定。
非阻塞方式的邮件可以在中断和线程间相互发送,但阻塞方式的邮件只能在线程间发送。
邮箱运行机制:
邮箱控制块:
- /**
- * mailbox structure
- */
- struct rt_mailbox
- {
- struct rt_ipc_object parent; /**< inherit from ipc_object */
-
- rt_uint32_t *msg_pool; /**< start address of message buffer */
-
- rt_uint16_t size; /**< size of message pool */
-
- rt_uint16_t entry; /**< index of messages in msg_pool */
- rt_uint16_t in_offset; /**< input offset of the message buffer */
- rt_uint16_t out_offset; /**< output offset of the message buffer */
-
- rt_list_t suspend_sender_thread; /**< sender thread suspended on this mailbox */
- };
- typedef struct rt_mailbox *rt_mailbox_t;
复制代码
相关函数
rt_mb_create(),rt_mb_delete(), rt_mb_send_wait()(阻塞),rt_mb_send ()(非阻塞),rt_mb_recv().
此内容由EEWORLD论坛网友传媒学子原创,如需转载或用于商业用途需征得作者同意并注明出处
|
|