FreeRTOS 的任务挂起和恢复

1. 任务挂起和恢复的 API 函数

API函数描述
vTaskSuspend()挂起任务
vTaskResume()恢复被挂起的任务
xTaskResumeFromISR()在中断中恢复被挂起的任务
  • 挂起:挂起任务类似暂停,可恢复; 删除任务,堆栈都给释放掉了,无法恢复,类似“人死两清”
  • 恢复:恢复被挂起的任务
  • “FromISR”:带FromISR后缀是在中断函数中专用的 API 函数。(不管你是 FreeRTOS 的哪一个 API 函数,只要你在中断服务函数中要调用,那么你肯定是有一个 FromISR 后缀的。)

挂起任务和删除任务本质上的区别:能否恢复。

1.1 任务挂起

函数原型:

void vTaskSuspend(TaskHandle_t xTaskToSuspend) 
形参描述
xTaskToSuspend待挂起任务的任务句柄
  • 此函数用于挂起任务,使用时需将宏 INCLUDE_vTaskSuspend 配置为 1。
  • 无论优先级如何,被挂起的任务都将不再被执行,直到任务被恢复 。

注意:当传入的参数为NULL,则代表挂起任务自身(当前正在运行的任务)

1.2 任务恢复

1.2.1 任务中恢复(在任务函数中调用)

函数原型:

void vTaskResume(TaskHandle_t xTaskToResume) 
形参描述
xTaskToResume待恢复任务的任务句柄
  • 使用该函数注意宏:INCLUDE_vTaskSuspend必须定义为 1

注意:任务无论被 vTaskSuspend() 挂起多少次,只需在任务中调用 vTakResume() 恢复一次,就可以继续运行。且被恢复的任务会进入就绪态! (任务挂起不支持嵌套)

1.2.2 中断中恢复(在中断服务函数中调用)

函数原型:

BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume)  
形参描述
xTaskToResume待恢复任务的任务句柄
返回值描述
pdTRUE任务恢复后需要进行任务切换
pdFALSE任务恢复后不需要进行任务切换

什么情况下需要进行任务切换?

  • 就是你要恢复的这个任务它的优先级大于我们当前正在执行的这个任务,这时候由于抢占式调度,优先级高的任务要抢占当前正在执行的任务,那这时候就得进行一个任务切换了。
  • 如果你的优先级比较低呢?那么此时它就返回 pdFALSE,那这时候你就不需要进行任务切换。

所以我们如果在中断服务函数中调用这个函数,那么你就要判断它的一个返回值了,来确定是否需要手动执行一次任务切换(portYIELD_FROM_ISR)。

  • 使用该函数注意宏:INCLUDE_vTaskSuspend 和 INCLUDE_xTaskResumeFromISR 必须定义为 1
  • 该函数专用于中断服务函数中,用于解挂被挂起任务

注意:中断服务程序中要调用 FreeRTOS 的 API 函数则中断优先级不能高于 FreeRTOS 所管理的最高优先级。

  • 我们的代码 FreeRTOS 所管理的中断优先级是 5~15,也就是说中断服务函数的中断优先级必须在这个范围内,那如果你在 0~4,比 5~15 优先级要高,它就不属于 FreeRTOS 所管理的一个范围了。

注意:任务优先级跟中断优先级的区别:任务优先级是数值越大优先级越高;中断优先级是数值越小优先级越高。

2. 实战编程

实验目的:学会使用FreeRTOS中的任务挂起与恢复相关API函数:vTaskSuspend( )、vTaskResume( )、xTaskResumeFromISR( )
实验设计:将设计四个任务:start_task、task1、task2、task3

四个任务的功能如下:

  • start_task:用来创建其他的三个任务
  • task1:实现LED0每500ms闪烁一次
  • task2:实现LED1每500ms闪烁一次
  • task3:判断按键按下逻辑,KEY0按下,挂起task1;按下KEY1,在任务中恢复task1;按下KEY2,在中断中恢复task1(外部中断线实现)

2.1 任务中恢复

2.1.1 宏 INCLUDE_vTaskSuspend 置 1

2.1.2 编写任务函数

task1、task2、task3 任务函数
/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{uint32_t task1_num = 0;						/* 运行次数 */while(1){printf("task1_num:%d\r\n",++task1_num);LED0_TOGGLE();vTaskDelay(500);}
}/* 任务二,实现LED1每500ms翻转一次 */
void task2( void * pvParameters )
{uint32_t task2_num = 0;while(1){printf("task2_num:%d\r\n",++task2_num);LED1_TOGGLE();vTaskDelay(500);}
}/* 任务三,判断按键按下逻辑 */
void task3( void * pvParameters )
{uint8_t key = 0;while(1){key = key_scan(0);if(key == KEY0_PRES){printf("挂起task1\r\n");vTaskSuspend(task1_handler);		/* 挂起 task1 任务 */}else if(key == KEY1_PRES){printf("在任务中恢复task1\r\n");vTaskResume(task1_handler);			/* 恢复 task1 任务 */}vTaskDelay(10);}
}

task3 的延时时间是比较快的,那它的运行次数肯定很多很多,如果打印的话你很难看到 task1 和 task2 的运行次数的打印信息。

2.2 中断中恢复

2.2.1 宏 INCLUDE_vTaskSuspend 和 INCLUDE_xTaskResumeFromISR 置 1

2.2.2 编写中断服务函数

exti.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/KEY/key.h"
#include "./BSP/EXTI/exti.h"
#include "FreeRTOS.h"
#include "task.h"extern TaskHandle_t    task1_handler;			/* 外部声明 task1 句柄 */
/*** @brief       KEY2 外部中断服务程序* @param       无* @retval      无*/
void KEY2_INT_IRQHandler(void)
{ HAL_GPIO_EXTI_IRQHandler(KEY2_INT_GPIO_PIN);        /* 调用中断处理公用函数 清除KEY2所在中断线 的中断标志位,中断下半部在HAL_GPIO_EXTI_Callback执行 */__HAL_GPIO_EXTI_CLEAR_IT(KEY2_INT_GPIO_PIN);        /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */
}/*** @brief       中断服务程序中需要做的事情在HAL库中所有的外部中断服务函数都会调用此函数* @param       GPIO_Pin:中断引脚号* @retval      无*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{delay_ms(20);      /* 消抖 */switch(GPIO_Pin){BaseType_t xYieldRequired;case KEY2_INT_GPIO_PIN:if (KEY2 == 0){xYieldRequired = xTaskResumeFromISR(task1_handler);printf("在中断中恢复task1\r\n");}if(xYieldRequired == pdTRUE){portYIELD_FROM_ISR(xYieldRequired);}break;default : break;}
}/*** @brief       外部中断初始化程序* @param       无* @retval      无*/
void extix_init(void)
{GPIO_InitTypeDef gpio_init_struct;key_init();    gpio_init_struct.Pin = KEY2_INT_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_IT_FALLING;            /* 下降沿触发 */gpio_init_struct.Pull = GPIO_PULLUP;HAL_GPIO_Init(KEY2_INT_GPIO_PORT, &gpio_init_struct);    /* KEY2配置为下降沿触发中断 */HAL_NVIC_SetPriority(KEY2_INT_IRQn, 5, 0);               /* 抢占5,子优先级0 */HAL_NVIC_EnableIRQ(KEY2_INT_IRQn);                       /* 使能中断线2 */}

因为我们要在中断服务函数里面调用 FreeRTOS 的 API 函数,而 FreeRTOS 要求我们:

  1. 把优先级分组的所有位都用做抢占式,而不能用作我们的一个子优先级;

FreeRTOS 官网:

  • 建议将所有优先级位指定为抢占优先级位,不留下任何优先级位作为子优先级位,其他的任何配置都会使 configMAX_SYSCALL_INTERRUPT_PRIORITY 宏设置与分配给各个外设中断的优先级之间的直接关系复杂化。

也就是说为什么都要设置为抢占式优先级,就是为了 FreeRTOS 方便管理,比如它非常复杂,官网明确表态了。

那怎么设置它全部用作抢占式优先级?通过调用:

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

来确保将所有优先级位分配为抢占优先级位。

stm32f4xx_hal.c
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
  1. 中断的优先级不能高于 FreeRTOS 所管理的优先级。

FreeRTOS 官网:

  • 以 “FromISR” 结尾的 FreeRTOS 函数是中断安全的,但前提是调用这些函数的中断的逻辑优先级不高于 configMAX_SYSCALL_INTERRUPT_PRIORITY 定义的优先级(FreeRTOS 可管理的最高中断优先级)。

所以我们一定要确保中断的逻辑优先级等于或小于 configMAX_SYSCALL_INTERRUPT_PRIORITY。

HAL_NVIC_SetPriority(KEY2_INT_IRQn, 5, 0);               /* 抢占5,子优先级0 */

如果不满足以上两点要求,就不能在中断服务函数中调用 FreeRTOS 的 API 函数。

exti.h
#ifndef __EXTI_H
#define __EXTI_H#include "./SYSTEM/sys/sys.h"/******************************************************************************************/
/* 引脚 和 中断编号 & 中断服务函数 定义 */ #define KEY0_INT_GPIO_PORT              GPIOH
#define KEY0_INT_GPIO_PIN               GPIO_PIN_3
#define KEY0_INT_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOH_CLK_ENABLE(); }while(0)   /* PH口时钟使能 */
#define KEY0_INT_IRQn                   EXTI3_IRQn
#define KEY0_INT_IRQHandler             EXTI3_IRQHandler#define KEY1_INT_GPIO_PORT              GPIOH
#define KEY1_INT_GPIO_PIN               GPIO_PIN_2
#define KEY1_INT_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOH_CLK_ENABLE(); }while(0)   /* PH口时钟使能 */
#define KEY1_INT_IRQn                   EXTI2_IRQn
#define KEY1_INT_IRQHandler             EXTI2_IRQHandler#define KEY2_INT_GPIO_PORT              GPIOC
#define KEY2_INT_GPIO_PIN               GPIO_PIN_13
#define KEY2_INT_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0)   /* PC口时钟使能 */
#define KEY2_INT_IRQn                   EXTI15_10_IRQn
#define KEY2_INT_IRQHandler             EXTI15_10_IRQHandler#define WKUP_INT_GPIO_PORT              GPIOA
#define WKUP_INT_GPIO_PIN               GPIO_PIN_0
#define WKUP_INT_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   /* PA口时钟使能 */
#define WKUP_INT_IRQn                   EXTI0_IRQn
#define WKUP_INT_IRQHandler             EXTI0_IRQHandler/******************************************************************************************/void extix_init(void);  /* 外部中断初始化 */#endif
main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/SDRAM/sdram.h"
#include "./MALLOC/malloc.h"
#include "freertos_demo.h"
#include "./BSP/EXTI/exti.h"int main(void)
{HAL_Init();                                 /* 初始化HAL库 */sys_stm32_clock_init(360, 25, 2, 8);        /* 设置时钟,180Mhz */delay_init(180);                            /* 延时初始化 */usart_init(115200);                         /* 串口初始化为115200 */led_init();                                 /* 初始化LED */key_init();                                 /* 初始化按键 */sdram_init();                               /* SRAM初始化 */lcd_init();                                 /* 初始化LCD */extix_init();								/* 外部中断初始化 */my_mem_init(SRAMIN);                        /* 初始化内部内存池 */my_mem_init(SRAMEX);                        /* 初始化外部内存池 */my_mem_init(SRAMCCM);                       /* 初始化CCM内存池 */freertos_demo();
}

3. 课堂总结

在这里插入图片描述

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

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

相关文章

计算机网络-第7章 网络安全(2)

7.5 密钥分配 7.6 互联网使用的安全协议 7.7 系统安全:防火墙与入侵检测

【故障电弧探测器 方案介绍】安科瑞电气火灾监控系统

什么是电弧? 电弧俗称‘电火花’ ,又叫‘打火’ ,本质是一种绝缘体被电压击穿后,由不导电变的导电,且发光发热的自然现象。 什么是故障电弧? 由于电气线路或设备中绝缘老化破损、电气连接松动、空气潮湿…

SpringCloud Hystrix 断路器

一、前言 接下来是开展一系列的 SpringCloud 的学习之旅,从传统的模块之间调用,一步步的升级为 SpringCloud 模块之间的调用,此篇文章为第五篇,即介绍 Hystrix 断路器。 二、概述 2.1 分布式系统面临的问题 复杂分布式体系结构中…

TIOBE 3月编程排行榜出炉:遥遥领先,霸榜第一!

哈喽呀~又到了每个月的语言排行榜啦! TIOBE 3月编程语言也已经公布,具体有啥新变化呢?快和我一起往下看~ Python遥遥领先,霸榜第一 先来看看本月排行榜top5的表现。毫无疑问,Python依然遥遥领先: Python 第…

RabbitMQ学习笔记——消息转化器

消息转化器的问题,如果我们发送方发送的数据为引用类型的对象,默认使用的是JDK内部自带的ObjectOutputStream进行对象序列化和反序列化,这样做比较复杂并且不安全。 我们可以引入Jackson来做对象的序列化,可将默认的JDK消息转化器…

Testng认识与配置-全网最简单

总结:testng支持注解,线程池,数据驱动,参数化,强大执行模式,也支持单元测试,功能测试,接口测试 TestNG is a testing framework inspired from JUnit and NUnit but introducing som…

Samtec前沿 | 信号发生器 - 决定可靠性的重要因素之一

【摘要/前言】 作为消费者,我们已经开始期待我们的技术达到一个前所未有的可靠性水平。无论是智能手机还是家庭汽车,我们已经开始期待智能电子设备在每次使用时都能在第一时间发挥作用。 【信号和电源发生器至关重要】 现代设备能够提供如此出色的…

外包干了5天,技术明显退步。。。。。

先说一下自己的情况,本科生,19年通过校招进入南京某软件公司,干了接近2年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了2年的功能测试&…

VXLAN学习笔记

声明:该博客内容大部分参考参考链接整理 什么是VXLAN? VXLAN(Virtual Extensible LAN)即虚拟扩展局域网,是大二层网络中广泛使用的网络虚拟化技术。在源网络设备与目的网络设备之间建立一条逻辑VXLAN隧道,采用MAC in UDP的封装方…

Vue+SpringBoot打造大学生相亲网站

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 查询会员4.2 查询相亲大会4.3 新增留言4.4 查询新闻4.5 新增新闻 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的大学生相亲网站,包含了会员管理模块、新闻管…

嵌入式系统中端口号的理解与分析

每当看到有人的简历上写着熟悉 tcp/ip, http 等协议时, 我就忍不住问问他们: 你给我说说, 端口是啥吧! 可惜, 很少有人能说得让人满意... 所以这次就来谈谈端口(port), 这个熟悉的陌生人. 在此过程中, 还会谈谈间接层, naming service 等概念, IoC, 依赖倒置等原则以及 TCP 协议…

*地宫取宝c++

题目 输入样例1: 2 2 2 1 2 2 1输出样例1: 2输入样例2: 2 3 2 1 2 3 2 1 5输出样例2: 14 思路 题目说从入口开始,只能向右或向下行走到达右下角,类似“摘花生”这道题的模型。题目又说只有当格子里的宝…

数字化浪潮下的选择:ETU-LINK光模块写码板全新升级至400G

随着光模块市场的蓬勃发展,ETU研发团队致力于不断改进SFP编码工具,以满足日益增长的客户需求。经过不懈努力,我们成功推出了3.0版本的写码板,在硬件和软件方面都进行了全面升级,为用户带来了极大的便利。今天&#xff…

一文弄懂空间金字塔池化网络

目录 空间金字塔池化网络是什么? 组成部分和工作原理 具体步骤 ​编辑 主要优点 空间金字塔池化网络是什么? 空间金字塔池化网络(Spatial Pyramid Pooling Network,SPPNet)是一种用于处理具有不同尺寸和比例的输入…

前端网络相关面试题

1. GET和POST请求的区别 传递参数不同: POST传递的参数在request body中GET传递的参数在url后拼接 GET请求一般用于查询,POST一般用于提交某种信息进行某些修改操作 POST相对GET请求安全 GET请求会被浏览器主动缓存,POST不会,要…

蓝牙耳机哪个好?2024年蓝牙耳机推荐榜单,新手上路!

​面对市场上种类繁多的蓝牙耳机,许多用户感到困惑不解。作为一个耳机爱好者,我根据自己的使用经验,整理了一些我认为值得推荐的蓝牙耳机,希望能为大家在选购时提供一些参考。 一、蓝牙耳机选购指南 1. 佩戴舒适度排名——开放式…

单元测试,写起来到底有多痛?

到底什么是单元测试 这个问题看似非常简单,单元测试嘛,不就是咱们开发自己写些测试类,来测试自己写的代码逻辑对不对。 这句话没有问题,但是不够准确。 首先我们要明白,这个测试二字前面还有两个字: 单元 。…

Windows-WSL2-VSCode+Docker配置C++开发环境

Windows-WSL2-VSCodeDocker配置C开发环境 写在前面 因为在学习工作中,需要不同的编码环境,若将这些不同的开发环境都状态一台设备上,很容易出问题,而且迁移性差,于是计划把不同的开发环境用docker隔离开来&#xff0…

面向对象编程第一式:封装 (Java篇)

本篇会加入个人的所谓‘鱼式疯言’ ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. 🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人…

PMP考试心得,与大家共勉

本人刚刚通过PMP考试,有一些自己的经历,写出来欢迎已经是PMP的兄弟们指正,希望能给正在PMP之路上奔跑的人们一些帮助。 其实很早就听说过PMP认证考试了,但是一直工作很忙没有时间来投入学习考试。因此一直拖到今年夏天才有时间参加…