嵌入式系统应用-第四章操作系统(rt_thread)内核kernel 下

4.3 线程通讯

4.3.1 邮箱

邮箱服务是实时操作系统中一种典型的线程间通信方法。举一个简单的例子,有两个线程,线程 1 检测按键状态并发送,线程 2 读取按键状态并根据按键的状态相应地改变 LED 的亮灭。这里就可以使用邮箱的方式进行通信,线程 1 将按键的状态作为邮件发送到邮箱,线程 2 在邮箱中读取邮件获得按键状态并对LED 执行亮灭操作。

这里的线程1 也可以扩展为多个线程。例如,共有三个线程,线程1 检测并发送按键状态,线程2 检测并发送ADC 采样信息,线程3 则根据接收的信息类型不同,执行不同的操作。

RT-Thread 操作系统的邮箱用于线程间通信,特点是开销比较低,效率较高。邮箱中的每一封邮件只能容纳固定的4 字节内容(针对32 位处理系统,指针的大小即为4 个字节,所以一封邮件恰好能够容纳一个指针)。典型的邮箱也称作交换消息,如下图所示,线程或中断服务例程把一封4 字节长度的邮件发送到邮箱中,而一个或多个线程可以从邮箱中接收这些邮件并进行处理。

在这里插入图片描述
对一个邮箱的操作包含:创建/ 初始化邮箱、发送邮件、接收邮件、删除/ 脱离邮箱。

创建和删除邮箱

rt_mailbox_t rt_mb_create (const char* name, rt_size_t size, rt_uint8_t flag);

rt_err_t rt_mb_delete (rt_mailbox_t mb);

初始化和脱离邮箱

rt_err_t rt_mb_init(rt_mailbox_t mb,const char* name,void* msgpool,rt_size_t size,rt_uint8_t flag)rt_err_t rt_mb_detach(rt_mailbox_t mb);

发送邮件

rt_err_t rt_mb_send (rt_mailbox_t mb, rt_uint32_t value);

rt_err_t rt_mb_send_wait (rt_mailbox_t mb,rt_uint32_t value,rt_int32_t timeout);

接受邮件

rt_err_t rt_mb_recv (rt_mailbox_t mb, rt_uint32_t* value, rt_int32_t timeout);

案例:按键发送邮箱,控制灯亮和灯灭

4.3.2 队列

消息队列是另一种常用的线程间通讯方式,是邮箱的扩展。可以应用在多种场合:线程间的消息交换、使用串口接收不定长数据等。消息队列能够接收来自线程或中断服务例程中不固定长度的消息,并把消息缓存在自己的内存空间中。其他线程也能够从消息队列中读取相应的消息,而当消息队列是空的时候,可以挂起读取线程。当有新的消息到达时,挂起的线程将被唤醒以接收并处理消息。消息队列是一种异步的通信方式。

消息队列能够接收来自线程或中断服务例程中不固定长度的消息,并把消息缓存在自己的内存空间中。其他线程也能够从消息队列中读取相应的消息,而当消息队列是空的时候,可以挂起读取线程。当有新的消息到达时,挂起的线程将被唤醒以接收并处理消息。消息队列是一种异步的通信方式。

在这里插入图片描述
创建和删除消息队列

rt_mq_t rt_mq_create(const char* name, rt_size_t msg_size,rt_size_t max_msgs, rt_uint8_t flag);
rt_err_t rt_mq_delete(rt_mq_t mq);

初始化和脱离消息队列

rt_err_t rt_mq_init(rt_mq_t mq, const char* name,void *msgpool, rt_size_t msg_size,rt_size_t pool_size, rt_uint8_t flag);
rt_err_t rt_mq_detach(rt_mq_t mq);

发送和等待
rt_mq_send_wait() 与rt_mq_send() 的区别在于有等待时间,如果消息队列已经满了,那么发送线程将根据设定的timeout 参数进行等待。如果设置的超时时间到达依然没有空出空间,这时发送线程将被唤醒并返回错误码。下表描述了该函数的输入参数与返回值:
发送紧急消息的过程与发送消息几乎一样,唯一的不同是,当发送紧急消息时,从空闲消息链表上取下来的消息块不是挂到消息队列的队尾,而是挂到队首,这样,接收者就能够优先接收到紧急消息,从而及时进行消息处理

rt_err_t rt_mq_send (rt_mq_t mq, void* buffer, rt_size_t size);
rt_err_t rt_mq_send_wait(rt_mq_t mq,const void *buffer,rt_size_t size,rt_int32_t timeout);
rt_err_t rt_mq_urgent(rt_mq_t mq, void* buffer, rt_size_t size);

接受消息

rt_err_t rt_mq_recv (rt_mq_t mq, void* buffer,rt_size_t size, rt_int32_t timeout);

4.3.3 信号

信号(又称为软中断信号),在软件层次上是对中断机制的一种模拟,在原理上,一个线程收到一个信号与处理器收到一个中断请求可以说是类似的。
信号在RT-Thread 中用作异步通信,POSIX 标准定义了sigset_t 类型来定义一个信号集,然而sigset_t类型在不同的系统可能有不同的定义方式,在RT-Thread 中,将sigset_t 定义成了unsigned long 型,并命名为rt_sigset_t,应用程序能够使用的信号为SIGUSR1(10)和SIGUSR2(12)。
信号本质是软中断,用来通知线程发生了异步事件,用做线程之间的异常通知、应急处理。一个线程不必通过任何操作来等待信号的到达,事实上,线程也不知道信号到底什么时候到达,线程之间可以互相通过调用rt_thread_kill() 发送软中断信号。收到信号的线程对各种信号有不同的处理方法,处理方法可以分为三类:
第一种是类似中断的处理程序,对于需要处理的信号,线程可以指定处理函数,由该函数来处理。
第二种方法是,忽略某个信号,对该信号不做任何处理,就像未发生过一样。
第三种方法是,对该信号的处理保留系统的默认值。
在这里插入图片描述
相关的操作函数
在这里插入图片描述

rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t[] handler);
void rt_signal_mask(int signo);
void rt_signal_unmask(int signo);
int rt_thread_kill(rt_thread_t tid, int sig);
int rt_signal_wait(const rt_sigset_t *set,rt_siginfo_t[] *si, rt_int32_t timeout);

4.4 软定时器

定时器,是指从指定的时刻开始,经过一定的指定时间后触发一个事件,例如定个时间提醒第二天能够按时起床。定时器有硬件定时器和软件定时器之分:
1)硬件定时器是芯片本身提供的定时功能。一般是由外部晶振提供给芯片输入时钟,芯片向软件模块
提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断。硬件定时器的精
度一般很高,可以达到纳秒级别,并且是中断触发方式。
2)软件定时器是由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不
受数目限制的定时器服务。

在这里插入图片描述

RT-Thread 操作系统提供软件实现的定时器,以时钟节拍(OS Tick)的时间长度为单位,即定时数值必须是OS Tick 的整数倍,例如一个OS Tick 是10ms,那么上层软件定时器只能是10ms,20ms,100ms等,而不能定时为15ms。RT-Thread 的定时器也基于系统的节拍,提供了基于节拍整数倍的定时能力。

RT-Thread 的定时器提供两类定时器机制:第一类是单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动停止。第二类是周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动的停止,否则将永远持续执行下去。

RT-Thread 定时器分为 HARD_TIMER 与 SOFT_TIMER,可以设置为单次定时与周期定时,这些属性均可在创建 / 初始化定时器时设置;而如果没有设置 HARD_TIMER 或 SOFT_TIMER,则默认使用HARD_TIMER。

HARD_TIMER 模式的定时器超时函数在中断上下文环境中执行,此模式在定时器初始化时指定。在中断上下文环境中执行时,对于超时函数的要求与中断服务例程的要求相同:执行时间应该尽量短,执行时不应导致当前上下文挂起。

SOFTTIMER 模式的定时器超时函数在系统的timer 线程上下文中执行(即RT-Thread 启动后会创建一个timer 线程)。通过宏定义RT_USING_TIMER_SOFT 来决定是否启用该模式。当启用SOFTTIMER 模式后,我们可以在定时器初始化时指定定时器工作在SOFTTIMER 模式
在这里插入图片描述

4.4.1 超时函数创建

超时函数就是定时器计时超过指定的时间时候,系统计时的时间。

#include <rtthread.h>
/* 定 时 器 的 控 制 块 */
static rt_timer_t timer1;
static rt_timer_t timer2;
static int cnt = 0;

/* 定 时 器 1 超 时 函 数 */
static void timeout1(void *parameter)
{
rt_kprintf("periodic timer is timeout %d\n", cnt);
/* 运 行 第 10 次, 停 止 周 期 定 时 器 */
if (cnt++>= 9)
{
rt_timer_stop(timer1);
rt_kprintf("periodic timer was stopped! \n");
}
}

4.4.2 定时器创建

定时器动态创建

int timer_sample(void)
{
	/* 创 建 定 时 器 1 周 期 定 时 器 */
	timer1 = rt_timer_create("timer1", timeout1,
	RT_NULL, 10,RT_TIMER_FLAG_PERIODIC);
	/* 启 动 定 时 器 1 */
	if (timer1 != RT_NULL)
	rt_timer_start(timer1);
	/* 创 建 定 时 器 2 单 次 定 时 器 */
	timer2 = rt_timer_create("timer2", timeout2,
	RT_NULL, 30,
	RT_TIMER_FLAG_ONE_SHOT);
	/* 启 动 定 时 器 2 */
	if (timer2 != RT_NULL)
	rt_timer_start(timer2);
	return 0;
}

定时器静态创建

4.4.2 定时器总结

周期性定时器 1 的超时函数,每 5 个 OS Tick 运行 1 次,共运行 5 次(5 次后调用 rt_timer_stop 使定时器 1 停止运行);单次定时器 2 的超时函数在第 15 个 OS Tick 时运行一次。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HHONGQI123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值