1. 任务挂起与恢复
1.1 任务的挂起与恢复的 API 函数
API 函数 | 描述 |
---|---|
vTaskSuspend() | 挂起任务, 类似暂停,可恢复 |
vTaskResume() | 恢复被挂起的任务 |
xTaskResumeFromISR() | 在中断中恢复被挂起的任务 |
1.1.1 任务挂起函数原型
void vTaskSuspend( TaskHandle_t xTaskToSuspend );
-
需要定义宏来启动任务挂起函数
#define INCLUDE_vTaskSuspend 1 /* 启用挂起任务的API */
-
xTaskToSuspend:待挂起任务的任务句柄 TCB ,为 NULL 表示挂起任务自身
1.1.2 任务恢复函数原型
void vTaskResume( TaskHandle_t xTaskToResume )
-
需要定义宏来启动任务挂起函数
#define INCLUDE_vTaskSuspend 1 /* 启用挂起任务的API */
-
不论任务被使用 vTaskSuspend() 挂起多少次,只需调用 vTaskResume() 一次,即可使其继续执行。被恢复的任务会重新进入就绪状态。
1.1.2 任务恢复函数(中断中恢复 FromISR )
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume )
- pdTRUE:任务恢复后需要进行任务切换。
- pdFALSE:任务恢复后不需要进行任务切换。
-
需要定义宏来启动任务恢复函数
#define INCLUDE_vTaskSuspend 1 /* 启用挂起任务的API */ #define INCLUDE_xTaskResumeFromISR 1 /* 启用任务恢复的API */
-
在中断服务程序中调用 FreeRTOS 的 API 函数时,中断的优先级不能高于FreeRTOS 所管理的最高中断优先级
1.2 挂起与恢复调度器
- vTaskSuspendAll():挂起任务调度器,调度器不会进行任务切换,当前任务一直运行,但允许中断。
- xTaskResumeAll():恢复任务调度器,调度器继续任务切换。
通常搭配使用俩者,用于执行一段关键代码,防止任务切换。
1.3 查看任务状态
函数原型:
void vTaskList( char * pcWriteBuffer ) // 存放任务信息的字符数组,需要足够大以容纳所有任务信息
需要启动宏来开启跟踪任务状态信息
#define configUSE_TRACE_FACILITY 1 #define configUSE_STATS_FORMATTING_FUNCTIONS 1
vTaskList() 典型输出示例
vTaskList()
以固定格式输出任务信息,列的含义如下:
任务名称 状态 优先级 堆栈使用 任务编号
--------------------------------------------
task3 X 4 68 5
IDLE R 0 111 2
task1 B 2 68 3
task2 S 3 60 4
字段 | 示例值 | 含义 |
---|---|---|
任务名称(Task Name) | task3 | 任务名称(xTaskCreate() 设定的) |
状态(State) | X | 任务当前的运行状态 |
优先级(Priority) | 4 | 任务优先级,数值越大优先级越高 |
堆栈使用(Stack Usage) | 68 | 任务运行时最小剩余堆栈大小(单位:字节) |
任务编号(Task Number) | 5 | 任务的唯一标识符 |
在 vTaskList()
输出中,状态字段表示任务的当前状态:
状态字符 | 含义 |
---|---|
X (Running) | 正在运行的任务(当前占用 CPU 的任务) |
R (Ready) | 就绪状态(等待调度运行的任务) |
B (Blocked) | 阻塞状态(任务在等待某个事件,如信号量、队列等) |
S (Suspended) | 暂停状态(调用 vTaskSuspend() 暂停) |
D (Deleted) | 已删除(任务被 vTaskDelete() 删除) |
1.4 实践操作
start_task:用来创建其他的三个任务。
task1:实现 LED1 每 1000ms 闪烁一次。
task2:实现 LED2 每 100ms 闪烁一次。
task3:判断按键按下逻辑,KEY1 按下,挂起 task2,按下 KEY2 恢复 task2,KEY3 按下,挂起调度器,KEY4 按下,恢复调度器,KEY5按下,打印任务的状态。
1 ) FreeRTOSConfig.h
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
extern uint32_t SystemCoreClock;
/*-----------------------------------------------------------
* 应用程序特定的定义。
*
* 这些定义应该根据您的硬件和应用程序需求进行调整。
*
* 这些参数在FreeRTOS API文档的“配置”部分中有详细描述。
*
* 请参阅:http://www.freertos.org/a00110.html
*----------------------------------------------------------*/
#define configUSE_PREEMPTION 1 /* 1: 抢占式调度器, 0: 协程式调度器, 无默认需定义 */
#define configUSE_IDLE_HOOK 0 /* 禁用空闲钩子 */
#define configUSE_TICK_HOOK 0 /* 禁用滴答钩子 */
#define configCPU_CLOCK_HZ ( ( unsigned long ) 72000000 ) /* CPU时钟频率,72 MHz */
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) /* 时钟节拍频率,每秒1000次 */
#define configMAX_PRIORITIES ( 5 ) /* 最大优先级数 */
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 ) /* 最小栈大小 */
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 8 * 1024 ) ) /* 堆大小,17KB */
#define configMAX_TASK_NAME_LEN ( 16 ) /* 任务名称最大长度 */
#define configUSE_16_BIT_TICKS 0 /* 禁用16位滴答 */
#define configIDLE_SHOULD_YIELD 1 /* 空闲任务应让出CPU */
/* 设置以下定义为1以包含API函数,或设置为0以排除API函数 */
#define INCLUDE_vTaskPrioritySet 1 /* 启用设置任务优先级的API */
#define INCLUDE_uxTaskPriorityGet 1 /* 启用获取任务优先级的API */
#define INCLUDE_vTaskDelete 1 /* 启用删除任务的API */
#define INCLUDE_vTaskCleanUpResources 0 /* 禁用清理任务资源的API */
#define INCLUDE_vTaskSuspend 1 /* 启用挂起任务的API */
#define INCLUDE_vTaskDelayUntil 1 /* 启用延迟直到的API */
#define INCLUDE_vTaskDelay 1 /* 启用延迟的API */
/* 这是Cortex-M3 NVIC的原始值。值的范围为255(最低)到0(最高)。 */
#define configKERNEL_INTERRUPT_PRIORITY 255 /* 内核中断优先级 */
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY 不能设置为零 !!!!
请参阅 http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html。 */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 191 /* 等价于0xb0,或优先级11 */ /* 如果 ISR 优先级高于该值,则可能打断 FreeRTOS 内核关键代码,破坏任务调度、数据同步 */
/* 这是根据ST库使用的值,允许16个优先级值,0到15。
这必须与configKERNEL_INTERRUPT_PRIORITY设置相匹配。
此处15对应最低的NVIC值255。 */
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY 15 /* 库内核中断优先级 */
/* 由于 stm32 和 FreeRTOS 对变量命名的不同,我们进行宏定义 */
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
/* 开启任务调度器API,允许查询 FreeRTOS 调度器的状态 */
#define INCLUDE_xTaskGetSchedulerState 1
// #define configSUPPORT_DYNAMIC_ALLOCATION 1
/* 启用静态创建任务 */
#define configSUPPORT_STATIC_ALLOCATION 1
/* 启用中断中任务恢复的API */
#define INCLUDE_xTaskResumeFromISR 1
/* 开启跟踪 task 信息 */
#define configUSE_TRACE_FACILITY 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
#endif /* FREERTOS_CONFIG_H */
2 ) 任务函数
/* 启动任务配置 */
#define TASK_START_STACK_SIZE 128
#define TASK_START_PRIORITY 1
TaskHandle_t Task_start_Handle; // 如果后续不需要操作这个任务(删除,挂起等),可以省略这句
StackType_t start_task_stack[TASK_START_STACK_SIZE];
StaticTask_t start_task_tcb;
void task_start(void *pvParameters);
/* 任务1配置 */
#define TASK1_STACK_SIZE 128
#define TASK1_PRIORITY 2
TaskHandle_t Task1_Handler;
void task1(void *pvParameters);
/* 任务2配置 */
#define TASK2_STACK_SIZE 128
#define TASK2_PRIORITY 3
TaskHandle_t Task2_Handler;
void task2(void *pvParameters);
/* 任务3配置 */
#define TASK3_STACK_SIZE 128
#define TASK3_PRIORITY 4
TaskHandle_t Task3_Handler;
void task3(void *pvParameters);
/* 空闲任务配置 */
#define IDLE_TASK_STACK_SIZE configMINIMAL_STACK_SIZE
StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[IDLE_TASK_STACK_SIZE];
/* 空闲任务内存申请函数 */
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize)
{
*ppxIdleTaskTCBBuffer = &idle_task_tcb;
*ppxIdleTaskStackBuffer = idle_task_stack;
*pulIdleTaskStackSize = IDLE_TASK_STACK_SIZE;
}
/**
* @brief : FreeRTOS 入口函数:创建任务函数并开始调度
*
*/
void FreeRTOS_start()
{
Task_start_Handle = xTaskCreateStatic((TaskFunction_t)task_start, // 任务函数指针
(char *)"task_start", // 任务名称
(configSTACK_DEPTH_TYPE)TASK_START_STACK_SIZE, // 任务栈大小
(void *)NULL, // 传递给任务函数的参数
(UBaseType_t)TASK_START_PRIORITY, // 任务优先级
(StackType_t *)start_task_stack, // 任务栈指针(用户手动分配)
(StaticTask_t *)&start_task_tcb); // 任务控制块(TCB)指针(用户手动分配)
/* 启动任务调度器:自动创建空闲任务 */
vTaskStartScheduler();
}
/**
* @brief : 启动任务函数,创建三个任务,并删除自己
*
* @param pvParameters
*/
void task_start(void *pvParameters)
{
/* 进入临界区:保护临界区里的代码不会被打断 */
taskENTER_CRITICAL();
/* 创建三个任务 */
xTaskCreate((TaskFunction_t)task1,
(char *)"task1",
(configSTACK_DEPTH_TYPE)TASK1_STACK_SIZE,
(void *)NULL,
(UBaseType_t)TASK1_PRIORITY,
(TaskHandle_t *)&Task1_Handler);
xTaskCreate((TaskFunction_t)task2,
(char *)"task2",
(configSTACK_DEPTH_TYPE)TASK2_STACK_SIZE,
(void *)NULL,
(UBaseType_t)TASK2_PRIORITY,
(TaskHandle_t *)&Task2_Handler);
xTaskCreate((TaskFunction_t)task3,
(char *)"task3",
(configSTACK_DEPTH_TYPE)TASK3_STACK_SIZE,
(void *)NULL,
(UBaseType_t)TASK3_PRIORITY,
(TaskHandle_t *)&Task3_Handler);
/* 启动任务只需要执行一次即可,用完就删除自己 */
vTaskDelete(NULL);
/* 退出临界区 */
taskEXIT_CRITICAL();
}
/**
* @brief : 任务1函数,每隔1s翻转一次LED
*
* @param pvParameters
*/
void task1(void *pvParameters)
{
while (1)
{
led[0].state = !led[0].state;
printf("task1 runing...\r\n");
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, (GPIO_PinState)led[0].state);
vTaskDelay(1000);
}
}
/**
* @brief : 任务2函数,每隔100ms翻转一次LED
*
* @param pvParameters
*/
void task2(void *pvParameters)
{
while (1)
{
led[1].state = !led[1].state;
printf("task2 runing...\r\n");
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, (GPIO_PinState)led[1].state);
vTaskDelay(100);
}
}
/**
* @brief : 任务3函数,每隔100ms检测一次按键
*
* @param pvParameters
*/
char taskInfo[512];
void task3(void *pvParameters)
{
while (1)
{
for (int i = 0; i < 16; i++)
{
if (key[i].flag == 1)
{
if (i == 0)
{
printf("task2 suspend...\r\n");
vTaskSuspend(Task2_Handler);
}
else if (i == 1)
{
printf("task2 resume...\r\n");
vTaskResume(Task2_Handler);
}
else if (i == 2)
{
printf("task schduler suspend...\r\n");
vTaskSuspendAll();
}
else if (i == 3)
{
printf("task schduler resume...\r\n");
xTaskResumeAll();
}
else if (i == 4)
{
vTaskList(taskInfo);
printf("\r\ntask list:\r\n%s\r\n", taskInfo);
}
sprintf((char *)text, " key%d pressed ", i + 1);
OLED_ShowString(1, 0, (char *)text);
key[i].flag = 0;
}
}
vTaskDelay(1000);
}
}