FreeRTOS任务通知实验

news2024/10/7 18:27:11

从 V8.2.0 版本开始,FreeRTOS 新增了任务通知这个功能,可以使用任务通 知来代替信号量、消息队列、事件组等这些东西。使用任务通知的话效率会更高。 本章要实现的功能是:使用任务通知方式实现消息队列、二值信号量、计数信号 量、事件标记功能实验。本章分为如下几部分内容: 11.1 任务通知简介 11.2 常用任务通知 API 函数 11.3 硬件设计 11.4 软件设计 11.5 实验现象

11.1 任务通知简介

FreeRTOS 从 V8.2.0 版本开始提供任务通知这个功能,每个任务都有一个 32 位的通知值,在大多数情况下,任务通知可以替代二值信号量、计数信号量、 事件组,也可以替代长度为 1 的队列(可以保存一个 32 位整数或指针值)。 相对于以前使用 FreeRTOS 内核通信的资源,必须创建队列、二进制信号量、 计数信号量或事件组的情况,使用任务通知显然更灵活。按照 FreeRTOS 官方的 说法,使用任务通知比通过信号量等 IPC 通信方式解除阻塞的任务要快 45%, 并且更加省 RAM 内存空间(使用 GCC 编译器,-o2 优化级别),任务通知的使 用无需创建队列。想要使用任务通知,必须将 FreeRTOSConfig.h 中的宏定义 configUSE_TASK_NOTIFICATIONS 设置为 1,其实 FreeRTOS 默认是为 1 的,所 以任务通知是默认使能的。 FreeRTOS 提供以下几种方式发送通知给任务 : ●发送通知给任务,如果有通知未读,不覆盖通知值。 ●发送通知给任务,直接覆盖通知值。 ●发送通知给任务,设置通知值的一个或者多个位,可以当做事件组来使用。 ●发送通知给任务,递增通知值,可以当做计数信号量使用。 通过对以上任务通知方式的合理使用,可以在一定场合下替代 FreeRTOS 的 信号量,队列、事件组等。 当然,凡是都有利弊,不然的话 FreeRTOS 还要内核的 IPC 通信机制干嘛, 消息通知虽然处理更快,RAM 开销更小,但也有以下限制 : ●只能有一个任务接收通知消息,因为必须指定接收通知的任务。 ●只有等待通知的任务可以被阻塞,发送通知的任务,在任何情况下都不会 因为发送失败而进入阻塞态

11.2 常用任务通知 API 函 数

11.2.1.1 xTaskNotifyGive()

xTaskNotifyGive()是一个宏,宏展开是调用函数 xTaskGenericNotify( ( xTaskToNotify ), ( 0 ), eIncrement ,NULL),即向 一个任务发送通知,并将对方的任务通知值加 1。该函数可以作为二值信号量和 计数信号量的一种轻量型的实现,速度更快,在这种情况下对象任务在等待任务 通知的时候应该是使用函数 ulTaskNotifyTake() 而不是 xTaskNotifyWait()。 xTaskNotifyGive()不能在中断里面使用,而是使用具有中断保护功能的 vTaskNotifyGiveFromISR()来代替。该函数的具体说明如下

11.2.1.2 vTaskNotifyGiveFromISR()

vTaskNotifyGiveFromISR()是 vTaskNotifyGive()的中断保护版本。用于在 中断中向指定任务发送任务通知,并更新对方的任务通知值(加 1 操作),在 某些场景中可以替代信号量操作,因为这两个通知都是不带有通知值的。该函数 的具体说明如下

从上面的函数说明我们大概知道 vTaskNotifyGiveFromISR()函数作用,每 次调用该函数都会增加任务的通知值,任务通过接收函数返回值是否大于零,判 断是否获取到了通知,任务通知值初始化为 0,(如果与信号量做对比)则对应 为信号量无效。当中断调用 vTaskNotifyGiveFromISR()通知函数给任务的时候, 任务的通知值增加,使其大于零,使其表示的通知值变为有效,任务获取有效的 通知值将会被恢复

11.2.1.3 xTaskNotify()

FreeRTOS 每个任务都有一个 32 位的变量用于实现任务通知,在任务创建 的时候初始化为 0。这个 32 位的通知值在任务控制块 TCB 里面定义。 xTaskNotify()用于在任务中直接向另外一个任务发送一个事件,接收到该任务 通知的任务有可能解锁。如果你想使用任务通知来实现二值信号量和计数信号 量,那么应该使用更加简单的函数 xTaskNotifyGive(),而不是使用 xTaskNotify(),xTaskNotify()函数在发送任务通知的时候会指定一个通知值, 并且用户可以指定通知值发送的方式。 注意:该函数不能在中断里面使用,而是使用具体中断保护功能的版本函数 xTaskNotifyFromISR()。xTaskNotify()函数的具体说明如下

11.2.1.4 xTaskNotifyFromISR( )

用于在中断中向指定的任务发送一个任务通知,该任务通知是带有通知值并 且用户可以指定通知的发送方式,不返回上一个任务在的通知值。函数的具体说 明如下

11.2.2.1 xTaskNotifyAndQuery()

xTaskNotifyAndQuery()与 xTaskNotify()很像,都是调用通用的任务通知 发送函数 xTaskGenericNotify() 来实现通知的发送,不同的是多了一个附加的 参数 pulPreviousNotifyValue 用于回传接收任务的上一个通知值,函数原型具 体见代码

11.2.2.2 xTaskNotifyAndQueryFromISR()

xTaskNotifyAndQueryFromISR()是 xTaskNotifyAndQuery()的中断版本,用 于向指定的任务发送一个任务通知,并返回对象任务的上一个通知值,该函数也 是一个宏定义,真正实现发送通知的是 xTaskGenericNotifyFromISR()。 xTaskNotifyAndQueryFromISR()函数说明如下。

11.2.3 获取任务通知函数 既然 FreeRTOS 中发送任务的函数有那么多个,那么任务怎么获取到通知 呢?我们说了,任务通知在某些场景可以替代信号量、消息队列、事件等。获取 任务通知函数只能用在任务中,没有带中断保护版本,因此只有两个 API 函数: ulTaskNotifyTake()和 xTaskNotifyWait()。前者是为代替二值信号量和计数信 号量而专门设计的,它和发送通知 API 函数 xTaskNotifyGive()、 vTaskNotifyGiveFromISR()配合使用;后者是全功能版的等待通知,可以根据不 同的参数实现轻量级二值信号量、计数信号量、事件组和长度为 1 的队列。 所有的获取任务通知 API 函数都带有指定阻塞超时时间参数,当任务因为 等待通知而进入阻塞时,用来指定任务的阻塞时间,这些超时机制与 FreeRTOS 的消息队列、信号量、事件等的超时机制一致

11.2.3.1 ulTaskNotifyTake()

ulTaskNotifyTake()作为二值信号量和计数信号量的一种轻量级实现,速度 更快。如果 FreeRTOS 中使用函数 xSemaphoreTake() 来获取信号量,这个时候 则可以试试使用函数 ulTaskNotifyTake()来代替。 对于这个函数,任务通知值为 0,对应信号量无效,如果任务设置了阻塞等 待,任务被阻塞挂起。当其他任务或中断发送了通知值使其不为 0 后,通知变 为有效,等待通知的任务将获取到通知,并且在退出时候根据用户传递的第一个 参数 xClearCountOnExit 选择清零通知值或者执行减一操作。 xTaskNotifyTake()在退出的时候处理任务的通知值的时候有两种方法,一 种是在函数退出时将通知值清零,这种方法适用于实现二值信号量;另外一种是 在函数退出时将通知值减 1,这种方法适用于实现计数信号量。当一个任务使用 其自身的任务通知值作为二值信号量或者计数信号量时,其他任务应该使用函数 xTaskNotifyGive()或者 xTaskNotify( ( xTaskToNotify ), ( 0 ),eIncrement ) 来向其发送信号量。如果是在中断中,则应该使用他们的中断版本函数。该函数 的具体说明如下

11.2.3.2 xTaskNotifyWait()

xTaskNotifyWait()函数用于实现全功能版的等待任务通知,根据用户指定 的参数的不同,可以灵活的用于实现轻量级的消息队列队列、二值信号量、计数 信号量和事件组功能,并带有超时等待。函数的具体说明如下。

11.3整体代码

1.任务通知代替消息队列

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
#include "limits.h"

//任务优先级
#define START_TASK_PRIO        1
//任务堆栈大小    
#define START_STK_SIZE         128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define LED1_TASK_PRIO        2
//任务堆栈大小    
#define LED1_STK_SIZE         50  
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);

//任务优先级
#define Receive1_TASK_PRIO        3
//任务堆栈大小    
#define Receive1_STK_SIZE         512  
//任务句柄
TaskHandle_t Receive1Task_Handler;
//任务函数
void Receive1_task(void *pvParameters);

//任务优先级
#define Receive2_TASK_PRIO        4
//任务堆栈大小    
#define Receive2_STK_SIZE         512  
//任务句柄
TaskHandle_t Receive2Task_Handler;
//任务函数
void Receive2_task(void *pvParameters);

//任务优先级
#define Send_TASK_PRIO        5
//任务堆栈大小    
#define Send_STK_SIZE         512  
//任务句柄
TaskHandle_t SendTask_Handler;
//任务函数
void Send_task(void *pvParameters);


#define  USE_CHAR  0  /* 测试字符串的时候配置为 1 ,测试变量配置为 0  */

/*******************************************************************************
* 函 数 名         : main
* 函数功能           : 主函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
int main()
{
    SysTick_Init(72);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
    LED_Init();
    KEY_Init();
    USART1_Init(115200);
    printf("FreeRTOS任务通知代替消息队列实验\r\n");
    printf("按下KEY_UP或者KEY1进行任务消息通知发送 \n");
    //创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
                             
    //创建LED1任务
    xTaskCreate((TaskFunction_t )led1_task,     
                (const char*    )"led1_task",   
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED1_TASK_PRIO,
                (TaskHandle_t*  )&LED1Task_Handler); 
    
    //创建Receive1任务
    xTaskCreate((TaskFunction_t )Receive1_task,     
                (const char*    )"Receive1_task",   
                (uint16_t       )Receive1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )Receive1_TASK_PRIO,
                (TaskHandle_t*  )&Receive1Task_Handler);
                
    //创建Receive2任务
    xTaskCreate((TaskFunction_t )Receive2_task,     
                (const char*    )"Receive2_task",   
                (uint16_t       )Receive2_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )Receive2_TASK_PRIO,
                (TaskHandle_t*  )&Receive2Task_Handler);
    
    //创建Send任务
    xTaskCreate((TaskFunction_t )Send_task,     
                (const char*    )"Send_task",   
                (uint16_t       )Send_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )Send_TASK_PRIO,
                (TaskHandle_t*  )&SendTask_Handler);
            
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
} 

//LED1任务函数
void led1_task(void *pvParameters)
{
    while(1)
    {
        LED1=0;
        vTaskDelay(200);
        LED1=1;
        vTaskDelay(800);
    }
}

//Receive1任务函数
void Receive1_task(void *pvParameters)
{
    BaseType_t xReturn = pdTRUE;
#if USE_CHAR
  char *r_char;
#else
  uint32_t r_num;
#endif
    
    while(1)
    {
        //获取任务通知 ,没获取到则一直等待
        xReturn=xTaskNotifyWait(0x0,            //进入函数的时候不清除任务bit
                                ULONG_MAX,      //退出函数的时候清除所有的bit
#if USE_CHAR
                                (uint32_t *)&r_char,          //保存任务通知值
#else
                                &r_num,          //保存任务通知值
#endif                        
                                portMAX_DELAY);    //阻塞时间
        if( pdTRUE == xReturn )
#if USE_CHAR
            printf("Receive1_Task 任务通知消息为 %s \n",r_char);                      
#else
            printf("Receive1_Task 任务通知消息为 %d \n",r_num);                      
#endif  
        
    }
}

//Receive2任务函数
void Receive2_task(void *pvParameters)
{
    BaseType_t xReturn = pdTRUE;
#if USE_CHAR
  char *r_char;
#else
  uint32_t r_num;
#endif
    
    while(1)
    {
        //获取任务通知 ,没获取到则一直等待
        xReturn=xTaskNotifyWait(0x0,            //进入函数的时候不清除任务bit
                                ULONG_MAX,      //退出函数的时候清除所有的bit
#if USE_CHAR
                                (uint32_t *)&r_char,          //保存任务通知值
#else
                                &r_num,          //保存任务通知值
#endif                        
                                portMAX_DELAY);    //阻塞时间
        if( pdTRUE == xReturn )
#if USE_CHAR
            printf("Receive2_Task 任务通知消息为 %s \n",r_char);                      
#else
            printf("Receive2_Task 任务通知消息为 %d \n",r_num);                      
#endif  
        
    }
}

//Send任务函数
void Send_task(void *pvParameters)
{
    BaseType_t xReturn = pdPASS;
    u8 key=0;
#if USE_CHAR
    char test_str1[] = "this is a mail test 1";/* 邮箱消息test1 */
    char test_str2[] = "this is a mail test 2";/* 邮箱消息test2 */
#else
    uint32_t send1 = 1;
    uint32_t send2 = 2;
#endif
    
    while(1)
    {
        key=KEY_Scan(0);
        if(key==KEY_UP_PRESS)
        {
            xReturn = xTaskNotify(Receive1Task_Handler, /*任务句柄*/
#if USE_CHAR 
                                (uint32_t)&test_str1, /* 发送的数据,最大为4字节 */
#else
                                send1, /* 发送的数据,最大为4字节 */
#endif
                                eSetValueWithOverwrite );/*覆盖当前通知*/
            if( xReturn == pdPASS )
                printf("Receive1_Task_Handle 任务通知消息发送成功!\r\n");
        }
        else if(key==KEY1_PRESS)
        {
            xReturn = xTaskNotify(Receive2Task_Handler, /*任务句柄*/
#if USE_CHAR 
                                (uint32_t)&test_str2, /* 发送的数据,最大为4字节 */
#else
                                send2, /* 发送的数据,最大为4字节 */
#endif
                                eSetValueWithOverwrite );/*覆盖当前通知*/
            if( xReturn == pdPASS )
                printf("Receive2_Task_Handle 任务通知消息发送成功!\r\n");
        }
        vTaskDelay(20);
    }
}

2.任务通知代替二值信号量

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"

//任务优先级
#define START_TASK_PRIO        1
//任务堆栈大小    
#define START_STK_SIZE         128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define LED1_TASK_PRIO        2
//任务堆栈大小    
#define LED1_STK_SIZE         50  
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);

//任务优先级
#define Receive1_TASK_PRIO        3
//任务堆栈大小    
#define Receive1_STK_SIZE         512  
//任务句柄
TaskHandle_t Receive1Task_Handler;
//任务函数
void Receive1_task(void *pvParameters);

//任务优先级
#define Receive2_TASK_PRIO        4
//任务堆栈大小    
#define Receive2_STK_SIZE         512  
//任务句柄
TaskHandle_t Receive2Task_Handler;
//任务函数
void Receive2_task(void *pvParameters);

//任务优先级
#define Send_TASK_PRIO        5
//任务堆栈大小    
#define Send_STK_SIZE         512  
//任务句柄
TaskHandle_t SendTask_Handler;
//任务函数
void Send_task(void *pvParameters);



/*******************************************************************************
* 函 数 名         : main
* 函数功能           : 主函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
int main()
{
    SysTick_Init(72);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
    LED_Init();
    KEY_Init();
    USART1_Init(115200);
    printf("FreeRTOS任务通知代替二值信号量实验\r\n");
    printf("按下KEY_UP或者KEY1进行任务与任务间的同步\n");
    //创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
                             
    //创建LED1任务
    xTaskCreate((TaskFunction_t )led1_task,     
                (const char*    )"led1_task",   
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED1_TASK_PRIO,
                (TaskHandle_t*  )&LED1Task_Handler); 
    
    //创建Receive1任务
    xTaskCreate((TaskFunction_t )Receive1_task,     
                (const char*    )"Receive1_task",   
                (uint16_t       )Receive1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )Receive1_TASK_PRIO,
                (TaskHandle_t*  )&Receive1Task_Handler);
                
    //创建Receive2任务
    xTaskCreate((TaskFunction_t )Receive2_task,     
                (const char*    )"Receive2_task",   
                (uint16_t       )Receive2_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )Receive2_TASK_PRIO,
                (TaskHandle_t*  )&Receive2Task_Handler);
    
    //创建Send任务
    xTaskCreate((TaskFunction_t )Send_task,     
                (const char*    )"Send_task",   
                (uint16_t       )Send_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )Send_TASK_PRIO,
                (TaskHandle_t*  )&SendTask_Handler);
            
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
} 

//LED1任务函数
void led1_task(void *pvParameters)
{
    while(1)
    {
        LED1=0;
        vTaskDelay(200);
        LED1=1;
        vTaskDelay(800);
    }
}

//Receive1任务函数
void Receive1_task(void *pvParameters)
{
    while(1)
    {
        //获取任务通知 ,没获取到则一直等待
        ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
        printf("Receive1_Task 任务通知获取成功!\n\n");
    }
}

//Receive2任务函数
void Receive2_task(void *pvParameters)
{    
    while(1)
    {
        //获取任务通知 ,没获取到则一直等待
        ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
        printf("Receive2_Task 任务通知获取成功!\n\n");
    }
}

//Send任务函数
void Send_task(void *pvParameters)
{
    BaseType_t xReturn = pdPASS;
    u8 key=0;
    
    while(1)
    {
        key=KEY_Scan(0);
        if(key==KEY_UP_PRESS)
        {
            xReturn = xTaskNotifyGive(Receive1Task_Handler);
            if( xReturn == pdTRUE )
                printf("Receive1_Task_Handle 任务通知发送成功!\r\n");
        }
        else if(key==KEY1_PRESS)
        {
            xReturn = xTaskNotifyGive(Receive2Task_Handler);
            if( xReturn == pdTRUE )
                printf("Receive2_Task_Handle 任务通知发送成功!\r\n");
        }
        vTaskDelay(20);
    }
}

3.任务通知代替计数信号量

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"


//任务优先级
#define START_TASK_PRIO        1
//任务堆栈大小    
#define START_STK_SIZE         128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define LED1_TASK_PRIO        2
//任务堆栈大小    
#define LED1_STK_SIZE         50  
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);

//任务优先级
#define Take_TASK_PRIO        3
//任务堆栈大小    
#define Take_STK_SIZE         512  
//任务句柄
TaskHandle_t TakeTask_Handler;
//任务函数
void Take_task(void *pvParameters);

//任务优先级
#define Give_TASK_PRIO        4
//任务堆栈大小    
#define Give_STK_SIZE         512  
//任务句柄
TaskHandle_t GiveTask_Handler;
//任务函数
void Give_task(void *pvParameters);



/*******************************************************************************
* 函 数 名         : main
* 函数功能           : 主函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
int main()
{
    SysTick_Init(72);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
    LED_Init();
    KEY_Init();
    USART1_Init(115200);
    printf("FreeRTOS任务通知代替计数信号量实验\r\n");
    printf("车位默认值为0个,按下KEY1申请车位,按下KEY2释放车位!\n\n");
    //创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
                             
    //创建LED1任务
    xTaskCreate((TaskFunction_t )led1_task,     
                (const char*    )"led1_task",   
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED1_TASK_PRIO,
                (TaskHandle_t*  )&LED1Task_Handler); 
    
    //创建Take任务
    xTaskCreate((TaskFunction_t )Take_task,     
                (const char*    )"Take_task",   
                (uint16_t       )Take_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )Take_TASK_PRIO,
                (TaskHandle_t*  )&TakeTask_Handler);
                
    //创建Give任务
    xTaskCreate((TaskFunction_t )Give_task,     
                (const char*    )"Give_task",   
                (uint16_t       )Give_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )Give_TASK_PRIO,
                (TaskHandle_t*  )&GiveTask_Handler);
    
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
} 

//LED1任务函数
void led1_task(void *pvParameters)
{
    while(1)
    {
        LED1=0;
        vTaskDelay(200);
        LED1=1;
        vTaskDelay(800);
    }
}

//Give任务函数
void Give_task(void *pvParameters)
{    
    BaseType_t xReturn = pdPASS;
    u8 key=0;
    
    while(1)
    {
        key=KEY_Scan(0);
        if(key==KEY2_PRESS)
        {
            xTaskNotifyGive(TakeTask_Handler);//发送任务通知
            if ( pdPASS == xReturn ) 
                printf( "KEY2被按下,释放1个停车位。\n" );
        }
        vTaskDelay(20);
    }
}

//Take任务函数
void Take_task(void *pvParameters)
{
    uint32_t take_num = pdTRUE;
    u8 key=0;
    
    while(1)
    {
        key=KEY_Scan(0);
        if(key==KEY1_PRESS)
        {
            //获取任务通知 ,没获取到则不等待
            take_num=ulTaskNotifyTake(pdFALSE,0);//pdFALSE,让值减1,有一个任务占用,就减1
            if(take_num > 0)
                printf( "KEY1被按下,成功申请到停车位,当前车位为 %d \n", take_num - 1);
            else
                printf( "KEY1被按下,车位已经没有了,请按KEY2释放车位\n" );  
        }
        vTaskDelay(20);
    }
}

4.任务通知代替事件组

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
#include "limits.h"

//任务优先级
#define START_TASK_PRIO        1
//任务堆栈大小    
#define START_STK_SIZE         128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define LED1_TASK_PRIO        2
//任务堆栈大小    
#define LED1_STK_SIZE         50  
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);

//任务优先级
#define LED2_TASK_PRIO        3
//任务堆栈大小    
#define LED2_STK_SIZE         50  
//任务句柄
TaskHandle_t LED2Task_Handler;
//任务函数
void led2_task(void *pvParameters);

//任务优先级
#define KEY_TASK_PRIO        4
//任务堆栈大小    
#define KEY_STK_SIZE         512  
//任务句柄
TaskHandle_t KEYTask_Handler;
//任务函数
void key_task(void *pvParameters);


#define KEY1_EVENT  (0x01 << 0)//设置事件掩码的位0
#define KEY2_EVENT  (0x01 << 1)//设置事件掩码的位1



/*******************************************************************************
* 函 数 名         : main
* 函数功能           : 主函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
int main()
{
    SysTick_Init(72);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
    LED_Init();
    KEY_Init();
    USART1_Init(115200);
    printf("FreeRTOS任务通知代替事件组实验\r\n");
    printf("按下KEY1|KEY2发送任务事件通知!\n");
    //创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
                             
    //创建LED1任务
    xTaskCreate((TaskFunction_t )led1_task,     
                (const char*    )"led1_task",   
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED1_TASK_PRIO,
                (TaskHandle_t*  )&LED1Task_Handler); 
    
    //创建LED2任务
    xTaskCreate((TaskFunction_t )led2_task,     
                (const char*    )"led2_task",   
                (uint16_t       )LED2_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED2_TASK_PRIO,
                (TaskHandle_t*  )&LED2Task_Handler);
                
    //创建KEY任务
    xTaskCreate((TaskFunction_t )key_task,     
                (const char*    )"key_task",   
                (uint16_t       )KEY_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )KEY_TASK_PRIO,
                (TaskHandle_t*  )&KEYTask_Handler);
    
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
} 

//LED1任务函数
void led1_task(void *pvParameters)
{
    while(1)
    {
        LED1=0;
        vTaskDelay(200);
        LED1=1;
        vTaskDelay(800);
    }
}

//LED2任务函数
void led2_task(void *pvParameters)
{
    BaseType_t xReturn = pdTRUE;
    uint32_t r_event = 0;  /* 定义一个事件接收变量 */
    uint32_t last_event = 0;/* 定义一个保存事件的变量 */
    
    while(1)
    {
        //获取任务通知 ,没获取到则一直等待
        xReturn = xTaskNotifyWait(0x0,            //进入函数的时候不清除任务bit
                                ULONG_MAX,      //退出函数的时候清除所有的bitR
                                &r_event,          //保存任务通知值                    
                                portMAX_DELAY);    //阻塞时间
                        
        if( pdTRUE == xReturn )
        {
            last_event |= r_event;
            if(last_event == (KEY1_EVENT|KEY2_EVENT))
            {
                last_event=0;
                printf ( "KEY1与KEY2都按下\n");
                LED2=!LED2;
            }
            else
                last_event = r_event;
        }
    }
}

//KEY任务函数
void key_task(void *pvParameters)
{
    u8 key=0;
    
    while(1)
    {
        key=KEY_Scan(0);
        if(key==KEY1_PRESS)
        {
            printf ( "KEY1被按下\n" );
            /* 触发一个事件1 */
            xTaskNotify((TaskHandle_t    )LED2Task_Handler,//接收任务通知的任务句柄
                        (uint32_t        )KEY1_EVENT,//要触发的事件
                        (eNotifyAction)eSetBits);//设置任务通知值中的位
        }
        else if(key==KEY2_PRESS)
        {
            printf ( "KEY2被按下\n" );    
            /* 触发一个事件2 */
            xTaskNotify((TaskHandle_t    )LED2Task_Handler,//接收任务通知的任务句柄
                        (uint32_t        )KEY2_EVENT,//要触发的事件
                        (eNotifyAction)eSetBits);//设置任务通知值中的位
        }
        vTaskDelay(20);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/194182.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

极狐场景化造车理念受热捧,北汽蓝谷构建未来5年核心竞争力

近日&#xff0c;极狐汽车以“一米视角”为设计思考的原点&#xff0c;围绕亲子出行的全场景&#xff0c;推出全球首款智能亲子车——考拉。作为北汽蓝谷场景化造车的首款产品&#xff0c;极狐汽车考拉无疑是继高阶智能驾驶标杆产品HI之后的又一次先行探索&#xff0c;致力于卡…

1月VR大数据:Quest 2增长2.91%,HTC份额跌至10%以下

Hello大家好&#xff0c;每月一期的VR内容/硬件大数据统计又和大家见面了。 想了解VR软硬件行情么&#xff1f;关注这里就对了。我们会统计Steam平台的用户及内容等数据&#xff0c;每月初准时为你推送&#xff0c;不要错过喔&#xff01;本数据报告包含&#xff1a;Steam VR硬…

初识C语言(对c语言的简单介绍)

初识C语言什么是C语言&#xff1f;第一个C语言程序数据类型类型的使用&#xff1a;变量、常量定义变量的方法变量的分类变量的使用变量的作用域和生命周期常量字符串转义字符注释字符串转义字符注释选择语句循环语句函数数组数组定义数组的使用操作符常见关键字关键字 typedef关…

MySQL进阶篇之SQL优化

03、SQL优化 3.1、插入数据 1、insert优化 批量插入 INSERT INTO 表名 (字段1,字段2,...) VALUES (值1,值2,...),(值1,值2,...),(值1,值2,...);INSERT INTO 表名 VALUES (值1,值2,...),(值1,值2,...),(值1,值2,...);手动提交事务 start transaction; INSERT INTO 表名 (字段1…

【SQL 审核查询平台】Archery使用介绍

Archery 读作&#xff1a;[ˈɑːrtʃəri] Archery目录界面截图功能清单依赖清单框架前端组件服务端部署准备运行配置启动访问修改配置项基础设置添加实例添加资源组资源组关联用户/实例添加权限组用户关联权限组/权限设置工单上线和查询的审批流程设置默认资源组和默认权限组…

数组的几种常见方法及其返回值

push()&#xff1a;向数组的末尾添加一个或多个元素&#xff1b;返回的是数组的新长度。unshift()&#xff1a;向数组的开头添加一个或多个元素&#xff1b;返回的是数组的新长度。shift()&#xff1a;删除数组的第一个元素&#xff0c;并返回被删除的&#xff08;即第一个元素…

品牌社交营销链路 | 小红书数据分析网站

【导语】 2022年&#xff0c;小红书品牌推广竞争愈演愈烈&#xff0c;从小红书用户画像分析&#xff0c;到抢占小红书关键词排名&#xff0c;营销动作内卷升级&#xff0c;那么在2023的新篇章&#xff0c;如何打通社交种草的链路呢&#xff1f; 1、运营企业账号&#xff0c;建立…

MQTT 代理助力ECARX实现汽车智能互联

一、应用背景 ECARX是中国汽车制造商吉利旗下的一家科技创新企业&#xff0c;致力于持续打造行业领先的智能网联生态开放平台&#xff0c;全面为车企赋能&#xff0c;创造更智能、更安全的出行体验&#xff0c;为智能互联汽车提供智能解决方案。 ECARX主要业务包括吉利汽车的…

让Apache Beam在GCP Cloud Dataflow上跑起来

简介 在文章《Apache Beam入门及Java SDK开发初体验》中大概讲了Apapche Beam的简单概念和本地运行&#xff0c;本文将讲解如何把代码运行在GCP Cloud Dataflow上。 本地运行 通过maven命令来创建项目&#xff1a; mvn archetype:generate \-DarchetypeGroupIdorg.apache.b…

Swift 新 async/await 同步机制小技巧:消除“多余”的 await 关键字

概览 在使用多个Actor 共同实现同步功能的时候&#xff0c;我们往往会看到如下使用场景&#xff1a; Actor A 必须在主线程上运行&#xff0c;Actor B可以在任意线程上运行&#xff0c;但需要适时的调用 Actor A 中的方法。 在这种情况下&#xff0c;我们会遇到如下代码&#…

从移动激光扫描数据中自动提取单棵树的双重生长方法

论文题目&#xff1a;A dual growing method for the automatic extraction of individual trees from mobile laser scanning data Abstract 在城市场景的杂乱点云中&#xff0c;街道树木与其他物体交织在一起&#xff0c;阻碍了对单个树木的自动提取。根据树木的一般构成&a…

React:安装配置使用scss

目录 前言&#xff1a; 1.暴露隐藏的webpack配置&#xff1b; 2.安装sass的相关包&#xff1b; 3.项目中新建一些scss文件&#xff1b; 4.在config文件夹中找到webpack.config.js文件&#xff0c;进行配置&#xff1b; 5.测试使用&#xff1b; 前言&#xff1a; 项目采用…

Python之Pandas的常用技能【写入数据】

1、背景&#xff1a; 最近在工作中遇到越来越多的的使用pandas或者python来处里写入操作&#xff0c;尤其是对excel文件或者csv文件的操作更是常见&#xff0c;这里将写入操作总结如下&#xff0c;方便记忆&#xff0c;也分享给大家&#xff0c;希望对阅读者能够有所帮助 2、…

nvdiffrec:Extracting Triangular 3D Models, Materials, and Lighting From Images

论文主页 https://nvlabs.github.io/nvdiffrec/git主页 https://github.com/NVlabs/nvdiffrec新闻报道 https://redian.news/wxnews/36324YuQiao0303 读后感 https://blog.csdn.net/qq_34342853/article/details/125622816b站演示效果视频 https://www.bilibili.com/video/BV1P…

8天获offer|祝贺信息技术老师获CSC资助赴意大利访学

I老师拟申报CSC青年骨干教师项目&#xff0c;指定欧洲学校&#xff0c;且要求半个月内获得邀请函。我们8天就取得了意大利帕多瓦大学的offer&#xff0c;研究方向完全相符&#xff0c;因而顺利通过了CSC审批。后经繁琐的手续&#xff0c;I老师最终获得签证&#xff0c;如期出国…

ABB机器人设置有效载荷的2种方法具体步骤(直接输入法+自动识别推算法2)

ABB机器人设置有效载荷的2种方法具体步骤(直接输入法+自动识别推算法2) 为什么要设置有效载荷Loaddata? 对于搬运应用的机器人只有设定正确的工具和载荷数据,机器人才能正确的工作; 对于搬运比较重的产品,或工具的重量也比较重,需要设置工具及搬运对象的重心和重量; …

2023年黑马Java入门到精通教程--程序流程控制

程序流程控制 程序执行的几种常见形式 分支结构 If分支 根据判定的结果&#xff08;真或假&#xff09;决定执行某个分支的代码 If分支的作用 If分支有三种格式 switch分支 也是匹配条件去执行分支, 适合做值匹配的分支选择&#xff0c;结构清晰&#xff0c;格式良好 swit…

通信原理笔记—基带信号的检测与最佳接收

目录 在加性白高斯噪声信道条件下数字基带信号的接收&#xff1a; 加性高斯白噪声干扰下的信号检测&#xff1a; 最大似然判决准则&#xff1a;误码率最小意义上的最佳判决&#xff1a; 先验等概及最佳判决时的误码率计算&#xff1a; 高斯噪声干扰下二进制信号的检测&…

Mock.js(简单代替后台)

Mock.js &#xff08;官网http://mockjs.com/&#xff09;是一款模拟数据生成器&#xff0c;旨在帮助前端开发人员独立于后端进行开发&#xff0c;帮助编写单元测试。二、为什么使用mockjs在做开发时&#xff0c;当后端的接口还未完成&#xff0c;前端为了不影响工作效率&#…

【实际开发12】- Utils / tools

目录 1. 自定义工具类 概念 / 解析 1. 构建方式 1. XxxClassl 类 public xxx() → new XxxClass() 2. XxxClassl 类 public static xxx() → XxxClass.xxx() 2. Interface 1. public interface XxxMapper → private final XxxMapper 2. public interface IXxxService →…