//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define KEY_TASK_PRIO 2
//任务堆栈大小
#define KEY_STK_SIZE 128
//任务句柄
TaskHandle_t KeyTask_Handler;
//任务函数
void key_task(void *pvParameters);
//任务优先级
#define TASK1_TASK_PRIO 3
//任务堆栈大小
#define TASK1_STK_SIZE 128
//任务句柄
TaskHandle_t Task1Task_Handler;
//任务函数
void task1_task(void *pvParameters);
主函数
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
delay_init(); //延时函数初始化
uart_init(115200); //初始化串口
LED_Init(); //初始化LED
KEY_Init(); //初始化按键
//创建开始任务
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(); //开启任务调度
}
开始任务函数
QueueHandle_t key_Queue; //队列创建成功承接返回值
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
key_Queue = xQueueCreate(1,sizeof(u8)); //创建队列
//创建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);
//创建TASK1任务
xTaskCreate((TaskFunction_t )task1_task,
(const char* )"task1_task",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_TASK_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
任务一:通过按键函数KEY_Scan(0);读取是否有按键信号,如果有按键信号则将按键信号值写 到队列里面:xQueueSend(key_Queue,&key,10);
其中key_Queue:是队列句柄
key:读取到的按键信号值
最后一个参数是阻塞时间
//key任务函数
void key_task(void *pvParameters)
{
u8 key;
while(1)
{
key=KEY_Scan(0);
if(key_Queue != NULL){
xQueueSend(key_Queue,&key,10);
}
vTaskDelay(10); //延时10ms
}
}
任务二 :从队列里面读取信号,并判断不同的信号值,做不同的操作
xQueueReceive(key_Queue,&buff,10);从队列里面读取信号函数
其中key_Queue:是队列句柄
buff:承接信号值的参数
最后一个参数是阻塞时间
//task1任务函数
void task1_task(void *pvParameters)
{
u8 buff;
u8 task1_num=0;
while(1)
{
task1_num++; //任务执1行次数加1 注意task1_num1加到255的时候会清零!!
if(key_Queue != NULL){
xQueueReceive(key_Queue,&buff,10);
switch(buff)
{
case WKUP_PRES: //KEY_UP控制LED1
LED1=!LED1;
break;
case KEY1_PRES: //KEY1控制蜂鸣器
LED0=!LED0;
break;
case KEY0_PRES: //KEY0刷新LCD背景
LED1=!LED1;
break;
}
}
vTaskDelay(10); //延时1s,也就是1000个时钟节拍
}
}
涉及到的函数
函数 | 描述 |
xTaskCreate() | 使用动态的方法创建一个任务 |
xTaskCreateStatic() | 使用静态的方法创建一个任务 |
xTaskCreateRestricted() | 创建一个使用 MPU 进行限制的任务,相关内存使用动态内存分配。 |
vTaskDelete() | 删除一个任务 |
函数说明
任务创建
1.函数 xTaxkCreate()
此函数用来创建一个任务,任务需要 RAM 来保存与任务有关的状态信息(任务控制块),任
务也需要一定的 RAM 来作为任务堆栈。如果使用函数 xTaskCreate()来创建任务的话那么这些
所需的 RAM 就会自动的从 FreeRTOS 的堆中分配, 因此必须提供内存管理文件,默认我们使用
heap_4.c 这个内存管理文件,而且宏 configSUPPORT_DYNAMIC_ALLOCATION 必须为 1。 如
果使用函数 xTaskCreateStatic()创建的话这些 RAM 就需要用户来提供了。新创建的任务默认就
是就绪态的,如果当前没有比它更高优先级的任务运行那么此任务就会立即进入运行态开始运
行,不管在任务调度器启动前还是启动后,都可以创建任务。 此函数也是我们以后经常用到的,
函数原型如下:
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint16_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
参数说明
pxTaskCode | 任务函数 |
pcName | 任务名字,一般用于追踪和调试,任务名字长度不能超过。 configMAX_TASK_NAME_LEN(16)。 |
usStackDepth | 任务堆栈大小,注意实际申请到的堆栈是 usStackDepth 的 4 倍。其中空闲任 务的任务堆栈大小为 configMINIMAL_STACK_SIZE。 |
pvParameters | 传递给任务函数的参数。 |
uxPriotiry | 任务优先级,范围 0~ configMAX_PRIORITIES-1。 |
pxCreatedTask | 任务句柄,任务创建成功以后会返回此任务的任务句柄, 这个句柄其实就是 任务的任务堆栈。 此参数就用来保存这个任务句柄。其他 API 函数可能会使 用到这个句柄。 |
返回值
pdPASS | 任务创建成功。 |
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY | 任务创建失败,因为堆内存不足! |
队列创建
函数xQueueCreate();
此函数本质上是一个宏,用来动态创建队列, 此宏最终调用的是函数 xQueueGenericCreate(),
函数原型如下
QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength,
UBaseType_t uxItemSize)
参数:
uxQueueLength | 要创建的队列的队列长度,这里是队列的项目数。 |
uxItemSize | 队列中每个项目(消息)的长度,单位为字节 |
返回值:
其他值 | 队列创捷成功以后返回的队列句柄! |
NULL | 队列创建失败 |
函数xQueueCreateStatic()
此函数也是用于创建队列的,但是使用的静态方法创建队列,队列所需要的内存由用户自
行分配,此函数本质上也是一个宏, 此宏最终调用的是函数 xQueueGenericCreateStatic(), 函数
原型如下:
QueueHandle_t xQueueCreateStatic(UBaseType_t uxQueueLength,
UBaseType_t uxItemSize,
uint8_t * pucQueueStorageBuffer,
StaticQueue_t * pxQueueBuffer)
参数说明:
uxQueueLength | 要创建的队列的队列长度,这里是队列的项目数。 |
uxItemSize | 队列中每个项目(消息)的长度,单位为字节 |
pucQueueStorage | 指向队列项目的存储区,也就是消息的存储区,这个存储区需要用户自 行分配。 此参数必须指向一个 uint8_t 类型的数组。 这个存储区要大于等 于(uxQueueLength * uxItemsSize)字节。 |
pxQueueBuffer | 此参数指向一个 StaticQueue_t 类型的变量,用来保存队列结构体 |
返回值:
其他值 | 队列创捷成功以后的队列句柄! |
NULL | 队列创建失败 |
分类 | 函数 | 描述 |
任务级入队函数 | xQueueSend() | 发送消息到队列尾部(后向入队),这两个函数是一样的 |
xQueueSendToBack() | ||
xQueueSendToFront() | 发送消息到队列头(向前入队) | |
xQueueOverwrite() | 发送消息到队列,带覆写功能,当队列满了以后自动覆盖掉旧的消息 | |
中断级入队函数 | xQueueSendFromISR() | 发送消息到队列尾部(后向入队),这两个函数是一样的,用于中断服务函数 |
xQueueSendToBackFromISR() | ||
xQueueSendToFrontFromISR() | 发送消息到队列头(向前入队)用于中断服务函数 | |
xQueueOverwriteFromISR() | 发送消息到队列,带覆写功能,当队列满了以后自动覆盖掉旧的消息,用于终端服务函数 |
函数 xQueueSend()、 xQueueSendToBack()和 xQueueSendToFront()
这三个函数都是用于向队列中发送消息的,这三个函数本质都是宏, 其中函数 xQueueSend()
和 xQueueSendToBack()是一样的,都是后向入队,即将新的消息插入到队列的后面。函数
xQueueSendToToFront()是前向入队,即将新消息插入到队列的前面。然而!这三个函数最后都
是调用的同一个函数: xQueueGenericSend()。这三个函数只能用于任务函数中,不能用于中断
服务函数,中断服务函数有专用的函数,它们以“FromISR”结尾, 这三个函数的原型如下
BaseType_t xQueueSend( QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait);
BaseType_t xQueueSendToBack(QueueHandle_t xQueue,
const void* pvItemToQueue,
TickType_t xTicksToWait);
BaseType_t xQueueSendToToFront(QueueHandle_t xQueue,
const void *pvItemToQueue,
TickType_t xTicksToWait);
参数:
xQueue | 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的 队列句柄 |
pvItemToQueue | 指向要发送的消息,发送时候会将这个消息拷贝到队列中。 |
xTicksToWait | 阻塞时间,此参数指示当队列满的时候任务进入阻塞态等待队列空闲的最大时间。如果为 0 的话当队列满的时候就立即返回; 当为 portMAX_DELAY 的 话就会一直等待,直到队列有空闲的队列 项,也就是死等,但是宏INCLUDE_vTaskSuspend 必须为 1 |
返回值:
pdPASS | 向队列发送消息成功! |
errQUEUE_FULL | 队列已经满了,消息发送失败 |
函数 xQueueOverwrite():
此函数也是用于向队列发送数据的, 当队列满了以后会覆写掉旧的数据, 不管这个旧数据
有没有被其他任务或中断取走。 这个函数常用于向那些长度为 1 的队列发送消息, 此函数也是
一个宏,最终调用的也是函数 xQueueGenericSend() 原型如下:
BaseType_t xQueueOverwrite(QueueHandle_t xQueue,
const void * pvItemToQueue);
参数:
xQueue | 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄。 |
pvItemToQueue | 指向要发送的消息,发送的时候会将这个消息拷贝到队列中 |
返回值:
pdPASS | 向队列发送消息成功,此函数也只会返回 pdPASS! 因为此函数执行过程中不在乎队列满不满,满了的话我就覆写掉旧的数据,总之肯定能成功 |
函数 xQueueSendFromISR()、xQueueSendToBackFromISR(), xQueueSendToFrontFromISR()
这三个函数也是向队列中发送消息的,这三个函数用于中断服务函数中。这三个函数本质
也宏,其中函数 xQueueSendFromISR ()和 xQueueSendToBackFromISR ()是一样的,都是后向入
队,即将新的消息插入到队列的后面。函数 xQueueSendToFrontFromISR ()是前向入队,即将新
消息插入到队列的前面。这三个函数同样调用同一个函数 xQueueGenericSendFromISR ()。这三
个函数的原型如下
BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,
const void * pvItemToQueue,
BaseType_t * pxHigherPriorityTaskWoken);
BaseType_t xQueueSendToBackFromISR(QueueHandle_t xQueue,
const void * pvItemToQueue,
BaseType_t * pxHigherPriorityTaskWoken);
BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,
const void * pvItemToQueue,
BaseType_t * pxHigherPriorityTaskWoken);
参数:
xQueue | 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄 |
pvItemToQueue | 指向要发送的消息,发送的时候会将这个消息拷贝到队列中。 |
pxHigherPriorityTaskWoken | 标记退出此函数以后是否进行任务切换这个变量的值由这三个函数来设置的,用户不用进行设置,用户只需要提供一 个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换 |
返回值:
pdTRUE | 向队列中发送消息成功! |
errQUEUE_FULL | 队列已经满了,消息发送失败 |
从队列读取消息
分类 | 函数 | 描述 |
任务级出队函数 | xQueueReceive() | 从队列中读取队列项(消息),并且读取完以后删除掉队列项(消息) |
xQueuePeek() | 从队列中读取队列项(消息),并且读取完以后不删除队列项(消息) | |
中断级出队函数 | xQueueReceiveFromISR() | 从队列中读取队列项(消息),并且读取完以后删除掉队列项(消息),用于中断服务函数中 |
xQueuePeekFromISR () | 从队列中读取队列项(消息),并且读取完以后不删除队列项(消息),用于中断服务函数中 |
函数 xQueueReceive() xQueuePeek()
BaseType_t xQueueReceive(QueueHandle_t xQueue,
void * pvBuffer,
TickType_t xTicksToWait);
BaseType_t xQueuePeek(QueueHandle_t xQueue,
void * pvBuffer,
TickType_t xTicksToWait);
参数:
xQueue | 队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此队列的队列句柄 |
pvBuffer | 保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区中 |
xTicksToWait | 阻塞时间,此参数指示当队列空的时候任务进入阻塞态等待队列有数据的最大时间。如果为 0 的话当队列空的时候就立即返回;当为 portMAX_DELAY的 话 就 会 一 直 等 待 , 直 到 队 列 有 数 据 , 也 就 是 死 等 , 但 是 宏 INCLUDE_vTaskSuspend 必须为 1。 |
返回值:
pdTRUE | 从队列中读取数据成功 |
pdFALSE | 从队列中读取数据失败 |
函数 xQueueReceiveFromISR()
BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue,
void* pvBuffer,
BaseType_t * pxTaskWoken);
参数:
xQueue | 队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此队列的队列句柄 |
pvBuffer | 保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区中 |
pxTaskWoken | 标记退出此函数以后是否进行任务切换,这个变量的值是由函数来设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。 |
返回值:
pdTRUE | 从队列中读取数据成功 |
pdFALSE | 从队列中读取数据失败 |
函数 xQueuePeekFromISR()
此函数是 xQueuePeek()的中断版本此函数在读取成功以后不会将消息删除,此函数原型如下:
BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue,
void * pvBuffer)
参数:
xQueue | 队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此队列的队列句柄 |
pvBuffer | 保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区中 |
返回值:
pdTRUE | 从队列中读取数据成功 |
pdFALSE | 从队列中读取数据失败 |
具体参考正点原子教程,初学整理可能有错误,如对大家学习造成误解,望大家谅解