FreeRTOS任务创建与删除实验
动态创建
实验:
1.创建start任务;
2.再start任务里面创建LED0、LED1、KEY三个任务;
3.LED0函数功能为每500ms翻转一次;
LED1函数功能为每500ms翻转一次;
KEY函数功能为按下删除LED0任务;
注:我的板子按键为PB12,按下为低电平;LED灯是PB14和PB15,低电平点亮。
流程:
1.将configSUPPORT_STSTIC_ALLOCATION设置为1(无需设置,默认就是1);
2.创建开始任务;
3.编写任务函数;
代码实现:
main.c
#include "stm32f10x.h"
#include "delay.h"
#include "led.h"
#include "sys.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_TASK_STACK_SIZE 128
//任务句柄
TaskHandle_t start_task_handler;
//开始任务
void start_task(void *pvParameters);
//任务优先级
#define LED0_TASK_PRIO 2
//任务堆栈大小
#define LED0_TASK_STACK_SIZE 128
//任务句柄
TaskHandle_t led0_task_handler;
//LED0任务
void led0_task(void *pvParameters);
//任务优先级
#define LED1_TASK_PRIO 3
//任务堆栈大小
#define LED1_TASK_STACK_SIZE 128
//任务句柄
TaskHandle_t led1_task_handler;
//LED1任务
void led1_task(void *pvParameters);
//KEY任务
void key_task(void *pvParameters);
//任务优先级
#define KEY_TASK_PRIO 4
//任务堆栈大小
#define KEY_TASK_STACK_SIZE 128
//任务句柄
TaskHandle_t key_task_handler;
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组 4
GPIO_INIT();
KEY_INIT();
uart_init(9600);
//创建开始任务
xTaskCreate( (TaskFunction_t ) start_task, //任务函数指针
(char * ) "start_task", //任务函数名称
(configSTACK_DEPTH_TYPE ) START_TASK_STACK_SIZE, //任务堆栈大小
(void * ) NULL, //传递给任务函数的参数(一般为空)
(UBaseType_t ) START_TASK_PRIO, //任务优先级
(TaskHandle_t * ) &start_task_handler ); //任务句柄
//开启任务调度
vTaskStartScheduler();
}
开始任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建LED0任务
xTaskCreate( (TaskFunction_t ) led0_task,
(char * ) "led0_task",
(configSTACK_DEPTH_TYPE ) LED0_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) LED0_TASK_PRIO,
(TaskHandle_t * ) &led0_task_handler );
//创建LED1任务
xTaskCreate( (TaskFunction_t ) led1_task,
(char * ) "led1_task",
(configSTACK_DEPTH_TYPE ) LED1_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) LED1_TASK_PRIO,
(TaskHandle_t * ) &led1_task_handler );
// //创建KEY任务
xTaskCreate( (TaskFunction_t ) key_task,
(char * ) "key_task",
(configSTACK_DEPTH_TYPE ) KEY_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) KEY_TASK_PRIO,
(TaskHandle_t * ) &key_task_handler );
//删除自己
vTaskDelete(NULL);
taskEXIT_CRITICAL(); //退出临界区
}
//LED0 任务函数
void led0_task(void *pvParameters)
{
while(1)
{
printf("任务1在运行\n");
LED0_TOGGLE();
vTaskDelay(500);
}
}
//LED1 任务函数
void led1_task(void *pvParameters)
{
while(1)
{
printf("任务2在运行\n");
LED1_TOGGLE();
vTaskDelay(500);
}
}
//KEY 任务函数
void key_task(void *pvParameters)
{
u8 key;
while(1)
{
key=KEY_SCAN();
printf("任务3在运行\n");
if(key==1)
{
if(led0_task_handler!=NULL)
{
printf("删除LED0函数\n");
vTaskDelete(led0_task_handler);
led0_task_handler = NULL;
}
}
vTaskDelay(20);
}
}
led.c
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include "delay.h"
//技术支持:
//初始化PA6和PA7为输出口.并使能GPIOA的时钟
//LED IO初始化
void GPIO_INIT()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB,PE端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14|GPIO_Pin_15; //LED0-->PB.5 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
void KEY_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB,PE端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //按键接到了PA12引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置为上拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB.5
}
void LED0_TOGGLE(void)
{
u8 led_state = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14);
if(led_state == 0)
{
LED0_OFF;
}
else
{
LED0_ON;
}
}
void LED1_TOGGLE(void)
{
u8 led_state = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_15);
if(led_state == 0)
{
LED1_OFF;
}
else
{
LED1_ON;
}
}
u8 KEY_SCAN(void)
{
// 按键WK UP
if (KEY_UP == 0)
{
vTaskDelay(10); // 延时10ms消抖
if (KEY_UP == 0)
{
while (KEY_UP); // 松手检测
return 1;
}else
{
return 0;
}
}
return 0;
}
led.h
#ifndef __LED_H
#define __LED_H
#include "sys.h"
//技术支持:
#define LED0_OFF GPIO_SetBits(GPIOB,GPIO_Pin_14);
#define LED0_ON GPIO_ResetBits(GPIOB,GPIO_Pin_14);
#define LED1_OFF GPIO_SetBits(GPIOB,GPIO_Pin_15);
#define LED1_ON GPIO_ResetBits(GPIOB,GPIO_Pin_15);
#define KEY_UP GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12)
void GPIO_INIT(void);//初始化
void LED0_TOGGLE(void);
void LED1_TOGGLE(void);
u8 KEY_SCAN(void);
#endif
临界区作用:关闭中断(任务切换就是在中断中进行的);
taskENTER_CRITICAL(); //进入临界区
taskEXIT_CRITICAL(); //退出临界区
不添加临界区,使用串口打印你会发现:
任务2的优先级更高,但是先打印的确实任务1;这是因为在创建任务1时,创建完成直接加入了就绪列表,立马开始执行,执行到延时函数时才会创建任务2开始执行。
添加临界区之后:
会把所有任务创建完成之后,才根据优先级大小开始执行。
静态创建
代码实现:
main.c
#include "stm32f10x.h"
#include "delay.h"
#include "led.h"
#include "sys.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
//开始任务
void start_task(void *pvParameters);
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_TASK_STACK_SIZE 50
//任务句柄
TaskHandle_t start_task_handler;
//任务堆栈
StackType_t start_task_stack[START_TASK_STACK_SIZE];
//任务控制块
StaticTask_t start_task_tcb;
//LED0任务
void led0_task(void *pvParameters);
//任务优先级
#define LED0_TASK_PRIO 2
//任务堆栈大小
#define LED0_TASK_STACK_SIZE 50
//任务句柄
TaskHandle_t led0_task_handler;
//任务堆栈
StackType_t led0_task_stack[LED0_TASK_STACK_SIZE];
//任务控制块
StaticTask_t led0_task_tcb;
//LED1任务
void led1_task(void *pvParameters);
//任务优先级
#define LED1_TASK_PRIO 3
//任务堆栈大小
#define LED1_TASK_STACK_SIZE 50
//任务句柄
TaskHandle_t led1_task_handler;
//任务堆栈
StackType_t led1_task_stack[LED1_TASK_STACK_SIZE];
//任务控制块
StaticTask_t led1_task_tcb;
//KEY任务
void key_task(void *pvParameters);
//任务优先级
#define KEY_TASK_PRIO 4
//任务堆栈大小
#define KEY_TASK_STACK_SIZE 50
//任务句柄
TaskHandle_t key_task_handler;
//任务堆栈
StackType_t key_task_stack[KEY_TASK_STACK_SIZE];
//任务控制块
StaticTask_t key_task_tcb;
StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];
//空闲任务内存分配
void vApplicationGetIdleTaskMemory(StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
* ppxIdleTaskTCBBuffer = &idle_task_tcb;
* ppxIdleTaskStackBuffer = idle_task_stack;
* pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组 4
GPIO_INIT();
KEY_INIT();
uart_init(9600);
start_task_handler = xTaskCreateStatic( (TaskFunction_t) start_task,
(char * ) "start_task",
(uint32_t ) START_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(StackType_t * ) start_task_stack,
(StaticTask_t *) &start_task_tcb );
//开启任务调度
vTaskStartScheduler();
}
//开始 任务函数
void start_task(void *pvParameters)
{
printf("666\n");
//进入临界区
taskENTER_CRITICAL();
//LED0创建任务
led0_task_handler = xTaskCreateStatic( (TaskFunction_t) led0_task,
(char * ) "led0_task",
(uint32_t ) LED0_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) LED0_TASK_PRIO,
(StackType_t * ) led0_task_stack,
(StaticTask_t *) &led0_task_tcb );
//LED1创建任务
led1_task_handler = xTaskCreateStatic( (TaskFunction_t) led1_task,
(char * ) "led1_task",
(uint32_t ) LED1_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) LED1_TASK_PRIO,
(StackType_t * ) led1_task_stack,
(StaticTask_t *) &led1_task_tcb );
//按键任务创建
key_task_handler = xTaskCreateStatic( (TaskFunction_t) key_task,
(char * ) "key_task",
(uint32_t ) KEY_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) KEY_TASK_PRIO,
(StackType_t * ) key_task_stack,
(StaticTask_t *) &key_task_tcb );
//删除自己
vTaskDelete(NULL);
//退出临界区
taskEXIT_CRITICAL();
}
//LED0 任务函数
void led0_task(void *pvParameters)
{
while(1)
{
printf("任务1在运行\n");
LED0_TOGGLE();
vTaskDelay(500);
}
}
//LED1 任务函数
void led1_task(void *pvParameters)
{
while(1)
{
printf("任务2在运行\n");
LED1_TOGGLE();
vTaskDelay(500);
}
}
//KEY 任务函数
void key_task(void *pvParameters)
{
u8 key;
while(1)
{
key=KEY_SCAN();
printf("任务3在运行\n");
if(key==1)
{
if(led0_task_handler!=NULL)
{
printf("删除LED0函数\n");
vTaskDelete(led0_task_handler);
led0_task_handler = NULL;
}
}
vTaskDelay(20);
}
}
led.c和led.h同上。
区别
1.静态创建的时候,要先给空闲任务和定时器任务分配内存,定时器任务不是必需的,上面代码没有配置定时器任务。
2.静态创建相比动态创建需要手动分配任务堆栈和任务控制块,动态创建时FreeRTOS系统从自身管理堆中自动分配的。
3.静态任务需要通过任务句柄接收xTaskCreateStatic()函数。