FreeRTOS简单任务创建和任务删除(基于stm32F407)

1. 实验目的

使用动态方法 xTaskCreate()创建任务,使用vTaskDelete()函数删除任务;创建开始任务start_task,在开始任务中创建其他三个任务,创建task1任务实现LED0每500ms闪烁一次,创建task2任务实现LED1每500ms闪烁一次,创建task3判断按键KEY0是否按下,按下则关掉task1。

2. 实验流程

  1. 宏定义检查;
  2. 创建开始任务函数;
  3. 创建任务函数;
  4. 编写任务函数;
  5. 编写main函数

2.1 宏定义检查

检查下面的几个宏定义,是否开启抢占式调度器,是否使能时间片调度,是否支持动态申请内存,这几个宏都定义在了FreeRTOSConfig.h中 ,一般默认都是开启的。

#define configUSE_PREEMPTION                            1                       /* 1: 抢占式调度器, 0: 协程式调度器, 无默认需定义 */
#define configUSE_TIME_SLICING                          1                       /* 1: 使能时间片调度, 默认: 1 */
#define configSUPPORT_DYNAMIC_ALLOCATION                1              /* 1: 支持动态申请内存, 默认: 1 */

2.2 创建开始任务函数

下面是动态创建任务函数:
在这里插入图片描述

//引入头文件
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"      //包含LED的文件
#include "./BSP/KEY/key.h"      //包含按键的文件
#include "FreeRTOS.h"
#include "task.h"
//宏定义
//定义任务堆栈的大小
#define START_TASK_STACK_SIZE 128   //剩余历史最小堆栈(后面可以根据这个函数来定义这个堆栈的大小)
//定义任务优先级
#define START_TASK_PRIO    1
//定义任务句柄
TaskHandle_t start_task_handler;
//-----------------------------------------------------------
//声明task1任务函数
//定义任务堆栈的大小
#define TASK1_STACK_SIZE 128   //剩余历史最小堆栈(后面可以根据这个函数来定义这个堆栈的大小)
//定义任务优先级
#define TASK1_PRIO    2
//定义任务句柄
TaskHandle_t task1_handler;
//-----------------------------------------------------------
//声明task2任务函数
//定义任务堆栈的大小
#define TASK2_STACK_SIZE 128   //剩余历史最小堆栈(后面可以根据这个函数来定义这个堆栈的大小)
//定义任务优先级
#define TASK2_PRIO    3
//定义任务句柄
TaskHandle_t task2_handler;
//声明task3任务函数
//void task3(void * pvParameters);
//定义任务堆栈的大小
#define TASK3_STACK_SIZE 128   //剩余历史最小堆栈(后面可以根据这个函数来定义这个堆栈的大小)
//定义任务优先级
#define TASK3_PRIO    4
//定义任务句柄
TaskHandle_t task3_handler;
//声明函数,方便调用
void start_task(void * pvParameters);
void task1(void * pvParameters);
void task2(void * pvParameters);
void task3(void * pvParameters);
//创建开始任务函数,在这个开始任务中创建其他三个任务
void freertos_demo(void)
{
		       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();		 //开启任务调度					
          //在这里开启任务调度,不加临界区,下面就会依次执行任务,先打印的就是task1,因为创建完task1就会运行task1,他的优先级就比start就高	
}		

2.3 创建任务函数

临界区:临界区是指那些必须完整运行的区域,在临界区中的代码必须完整运行,不能被打断。所以在退出临界区以后才会进行调度任务,这样最先开始的任务就是优先级最高的任务,也就是task3。

void start_task(void * pvParameters ){   //只需要创建一次,不用加while(1)
							taskENTER_CRITICAL();   //进入临界区	    
	            //创建任务1
	           xTaskCreate( (TaskFunction_t           )        task1,              //创建开始函数
                            ( char *                 )       "task1",             //任务名字
                            ( configSTACK_DEPTH_TYPE )       TASK1_STACK_SIZE,    //堆栈空间
                            ( void *                 )       NULL,                //没有入口参数
                            ( UBaseType_t            )       TASK1_PRIO,          //任务优先级
                            ( TaskHandle_t *         )       &task1_handler);      //任务句柄
														
							//创建任务2
	           xTaskCreate( (TaskFunction_t           )        task2,               //创建开始函数
                            ( char *                 )       "task2",              //任务名字
                            ( configSTACK_DEPTH_TYPE )       TASK2_STACK_SIZE,     //堆栈空间
                            ( void *                 )       NULL,                 //没有入口参数
                            ( UBaseType_t            )       TASK2_PRIO,           //任务优先级
                            ( TaskHandle_t *         )       &task2_handler);       //任务句柄
	            //创建任务3
	           xTaskCreate( (TaskFunction_t           )        task3,               //创建开始函数
                            ( char *                 )       "task3",              //任务名字
                            ( configSTACK_DEPTH_TYPE )       TASK3_STACK_SIZE,     //堆栈空间
                            ( void *                 )       NULL,                 //没有入口参数
                            ( UBaseType_t            )       TASK3_PRIO,           //任务优先级
                            ( TaskHandle_t *         )       &task3_handler);       //任务句柄
              
							//创建完三个任务以后,需要把自己给删除
							//当参数是NULL的时候就是代表删除任务自己,这里传入开始任务的任务句柄也是可以的start_task_handler	
							               vTaskDelete(NULL);  	
//							              vTaskDelete(start_task_handler);  /* 删除开始任务 */
										 taskEXIT_CRITICAL();               //退出临界区,所以Task3会优先执行,退出临界区才会调度	
}

2.4 编写任务函数

//定义task1任务函数
//实现LED0每500ms翻转一次
void task1(void * pvParameters){
	while(1){
		printf("task1正在运行!!!\r\n");
		LED0_TOGGLE();   //LED0翻转
		vTaskDelay(500);          //进入这里会阻塞!!!!!
	}
}

//定义task2任务函数
//实现LED1每500ms翻转一次
void task2(void * pvParameters){
 while(1){
	  printf("task2正在运行!!!\r\n");
		LED1_TOGGLE();  //LED0翻转
		vTaskDelay(500); //延时500ms
	}
}

//定义task3任务函数
//判断按键按下KEY0,按下KEY0删除task1
void task3(void * pvParameters){
	uint8_t key = 0;
	while(1){
		printf("task3正在运行!!!\r\n");
		key = key_scan(0);                     //扫描按键
		if(key == KEY0_PRES){                  //KEY0按下
			printf("KEY0按下了!!!\r\n");
			if(task1_handler != NULL){         //判断句柄是否为0  
				printf("删除task1任务!!!\r\n");
		        vTaskDelete(task1_handler);      //删除任务1	
				task1_handler = NULL;
			}	
		}
	 vTaskDelay(50); //延时50ms
	}
}

2.5 main.c函数

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
    delay_init(168);                    /* 延时初始化 */
    usart_init(115200);                 /* 串口初始化为115200 */
    led_init();                         /* 初始化LED */
    key_init();                         /* 初始化按键 */
    sram_init();                        /* SRAM初始化 */
    my_mem_init(SRAMIN);                /* 初始化内部SRAM内存池 */
    my_mem_init(SRAMEX);                /* 初始化外部SRAM内存池 */
    my_mem_init(SRAMCCM);               /* 初始化内部CCM内存池 */
	freertos_demo();
}

3. 实验结果

按下复位键,观察串口助手打印情况:
在这里插入图片描述
可以看到任务优先级高task3的先执行,随后是task2,task1。
按下KEY0按键,观察串口助手:
在这里插入图片描述
烧录程序到开发版,观察现象:

freertos-led


实验现象与预期一致,两个灯一起交替闪烁,按下按键,删除task1,会发现LED0不会交替闪烁(只会长亮或暗),LED1继续交替闪烁。

4. 总结

1.串口打印中文标点乱码
在这里插入图片描述
发现中文并没有乱码,但是有中文标点的!和汉字在一起就会乱码,检查了波特率和keil的设置没有问题后,换新版的串口助手解决问题,这里开始用的是2.6版本,后面用了2.8版本的,下图是2.8版本能正常打印。
在这里插入图片描述

2.句柄没有加&,导致删除task1的时候,不能正常删除,就是句柄task1_handler不指向任务task1。
在这里插入图片描述

//定义任务句柄
TaskHandle_t  task1_handler;

//创建任务1
	            xTaskCreate( (TaskFunction_t           )        task1,              //创建开始函数
                            ( char *                 )       "task1",             //任务名字
                            ( configSTACK_DEPTH_TYPE )       TASK1_STACK_SIZE,    //堆栈空间
                            ( void *                 )       NULL,                //没有入口参数
                            ( UBaseType_t            )       TASK1_PRIO,          //任务优先级
                            ( TaskHandle_t *         )       task1_handler);      //任务句柄,这里没有加&

( TaskHandle_t * )是取指针,所以后面的句柄参数要加一个取地址符&。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值