FreeRTOS学习笔记-基于stm32(2)任务的创建与删除,挂起与恢复

一、任务创建与删除

1、动态任务创建

xTaskCreate(	TaskFunction_t pxTaskCode,const char * const pcName,const uint16_t usStackDepth,void * const pvParameters,UBaseType_t uxPriority,TaskHandle_t * const pxCreatedTask );

①:使用此函数前需将宏 configSUPPORT_DYNAMIC_ALLOCATION 配置为置1

#define configSUPPORT_DYNAMIC_ALLOCATION        1                       //支持动态内存申请

②: 定义函数入口参数

        首先进行强制类型转换,避免警告(就是给定义的参数类型带上括号)

        任务函数:就指向任务函数的指针,我此处的任务函数是 void start_task( void * pvParameters ),所以任务函数就是 start_task;

        任务名称:一般与任务函数一致;

        任务堆栈大小:通过 #define TASK1_STACK_SIZE   128 宏定义,将堆栈设置为128字,即128*4字节

        传递给任务函数的参数:无,即NULL;

        任务优先级:通过 #define START_TASK_PRIO         1 宏定义,将优先级设为1;

        任务句柄:定义 TaskHandle_t 类型的任务句柄,将地址传入;

/*START_TASK任务配置*/
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128     //128字=128*4字节
TaskHandle_t start_task_handler;
void start_task( void * pvParameters );/*入口函数*/
void freertos_demo(void)
{xTaskCreate(	(TaskFunction_t )    start_task,                //任务函数(char *         )    "start_task",              //任务名称(uint16_t       )    START_TASK_STACK_SIZE,     //任务堆栈大小(void *         )    NULL,                      //传递给任务函数的参数(UBaseType_t    )    START_TASK_PRIO,           //任务优先级(TaskHandle_t * )    &start_task_handler );     //任务句柄vTaskStartScheduler();         //开启任务调度器
}void start_task( void * pvParameters )
{for(::){//任务主体}
}

③:编写任务函数

        任务将会在一个无限循环中循环执行,可以使用for循环,也可以使用while循环,后者比较简单,笔者喜欢后者。此任务执行的是LED0不断闪烁并在串口打印 “task1正在运行”。

void start_task( void * pvParameters )
{while(1){printf("task1正在运行\r\n");LED0=!LED0;vTaskDelay(500);}
}

2、静态任务创建

xTaskCreateStatic(	TaskFunction_t pxTaskCode,const char * const pcName,const uint32_t ulStackDepth,void * const pvParameters,UBaseType_t uxPriority,StackType_t * const puxStackBuffer,StaticTask_t * const pxTaskBuffer );

 ①:使用此函数前需将宏 configSUPPORT_STATIC_ALLOCATION 配置为1

#define configSUPPORT_STATIC_ALLOCATION			1						//只是静态内存

②: 实现空闲任务与软件定时器接口函数

        定义三个参数,任务控制块、任务堆栈、堆栈大小。空闲任务使用的堆栈大小在FreeRTOSConfig.h 中有定义:

#define configMINIMAL_STACK_SIZE				((unsigned short)130)   //空闲任务使用的堆栈大小

        软件定时器任务堆栈大小在FreeRTOSConfig.h 中也有定义:

#define configTIMER_TASK_STACK_DEPTH	        (configMINIMAL_STACK_SIZE*2)    //软件定时器任务堆栈大小

        在函数内部分别将定义好的参数赋值;

/*空闲任务内存分配*/
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;}/*软件内存分配*/
StaticTask_t timer_task_tcb;
StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH];
void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer,  //任务控制块StackType_t **ppxTimerTaskStackBuffer, //任务堆栈uint32_t *pulTimerTaskStackSize )      //堆栈大小
{*ppxTimerTaskTCBBuffer=&timer_task_tcb;*ppxTimerTaskStackBuffer=timer_task_stack;*pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;
}

③:定义函数入口参数

        与动态任务创建类似,不同的是需要再多定义一个用户分配任务堆栈和用户分配任务控制块指针,而动态创建是计算机自己分配的。静态创建好处就是可以自己规定存储位置,坏处就是比较繁琐

        用户分配任务堆栈一般定义为一个数组,数组的大小即任务堆栈的大小:START_TASK_STACK_SIZE

        xTaskCreateStatic()参数中没有句柄,那如果要找到这个任务要怎么办呢?其实它的句柄正是这个函数的返回值!!!所以还要用刚定义的句柄:task1_handler 来接住返回值。

/*START_TASK任务配置*/
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128     //128字=128*4字节
StackType_t  start_task_stack[START_TASK_STACK_SIZE];
TaskHandle_t start_task_handler;
StaticTask_t start_task_tcb;
void start_task( void * pvParameters );/*入口函数*/
void freertos_demo(void)
{//创建开始任务start_task_handler=xTaskCreateStatic(   (TaskFunction_t	)start_task,		    //指向任务函数的指针(const 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 )
{while(1){//任务主体}
}

④: 编写任务函数

void start_task( void * pvParameters )
{while(1){printf("task1正在运行\r\n");LED0=!LED0;vTaskDelay(500);}
}

        此代码与动态任务创建很类似就不做展示了。 

3、动态静态的区别

        动态创建任务:任务的控制块以及任务的栈空间所需的内存均由 FreeRTOS 从 FreeRTOS 管理的堆中分配;

        静态创建任务:任务的任务控制块以及任务的栈空间所需的内存,需要用户分配提供。

4、任务删除

①:使用此函数前需将宏 INCLUDE_vTaskDelete 配置为1

#define INCLUDE_vTaskDelete				        1

②: 调用函数 vTaskDelete();

vTaskDelete(NULL);      //vTaskDelete(start_task_handler);

        当参数为对应任务的句柄时,将删除对应任务;

        若参数为 NULL ,则删除正在执行的任务;

        空闲任务会负责释放被删除任务中由系统分配的内存(动态创建),但由用户在任务删除前申请的内存(静态创建),则需要用户在任务被删除前提前释放,否则将导致内存泄露。

 5、完整代码

        此代码使用动态任务创建的方式,先创建了start_task任务,然后在start_task任务中创建了task1、task2、task3任务。task1 和 task2 用来让 LED0 和 LED1 闪烁,task3 用来判断 KEY0 是否被按下,如果按下则删除 task1。

1、vTaskStartScheduler();         //开启任务调度器

        此函数用来开启任务调度器,在任务调度器开启后,任务调度器就会开始去就绪列表调用任务

2、  taskENTER_CRITICAL();       //进入临界区

        taskEXIT_CRITICAL();        //退出临界区

        此函数用来关闭freertos所管理的中断以保护那些不想被打断的程序段,中断关闭后,任务将不能被调度,必须等退出临界区任务才能被调度

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "timer.h"
#include "lcd.h"
#include "FreeRTOS.h"
#include "task.h"
#include "freertos_demo.h"/*START_TASK任务配置*/
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128     //128字=128*4字节
TaskHandle_t start_task_handler;
void start_task( void * pvParameters );/*TASK1任务配置*/
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128     //128字=128*4字节
TaskHandle_t task1_handler;
void task1( void * pvParameters );/*TASK2任务配置*/
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128     //128字=128*4字节
TaskHandle_t task2_handler;
void task2( void * pvParameters );/*TASK3任务配置*/
#define TASK3_PRIO         4
#define TASK3_STACK_SIZE   128     //128字=128*4字节
TaskHandle_t task3_handler;
void task3( void * pvParameters );/*入口函数*/
void freertos_demo(void)
{xTaskCreate(	(TaskFunction_t )    start_task,                //任务函数(char *         )    "start_task",              //任务名称(uint16_t       )    START_TASK_STACK_SIZE,     //任务堆栈大小(void *         )    NULL,                      //传递给任务函数的参数(UBaseType_t    )    START_TASK_PRIO,           //任务优先级(TaskHandle_t * )    &start_task_handler );     //任务句柄 vTaskStartScheduler();         //开启任务调度器
}void start_task( void * pvParameters )
{taskENTER_CRITICAL();       //进入临界区printf("start_task正在运行\r\n");xTaskCreate(	(TaskFunction_t )    task1,                //任务函数(char *         )    "task1",              //任务名称(uint16_t       )    TASK1_STACK_SIZE,     //任务堆栈大小(void *         )    NULL,                 //传递给任务函数的参数(UBaseType_t    )    TASK1_PRIO,           //任务优先级(TaskHandle_t * )    &task1_handler );     //任务句柄xTaskCreate(	(TaskFunction_t )    task2,                //任务函数(char *         )    "task2",              //任务名称(uint16_t       )    TASK2_STACK_SIZE,     //任务堆栈大小(void *         )    NULL,                 //传递给任务函数的参数(UBaseType_t    )    TASK2_PRIO,           //任务优先级(TaskHandle_t * )    &task2_handler );     //任务句柄xTaskCreate(	(TaskFunction_t )    task3,                //任务函数(char *         )    "task3",              //任务名称(uint16_t       )    TASK3_STACK_SIZE,     //任务堆栈大小(void *         )    NULL,                 //传递给任务函数的参数(UBaseType_t    )    TASK3_PRIO,           //任务优先级(TaskHandle_t * )    &task3_handler );     //任务句柄vTaskDelete(NULL);      //vTaskDelete(start_task_handler);taskEXIT_CRITICAL();        //退出临界区
}void task1( void * pvParameters )
{while(1){printf("task1正在运行\r\n");LED0=!LED0;vTaskDelay(500);}
}void task2( void * pvParameters )
{while(1){printf("task2正在运行\r\n");LED1=!LED1;vTaskDelay(500);}
}void task3( void * pvParameters )
{uint8_t key=0;while(1){printf("task3正在运行,key=%u\r\n",&key);key=KEY_Scan(0);if(key==KEY0_PRES){if(task1_handler!=NULL){printf("task1被删除\r\n");vTaskDelete(task1_handler);task1_handler=NULL;}}vTaskDelay(10);}
}

二、任务挂起与恢复

1、任务挂起函数

        xTaskSuspend()调用后任务将被挂起,不能再执行,直到任务被恢复。

        使用前需将此宏 INCLUDE_vTaskSuspend 配置为1;

        当参数为对应任务的句柄时,将挂起对应任务;

        若参数为 NULL ,则挂起正在执行的任务;

2、任务恢复函数(任务中恢复)

        vTaskResume()调用后,任务被恢复,并进入就绪态。

        使用前需将此宏 INCLUDE_vTaskSuspend 配置为1;

void task3( void * pvParameters )
{uint8_t key=0;while(1){key=KEY_Scan(0);if(key==KEY0_PRES){printf("任务挂起");vTaskSuspend(task1_handler);}else if(key==KEY1_PRES){printf("任务解挂");vTaskResume(task1_handler);}vTaskDelay(10);}
}

3、任务恢复函数(中断中恢复)

        xTaskResumeFromISR()调用后,任务被恢复,并进入就绪态(专用于中断服务函数)。

        使用前需将宏 INCLUDE_vTaskSuspend 和 INCLUDE_xTaskResumeFromISR 都配置为1;

        需判断返回值(返回值类型为:BaseType_t):

        pdTRUE:说明任务优先级大于正在执行的任务,任务恢复后需要进行任务切换;

        pdFALSE:任务恢复后不需要进行任务切换;

        中断服务程序中只要调用了FreeRTOS的API函数则中断优先级不能高于FreeRTOS所管理的最高优先级FreeRTOS所管理的中断优先级范围是:5~15

        为了方便处理,中断分组需设置到第4组,,即抢占优先级0~15,子优先级0。

extern TaskHandle_t task1_handler;void EXTI0_IRQHandler(void)        //中断服务函数
{delay_xms(10);    //消抖BaseType_t xYieldRequired;    //定义变量来接收返回值if(WK_UP==1){	printf("中断解挂");xYieldRequired=xTaskResumeFromISR(task1_handler);}if(xYieldRequired==pdTRUE){portYIELD_FROM_ISR(xYieldRequired);//任务切换}EXTI_ClearITPendingBit(EXTI_Line0);  //清除EXTI0线路挂起位
}

        由于在中断服务程序中,在 a.c 中定义的变量要在 b.c 中使用,就需要将 task1_handler 定义为全局变量:

extern TaskHandle_t task1_handler;

        任务切换函数:

portYIELD_FROM_ISR(xYieldRequired);//任务切换

三、总结

        大家一定要多动手。不管再简单的知识,再简单的任务,自己动手敲一遍总能遇到一些意想不到的问题。而解决这些问题就是我们不断进步的阶梯!!!

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

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

相关文章

消息队列以及Kafka的使用

什么是消息队列 消息队列:一般我们会简称它为MQ(Message Queue)。其主要目的是通讯。 ps:消息队列是以日志的形式将数据顺序存储到磁盘当中。通常我们说从内存中IO读写数据的速度要快于从硬盘中IO读写的速度是对于随机的写入和读取。但是对于这种顺序存…

SQL 中 IN 与 <= 且 >= 的效率比较

1. 索引利用 当查询条件中的值是离散的、非连续的&#xff0c;或者是在多个不相邻的范围内时&#xff0c;使用 IN 可以更高效&#xff0c;因为 IN 可以直接跳到索引中的这些特定值。而 < 且 > 通常用于连续范围的查询&#xff0c;如果查询的是一个连续的区间&#xff0c…

2024年:AI领航研发新纪元

导言&#xff1a; 在21世纪的科技巨浪中&#xff0c;人工智能&#xff08;AI&#xff09;已经崭露头角&#xff0c;成为研发领域的核心变革者。其强大的潜力和无所不在的应用正在改变人类解决问题的方式&#xff0c;为未来的发展开启了无限可能。随着机器学习、自然语言处理、计…

模型精度fp16和fp32

FP16和FP32是两种不同的浮点数精度格式&#xff0c;在计算机科学特别是深度学习领域中广泛应用。 FP32&#xff08;单精度浮点数&#xff09;&#xff1a; FP32代表32位&#xff08;4字节&#xff09;单精度浮点数格式&#xff0c;这是传统上大多数深度学习模型训练和推理的标准…

代码随想录算法训练营第五十四天|392.判断子序列、115.不同的子序列

392.判断子序列 思路&#xff1a;判断子序列的问题&#xff0c;其实与最大公共子序列的问题是一样的&#xff0c;所以基本上写出来是一样的&#xff0c;但是今天又犯了一个错误&#xff0c;对于非连续子序列&#xff0c;不仅要在相等的时候进行更新&#xff0c;而且不等的时候也…

周立功USBCAN-E-mini分析仪的安装测试笔记

一、介绍 USBCAN-E-mini 智能 CAN 接口卡是系列 USBCAN 便携版本&#xff0c;与 USBCAN—E-U 单路智能 CAN 接口卡完全兼容。USBCAN-E-mini 智能 CAN 接口卡与 USB1.1 总线兼容的&#xff0c;集成 1 路 CAN 接口的智能型 CAN-bus 总线通讯接口卡。采用 USBCAN-E-mini 智能 CAN …

【Linux】Linux操作命令—最全版

目录 一、Linux下基本指令 01. ls 指令 02.cd 指令 03.touch指令 04 mkdir指令 05 man指令 06 cp指令 07 mv指令 08 cat指令 09 more指令 10 less指令 11 head 命令 12 tail 命令 二、时间相关的指令 01 date显示 1.在显示方面&#xff0c;使用者可以设定…

6、string字符串拼接

#include <iostream> using namespace std;void test01 () {string s1 "我";s1 "爱玩游戏";cout << s1 << endl;s1 :;string s2 "lol dnf";s1 s2;cout << s1 << endl;string s3 "i";s3.append(&q…

Vue 使用@别名

1. 安装 types/node types/node 包允许您在TypeScript项目中使用Node.js的核心模块和API&#xff0c;并提供了对它们的类型检查和智能提示的支持。 npm install types/node --save-dev 比如安装之后&#xff0c;就可以导入nodejs的 path模块&#xff0c;在下面代码 import pat…

【ICCV】AIGC时代下的SOTA人脸表征提取器TransFace,FaceChain团队出品

一、论文 本文介绍被计算机视觉顶级国际会议ICCV 2023接收的论文 "TransFace: Calibrating Transformer Training for Face Recognition from a Data-Centric Perspective" 论文链接&#xff1a;https://arxiv.org/abs/2308.10133 开源代码&#xff1a;https://an…

GAMMA电源维修高压直流电源ES30P-5W ES系列

美国Gamma高压电源维修型号&#xff1a;D-ES30R-10N-5W/M&#xff0c;LXR30-1N&#xff0c;XRM5N-100W&#xff0c;ES50P-10W/DDPM&#xff0c;ES60P-10W/DDPM&#xff0c;RR20-20P/DDPM&#xff0c;ES30P-10W&#xff0c;ES60P-10W DDPM&#xff0c;RR60-18P/220V&#xff0c;…

快看!提高效率,还得看工作流表单引擎

经常有客户会问我们&#xff1a;究竟用什么样的工具才能打破信息孤岛&#xff0c;让大家互联联络起来&#xff1f;其实&#xff0c;说到这&#xff0c;有不少人会联想到低代码技术平台。不错&#xff0c;在快节奏的发展社会中&#xff0c;高效率的办公目的已经是大家的追求和发…

java的参数传递机制(引用类型)

1.除了非引用类型的形参传递&#xff0c;还有引用类型的变量形参传递&#xff0c;但引用类型的形参变量传递与非引用类型是不同的&#xff01;&#xff01;&#xff01; public class MethodDemo2 {public static void main(String[] args) {int[] arr new int[]{10,20,30,9}…

邮寄收款码申请所需时间详解

在移动支付风靡的今天&#xff0c;收款码作为连接线上支付与线下商业活动的重要桥梁&#xff0c;已成为众多商家的必备工具。然而&#xff0c;对于初次接触收款码的商家来说&#xff0c;如何申请、需要准备哪些材料以及申请流程需要多长时间等问题&#xff0c;往往成为他们关注…

nginx 端口转发8848的nacos,达到不暴露nacos端口进行web访问nacos

一些客户端原因&#xff0c;云主机未开放8848端口&#xff0c;我们无法直接访问8848/nacos&#xff0c;进行nacos的配置 方法&#xff1a; 以docker启动的nginx和nacos(2.0.3)为例 配置nginx 80端口&#xff0c;配置转发到内网的IP和8848端口&#xff0c;172.17.0.1为docker…

Golang单例模式学习笔记

前言 单例模式是常用的一种设计模式&#xff0c;一般用于比如客户端、连接的创建等&#xff0c;防止创建多个导致性能消耗。所以我认为单例模式的核心&#xff0c;就是“防止重复”。本文将在Golang中进行单例模式的实现。 实现 版本1——检测-创建 最基础的版本&#xff0…

未来仓储解决方案:深入了解自动码垛托盘分配技术

在现代物料处理过程中&#xff0c;自动码垛托盘&#xff08;栈板&#xff09;分配机扮演着至关重要的角色。这类设备被广泛应用于制造业、物流仓储等领域&#xff0c;凭借其高效、准确且可靠的性能&#xff0c;成为提高生产效率和降低运营成本的关键工具。 自动码垛托盘分配机…

开发一个带有Servlet的webapp(重点)

【具体步骤如下】 ①在webapps目录下新建一个目录&#xff0c;起名crm&#xff08;这个crm就是webapp的名字&#xff09;。当然&#xff0c;也可以是其他目录&#xff0c;名字自拟 注意&#xff1a;crm就是这个webapp的根 ②在webapp的根下新建一个目录&#xff1a;WEB…

0-超详细基础安装linux系统-kali

软件以及kali镜像获取 vm 链接&#xff1a;https://pan.baidu.com/s/1u5Kfc6FVc_1awIHECVXh-Q?pwde963 提取码&#xff1a;e963 kali 链接&#xff1a;https://pan.baidu.com/s/1TeGXkwFKrpz9aUL8k2ff2g?pwdhu3v 提取码&#xff1a;hu3v 1.打开虚拟机选择新建虚拟机 安…

MySQL常见的存储引擎介绍

我将为您详细讲解 MySQL 常见的存储引擎&#xff0c;以及它们的使用场景、特点、区别和优势。MySQL 支持多种存储引擎&#xff0c;每种存储引擎都有其特定的优势和局限性。了解这些存储引擎的特点和适用场景对于选择合适的存储引擎以及优化数据库性能至关重要。 1. InnoDB 存储…