FreeRTOS的线程间通信

一、分类

FreeRTOS的线程间通信分为这几大类

FreeRTOS的线程间通信_第1张图片

由于我还在学习中,目前显从信号开始记录学习

二、逐块讲解

1、信号(osSignalWait  osSignalSet)

FreeRTOS从V8.2.0版本开始提供任务通知这个功能,每个任务多有一个32位的通知值,任务通知比二进制信号量方式解除阻塞任务速率提升45%,并且无需创建队列更加省内存。

osSignalSet和osSignalWait是CMSIS中的任务通知,在文档cmsis_os.c中定义;
这两个函数接口为我们正常使用中用到的

int32_t osSignalSet (osThreadId thread_id, int32_t signal)这个就是发送信号函数原型

thread_id:是任务句柄,任务通知的目的地任务;

signal:任务通知值,一个事件用一个的值表示,过查看源码可知0~0x80000000(不包括0x80000000)之间的数都可以用来当作信号,0x80000000之所以不能作为信号,是因为 0x80000000 被用来代表信号错误

返回值:成功就返回上一次发送的信号,失败就返回 0x80000000

osEvent osSignalWait (int32_t signals, uint32_t millisec)这个就是接收信号函数原型

signals:就是发送函数装在的信号值

millisec:超时设置

0: 不管有没有成功收到信号都立即返回
osWaitForever:没有收到信号就一直休眠(阻塞)着,直到收到信号为止,不会往下执行。
其它值,比如 100:如果没有收到信号时休眠阻塞 100ms,然后计时到100ms后,就执行判断有无接到通知,有则执行,无则返回超时返回,以此类推,在睡眠100ms,在判断.........
返回值:返回类型为 osEvent 这个结构体类型

上面的API函数接口,我们讲解完了,下面我们进行实际应用举例

eg:

void query_task(void const * argument)
{
    static u8 t = 0;
    while(1)
    {
     if(t>250)
        osSignalSet (dgthy_tasktTaskHandle, 0x01);  //发送通知
     }
}

void dgthy_taskt(void const * argument)
{
    static u8 t = 0;
    osEvent ret; //创建信号   
    while(1)
    { 
        ret = osSignalWait(0x01, 1000);
        if(ret.status == osEventSignal)//如果接收到通知
        {       
            if(ret.value.signals & 0x01)//接收的通知是否为0x01
            {
                printf("这里可以放上你想执行的函数\n");
            }
        }
        else
        {
            printf("超时\n");
        }
        osDelay(100);//这个睡眠时间看你自己任务需求
    }
}

以上就是实际应用的举例

当然如果你有多个信号并发,但是等待接收函数只有一个

osSignalSet (query_tasktTaskHandle, F_SentScrin);

osSignalSet (query_tasktTaskHandle, UART1_RX_EVENT);

osSignalSet (query_tasktTaskHandle, UART2_RX_EVENT);

..............................

ret = osSignalWait(F_SentScrin|UART1_RX_EVENT|UART2_RX_EVENT|f_Rest|flag_FONT|flag_whilet|wgFlag|in1_Flag, 1000);

        if(ret.status == osEventSignal)//如果接收到通知
        {       
            if(ret.value.signals & F_SentScrin)//接收的通知为F_SentScrin
            {
                //处理显示接收数据
            }
            if(ret.value.signals & flag_FONT)//接收的通知为flag_FONT
            {
               //擦除外部flash
            }

             ................

        }

   发送可以多个信号,接收你就可以向上面距离装载异或上多个值,最终在函数执行判断就可以。

任务通知的使用限制条件:

(1)只能有一个任务接收通知事件。

(2)接收通知的任务可以因为等待通知而进入阻塞状态,但是发送通知的任务即便不能立即完成通知发送也不能进入阻塞状态。

通知的发送可以从多个任务或者多个中断中发出,但是等待任务通知函数只能有一个。这个地方可能有的人会理解错误,以为只能有一个等待任务通知,其实这里说的是一个任务中只能有一个等待任务通知函数,其他任务中还可以有等待任务通知函数;

到这里,信号功能作用就到此。

详细可参考这两篇大佬文章:

CMSIS中的FreeRTOS第一篇——任务通知:osSignalSet和osSignalWait_Amos_Lee1987的博客-CSDN博客
【FreeRTOS】FreeRTOS学习笔记(12)— FreeRTOS的线程间通信(CMSIS_API)_51CTO博客_freertos进程间通信

2、信号量

数据的同步是指,如何能通知任务新数据的到来并同时提高CPU的利用率。

假设一个简单的生产者消费者模型--有两个任务,一个任务是数据的生产者(Producer)任务,一个任务是数据的消费者(Consumer)任务,消费者任务等待生产者任务产生的数据并进行处理。按照正常的思路,消费者任务和生产者任务会轮流执行,但是如果在消费者任务执行的时候数据还没有产生的话,消费者任务的运行就是无用的,会降低CPU的使用效率。为此,FreeRTOS引入了信号量(Semaphore)概念,通过信号量的同步机制可以使消费者任务在数据还没到达的时候进入阻塞状态,并让出CPU资源给其他任务。信号量是一种同步机制,可以起到消息通知和提高CPU利用率的作用。

信号量又分为二进制信号量(binary semaphore)计数信号量

二进制信号量中信号量的数目最多为1,即最多只能通知解锁一个任务;

计数信号量中信号量的数目可以自定义设定为多个,即可以通知解锁多个任务

伪代码

SemaphoreHandle_t   xBinarySemaphore ;//首先申明个信号量的全局变量

int main(void)
{
    ...
    xBinarySemaphore = xSemaphoreCreateBinary();//main函数中创建信号量
    ...
}

void Task_Producer(void const * argument)
{
    //生产者任务负责生产数据并给予信号量
    for( ;; )
    {
        ...
        if(true==produce_some_data())
        {
           xSemaphoreGive(xBinarySemaphore);//给予信号量
        }
        ...
    } 
}

void Task_Consumer(void const * argument)
{
    //消费者任务等待获取信号量并进行数据处理,有在进行下面数据处理
    for( ;; )
    {
        xSemaphoreTake( xBinarySemaphore, portMAX_DELAY );//
        //下面是对数据的处理函数
        ...   
    }    
}

以上就是二值信号量的代码

二进制信号量适用于在数据产生的频率比较低的场合。如果数据产生的频率较高,因为信号量最多只能保存一个,所以需要使用计数信号量

计数信号量相关的函数如下

SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount,
UBaseType_t uxInitialCount );信号,更多产生的数据将会直接被忽略抛弃。对此,我们需要使用计数信号量

  • uxMaxCount 计数信号量包含信号量的最大值
  • uxInitialCount 计数信号量中信号量的初始值

计数信号量创建好对信号量的操作函数和二进制信号量一样,所以应用代码参照二进制信号量

伪代码

SemaphoreHandle_t   xCountingSemaphore;/* 计数型信号量句柄 */

int main( void )

{

        ................

        xCountingSemaphore = xSemaphoreCreateCounting(3, 0);

        ................

}

void Task_Producer(void const * argument)
{
    //生产者任务负责生产数据并给予信号量
    for( ;; )
    {
        ...
        if(true==produce_some_data())
        {

            for(i = 0;i<4;i++)
                xSemaphoreGive(xCountingSemaphore);//给予信号量
        }
       osDelay(10); 
    } 
}

void Task_Consumer(void const * argument)
{
    //消费者任务等待获取信号量并进行数据处理,有在进行下面数据处理
    for( ;; )
    {
         xSemaphoreTake( xCountingSemaphore, portMAX_DELAY );
        //下面是对数据的处理函数
        ...   
    }    
}

以上就是信号量的使用用法

你可能感兴趣的:(原理,单片机,嵌入式硬件)