以下内容皆是个人学习过程中的总结,记录一下整个过程,用于后期复习,如有不对之处,麻烦各位大佬指出~
(喜欢的朋友麻烦点个关注~~~ 后期还会进行持续更新)
一、 任务状态
首先我们要知道,在FreeRTOS 中任务永远处于下面几个状态中的某一个:
● 运行态 当一个任务正在运行时,那么就说这个任务处于运行态,处于运行态的任务就是当前正在 使用处理器的任务。如果使用的是单核处理器的话那么不管在任何时刻永远都只有一个任务处 于运行态。
● 就绪态 处于就绪态的任务是那些已经准备就绪(这些任务没有被阻塞或者挂起),可以运行的任务, 但是处于就绪态的任务还没有运行,因为有一个同优先级或者更高优先级的任务正在运行!
● 阻塞态 如果一个任务当前正在等待某个外部事件的话就说它处于阻塞态,比如说如果某个任务调 用了函数 vTaskDelay()的话就会进入阻塞态,直到延时周期完成。任务在等待队列、信号量、事 件组、通知或互斥信号量的时候也会进入阻塞态。任务进入阻塞态会有一个超时时间,当超过 这个超时时间任务就会退出阻塞态,即使所等待的事件还没有来临!
● 挂起态 像阻塞态一样,任务进入挂起态以后也不能被调度器调用进入运行态,但是进入挂起态的 任务没有超时时间。任务进入和退出挂起态通过调用函数 vTaskSuspend()和 xTaskResume()。
任务状态之间的转换如下图所示:
二、 任务恢复与挂起API函数
有时候我们需要暂停某个任务的运行,过一段时间以后在重新运行。这个时候要是使用任 务删除和重建的方法的话那么任务中变量保存的值肯定丢失了!FreeRTOS 给我们提供了解决 这种问题的方法,那就是任务挂起和恢复,当某个任务要停止运行一段时间的话就将这个任务 挂起,当要重新运行这个任务的话就恢复这个任务的运行。FreeRTOS 的任务挂起和恢复 API 函 数如下表所示:
2.1、函数 vTaskSuspend()
此函数用于将某个任务设置为挂起态,进入挂起态的任务永远都不会进入运行态。退出挂起态的唯一方法就是调用任务恢复函数 vTaskResume()或 xTaskResumeFromISR(),函数原型如 下:
void vTaskSuspend( TaskHandle_t xTaskToSuspend)
参数:
xTaskToSuspend:要挂起的任务的任务句柄,创建任务的时候会为每个任务分配一个任务句柄。如果使用函数 xTaskCreate()创建任务的话那么函数的参数 pxCreatedTask 就是此任务的任务句柄,如果使用函数 xTaskCreateStatic() 创建任务的话那么函数的返回值就是此任务的任务句柄。也可以通过函 数 xTaskGetHandle()来根据任务名字来获取某个任务的任务句柄。 注意!如果参数为 NULL 的话表示挂起任务自己。
返回值: 无
2.2、函数 vTaskResume()
将一个任务从挂起态恢复到就绪态,只有通过函数 vTaskSuspend()设置为挂起态的任务才 可以使用 vTaskRexume()恢复!函数原型如下:
void vTaskResume( TaskHandle_t xTaskToResume)
参数:
xTaskToResume: 要恢复的任务的任务句柄。
返回值: 无。
2.3、函数 xTaskResumeFromISR()
此函数是 vTaskResume()的中断版本,用于在中断服务函数中恢复一个任务。函数原型如下:
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume)
参数:
xTaskToResume: 要恢复的任务的任务句柄。
返回值:
pdTRUE: 恢复运行的任务的任务优先级等于或者高于正在运行的任务(被中断打 断的任务),这意味着在退出中断服务函数以后必须进行一次上下文切换。
pdFALSE: 恢复运行的任务的任务优先级低于当前正在运行的任务(被中断打断的 任务),这意味着在退出中断服务函数的以后不需要进行上下文切换。
三、任务创建与恢复实验
3.1、实验目的
学习使用 FreeRTOS 的任务挂起和恢复相关 API 函数,包括 vTaskSuspend()、vTaskResume()
3.2、实验设计
本实验设计4个任务:start_task、task1_task、task2_task、key_task,这四个任务的功能分别如下:
start_task:用于创建其他3个任务。
key_task:按键服务任务,检测按键按下的结果,根据不同的按键结果执行不同的操作
task1_task:应用任务1
task2_task:应用任务2
实现需要两个按键,KEY0、KEY1,这两个按键的功能分别如下:
KEY0:此按键为输入按键,用于挂起任务1
KEY1:此按键为输入按键,用于恢复任务1
3.3、实验效果
一开始任务 1 和任务 2 都正常运行,当挂起任务1以后任务1就会停止运行,直到下一次重新恢复任务1的运行。重点是,保存任务运行次数的变量都没有发生数据丢失,如果用任务删除和重建的方法这些数据必然会丢失掉的!
3.4、代码部分
最后附上main.c以及key.c、key.h代码,供大家学习参考
key.c
#include "stm32f10x.h"
#include "key.h"
#include "sys.h"
#include "delay.h"
//按键初始化函数
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);//使能PORTE时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12;//KEY1-KEY4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置为上拉输入
GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE9.10.11.12
}
//检测按键
u8 KEY_Scan(void)
{
if(KEY0==0)
{
delay_ms(500);
if(KEY0==0)
{
return Presskey0; //按键0摁下,返回Presskey0
}
}else if(KEY1==0)
{
delay_ms(500);
if(KEY1==0)
{
return Presskey1; //按键1摁下,返回Presskey1
}
}
return 0; //如果没有按键摁下,返回0
}
key.h
#ifndef __KEY_H
#define __KEY_H
#include "sys.h"
#define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_9)//读取引脚0
#define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_10)//读取引脚1
#define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_11)//读取引脚2
#define KEY3 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_12)//读取引脚3
#define Presskey0 1 //KEY0
#define Presskey1 2 //KEY1
void KEY_Init(void);//IO初始化
u8 KEY_Scan(void);
#endif
main.c
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
#define START_TASK_PRIO 1 //任务优先级
#define START_STK_SIZE 128 //任务堆栈大小
TaskHandle_t StartTask_Handler; //任务句柄
void start_task(void *pvParameters); //任务函数
#define TASK1_TASK_PRIO 3 //任务优先级
#define TASK1_STK_SIZE 128 //任务堆栈大小
TaskHandle_t Task1Task_Handler; //任务句柄
void task1_task(void *pvParameters); //任务函数
#define TASK2_TASK_PRIO 2 //任务优先级
#define TASK2_STK_SIZE 128 //任务堆栈大小
TaskHandle_t Task2Task_Handler; //任务句柄
void task2_task(void *pvParameters); //任务函数
#define KEY_TASK_PRIO 4 //任务优先级
#define KEY_STK_SIZE 128 //任务堆栈大小
TaskHandle_t KeyTask_Handler; //任务句柄
void key_task(void *pvParameters); //任务函数
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级
delay_init(); //延时初始化
uart_init(115200); //串口初始化
LED_Init(); //初始化LED
KEY_Init(); //³õʼ»¯KEY
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(); //开启任务调度
}
//start_task任务函数
void start_task(void *pvParameters)
{
while(1)
{
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); //任务句柄
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); //任务句柄
vTaskDelete(StartTask_Handler); //删除start_task()任务
taskEXIT_CRITICAL(); //退出临界区
}
}
//task1_task任务函数
void task1_task(void *pvParameters)
{
int num=0;
while(1)
{
num++;
LED0=~LED0;
printf("\r\n正在运行task1_task:%d\r\n",num);
vTaskDelay(500);
}
}
//task2_task任务函数
void task2_task(void *pvParameters)
{
int num=0;
while(1)
{
num++;
LED1=1;
vTaskDelay(500);
LED1=0;
vTaskDelay(800);
printf("\r\n正在运行tatk2_task:%d\r\n",num);
}
}
//key_task任务函数
void key_task(void *pvParameters)
{
u8 key=0;
while(1)
{
key=KEY_Scan();
switch(key)
{
case Presskey0:
vTaskSuspend(Task1Task_Handler);
printf("\r\n挂起任务1\r\n");
break;
case Presskey1:
vTaskResume(Task1Task_Handler);
printf("\r\n恢复任务1\r\n");
break;
}
vTaskDelay(10);
}
}