目录
一.什么是多任务系统
不同于我们之前学习的51,32等单片机裸机(未使用操作系统),我们一般编写代码主要在main函数中,将封装好的函数放置main函数的一个主循环中,代码按照先后顺序来执行,中断服务函数触发可打断主函数的执行,来执行中断服务函数,这个就是单任务系统。FreeRtos是一个抢占式的实时多任务系统,其任务调度器也是抢占式的,它通过给任务分配优先级,优先级别高的先执行,高优先级的任务执行完而后将CPU的使用权交给低优先级的任务。
二.任务状态
1.就绪态:该任务处于就绪列表当中,已经具备执行的能力,等待任务调度器进行调度,新创建的任务初始化为就绪态。
2.运行态:任务处于运行状态中,此时该任务正在占用CPU。
3.阻塞态:如果任务当前正在等待某个时序或外部中断,我们就说这个任 务处于阻塞状态,该任务不在就绪列表中。包含任务被挂起、任务被延时、任务 正在等待信号量、读写队列或者等待读写事件等。
4.挂起态:当任务被挂起时,对于任务调度器来说是不可见的,该任务不会被任务调度器所调度。让任务处于挂起态仅能通过调用 vTaskSuspend()函数,而将处于挂起态任务的恢复的途径是调用 vTaskResume() 或 vTaskResumeFromISR()函 数(带ISR指仅可在中断中使用),调用这两个函数便可将处于挂起态的任务重新恢复成正常任务,能够重新别任务调度器所识别,进行正常的任务调度。
三.常用函数详解
1.任务挂起函数
void vTaskSuspend(TaskHandle_t xTaskToSuspend)
xTaskToSuspend:该参数为要挂起的任务句柄,如果使用函数xTaskCreate()创建任务的话那么函数的参数pxCreatedTask 就是此任务的任务句柄,如果使用函数xTaskCreateStatic()创建任务的话那么函数的返回值就是此任务的任务句柄。。通过传入指定的任务句柄,则挂起的任务不会获得CPU的使用权,除非将其解除挂起状态不然无法被任务调度器所调度。
void vTaskSuspendAll(void)
挂起所有的任务,所有任务无法得到cpu的使用权,无法正常执行。
2.任务恢复函数
void vTaskResume(TaskHandle_t xTaskToResume)
TaskHandle_t xTaskToResume:参数为需要恢复的任务句柄,将一个任务从挂起态恢复到就绪态。只有通过函数vTaskSuspend()设置为挂起态的任务才可以使用vTaskRexume()恢复!
void xTaskResumeFromISR(TaskHandle_t xTaskToResume) 与上述函数类似运用于中断服务函数当中
3.任务删除函数
void vTaskDelect(TaskHandle_t xTaskToDelect)
TaskHandle_t xTaskToDelect:参数为需要删除的任务的任务句柄。删除的任务将从所有就绪,阻塞, 挂起和事件列表中删除。
4..任务延时函数
void xTaskDelay(const TickType_t xTickToDelay)
通过该函数能够让每个处于死循环的任务进入阻塞态,这样高优先级的任务在使用CPU执行完任务后,通过任务延时函数进入阻塞态,让出CPU的使用权,给低优先级别的任务使用CPU执行任务。
void vTaskDelayUntil()
这个绝对延时常用于较精确的周期运行任务,比如我有一个任务希望它以固定频率定期执行,而不受外部的影响,任务从上一次运行开始到下一次运行开始的时间间隔是绝对的,而不是相对的。
四.任务管理实验
#include "FreeRTOS.h"
#include "task.h"
#include "bsp_led.h"
#include "bsp_usart.h"
/**************************** 任务句柄 ********************************/
/* 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
* 以后我们想要操作这个任务都需要这个任务句柄,如果是自身操作自己,那么
* 这个句柄就是NULL
*
*/
/* 创建任务任务句柄 */
TaskHandle_t AppTaskCreate_Handle = NULL;
/* LED任务句柄*/
TaskHandle_t LED_Task_Handle = NULL;
/* KEY任务句柄 */
TaskHandle_t KEY_Task_Handle = NULL;
/*
*************************************************************************
* 函数声明
*************************************************************************
*/
void AppTaskCreate(void);/*用于任务创建 */
void LED_Task(void* Parameter);/* LED_Task任务 */
void KEY_Task(void* parameter); /* 按键任务 */
void BSP_Init(void);/* 用于初始化板载相关资源 */
/*****************************************************************
* @brief 主函数
* @param 无
* @retval 无
* @note 第一步:开发板硬件初始化
第二步:创建APP应用任务
第三步:启动FreeRTOS,开始多任务调度
****************************************************************/
int main(void)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
/* 开发板硬件初始化 */
BSP_Init();
/* 创建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); /* 正常不会执行到这里 */
}
void AppTaskCreate(void)
{
taskENTER_CRITICAL(); //进入临界区
/* 创建LED_Task任务 */
xTaskCreate((TaskFunction_t )LED_Task, /* 任务入口函数 */
(const char* )"LED_Task",/* 任务名字 */
(uint16_t )128, /* 任务栈大小 */
(void* )NULL, /* 任务入口函数参数 */
(UBaseType_t )2, /* 任务的优先级 */
(TaskHandle_t* )&LED_Task_Handle);/* 任务控制块指针 */
/* 创建KEY_Task任务 */
xTaskCreate((TaskFunction_t )KEY_Task, /* 任务入口函数 */
(const char* )"KEY_Task",/* 任务名字 */
(uint16_t )256, /* 任务栈大小 */
(void* )NULL,/* 任务入口函数参数 */
(UBaseType_t )6, /* 任务的优先级 */
(TaskHandle_t* )&KEY_Task_Handle);/* 任务控制块指针 */
vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
taskEXIT_CRITICAL(); //退出临界区
}
void LED_Task(void* parameter)
{
/*初始化USART 配置模式为 115200 8-N-1,中断接收*/
USART_Config();
/* 发送一个字符串 */
printf("FreeRTOS中断管理实验\r\n");
while (1)
{
LED1_ON;
vTaskDelay(500); /* 延时500个tick */
LED1_OFF;
vTaskDelay(500); /* 延时500个tick */
}
}
void KEY_Task(void* parameter) //任务挂起与恢复按键
{
uint8_t key=0;
while(1)
{
key=Key_GetNum();
if(key== 1)
{
printf("%d\r\n",key);
//OLED_ShowString(2,1,"KEY:");
//OLED_ShowNum(2,5,key,1);
vTaskSuspend(LED_Task_Handle);/* 挂起LED任务 */
}
else if(key== 2)
{
vTaskResume(LED_Task_Handle);/* 恢复LED任务!*/
//OLED_ShowString(2,1,"KEY:");
//OLED_ShowNum(2,5,key,1); //OLED显示key的值
printf("%d\r\n",key); //将key的值打印至串口
}
vTaskDelay(20);
}
}
void BSP_Init(void)
{
/*
* STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
* 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
* 都统一用这个优先级分组,千万不要再分组,切忌。
*/
SysTick_Init(72);//延时时钟初始化
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
/* LED 初始化 */
LED_GPIO_Config();
/*串口初始化*/
USART_Config();
/*按键初始化*/
Key_GPIO_Config();
}
实验现象为按下按键1则LED_Task任务被挂起,LED停止闪烁,串口接收内容key的值为1.按下按键2LED_Task恢复运行,LED继续闪烁,串口接收内容key的值为2。
参考博客:
https://blog.csdn.net/tichimi3375/article/details/80671840
https://blog.csdn.net/qq_61672347/article/details/125554394?spm=1001.2014.3001.5502