22.5.1 任务通知代替队列消息
任务通知代替消息队列是在 FreeRTOS 中创建了三个任务, 其中两个任务是用于接收任务通知,另一个任务发送任务通知。三个任务独立运行,发送消息任务是通过检测按键的按下情况来发送消息通知,另两个任务获取消息通知,在任务通知中没有可用的通知之前就一直等待消息,一旦获取到消息通知就把消息打印在串口调试助手里。
/*
*************************************************************************
* 包含的头文件
*************************************************************************
*/
/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_key.h"
#include "limits.h"
/**************************** 任务句柄 ********************************/
/*
* 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
* 这个句柄可以为NULL。
*/
static TaskHandle_t AppTaskCreate_Handle = NULL;/* 创建任务句柄 */
static TaskHandle_t Receive1_Task_Handle = NULL;/* Receive1_Task任务句柄 */
static TaskHandle_t Receive2_Task_Handle = NULL;/* Receive2_Task任务句柄 */
static TaskHandle_t Send_Task_Handle = NULL;/* Send_Task任务句柄 */
/********************************** 内核对象句柄 *********************************/
/*
* 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
* 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
* 们就可以通过这个句柄操作这些内核对象。
*
* 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
* 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
* 来完成的
*
*/
/******************************* 全局变量声明 ************************************/
/*
* 当我们在写应用程序的时候,可能需要用到一些全局变量。
*/
/******************************* 宏定义 ************************************/
/*
* 当我们在写应用程序的时候,可能需要用到一些宏定义。
*/
#define USE_CHAR 0 /* 测试字符串的时候配置为 1 ,测试变量配置为 0 */
/*
*************************************************************************
* 函数声明
*************************************************************************
*/
static void AppTaskCreate(void);/* 用于创建任务 */
static void Receive1_Task(void* pvParameters);/* Receive1_Task任务实现 */
static void Receive2_Task(void* pvParameters);/* Receive2_Task任务实现 */
static void Send_Task(void* pvParameters);/* Send_Task任务实现 */
static void BSP_Init(void);/* 用于初始化板载相关资源 */
/*****************************************************************
* @brief 主函数
* @param 无
* @retval 无
* @note 第一步:开发板硬件初始化
第二步:创建APP应用任务
第三步:启动FreeRTOS,开始多任务调度
****************************************************************/
int main(void)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
/* 开发板硬件初始化 */
BSP_Init();
printf("这是一个[野火]-STM32全系列开发板-FreeRTOS任务通知代替消息队列实验!\n");
printf("按下KEY1或者KEY2进行任务消息通知发送 \n");
/* 创建AppTaskCreate任务 */
xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任务入口函数 */
(const char* )"AppTaskCreate",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL,/* 任务入口函数参数 */
(UBaseType_t )1, /* 任务的优先级 */
(TaskHandle_t* )&AppTaskCreate_Handle);/* 任务控制块指针 */
/* 启动任务调度 */
if(pdPASS == xReturn)
vTaskStartScheduler(); /* 启动任务,开启调度 */
else
return -1;
while(1); /* 正常不会执行到这里 */
}
/***********************************************************************
* @ 函数名 : AppTaskCreate
* @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
* @ 参数 : 无
* @ 返回值 : 无
**********************************************************************/
static void AppTaskCreate(void)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
taskENTER_CRITICAL(); //进入临界区
/* 创建Receive1_Task任务 */
xReturn = xTaskCreate((TaskFunction_t )Receive1_Task, /* 任务入口函数 */
(const char* )"Receive1_Task",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL, /* 任务入口函数参数 */
(UBaseType_t )2, /* 任务的优先级 */
(TaskHandle_t* )&Receive1_Task_Handle);/* 任务控制块指针 */
if(pdPASS == xReturn)
printf("创建Receive1_Task任务成功!\r\n");
/* 创建Receive2_Task任务 */
xReturn = xTaskCreate((TaskFunction_t )Receive2_Task, /* 任务入口函数 */
(const char* )"Receive2_Task",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL, /* 任务入口函数参数 */
(UBaseType_t )3, /* 任务的优先级 */
(TaskHandle_t* )&Receive2_Task_Handle);/* 任务控制块指针 */
if(pdPASS == xReturn)
printf("创建Receive2_Task任务成功!\r\n");
/* 创建Send_Task任务 */
xReturn = xTaskCreate((TaskFunction_t )Send_Task, /* 任务入口函数 */
(const char* )"Send_Task",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL,/* 任务入口函数参数 */
(UBaseType_t )4, /* 任务的优先级 */
(TaskHandle_t* )&Send_Task_Handle);/* 任务控制块指针 */
if(pdPASS == xReturn)
printf("创建Send_Task任务成功!\n\n");
vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
taskEXIT_CRITICAL(); //退出临界区
}
/**********************************************************************
* @ 函数名 : Receive_Task
* @ 功能说明: Receive_Task任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void Receive1_Task(void* parameter)
{
BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为pdPASS */
#if USE_CHAR
char *r_char;
#else
uint32_t r_num;
#endif
while (1)
{
/* BaseType_t xTaskNotifyWait(uint32_t ulBitsToClearOnEntry,
uint32_t ulBitsToClearOnExit,
uint32_t *pulNotificationValue,
TickType_t xTicksToWait );
* ulBitsToClearOnEntry:当没有接收到任务通知的时候将任务通知值与此参数的取
反值进行按位与运算,当此参数为Oxfffff或者ULONG_MAX的时候就会将任务通知值清零。
* ulBits ToClearOnExit:如果接收到了任务通知,在做完相应的处理退出函数之前将
任务通知值与此参数的取反值进行按位与运算,当此参数为0xfffff或者ULONG MAX的时候
就会将任务通知值清零。
* pulNotification Value:此参数用来保存任务通知值。
* xTick ToWait:阻塞时间。
*
* 返回值:pdTRUE:获取到了任务通知。pdFALSE:任务通知获取失败。
*/
//获取任务通知 ,没获取到则一直等待
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
LED1_TOGGLE;
}
}
/**********************************************************************
* @ 函数名 : Receive_Task
* @ 功能说明: Receive_Task任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void Receive2_Task(void* parameter)
{
BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为pdPASS */
#if USE_CHAR
char *r_char;
#else
uint32_t r_num;
#endif
while (1)
{
/* BaseType_t xTaskNotifyWait(uint32_t ulBitsToClearOnEntry,
uint32_t ulBitsToClearOnExit,
uint32_t *pulNotificationValue,
TickType_t xTicksToWait );
* ulBitsToClearOnEntry:当没有接收到任务通知的时候将任务通知值与此参数的取
反值进行按位与运算,当此参数为Oxfffff或者ULONG_MAX的时候就会将任务通知值清零。
* ulBits ToClearOnExit:如果接收到了任务通知,在做完相应的处理退出函数之前将
任务通知值与此参数的取反值进行按位与运算,当此参数为0xfffff或者ULONG MAX的时候
就会将任务通知值清零。
* pulNotification Value:此参数用来保存任务通知值。
* xTick ToWait:阻塞时间。
*
* 返回值:pdTRUE:获取到了任务通知。pdFALSE:任务通知获取失败。
*/
//获取任务通知 ,没获取到则一直等待
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
LED2_TOGGLE;
}
}
/**********************************************************************
* @ 函数名 : Send_Task
* @ 功能说明: Send_Task任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void Send_Task(void* parameter)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
#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)
{
/* KEY1 被按下 */
if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
{
/* 原型:BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify,
uint32_t ulValue,
eNotifyAction eAction );
* eNoAction = 0,通知任务而不更新其通知值。
* eSetBits, 设置任务通知值中的位。
* eIncrement, 增加任务的通知值。
* eSetvaluewithoverwrite,覆盖当前通知
* eSetValueWithoutoverwrite 不覆盖当前通知
*
* pdFAIL:当参数eAction设置为eSetValueWithoutOverwrite的时候,
* 如果任务通知值没有更新成功就返回pdFAIL。
* pdPASS: eAction 设置为其他选项的时候统一返回pdPASS。
*/
xReturn = xTaskNotify( Receive1_Task_Handle, /*任务句柄*/
#if USE_CHAR
(uint32_t)&test_str1, /* 发送的数据,最大为4字节 */
#else
send1, /* 发送的数据,最大为4字节 */
#endif
eSetValueWithOverwrite );/*覆盖当前通知*/
if( xReturn == pdPASS )
printf("Receive1_Task_Handle 任务通知消息发送成功!\r\n");
}
/* KEY2 被按下 */
if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON )
{
xReturn = xTaskNotify( Receive2_Task_Handle, /*任务句柄*/
#if USE_CHAR
(uint32_t)&test_str2, /* 发送的数据,最大为4字节 */
#else
send2, /* 发送的数据,最大为4字节 */
#endif
eSetValueWithOverwrite );/*覆盖当前通知*/
/* 此函数只会返回pdPASS */
if( xReturn == pdPASS )
printf("Receive2_Task_Handle 任务通知消息发送成功!\r\n");
}
vTaskDelay(20);
}
}
/***********************************************************************
* @ 函数名 : BSP_Init
* @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
* @ 参数 :
* @ 返回值 : 无
*********************************************************************/
static void BSP_Init(void)
{
/*
* STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
* 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
* 都统一用这个优先级分组,千万不要再分组,切忌。
*/
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
/* LED 初始化 */
LED_GPIO_Config();
/* 串口初始化 */
USART_Config();
/* 按键初始化 */
Key_GPIO_Config();
}
/********************************END OF FILE****************************/
22.5.2 任务通知代替二值信号量
任务通知代替消息队列是在 FreeRTOS 中创建了三个任务,其中两个任务是用于接收任务通知,另一个任务发送任务通知。三个任务独立运行,发送通知任务是通过检测按键的按下情况来发送通知,另两个任务获取通知,在任务通知中没有可用的通知之前就一直等待任务通知,获取到通知以后就将通知值清 0,这样子是为了代替二值信号量,任务同步成功则继续执行,然后在串口调试助手里将运行信息打印出来
/**
*********************************************************************
* @file main.c
* @author fire
* @version V1.0
* @date 2018-xx-xx
* @brief FreeRTOS V9.0.0 + STM32 任务通知代替二值信号量
*********************************************************************
* @attention
*
* 实验平台:野火 STM32全系列开发板
* 论坛 :http://www.firebbs.cn
* 淘宝 :https://fire-stm32.taobao.com
*
**********************************************************************
*/
/*
*************************************************************************
* 包含的头文件
*************************************************************************
*/
/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_key.h"
/**************************** 任务句柄 ********************************/
/*
* 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
* 这个句柄可以为NULL。
*/
static TaskHandle_t AppTaskCreate_Handle = NULL;/* 创建任务句柄 */
static TaskHandle_t Receive1_Task_Handle = NULL;/* Receive1_Task任务句柄 */
static TaskHandle_t Receive2_Task_Handle = NULL;/* Receive2_Task任务句柄 */
static TaskHandle_t Send_Task_Handle = NULL;/* Send_Task任务句柄 */
/********************************** 内核对象句柄 *********************************/
/*
* 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
* 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
* 们就可以通过这个句柄操作这些内核对象。
*
* 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
* 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
* 来完成的
*
*/
/******************************* 全局变量声明 ************************************/
/*
* 当我们在写应用程序的时候,可能需要用到一些全局变量。
*/
/******************************* 宏定义 ************************************/
/*
* 当我们在写应用程序的时候,可能需要用到一些宏定义。
*/
/*
*************************************************************************
* 函数声明
*************************************************************************
*/
static void AppTaskCreate(void);/* 用于创建任务 */
static void Receive1_Task(void* pvParameters);/* Receive1_Task任务实现 */
static void Receive2_Task(void* pvParameters);/* Receive2_Task任务实现 */
static void Send_Task(void* pvParameters);/* Send_Task任务实现 */
static void BSP_Init(void);/* 用于初始化板载相关资源 */
/*****************************************************************
* @brief 主函数
* @param 无
* @retval 无
* @note 第一步:开发板硬件初始化
第二步:创建APP应用任务
第三步:启动FreeRTOS,开始多任务调度
****************************************************************/
int main(void)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
/* 开发板硬件初始化 */
BSP_Init();
printf("这是一个[野火]-STM32全系列开发板-FreeRTOS任务通知代替二值信号量实验!\n");
printf("按下KEY1或者KEY2进行任务与任务间的同步\n");
/* 创建AppTaskCreate任务 */
xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任务入口函数 */
(const char* )"AppTaskCreate",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL,/* 任务入口函数参数 */
(UBaseType_t )1, /* 任务的优先级 */
(TaskHandle_t* )&AppTaskCreate_Handle);/* 任务控制块指针 */
/* 启动任务调度 */
if(pdPASS == xReturn)
vTaskStartScheduler(); /* 启动任务,开启调度 */
else
return -1;
while(1); /* 正常不会执行到这里 */
}
/***********************************************************************
* @ 函数名 : AppTaskCreate
* @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
* @ 参数 : 无
* @ 返回值 : 无
**********************************************************************/
static void AppTaskCreate(void)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
taskENTER_CRITICAL(); //进入临界区
/* 创建Receive1_Task任务 */
xReturn = xTaskCreate((TaskFunction_t )Receive1_Task, /* 任务入口函数 */
(const char* )"Receive1_Task",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL, /* 任务入口函数参数 */
(UBaseType_t )2, /* 任务的优先级 */
(TaskHandle_t* )&Receive1_Task_Handle);/* 任务控制块指针 */
if(pdPASS == xReturn)
printf("创建Receive1_Task任务成功!\r\n");
/* 创建Receive2_Task任务 */
xReturn = xTaskCreate((TaskFunction_t )Receive2_Task, /* 任务入口函数 */
(const char* )"Receive2_Task",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL, /* 任务入口函数参数 */
(UBaseType_t )3, /* 任务的优先级 */
(TaskHandle_t* )&Receive2_Task_Handle);/* 任务控制块指针 */
if(pdPASS == xReturn)
printf("创建Receive2_Task任务成功!\r\n");
/* 创建Send_Task任务 */
xReturn = xTaskCreate((TaskFunction_t )Send_Task, /* 任务入口函数 */
(const char* )"Send_Task",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL,/* 任务入口函数参数 */
(UBaseType_t )4, /* 任务的优先级 */
(TaskHandle_t* )&Send_Task_Handle);/* 任务控制块指针 */
if(pdPASS == xReturn)
printf("创建Send_Task任务成功!\n\n");
vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
taskEXIT_CRITICAL(); //退出临界区
}
/**********************************************************************
* @ 函数名 : Receive_Task
* @ 功能说明: Receive_Task任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void Receive1_Task(void* parameter)
{
while (1)
{
/* uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
* xClearCountOnExit:pdTRUE 在退出函数的时候任务任务通知值清零,类似二值信号量
* pdFALSE 在退出函数ulTaskNotifyTakeO的时候任务通知值减一,类似计数型信号量。
*/
//获取任务通知 ,没获取到则一直等待
ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
printf("Receive1_Task 任务通知获取成功!\n\n");
LED1_TOGGLE;
}
}
/**********************************************************************
* @ 函数名 : Receive_Task
* @ 功能说明: Receive_Task任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void Receive2_Task(void* parameter)
{
while (1)
{
/* uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
* xClearCountOnExit:pdTRUE 在退出函数的时候任务任务通知值清零,类似二值信号量
* pdFALSE 在退出函数ulTaskNotifyTakeO的时候任务通知值减一,类似计数型信号量。
*/
//获取任务通知 ,没获取到则一直等待
ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
printf("Receive2_Task 任务通知获取成功!\n\n");
LED2_TOGGLE;
}
}
/**********************************************************************
* @ 函数名 : Send_Task
* @ 功能说明: Send_Task任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void Send_Task(void* parameter)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
while (1)
{
/* KEY1 被按下 */
if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
{
/* 原型:BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify ); */
xReturn = xTaskNotifyGive(Receive1_Task_Handle);
/* 此函数只会返回pdPASS */
if( xReturn == pdTRUE )
printf("Receive1_Task_Handle 任务通知发送成功!\r\n");
}
/* KEY2 被按下 */
if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON )
{
xReturn = xTaskNotifyGive(Receive2_Task_Handle);
/* 此函数只会返回pdPASS */
if( xReturn == pdPASS )
printf("Receive2_Task_Handle 任务通知发送成功!\r\n");
}
vTaskDelay(20);
}
}
/***********************************************************************
* @ 函数名 : BSP_Init
* @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
* @ 参数 :
* @ 返回值 : 无
*********************************************************************/
static void BSP_Init(void)
{
/*
* STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
* 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
* 都统一用这个优先级分组,千万不要再分组,切忌。
*/
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
/* LED 初始化 */
LED_GPIO_Config();
/* 串口初始化 */
USART_Config();
/* 按键初始化 */
Key_GPIO_Config();
}
/********************************END OF FILE****************************/
22.5.3 任务通知代替计数信号量
任务通知代替计数信号量是基于计数型信号量实验修改而来,模拟停车场工作运行。并且在 FreeRTOS 中创建了两个任务:一个是获取任务通知,一个是发送任务通知,两个任务独立运行,获取通知的任务是通过按下 KEY1 按键获取,模拟停车场停车操作,其等待时间是 0;发送通知的任务则是通过检测 KEY2 按键按下进行通知的发送,模拟停车场取车操作,并且在串口调试助手输出相应信息。
/*
*************************************************************************
* 包含的头文件
*************************************************************************
*/
/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_key.h"
/**************************** 任务句柄 ********************************/
/*
* 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
* 这个句柄可以为NULL。
*/
static TaskHandle_t AppTaskCreate_Handle = NULL;/* 创建任务句柄 */
static TaskHandle_t Take_Task_Handle = NULL;/* Take_Task任务句柄 */
static TaskHandle_t Give_Task_Handle = NULL;/* Give_Task任务句柄 */
/********************************** 内核对象句柄 *********************************/
/*
* 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
* 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
* 们就可以通过这个句柄操作这些内核对象。
*
* 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
* 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
* 来完成的
*
*/
SemaphoreHandle_t CountSem_Handle =NULL;
/******************************* 全局变量声明 ************************************/
/*
* 当我们在写应用程序的时候,可能需要用到一些全局变量。
*/
/******************************* 宏定义 ************************************/
/*
* 当我们在写应用程序的时候,可能需要用到一些宏定义。
*/
/*
*************************************************************************
* 函数声明
*************************************************************************
*/
static void AppTaskCreate(void);/* 用于创建任务 */
static void Take_Task(void* pvParameters);/* Take_Task任务实现 */
static void Give_Task(void* pvParameters);/* Give_Task任务实现 */
static void BSP_Init(void);/* 用于初始化板载相关资源 */
/*****************************************************************
* @brief 主函数
* @param 无
* @retval 无
* @note 第一步:开发板硬件初始化
第二步:创建APP应用任务
第三步:启动FreeRTOS,开始多任务调度
****************************************************************/
int main(void)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
/* 开发板硬件初始化 */
BSP_Init();
printf("这是一个[野火]-STM32全系列开发板-FreeRTOS任务通知代替计数信号量实验!\n");
printf("车位默认值为0个,按下KEY1申请车位,按下KEY2释放车位!\n\n");
/* 创建AppTaskCreate任务 */
xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任务入口函数 */
(const char* )"AppTaskCreate",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL,/* 任务入口函数参数 */
(UBaseType_t )1, /* 任务的优先级 */
(TaskHandle_t* )&AppTaskCreate_Handle);/* 任务控制块指针 */
/* 启动任务调度 */
if(pdPASS == xReturn)
vTaskStartScheduler(); /* 启动任务,开启调度 */
else
return -1;
while(1); /* 正常不会执行到这里 */
}
/***********************************************************************
* @ 函数名 : AppTaskCreate
* @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
* @ 参数 : 无
* @ 返回值 : 无
**********************************************************************/
static void AppTaskCreate(void)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
taskENTER_CRITICAL(); //进入临界区
/* 创建Take_Task任务 */
xReturn = xTaskCreate((TaskFunction_t )Take_Task, /* 任务入口函数 */
(const char* )"Take_Task",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL, /* 任务入口函数参数 */
(UBaseType_t )2, /* 任务的优先级 */
(TaskHandle_t* )&Take_Task_Handle);/* 任务控制块指针 */
if(pdPASS == xReturn)
printf("创建Take_Task任务成功!\r\n");
/* 创建Give_Task任务 */
xReturn = xTaskCreate((TaskFunction_t )Give_Task, /* 任务入口函数 */
(const char* )"Give_Task",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL,/* 任务入口函数参数 */
(UBaseType_t )3, /* 任务的优先级 */
(TaskHandle_t* )&Give_Task_Handle);/* 任务控制块指针 */
if(pdPASS == xReturn)
printf("创建Give_Task任务成功!\n\n");
vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
taskEXIT_CRITICAL(); //退出临界区
}
/**********************************************************************
* @ 函数名 : Take_Task
* @ 功能说明: Take_Task任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void Take_Task(void* parameter)
{
uint32_t take_num = pdTRUE;/* 定义一个创建信息返回值,默认为pdPASS */
/* 任务都是一个无限循环,不能返回 */
while (1)
{
//如果KEY1被单击
if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
{
/* uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
* xClearCountOnExit:pdTRUE 在退出函数的时候任务任务通知值清零,类似二值信号量
* pdFALSE 在退出函数ulTaskNotifyTakeO的时候任务通知值减一,类似计数型信号量。
*/
//获取任务通知 ,没获取到则不等待
take_num=ulTaskNotifyTake(pdFALSE,0);//
if(take_num > 0)
printf( "KEY1被按下,成功申请到停车位,当前车位为 %d \n", take_num - 1);
else
printf( "KEY1被按下,车位已经没有了,请按KEY2释放车位\n" );
}
vTaskDelay(20); //每20ms扫描一次
}
}
/**********************************************************************
* @ 函数名 : Give_Task
* @ 功能说明: Give_Task任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void Give_Task(void* parameter)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
/* 任务都是一个无限循环,不能返回 */
while (1)
{
//如果KEY2被单击
if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON )
{
/* 原型:BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify ); */
/* 释放一个任务通知 */
xTaskNotifyGive(Take_Task_Handle);//发送任务通知
/* 此函数只会返回pdPASS */
if ( pdPASS == xReturn )
printf( "KEY2被按下,释放1个停车位。\n" );
}
vTaskDelay(20); //每20ms扫描一次
}
}
/***********************************************************************
* @ 函数名 : BSP_Init
* @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
* @ 参数 :
* @ 返回值 : 无
*********************************************************************/
static void BSP_Init(void)
{
/*
* STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
* 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
* 都统一用这个优先级分组,千万不要再分组,切忌。
*/
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
/* LED 初始化 */
LED_GPIO_Config();
/* 串口初始化 */
USART_Config();
/* 按键初始化 */
Key_GPIO_Config();
}
/********************************END OF FILE****************************/
22.4 任务通知代替事件组
任务通知代替事件组实验是在事件标志组实验基础上进行修改,实验任务通知替代事件实现事件类型的通信,该实验是在 FreeRTOS 中创建了两个任务,一个是发送事件通知任务,一个是等待事件通知任务,两个任务独立运行, 发送事件通知任务通过检测按键的按下情况设置不同的通知值位,等待事件通知任务则获取这任务通知值,并且根据通知值判断两个事件是否都发生,如果是则输出相应信息, LED 进行翻转。 等待事件通知任务的等待时间是 portMAX_DELAY,一直在等待事件通知的发生, 等待获取到事件之后清除对应的任务通知值的位,。
/**
*********************************************************************
* @file main.c
* @author fire
* @version V1.0
* @date 2018-xx-xx
* @brief FreeRTOS V9.0.0 + STM32 任务通知代替事件组
*********************************************************************
* @attention
*
* 实验平台:野火 STM32全系列开发板
* 论坛 :http://www.firebbs.cn
* 淘宝 :https://fire-stm32.taobao.com
*
**********************************************************************
*/
/*
*************************************************************************
* 包含的头文件
*************************************************************************
*/
/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_key.h"
#include "limits.h"
/**************************** 任务句柄 ********************************/
/*
* 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
* 这个句柄可以为NULL。
*/
static TaskHandle_t AppTaskCreate_Handle = NULL;/* 创建任务句柄 */
static TaskHandle_t LED_Task_Handle = NULL;/* LED_Task任务句柄 */
static TaskHandle_t KEY_Task_Handle = NULL;/* KEY_Task任务句柄 */
/********************************** 内核对象句柄 *********************************/
/*
* 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
* 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
* 们就可以通过这个句柄操作这些内核对象。
*
* 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
* 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
* 来完成的
*
*/
/******************************* 全局变量声明 ************************************/
/*
* 当我们在写应用程序的时候,可能需要用到一些全局变量。
*/
/******************************* 宏定义 ************************************/
/*
* 当我们在写应用程序的时候,可能需要用到一些宏定义。
*/
#define KEY1_EVENT (0x01 << 0)//设置事件掩码的位0
#define KEY2_EVENT (0x01 << 1)//设置事件掩码的位1
/*
*************************************************************************
* 函数声明
*************************************************************************
*/
static void AppTaskCreate(void);/* 用于创建任务 */
static void LED_Task(void *pvParameters);/* LED_Task 任务实现 */
static void KEY_Task(void *pvParameters);/* KEY_Task 任务实现 */
static void BSP_Init(void);/* 用于初始化板载相关资源 */
/*****************************************************************
* @brief 主函数
* @param 无
* @retval 无
* @note 第一步:开发板硬件初始化
第二步:创建APP应用任务
第三步:启动FreeRTOS,开始多任务调度
****************************************************************/
int main(void)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
/* 开发板硬件初始化 */
BSP_Init();
printf("这是一个[野火]-STM32全系列开发板-FreeRTOS任务通知代替事件组实验!\n");
printf("按下KEY1|KEY2发送任务事件通知!\n");
/* 创建AppTaskCreate任务 */
xReturn = xTaskCreate((TaskFunction_t)AppTaskCreate, /* 任务入口函数 */
(const char *)"AppTaskCreate", /* 任务名字 */
(uint16_t)512, /* 任务栈大小 */
(void *)NULL, /* 任务入口函数参数 */
(UBaseType_t)1, /* 任务的优先级 */
(TaskHandle_t *)&AppTaskCreate_Handle); /* 任务控制块指针 */
/* 启动任务调度 */
if (pdPASS == xReturn)
vTaskStartScheduler(); /* 启动任务,开启调度 */
else
return -1;
while (1); /* 正常不会执行到这里 */
}
/***********************************************************************
* @ 函数名 : AppTaskCreate
* @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
* @ 参数 : 无
* @ 返回值 : 无
**********************************************************************/
static void AppTaskCreate(void)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
taskENTER_CRITICAL(); //进入临界区
/* 创建LED_Task任务 */
xReturn = xTaskCreate((TaskFunction_t)LED_Task, /* 任务入口函数 */
(const char *)"LED_Task", /* 任务名字 */
(uint16_t)512, /* 任务栈大小 */
(void *)NULL, /* 任务入口函数参数 */
(UBaseType_t)2, /* 任务的优先级 */
(TaskHandle_t *)&LED_Task_Handle); /* 任务控制块指针 */
if (pdPASS == xReturn)
printf("创建LED_Task任务成功!\r\n");
/* 创建KEY_Task任务 */
xReturn = xTaskCreate((TaskFunction_t)KEY_Task, /* 任务入口函数 */
(const char *)"KEY_Task", /* 任务名字 */
(uint16_t)512, /* 任务栈大小 */
(void *)NULL, /* 任务入口函数参数 */
(UBaseType_t)3, /* 任务的优先级 */
(TaskHandle_t *)&KEY_Task_Handle); /* 任务控制块指针 */
if (pdPASS == xReturn)
printf("创建KEY_Task任务成功!\n");
vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
taskEXIT_CRITICAL(); //退出临界区
}
/**********************************************************************
* @ 函数名 : LED_Task
* @ 功能说明: LED_Task任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void LED_Task(void *parameter)
{
uint32_t r_event = 0; /* 定义一个事件接收变量 */
uint32_t last_event = 0;/* 定义一个保存事件的变量 */
BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为pdPASS */
/* 任务都是一个无限循环,不能返回 */
while (1)
{
/* BaseType_t xTaskNotifyWait(uint32_t ulBitsToClearOnEntry,
uint32_t ulBitsToClearOnExit,
uint32_t *pulNotificationValue,
TickType_t xTicksToWait );
* ulBitsToClearOnEntry:当没有接收到任务通知的时候将任务通知值与此参数的取
反值进行按位与运算,当此参数为Oxfffff或者ULONG_MAX的时候就会将任务通知值清零。
* ulBits ToClearOnExit:如果接收到了任务通知,在做完相应的处理退出函数之前将
任务通知值与此参数的取反值进行按位与运算,当此参数为0xfffff或者ULONG MAX的时候
就会将任务通知值清零。
* pulNotification Value:此参数用来保存任务通知值。
* xTick ToWait:阻塞时间。
*
* 返回值:pdTRUE:获取到了任务通知。pdFALSE:任务通知获取失败。
*/
//获取任务通知 ,没获取到则一直等待
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");
LED1_TOGGLE; //LED1 反转
}
else /* 否则就更新事件 */
last_event = r_event; /* 更新上一次触发的事件 */
}
}
}
/**********************************************************************
* @ 函数名 : KEY_Task
* @ 功能说明: KEY_Task任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void KEY_Task(void *parameter)
{
/* 任务都是一个无限循环,不能返回 */
while (1)
{
if (Key_Scan(KEY1_GPIO_PORT, KEY1_GPIO_PIN) == KEY_ON)
{
printf("KEY1被按下\n");
/* 原型:BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify,
uint32_t ulValue,
eNotifyAction eAction );
* eNoAction = 0,通知任务而不更新其通知值。
* eSetBits, 设置任务通知值中的位。
* eIncrement, 增加任务的通知值。
* eSetvaluewithoverwrite,覆盖当前通知
* eSetValueWithoutoverwrite 不覆盖当前通知
*
* pdFAIL:当参数eAction设置为eSetValueWithoutOverwrite的时候,
* 如果任务通知值没有更新成功就返回pdFAIL。
* pdPASS: eAction 设置为其他选项的时候统一返回pdPASS。
*/
/* 触发一个事件1 */
xTaskNotify((TaskHandle_t)LED_Task_Handle, //接收任务通知的任务句柄
(uint32_t)KEY1_EVENT, //要触发的事件
(eNotifyAction)eSetBits); //设置任务通知值中的位
}
if (Key_Scan(KEY2_GPIO_PORT, KEY2_GPIO_PIN) == KEY_ON)
{
printf("KEY2被按下\n");
/* 触发一个事件2 */
xTaskNotify((TaskHandle_t)LED_Task_Handle, //接收任务通知的任务句柄
(uint32_t)KEY2_EVENT, //要触发的事件
(eNotifyAction)eSetBits); //设置任务通知值中的位
}
vTaskDelay(20); //每20ms扫描一次
}
}
/***********************************************************************
* @ 函数名 : BSP_Init
* @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
* @ 参数 :
* @ 返回值 : 无
*********************************************************************/
static void BSP_Init(void)
{
/*
* STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
* 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
* 都统一用这个优先级分组,千万不要再分组,切忌。
*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
/* LED 初始化 */
LED_GPIO_Config();
/* 串口初始化 */
USART_Config();
/* 按键初始化 */
Key_GPIO_Config();
}
/********************************END OF FILE****************************/