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

news2025/1/9 2:29:28

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 * )是取指针,所以后面的句柄参数要加一个取地址符&。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/630273.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux C简易聊天室

对于初学者而已,我们学习的网络编程(如TCP,UDP编程),我们通常都是在局域网内进行通信测试,有时候我们或者会想,我们现在写的内网网络数据和外网的网络数据有什么不同,我们内网的数据是如何走出外…

notepadd++快捷键记录

记录下 notepadd 常用快捷键 1.搜索 普通搜索:Ctrl F 正则表达式搜索: 查找模式用 正则表达式 。如 A|B|C ,搜索多个关键字, 更多正则表达式探索中。 还可以选中 选取范围内 ,就会只在鼠标选中区域内查找。 2.区…

Visual Studio Code 1.79 发布

发布模式 - 将工作区中的特定文件和文件夹标记为只读。 在某些开发场景中,将工作区的某些文件夹或文件显式标记为只读会很有帮助。例如,如果文件夹或文件内容由不同的进程管理(例如 node_modules 由 Node.js 包管理器管理的文件夹),则将它们…

E往无前 | get正确使用姿势!腾讯云大数据ES日志场景优化案例回顾

导语: 随着ELK方案在开源日志分析领域越来越流行,各种业务场景也给ELK方案带来了越来越多的挑战。本文将回顾一次真实客户案例,从使用姿势上,提供一些大集群、多日志主题场景下的集群优化思路。 一、ELK不香了? 我们…

DevOps系列文章之 Docker-compose

一,Docker-compose全集 1,Docker-compose简介 Docker-Compose项目是Docker官方的开源项目,负责实现对Docker容器集群的快速编排。 Docker-Compose将所管理的容器分为三层,分别是工程(project)&#xff0c…

ChatGLM简介和SSE聊天接口测试效果

开发公司 智谱AI是由清华大学计算机系技术成果转化而来的公司,致力于打造新一代认知智能通用模型。公司合作研发了双语千亿级超大规模预训练模型GLM-130B,并构建了高精度通用知识图谱,形成数据与知识双轮驱动的认知引擎,基于此模型…

java异常 | 处理规范、全局异常、Error处理

文章目录 🚃异常类型🎠显示声明异常:①:try-catch②方法签名 🚃异常处理规范⚓️异常包装⚓️异常传递⚓️异常日志记录⚓️异常处理的最佳实践 🚃全局异常处理⛵️优点:⛵️代码示例&#xff1…

骨传导蓝牙耳机哪个牌子好,列举几款知名的骨传导耳机

​骨传导耳机,顾名思义就是通过骨头传播声音的耳机,由于它在声音传播方式上与传统耳机不同,不需要借助外耳、耳道,也能让耳朵更好地感受到外界的声音,在一些特殊场合下可以让使用者听到环境音。骨传导耳机虽然很小巧轻…

AI实战营:目标检测与MMDetection

目标检测的基本范式 什么是目标检测 目标检测 vs 图像分类 目标检测 in 人脸识别 目标检测 in 智慧城市 目标检测 in 自动驾驶 目标检测 in 下游视觉任务 目标检测技术的演进 基础知识 框、边界框(Bounding Box) 交并比 Intersection Over Union 目标检…

奇舞周刊第495期:软件高可用实践那些事

记得点击文章末尾的“ 阅读原文 ”查看哟~ 下面先一起看下本期周刊 摘要 吧~ 奇舞推荐 ■ ■ ■ 软件高可用实践那些事儿 本文从高可用落地实践的角度,通过协作效率,技术落地和运营规范等几个层面,阐述了高可用具体实施步骤和落地细节。 浏览…

如何使用微软官方工具制作win11启动盘

windows11 启动盘制作和使用 一、启动盘制作准备二、制作步骤三、对其他电脑安装windows11系统 一、启动盘制作准备 1.至少存储空间为8GB的空白U盘 2.一台电脑 二、制作步骤 1.在官方网站中选择下载工具,选择创建windows11安装,下载完毕之后&#xff…

混淆矩阵、准确率、召回率、漏报率、误报率、F1分数

1、混淆矩阵 在二分类问题中,混淆矩阵被用来度量模型的准确率。因为在二分类问题中单一样本的预测结果只有Yes or No,即:真或者假两种结果,所以全体样本的经二分类模型处理后,处理结果不外乎四种情况,每种情…

ASP.NET Core MVC 从入门到精通之Html辅助标签(一)

随着技术的发展,ASP.NET Core MVC也推出了好长时间,经过不断的版本更新迭代,已经越来越完善,本系列文章主要讲解ASP.NET Core MVC开发B/S系统过程中所涉及到的相关内容,适用于初学者,在校毕业生&#xff0c…

微信小程序快速入门【一】

微信小程序快速入门【一】 文章目录 微信小程序快速入门【一】👨‍🏫内容1:背景👨‍⚖️内容2:准备工作👨‍💻内容3:新建一个小程序🍉文末推荐 👨‍&#x1f…

有关 python 切片的趣事

哈喽大家好,我是咸鱼 今天来讲一个我在实现 python 列表切片时遇到的趣事 在正式开始之前,我们先来了解一下切片(slice) 切片操作是访问序列(列表、字符串…)中元素的另一种方法,它可以访问一…

莱特兄弟的家庭教育

莱特兄弟的三个设计经验 你可以从他们如何使得一辆自行车飞行中学到很多东西 克莱夫汤普森 大家都知道莱特兄弟是第一个实现动力飞行的人——他们的飞机于1903年12月17日在北卡罗来纳州的基蒂霍克起飞。 但是在实现这一突破之前的过程? 这真是令人感兴趣,并且充满了…

【PWN · ret2text | ‘/bin/sh‘写在bss段】[HNCTF 2022 Week1]ezr0p32

目录 前言 一、题目 二、解题过程 payload的构造 三、exp 总结 前言 一直在做libc的中规中矩的题目,遇到一题有点老的类型的题目有些陌生。但其实其中原理比较简单,但是涉及到/bin/sh获取的常规操作,而自己也没整理过,于…

Git->分支

⭐作者介绍:大二本科网络工程专业在读,持续学习Java,努力输出优质文章 ⭐作者主页:逐梦苍穹 ⭐所属专栏:Git ⭐如果觉得文章写的不错,欢迎点个关注一键三连😉有写的不好的地方也欢迎指正&#x…

springboot+vue体育馆场地器材管理系统的设计与实现

体育馆管理系统有管理员和用户两个角色。用户功能有场地信息,员工信息,器材信息,留言反馈,个人中心。管理员功能有个人中心,用户管理,场地信息管理,场地类型管理,员工信息管理&#…

Redis - 缓存雪崩,缓存穿透,缓存击穿

Redis是一个完全开源的,遵守BSD协议的,高性能的key-value的数据存储结构系统,它支持数据持久化,可以将内存中的数据保存在磁盘中。不仅支持简单的key-value类型的数据结构,同事还提供list,zset,…