十、FreeRTOS之FreeRTOS任务调度

news2025/1/18 11:45:00

本节需要掌握的内容如下:

1,开启任务调度器(熟悉)

2,启动第一个任务(熟悉)

3,任务切换(掌握)

一,开启任务调度器(熟悉)

vTaskStartScheduler()

作用:用于启动任务调度器,任务调度器启动后, FreeRTOS 便会开始进行任务调度

该函数内部实现,如下:

1、创建空闲任务

2、如果使能软件定时器,则创建定时器任务

3、关闭中断,防止调度器开启之前或过程中,受中断干扰,会在运行第一个任务时打开中断

4、初始化全局变量,并将任务调度器的运行标志设置为已运行

5、初始化任务运行时间统计功能的时基定时器

6、调用函数 xPortStartScheduler()

xPortStartScheduler()

作用:该函数用于完成启动任务调度器中与硬件架构相关的配置部分,以及启动第一个任务

该函数内部实现,如下:(感兴趣的小伙伴可以自行对源码进行解读哈,还挺有意思的)

1、检测用户在 FreeRTOSConfig.h 文件中对中断的相关配置是否有误

2、配置 PendSV SysTick 的中断优先级为最低优先级

3、调用函数 vPortSetupTimerInterrupt()配置 SysTick

4、初始化临界区嵌套计数器为 0

5、调用函数 prvEnableVFP()使能 FPU

6、调用函数 prvStartFirstTask()启动第一个任务

源码如下:


void vTaskStartScheduler( void )
{
    BaseType_t xReturn;

    /* Add the idle task at the lowest priority. */
    #if ( configSUPPORT_STATIC_ALLOCATION == 1 )
        {
            StaticTask_t * pxIdleTaskTCBBuffer = NULL;
            StackType_t * pxIdleTaskStackBuffer = NULL;
            uint32_t ulIdleTaskStackSize;

            /* The Idle task is created using user provided RAM - obtain the
             * address of the RAM then create the idle task. */
            vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize );
            xIdleTaskHandle = xTaskCreateStatic( prvIdleTask,
                                                 configIDLE_TASK_NAME,
                                                 ulIdleTaskStackSize,
                                                 ( void * ) NULL,       /*lint !e961.  The cast is not redundant for all compilers. */
                                                 portPRIVILEGE_BIT,     /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
                                                 pxIdleTaskStackBuffer,
                                                 pxIdleTaskTCBBuffer ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */

            if( xIdleTaskHandle != NULL )
            {
                xReturn = pdPASS;
            }
            else
            {
                xReturn = pdFAIL;
            }
        }
    #else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
        {
            /* The Idle task is being created using dynamically allocated RAM. */
            xReturn = xTaskCreate( prvIdleTask,
                                   configIDLE_TASK_NAME,
                                   configMINIMAL_STACK_SIZE,
                                   ( void * ) NULL,
                                   portPRIVILEGE_BIT,  /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
                                   &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
        }
    #endif /* configSUPPORT_STATIC_ALLOCATION */

    #if ( configUSE_TIMERS == 1 )
        {
            if( xReturn == pdPASS )
            {
                xReturn = xTimerCreateTimerTask();
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
    #endif /* configUSE_TIMERS */

    if( xReturn == pdPASS )
    {
        /* freertos_tasks_c_additions_init() should only be called if the user
         * definable macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is
         * the only macro called by the function. */
        #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT
            {
                freertos_tasks_c_additions_init();
            }
        #endif

        /* Interrupts are turned off here, to ensure a tick does not occur
         * before or during the call to xPortStartScheduler().  The stacks of
         * the created tasks contain a status word with interrupts switched on
         * so interrupts will automatically get re-enabled when the first task
         * starts to run. */
        portDISABLE_INTERRUPTS();

        #if ( configUSE_NEWLIB_REENTRANT == 1 )
            {
                /* Switch Newlib's _impure_ptr variable to point to the _reent
                 * structure specific to the task that will run first.
                 * See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
                 * for additional information. */
                _impure_ptr = &( pxCurrentTCB->xNewLib_reent );
            }
        #endif /* configUSE_NEWLIB_REENTRANT */

        xNextTaskUnblockTime = portMAX_DELAY;
        xSchedulerRunning = pdTRUE;
        xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;

        /* If configGENERATE_RUN_TIME_STATS is defined then the following
         * macro must be defined to configure the timer/counter used to generate
         * the run time counter time base.   NOTE:  If configGENERATE_RUN_TIME_STATS
         * is set to 0 and the following line fails to build then ensure you do not
         * have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your
         * FreeRTOSConfig.h file. */
        portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();

        traceTASK_SWITCHED_IN();

        /* Setting up the timer tick is hardware specific and thus in the
         * portable interface. */
        if( xPortStartScheduler() != pdFALSE )
        {
            /* Should not reach here as if the scheduler is running the
             * function will not return. */
        }
        else
        {
            /* Should only reach here if a task calls xTaskEndScheduler(). */
        }
    }
    else
    {
        /* This line will only be reached if the kernel could not be started,
         * because there was not enough FreeRTOS heap to create the idle task
         * or the timer task. */
        configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
    }

    /* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0,
     * meaning xIdleTaskHandle is not used anywhere else. */
    ( void ) xIdleTaskHandle;

    /* OpenOCD makes use of uxTopUsedPriority for thread debugging. Prevent uxTopUsedPriority
     * from getting optimized out as it is no longer used by the kernel. */
    ( void ) uxTopUsedPriority;
}
/*-----------------------------------------------------------*/

二,启动第一个任务(掌握)

2.1prvStartFirstTask ()   /* 开启第一个任务 */

想象下应该如何启动第一个任务?

假设我们要启动的第一个任务是任务A,那么就需要将任务A的寄存器值恢复到CPU寄存器

任务A的寄存器值,在一开始创建任务时就保存在任务堆栈里边!

注意:

1、中断产生时,硬件自动将xPSR,PC(R15),LR(R14),R12,R3-R0出/入栈;
R4~R11需要手动出/入栈

2、进入中断后硬件会强制使用MSP指针 ,此时LR(R14)的值将会被自动被更新为特殊的EXC_RETURN

用于初始化启动第一个任务前的环境,主要是重新设置MSP 指针,并使能全局中断

1、什么是MSP指针?

程序在运行过程中需要一定的栈空间来保存局部变量等一些信息。当有信息保存到栈中时,
MCU 会自动更新 SP 指针,ARM Cortex-M 内核提供了两个栈空间:

主堆栈指针(MSP):它由 OS 内核、异常服务例程以及所有需要特权访问的应用程序代码来使用

进程堆栈指针(PSP):用于常规的应用程序代码(不处于异常服务例程中时)。

FreeRTOS中,中断使用MSP(主堆栈),中断以外使用PSP(进程堆栈)

2.2vPortSVCHandler ()   /* SVC中断服务函数 */

当使能了全局中断,并且手动触发 SVC 中断后,就会进入到 SVC 的中断服务函数中

注意:SVC中断只在启动第一次任务时会调用一次,以后均不调用

三、任务切换(掌握)

任务切换的本质:就是CPU寄存器的切换。

假设当由任务A切换到任务B时,主要分为两步:

第一步:需暂停任务A的执行,并将此时任务A的寄存器保存到任务堆栈,这个过程叫做保存现场

第二步:将任务B的各个寄存器值(被存于任务堆栈中)恢复到CPU寄存器中,这个过程叫做恢复现场

对任务A保存现场,对任务B恢复现场,这个整体的过程称之为:上下文切换

PendSV中断是如何触发的?

1、滴答定时器中断调用

2、执行FreeRTOS提供的相关API函数:portYIELD()

本质:通过向中断控制和状态寄存器 ICSR bit28 写入 1 挂起 PendSV 来启动 PendSV 中断

上表摘取于《Cortex M3权威指南(中文)》131页。

PendSV中断是如何触发的?

1、滴答定时器中断调用

1PendSV的任务切换操作(出栈,即恢复现场)

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

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

相关文章

java高校实验室排课学生考勤系统springboot+vue

随着各高校办学规模的迅速扩大,学科专业的不断拓宽,传统的实验教学和实验室管理方法已经不能适应学校管理的要求,特别是化学实验室的管理,化学实验室仪器药品繁杂多样,管理任务繁重,目前主要使用人工记录方法管理,使用不便,效率低下,而且容易疏漏.时间一长将产生大量的文件和数…

【C++】异常处理 ⑦ ( 异常类的继承层次结构 | 抛出 / 捕获 多个类型异常对象 | 抛出子类异常对象 / 捕获并处理 父类异常对象 )

文章目录 一、抛出 / 捕获 多个类型异常对象1、抛出 / 捕获 多个类型异常对象2、操作弊端3、完整代码示例 二、异常类的继承层次结构1、抛出子类异常对象 / 捕获并处理 父类异常对象2、完整代码示例 - 抛出子类异常对象 / 捕获并处理 父类异常对象 自定义的 异常类 , 可能存在 …

Java数据结构 之 包装类简单认识泛类

生命不息,奋斗不止 目录 1. 什么是包装类? 1.1 装箱和拆箱 1.2 自动装箱和自动拆箱 2. 什么是泛型 3. 引出泛型 3.1 语法 4 泛型类的使用 4.1 语法 4.2 示例 4.3 类型推导(Type Inference) 5. 裸类型(Raw Type) (了解&#xff09…

〖大前端 - 基础入门三大核心之JS篇㊻〗- JS + CSS实现动画

说明:该文属于 大前端全栈架构白宝书专栏,目前阶段免费,如需要项目实战或者是体系化资源,文末名片加V!作者:不渴望力量的哈士奇(哈哥),十余年工作经验, 从事过全栈研发、产品经理等工作&#xf…

JSP格式化标签 parseDate将指定时间格式字符串转为真正的date对象

格式化标签最后一个就是 parseDate 作用 将一个日期/时间格式字符串 转为 真正的date时间类型 有点无语 这种 东西应该都是在java中去做的 而不是在java中 这个建议也是做个了解即可 作用不是那么大 基本语法如下 这里 我们 直接编写代码如下 <% page contentType"…

智能优化算法应用:基于天牛须算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于天牛须算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于天牛须算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.天牛须算法4.实验参数设定5.算法结果6.参考文献7.…

2024年AMC8美国初中数学竞赛最后一个月复习指南(附资料)

还有一个半月的时间&#xff0c;2024年AMC8&#xff08;大家默认都直接叫这个比赛的英文名&#xff0c;而不叫中文名美国数学竞赛或美国初中数学竞赛了&#xff09;就要开始了。 有志于在2024年AMC8的比赛中拿到奖项的孩子已经在“磨拳霍霍”了。那么最后一个半月的时间该如何…

智能优化算法应用:基于热交换算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于热交换算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于热交换算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.热交换算法4.实验参数设定5.算法结果6.参考文献7.…

Java线程池的使用和最佳实践

第1章&#xff1a;引言 处理并发问题时&#xff0c;如果每次都新建线程&#xff0c;那系统的压力得有多大&#xff1f;这时候&#xff0c;线程池就像一个英雄一样出现了&#xff0c;它帮我们有效地管理线程&#xff0c;提高资源利用率&#xff0c;降低开销。那么&#xff0c;为…

还搞不懂什么是参数,超参数吗?三分钟快速了解参数与超参数的概念和区别!!!

文章目录 前言一、参数是什么&#xff1f;二、超参数是什么三&#xff0c;常使用的超参数有哪些 前言 参数是模型中可被学习和调整的参数&#xff0c;通过训练数据进行学习和优化&#xff1b; 而超参数则是手动设置的参数&#xff0c;用于控制模型的行为和性能&#xff0c;超…

探秘Python FastAPI、Sanic、Tornado 与Golang Gin性能之战!

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com Python和Golang作为两种流行的编程语言&#xff0c;都拥有强大的异步框架&#xff0c;为开发者提供了在构建高性能应用时的选择。在Python阵营中&#xff0c;FastAPI、Sanic、Tornado等框架因其异步特性和高效的…

viple模拟器使用(四):unity模拟器中实现两距离局部最优迷宫算法

名字解读 两距离&#xff1a;指的是左侧距离和右侧距离 局部最优&#xff1a;对当前状态来说最好的选择&#xff0c;至于整体能不能达到最优&#xff0c;是无法确定的。 从节点1到节点5&#xff0c;一共有3条路 第1条路线&#xff1a;1→2→4→5&#xff0c;对应的花销是&…

LeetCode刷题---反转链表

个人主页&#xff1a;元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏&#xff1a;http://t.csdnimg.cn/ZxuNL http://t.csdnimg.cn/c9twt 前言&#xff1a;这个专栏主要讲述递归递归、搜索与回溯算法&#xff0c;所以下面题目主要也是这些算法做的 我讲述…

Linux 基础认识

文章目录 前言Linux历史window历史Linux地位发行版本 前言 建议只看概述 Linux历史 概述&#xff1a; 由一个研究生受Minix操作系统启发编写的&#xff0c;因为功能实用&#xff0c;代码开源被世界人接收和开发 &#xff0c;最终正式发布 。 详情&#xff1a; 1991年10月5日…

JavaSE学习路线及经验所谈

前言 一.学习框架二.学习经验 相信很多小白刚开始学习Java时&#xff0c;都是靠自己在网上搜集资料&#xff0c;并没有明确规划&#xff0c;不知道要学习什么内容&#xff0c;也不知道学习的重点是什么&#xff0c;那么这篇文章会给你一个大致的指引&#xff0c;当然也可以作为…

Apache Doris 详细教程(二)

5、doris的查询语法 5.1、doris查询语法整体结构 SELECT [ALL | DISTINCT | DISTINCTROW ] -- 对查询字段的结果是否需要去重&#xff0c;还是全部保留等参数 select_expr [, select_expr ...] -- select的查询字段 [FROM table_references [PARTITION…

Project 1: The Game of Hog(CS61A)

&#xff08;第一阶段&#xff09;问题 5a&#xff08;3 分&#xff09; 实现该函数&#xff0c;该函数模拟了完整的 Hog 游戏。球员 交替轮流掷骰子&#xff0c;直到其中一名玩家达到分数。playgoal 您现在可以忽略 Feral Hogs 规则和论点; 您将在问题 5b 中实现它。feral_h…

微信小程序:调用 摄像头、选择照片或视频 都没反应 / wx.chooseImage 选择上传图片无反应

一、问题描述 微信小程序 调用 摄像头、选中的照片或视频&#xff0c;都没反应 wx.chooseImage 选择上传图片无反应 二、问题解决 2.1、设置 登录后台关联&#xff0c;点“设置” 2.2、服务内容声明 服务内容声明&#xff0c;用户隐私保护指引&#xff0c;更新 2.3、添加…

React立即更新DOM

正常情况下&#xff0c;react会等待set完毕后再进行页面渲染&#xff0c;所以在set时无法拿到更新后的dom import { useRef, useState } from "react"export default () > {const div useRef(null)const [count, setCount] useState(0)const btnClick () >…

手写实现一个动态代理框架

手写实现一个动态代理框架 什么是代理模式什么是动态代理动态代理中的编译、类加载与对象实例化手写实现一个动态代理框架实现细节DynamicProxyHandlerProxy生成代码写入代码到磁盘文件调用编译器进行编译调用类加载器进行类加载反射实例化删除前面生成的java文件和class文件 C…