本文是FreeRTOS复习笔记的第三节,任务挂起和恢复,使用的开发板是stm32f407VET6,创建两个任务,task1负责闪烁LED,task2负责按键控制,当按键按下时控制任务挂起,按键再次按下恢复任务,并通过串口打印任务状态。
上一篇文章: 【复习笔记】FreeRTOS(二)创建和删除任务
一、实验目的
首先回顾一下FreeRTOS的四个任务状态:就绪态、运行态、挂起态和阻塞态。他们之间的关系如下:
- 就绪态:该任务在就绪列表中,就绪的任务已经具备执行的能力,只等待调度器进行调度,新创建的任务会初始化为就绪态。
- 运行态:该状态表明任务正在执行,此时它占用处理器,FreeRTOS 调度器选择运行的永远是处于最高优先级的就绪态任务,当任务被运行的一刻,它的任务状态就变成了运行态。
- 阻塞态:如果任务当前正在等待某个时序或外部中断,我们就说这个任务处于阻塞状态,该任务不在就绪列表中。包含任务被挂起、任务被延时、任务正在等待信号量、读写队列或者等待读写事件等。
- 挂起态:处于挂起态的任务对调度器而言是不可见的,让一个任务进入挂起状态的唯一办法就是调用 vTaskSuspend()函数;而把 一 个挂起状态的任务恢复的唯一途径就是调用vTaskResume() 或 vTaskResumeFromISR()函数。我们可以这么理解挂起态与阻塞态的区别,当任务有较长的时间不允许运行的时候,我们可以挂起任务,这样子调度器就不会管这个任务的任何信息,直到我们调用恢复任务的 API 函数;而任务处于阻塞态的时候,系统还需要判断阻塞态的任务是否超时,是否可以解除阻塞。
本篇内容复习的是FreeRTOS中的任务恢复与挂起。
有时候我们需要将暂停某个任务的运行,过一段时间以后在重新运行,如果使用任务删除和重建的方法,那么任务中变量保存的值肯定丢了.FreeRTOS给我们提供了解决这种问题的方法,那就是任务的恢复与挂起,当莫个任务要停止运行一段时间的话就将这个任务挂起,当要重新运行这个任务的话就恢复这个任务的运行。FreeRTOS的主要用到3个API函数:
函数 | 作用 |
---|---|
vTaskSuspend() | 挂起一个任务,进入挂起态的任务永远都不会进入运行态 |
vTaskResume() | 恢复一个任务,恢复运行被挂起的任务 |
xTaskResumeFromISR() | 中断服务函数中恢复一个任务 |
1.任务挂起函数:
vTaskSuspend()
此函数用于将某个任务设置为挂起态,进入挂起态的任务永远都不会进入运行态。退出挂起态的唯一方法就是调用任务恢复函数vTaskResume()xTaskResumeFromISR()。
2.任务恢复函数:
vTaskResume()
将一个任务从挂起态恢复到就绪态,只有通过函数 vTaskSuspend()设置为挂起态的任务才可以使用 vTaskRexume()恢复。
3.中断服务函数中使用的任务恢复函数:
BaseType_t xTaskResumeFromISR()
此函数是 vTaskResume()的中断版本,用于在中断服务函数中恢复一个任务
返回值:
pdTrue:恢复运行的任务的任务优先级等于或者高于正在运行的任务(被中断打断的任务),在退出中断服务函数以后必须进行一次上下文切换。
pdFALSE: 恢复运行的任务的任务优先级低于当前正在运行的任务(被中断打断的任务),在退出中断服务函数的以后不需要进行上下文切换。
本次实验就是使用一个按键控制任务的挂起和恢复。
二、测试例程
主函数 main.c代码如下:
/**
* @file main.c
* @author HuiTanglang
* @version V1.0
* @date 2023.05
* @brief This file provides firmware functions to manage the following
* functionalities of HuiTanglang;
*/
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"
#include "key.h"
#include "FreeRTOS.h"
#include "task.h"
#define START_TASK_PRIO 1 //任务优先级
#define START_STK_SIZE 128 //任务堆栈大小
TaskHandle_t StartTask_Handler; //任务句柄
void start_task(void *pvParameters); //任务函数
#define TASK1_TASK_PRIO 2 //任务优先级
#define TASK1_STK_SIZE 128 //任务堆栈大小
TaskHandle_t TASK1Task_Handler; //任务句柄
void task1_task(void *p_arg); //任务函数
#define TASK2_TASK_PRIO 3 //任务优先级
#define TASK2_STK_SIZE 128 //任务堆栈大小
TaskHandle_t TASK2Task_Handler; //任务句柄
void task2_task(void *p_arg); //任务函数
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口
// EXTIX_Init();
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(); //开启任务调度
}
/**
* @brief 开始任务任务函数
* @param None
* @retval None
*/
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
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);
xTaskCreate((TaskFunction_t )task2_task,
(const char* )"task2_task",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_TASK_PRIO,
(TaskHandle_t* )&TASK2Task_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
/**
* @brief 显示任务1运行,而且LED 0.5s闪烁
* @param None
* @retval None
*/
void task1_task(void *pvParameters)
{
u8 task_num=0;
while(1)
{
task_num++;
LED0=~LED0;
printf("任务1运行次数%d\r\n",task_num);
vTaskDelay(1000);
}
}
/**
* @brief 按键按下就删除任务1的执行程序,按一下挂起,再按一下恢复
* @param None
* @retval None
*/
void task2_task(void *pvParameters)
{
u8 key1;
u8 key_num=0;
while(1)
{
key1=KEY_Scan(0);
if(key1==WKUP_PRES)
{
key_num++;
}
if(key_num%2==0 && key_num!=0)
{
vTaskResume(TASK1Task_Handler); //恢复任务1
printf("恢复任务1的运行!\r\n");
}
if(key_num%2==1)
{
vTaskSuspend(TASK1Task_Handler);//挂起任务1
printf("挂起任务1的运行!\r\n");
}
vTaskDelay(1000);
}
}
按键驱动函数,key.c
#include "key.h"
#include "delay.h"
/**
* @brief 按键初始化函数,设置PA0为按键接口,按下就通过电阻接3.3上拉
* @param None
* @retval None
*/
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA,时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//WK_UP对应引脚PA0--K1按键
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100M
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN ;//下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA0
}
/**
* @brief 按键处理函数,返回按键值
* @param mode:0,不支持连续按;1,支持连续按;
* @retval 0,没有任何按键按下
* @retval 1,WKUP按下 --对应K1按键
*/
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;//按键按松开标志
if(mode)key_up=1; //支持连按
if(key_up&&(WK_UP==1))
{
delay_ms(10);//去抖动
key_up=0;
if(WK_UP==1) return WKUP_PRES;
}
else if(WK_UP==0)key_up=1;
return 0;// 无按键按下
}
三、实验效果
本实验使用的开发板是stm32f407VET6最小系统板。
实验效果如下:
可以看到led在闪烁,接上串口,通过串口工具可以看到打印的任务1运行次数,每过1秒次数加1。当第一次按下按键时,LED停止闪烁,串口显示挂起任务1状态。当再次按下按键时,LED恢复闪烁,通过串口可以看到打印的任务1运行次数,运行次数是从挂起之前的次数开始计数,起到了一个恢复计数的功能。文章来源:https://www.toymoban.com/news/detail-471230.html
本节主要是通过一个小实验,学习和掌握FreeRTOS任务挂起和恢复相关API函数使用。
完整程序放在gitee上:程序下载地址文章来源地址https://www.toymoban.com/news/detail-471230.html
到了这里,关于【复习笔记】FreeRTOS(三)任务挂起和恢复的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!