FreeRTOS 的任务挂起和恢复

news2024/10/6 22:27:37

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.coloradmin.cn/o/1514485.html

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

相关文章

某电信公司组织结构优化咨询项目成功案例纪实

——构建前后端组织结构,提升组织运营效率 随着企业的不断发展,行业的竞争也越来越激烈,企业只能不断调整自身的战略才能更好的适应这样的大环境。在战略调整的过程中,企业往往会面临这样的问题:管理层的经营理念各不…

从0到1快速搭建一个jeecg 企业级应用管理后台

一. 基本介绍 官网地址:https://jeecg.com/ JeecgBoot 是一款企业级的低代码平台!前后端分离架构 SpringBoot2.x,SpringCloud,Ant Design&Vue3,Mybatis-plus,Shiro,JWT 支持微服务。强大的…

Linux内核编译(版本6.0以及版本v0.01)并用qemu驱动

系统环境: ubuntu-22.04.1-desktop-amd64 目标平台: x86 i386 内核版本: linux-6.0.1 linux-0.0.1 环境配置 修改root密码 sudo passwd 修改软件源(非必要) vmtools安装(实现win-linux软件互传) 安装一些必须的软件&…

day02vue学习

day02 一、今日学习目标 1.指令补充 指令修饰符v-bind对样式增强的操作v-model应用于其他表单元素 2.computed计算属性 基础语法计算属性vs方法计算属性的完整写法成绩案例 3.watch侦听器 基础写法完整写法 4.综合案例 (演示) 渲染 / 删除 / 修…

ctf杂项总结

1.文件无法打开 1.1.文件拓展名损坏/错误导致 方法: 1.使用kali当中的file命令查看,之后修改为正确的后缀即可 2.通过16进制编辑器打开查看文件头 3.文件头残缺/错误,可以先使用kail当中的file命令查看它的类型,之后再通过 16…

[SAP ABAP] 数值向上/向下取整

ceil()函数对数值进行向上取整,floor()函数对数值进行向下取整 输出结果:

记录西门子:SCL设置不同顺序

一台搅拌的设备,需要控制三种料的进料顺序和进料重量,顺序和重量可以随便设定,也可以是几十种料。触摸屏上面有A、B、C三种液体原料,需要设定三种液体原料重量,并设定序号。 假设如下面所示设定:那将先打开…

【C++补充1】map容器

1.单映射 1.单映射&#xff0c;first:键 second:值 2.键唯一&#xff0c;如果重复&#xff0c;相同键插入 会覆盖值。 使用方法&#xff1a;pair<int, string> data(1, "Iloveyou"); 1.main int main() {//单映射//first:键 second:值//键唯一&am…

科研学习|论文解读——一种修正评分偏差并精细聚类中心的协同过滤推荐算法

知网链接 一种修正评分偏差并精细聚类中心的协同过滤推荐算法 - 中国知网 (cnki.net) 摘要 协同过滤作为国内外学者普遍关注的推荐算法之一&#xff0c;受评分失真和数据稀疏等问题影响&#xff0c;算法推荐效果不尽如人意。为解决上述问题&#xff0c;本文提出了一种改进的聚类…

【Linux】Shell编程【二】

目录 Shell流程控制条件测试注意事项示例[ condition ]与[[ condition ]]的区别 if条件单分支语法示例1&#xff1a;统计根分区使用率示例2&#xff1a;创建目录 双分支if条件语句语法案例1&#xff1a;备份mysql数据库案例2&#xff1a;判断apache是否启动&#xff0c;如果没有…

tablulator 表格插件使用 vue3 + ts

项目中使用的是layui框架&#xff0c;layui整体使用起来还是挺好用的&#xff0c;界面风格简约&#xff0c;上手也简单&#xff0c;但是layui自带的表格性能真的不咋行&#xff0c;基本上显示超过500条&#xff0c;就很出现浏览器卡顿&#xff0c;全选的时候&#xff0c;浏览器…

OpenCASCADE开发指南<五>:OCC 内存管理器和异常类

一个软件首先要规定能处理的数据类型&#xff0c; 其次要实现三项最基本的功能——引用管理、内存管理和异常管理。在 OCC 中&#xff0c;这三项功能分别对应基础类中的句柄、内存管理器和异常类。 1 异常类 1. 1 异常类的定义 异常处理机制实现了正常程序逻辑与错误处理的分离…

淘宝基于Nginx二次开发的Tengine服务器

最近在群里看到这样一张阿里云网关报错的截图&#xff0c;我保存下来看了下 看到下面有 Tengine提供技术支持&#xff0c;这个Tengine是什么东西呢&#xff1f;我搜索了下似乎是淘宝在nginx的基础上自己改的Web服务器 Tengine还支持OpenResty框架&#xff0c;该框架是基于Ngin…

与鲸同行,智领未来!和鲸科技高校市场渠道合作伙伴正式开启招募

AI 浪潮来袭&#xff0c;技术日新月异&#xff0c;校企合作已成为高校培养符合产业需求的应用型人才、加速科研创新与成果转化的关键途径。从单一应用到多元化布局&#xff0c;各企业更需要技术领先、战略协同的领域伙伴协力共进。 和鲸科技以“协同平台实践社区竞赛”三位一体…

26 easy 35. 搜索插入位置

//给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 // // 请必须使用时间复杂度为 O(log n) 的算法。 // // // // 示例 1: // // //输入: nums [1,3,5,6], …

算法提高之楼兰图腾(树状数组)

楼兰图腾(树状数组) 核心算法&#xff1a;树状数组 将下标转化为二进制 例如11100100 父节点下标x 子节点下标i 由下图可知 每一个数都可以由其子节点**(如果有)**求和得到**由父节点找子节点&#xff1a;**每个子节点下标 –> x – 1 – lowbit(x – 1)由子节点找父节点&am…

19.创建帖子

文章目录 一、建立路由二、开发CreatePostHandler三、编写logic四、编写dao层五、编译测试运行 一、建立路由 这里要稍微注意的是&#xff1a;需要登录后才可以发表帖子&#xff0c;所以需要用到我们之前写的鉴权中间件。中间件对用户携带的token解析成功后&#xff0c;便会将…

深度解析Nginx正向代理的原理与实现

目录 前言 1. 什么是正向代理 2. Nginx正向代理的配置 3. Nginx正向代理的实现原理 4. 示例代码 5. 总结 前言 Nginx是一个高性能的Web服务器和反向代理服务器&#xff0c;但它也可以用作正向代理服务器。本文将深入解析Nginx正向代理的原理和实现&#xff0c;并提供相关…

排序算法之快速排序算法介绍

目录 快速排序介绍 时间复杂度和稳定性 代码实现 C语言实现 c实现 java实现 快速排序介绍 快速排序(Quick Sort)使用分治法策略。 它的基本思想是&#xff1a;选择一个基准数&#xff0c;通过一趟排序将要排序的数据分割成独立的两部分&#xff1b;其中一部分的所有数据…

市场复盘总结 20240313

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票 10%的时候可以操作&#xff0c; 90%的时间适合空仓等待 昨天选出的个股&#xff0c;今日涨6.87% 二…