RTX线程通信之——线程标志

Posted 玖道

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RTX线程通信之——线程标志相关的知识,希望对你有一定的参考价值。

Thread Flags

In a real application, we need to be able to communicate between threads in order to make an application useful. To this end, a typical RTOS supports several different communication objects which can be used to link the threads together to form a meaningful program. The CMSIS-RTOS2 API supports inter-thread communication with thread and event flags, semaphores, mutexes, mailboxes and message queues. In the first section, the key concept was concurrency. In this section, the key concept is synchronizing the activity of multiple threads.

在实际应用中,多个线程之间往往需要相互通信,来更好地实现产品功能。RTX提供了以下几个对象,用来实现线程间通信。

  • Thread Flags: 线程标志
  • Event Flags : 事件标志
  • semaphores: 信号量
  • mutexes: 互斥锁
  • mailboxes: 邮箱(或者内存池,memory pool)
  • message queues: 消息队列

不了解这些概念?没关系,都会有的,都会讲到的~

本文就先拿线程标志开席吧,让大家尝尝鲜~

概念

Keil RTX5 supports up to thirty two thread flags for each thread. These thread flags are stored in the thread control block. It is possible to halt the execution of a thread until a particular thread flag or group of thread flags are set by another thread in the system.

RTX中每个线程可拥有高达32个线程标志,这些线程标志存放在线程控制块中,其余线程可以通过设置这些Thread Flags来中断该线程的执行。当某个执行线程调用osThreadFlagsWait()时,该线程会被阻塞,并进入wait_event状态。若其他线程将所选(specified)的标志中的某个或全部Thread Flags置位,则该线程将会变为就绪态。

Thread Flags are a more specialized version of the Event Flags. See Event Flags. While Event Flags can be used to globally signal a number of threads, thread flags are only send to a single specific thread. Every thread instance can receive thread flags without any additional allocation of a thread flags object.

抓重点,线程标志和事件标志的区别:

  • 线程标志只能发送给单个指定线程。
  • 事件标志可以发送给多个线程。

RTX线程标志API

函数

osThreadFlagsSet

uint32_t osThreadFlagsSet(osThreadId_t thread_id, uint32_t flags)
// 输入
* thread_id : 线程ID
* flags: 设置多个线程标志
// 输出
* 返回设置后的线程标志,或者错误码(最高位被置位)

osThreadFlagsClear

uint32_t osThreadFlagsClear(uint32_t flags)
// 输入
* flags : 线程标志
// 输出
* 清除前的线程标志或者错误码(如果最高位被置位)

osThreadFlagsGet

uint32_t osThreadFlagsGet(void)
// 输入
*// 输出
* 当前线程标志

osThreadFlagsWait

uint32_t  osThreadFlagsWait (uint32_t flags,uint32_t options,uint32_t timeout)     
// 输入
* flags : 等待的线程标志,选定等待的标志位集合,比如1U表示只有标志0,3U则表示选中标志0和标志1
* options : 标志选项
* timeout : 超时设定
// 输出
* 清除之前的线程标志,注意该函数返回时会自动清除等待线程标志(除非选项设定为osFlagsNoClear)

ps: 关于标志,RTX提供的32个线程标志中的任意几个,随你选。具体选哪几个,通过一个类型为uint32_t的数来指定。比如0x00000001U,尾部的U表示这是无符号数,1表示最低位,即选中标志0,同理0x00000003U,最后两位为1,表示选中标志0标志1,以此类推。

线程标志模式共有三种:

  • osFlagsWaitAny: 任何一个选中标志被置位,线程变为就绪态。
  • osFlagsWaitAll: 所有选中标志位被置位,线程变为就绪态。
  • osFlagsNoClear : 不要清除指定等待的标志位(因为函数默认返回时会自动清除所有标志位)
// 官方案例
#include "cmsis_os2.h"
 
void Thread (void* arg) 
  ;
  osThreadFlagsWait(0x00000001U, osFlagsWaitAny, osWaitForever); // Wait forever until thread flag 1(个人认为该为0) is set.
  ;
  osThreadFlagsWait(0x00000003U, osFlagsWaitAny, osWaitForever); // Wait forever until either thread flag 0 or 1 is set.
  ;
  osThreadFlagsWait(0x00000003U, osFlagsWaitAll, 10U); // Wait for 10 timer ticks until thread flags 0 and 1 are set. Timeout afterwards.
  ;
  osThreadFlagsWait(0x00000003U, osFlagsWaitAll | osFlagsNoClear, osWaitForever); // Same as the above, but the flags will not be cleared.

任何线程都可以将其他线程的标志位置位,线程本身只能复位自己的标志位。

#include "cmsis_os2.h"
 
osThreadId_t tid;
uint32_t     flagsX;
uint32_t     flags;
 
void threadX (void *argument) 
  osDelay(1U);
  for (;;) 
    flagsX = osThreadFlagsWait(0x0001U, osFlagsWaitAny, osWaitForever); /* B */
  

 
void app_main (void *argument) 
  tid = osThreadNew(threadX, NULL, NULL);
  flags = osThreadFlagsSet(tid, 0x0002U); /* A */
  osDelay(2U);
  flags = osThreadFlagsSet(tid, 0x0005U); /* C */
  osDelay(2U);
  for(;;);

案例:LED灯同步闪亮

下面通过一个具体例子来进行巩固线程标志:基本思路是我们让LED1灯跟随LED2灯闪亮。实现方法是让LED2线程负责置位LED1的线程标志。

// green 线程函数
void led2(void* arg)
     while(1)
        LED2(ON);
        osThreadFlagSet(red,0x01);  // 置位red灯的线程标志0
        osDelay(500);               // 这里延时必须有
        LED2(OFF);
        osThreadFlagSet(red,0x01);  // 由于red灯的线程标志0会自动清零,所以需要重新置位
        osDelay(500);
     


static const osThreadAttr_t ThreadAttr_LED2 = 
	.name = "LED2",
	.priority = osPriorityNormal,
;
// red 线程函数
void led1(void* arg)
    while(1)
        osThreadFlagsWait(0x01,osWaitForever);  // 无限等待线程标志0被置位,函数返回时会自动清除线程标志0
        LED1(ON);
        osThreadFlagsWait(0x01,osWaitForever);
        LED1(OFF);
    


static const osThreadAttr_t ThreadAttr_LED1 = 
	.name = "LED1",
	.priority = osPriorityAboveNormal,    // 注意LED1的优先级必须高于LED2,才能实现闪烁同步,想想为什么?
;

小结

本文主要介绍了线程标志的基本知识,重点包括:

  • 线程标志的概念
  • RTX中线程标志的函数接口
  • 线程标志编程的步骤

深入了解,请参考官方文档~

参考资料

☞官方资料:线程标志

以上是关于RTX线程通信之——线程标志的主要内容,如果未能解决你的问题,请参考以下文章

RTX线程通信之——线程标志

RTX线程通信之——线程标志

RTX线程通信之——消息队列

RTX线程通信之——消息队列

RTX线程通信之——互斥锁

RTX线程通信之——互斥锁