首页 > 其他分享 >FreeRTOS 任务

FreeRTOS 任务

时间:2023-05-23 15:22:47浏览次数:40  
标签:FreeRTOS 中断 PendSV 列表 任务 寄存器 SVC

使用RTOS时,一个实时任务可以作为一个独立的任务,任何一个时间点只有一个任务运行,具体由RTOS调度器决定。

RTOS调度器的职责是确保当一个任务开始执行的时候上下文环境与上一次推出的时候相同,每个任务都有堆栈,任务切换的时候将上下文保存在堆栈中。

任务特性:

1、简单

2、没有使用限制

3、支持抢占

4、支持优先级

5、每个任务都拥有堆栈导致RAM的使用量增大

6、如果使用抢占的话必须仔细的考虑重入的问题

 

(代码73)

 

 

列表是FreeRTOS中的一个数据结构,用来跟踪任务。

typedef struct xLIST
{
    listFIRST_LIST_INTEGRITY_CHECK_VALUE (1)
    configLIST_VOLATILE UBaseType_t uxNumberOfItems; (2)
    ListItem_t * configLIST_VOLATILE pxIndex; (3)
    MiniListItem_t xListEnd; (4)
    listSECOND_LIST_INTEGRITY_CHECK_VALUE (5)
} List_t;

(1) 和 (5) 、 这 两 个 都 是 用 来 检 查 列 表 完 整 性 的 , 需 要 将 宏
configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 设置为 1,开启以后会向这两个地方分别添加一个变量 xListIntegrityValue1 和 xListIntegrityValue2,在初始化列表的时候会这两个变量中写入一个特殊的值,默认不开启这个功能。
(2)、uxNumberOfItems 用来记录列表中列表项的数量。
(3)、pxIndex 用来记录当前列表项索引号,用于遍历列表。
(4)、列表中最后一个列表项,用来表示列表结束

 

列表项就是存放在列表中的项目,FreeRTOS 提供了两种列表项:列表项和迷你列表项

struct xLIST_ITEM
{
    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE (1)
    configLIST_VOLATILE TickType_t xItemValue; (2)
    struct xLIST_ITEM * configLIST_VOLATILE pxNext; (3)
    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; (4)
    void * pvOwner; (5)
    void * configLIST_VOLATILE pvContainer; (6)
    listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE (7)
};
typedef struct xLIST_ITEM ListItem_t;

 (1)和(7)、用法和列表一样,用来检查列表项完整性的。

(2)、xItemValue 为列表项值。
(3)、pxNext 指向下一个列表项。
(4)、pxPrevious 指向前一个列表项,和 pxNext 配合起来实现类似双向链表的功能。
(5)、pvOwner 记录此链表项归谁拥有,通常是任务控制块。
(6)、pvContainer 用来记录此列表项归哪个列表。

 

 

 任务调度器

void vTaskStartScheduler( void )
{
    BaseType_t xReturn;
    xReturn = xTaskCreate( prvIdleTask, (1)
                            "IDLE", configMINIMAL_STACK_SIZE,
                            ( void * ) NULL,
                            ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
                            &xIdleTaskHandle );
    #if ( configUSE_TIMERS == 1 ) //使用软件定时器使能
    {
        if( xReturn == pdPASS )
        {
            xReturn = xTimerCreateTimerTask(); (2)
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    #endif /* configUSE_TIMERS */
    if( xReturn == pdPASS ) //空闲任务和定时器任务创建成功。
    {
        portDISABLE_INTERRUPTS(); (3)
        #if ( configUSE_NEWLIB_REENTRANT == 1 ) //使能 NEWLIB
        {
            _impure_ptr = &( pxCurrentTCB->xNewLib_reent );
        }
        #endif /* configUSE_NEWLIB_REENTRANT */
        xNextTaskUnblockTime = portMAX_DELAY;
        xSchedulerRunning = pdTRUE; (4)
        xTickCount = ( TickType_t ) 0U;
        portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); (5)
        if( xPortStartScheduler() != pdFALSE ) (6)
        {
            //如果调度器启动成功的话就不会运行到这里,函数不会有返回值的
        }
        else
        {
        //不会运行到这里,除非调用函数 xTaskEndScheduler()。
        }
    }
    else
    {
        //程序运行到这里只能说明一点,那就是系统内核没有启动成功,导致的原因是在创建
        //空闲任务或者定时器任务的时候没有足够的内存。
        configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
    }
    //防止编译器报错,比如宏 INCLUDE_xTaskGetIdleTaskHandle 定义为 0 的话编译器就会提
    //示 xIdleTaskHandle 未使用。
    ( void ) xIdleTaskHandle;
}

(1)、创建空闲任务,如果使用静态内存的话使用函数 xTaskCreateStatic()来创建空闲任务,优先级为 tskIDLE_PRIORITY,宏 tskIDLE_PRIORITY 为 0,也就是说空闲任务的优先级为最低。
(2)、如果使用软件定时器的话还需要通过函数 xTimerCreateTimerTask()来创建定时器服务任务。
(3)、关闭中断,在 SVC 中断服务函数 vPortSVCHandler()中会打开中断。
(4)、变量 xSchedulerRunning 设置为 pdTRUE,表示调度器开始运行。
(5)、当宏 configGENERATE_RUN_TIME_STATS 为 1 的时候说明使能时间统计功能,此时
需要用户实现宏 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS,此宏用来配置一个定时器/计数器。
(6)、调用函数 xPortStartScheduler()来初始化跟调度器启动有关的硬件,比如滴答定时器、FPU 单元和 PendSV 中断等等。

 

内核相关硬件初始化函数分析

FreeRTOS 系统时钟是由滴答定时器来提供的,而且任务切换也会用到PendSV 中断,这些硬件的初始化由函数 xPortStartScheduler()来完成,缩减后的函数代码如下: 

BaseType_t xPortStartScheduler( void )
{
    /******************************************************************/
    /****************此处省略一大堆的条件编译代码**********************/
    /*****************************************************************/
    portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; (1)
    portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; (2)
    vPortSetupTimerInterrupt(); (3)
    uxCriticalNesting = 0; (4)
    prvStartFirstTask(); (5)
    //代码正常执行的话是不会到这里的!
    return 0;
}

 (1)、设置 PendSV 的中断优先级,为最低优先级。
(2)、设置滴答定时器的中断优先级,为最低优先级。
(3)、调用函数 vPortSetupTimerInterrupt()来设置滴答定时器的定时周期,并且使能滴答定时
器的中断,函数比较简单,大家自行查阅分析。
(4)、初始化临界区嵌套计数器。
(5)、调用函数 prvStartFirstTask()开启第一个任务。

 

 

上下文切换被触发的场合可以是:

  执行一个系统调用

  系统滴答定时器(SyeTick)中断

 

  1. SVC中断(软件中断):SVC中断是一种软件中断,通常用于任务之间的通信。在FreeRTOS中,SVC中断可以打断最高优先级的任务。然而,任务永远不能被SVC中断打断。在SVC中断处理函数中,可以使用FreeRTOS提供的API函数,但需要注意的是,一些API函数在ISR内部是无效的,例如将任务置于阻塞状态。
  2. ISR中断(硬件中断):ISR中断是一种硬件中断,通常用于处理外部硬件事件,如输入输出、定时器到期等。在FreeRTOS中,ISR中断可以打断任何正在运行的任务。与SVC中断不同,ISR中断处理函数中可以使用独立的中断安全API函数。这些API函数可以在ISR内部执行,不会将任务置于阻塞状态。

 

  1. SVC中断(系统调用):SVC中断是一种软件中断,由SVC指令触发调用。它在FreeRTOS中用于在任务调度中开启第一个任务。SVC中断处理函数中可以使用FreeRTOS提供的API函数,但在ISR内部一些API函数可能会失效。
  2. PendSV中断(可悬起的系统调用):与SVC相关的是PendSV中断。PendSV中断是一种可悬起的系统调用,意味着它可以延迟响应,直到其他高优先级的中断处理完成。在FreeRTOS中, PendSV中断用于任务切换。当需要发生任务切换时,如果当前正在执行一个中断,则等待该中断执行完毕后,再进行任务切换。PendSV中断处理函数中可以使用独立的中断安全API函数。

 

(p144图)

(1) 任务 A 呼叫 SVC 来请求任务切换(例如,等待某些工作完成)
(2) OS 接收到请求,做好上下文切换的准备,并且 pend 一个 PendSV 异常。
(3) 当 CPU 退出 SVC 后,它立即进入 PendSV,从而执行上下文切换。
(4) 当 PendSV 执行完毕后,将返回到任务 B,同时进入线程模式。
(5) 发生了一个中断,并且中断服务程序开始执行
(6) 在 ISR 执行过程中,发生 SysTick 异常,并且抢占了该 ISR。
(7) OS 执行必要的操作,然后 pend 起 PendSV 异常以作好上下文切换的准备。
(8) 当 SysTick 退出后,回到先前被抢占的 ISR 中, ISR 继续执行
(9) ISR 执行完毕并退出后, PendSV 服务例程开始执行,并且在里面执行上下文切换。
(10) 当 PendSV 执行完毕后,回到任务 A,同时系统再次进入线程模式。 

 

 

在FreeRTOS中,有3个状态寄存器,分别是:

  1. xPSR(程序状态寄存器):记录ALU标志(0标志,进位标志,负数标志,溢出标志),执行状态以及当前正服务的中断号。当任务被中断时,xPSR将保存任务被中断时的状态,以便在中断处理完成后,恢复任务执行状态。
  2. PRIMASK(中断屏蔽寄存器):用于选择性地阻塞或激活异常/中断。如果PRIMASK的对应位为1,则相应的异常/中断将被禁止;如果为0,则相应的异常/中断将被允许。
  3. BASEPRI(基本优先级寄存器):用于设置优先级基线。当异常/中断发生时,处理器会根据当前的BASEPRI值来确定是否抢占正在执行的任务。如果某个异常/中断的优先级高于当前的BASEPRI,则该异常/中断将被抢占执行;否则,该异常/中断将被阻塞。

总之,FreeRTOS中的状态寄存器主要用于保存任务被中断时的状态以及控制异常/中断的执行。这些寄存器可以帮助任务在中断处理过程中正确地保存和恢复状态,确保系统稳定运行。

 

PendSV中断服务函数

__asm void xPortPendSVHandler( void )
{
    extern uxCriticalNesting;
    extern pxCurrentTCB;
    extern vTaskSwitchContext;
    PRESERVE8
    mrs r0, psp (1)
    //读取进程栈指针,保存在寄存器R0里面
        
    isb
    ldr r3, =pxCurrentTCB (2)
    ldr r2, [r3] (3)
    //获取当前任务的任务控制块,并将任务控制块的地址保存在寄存器R2里面
        
    stmdb r0!, {r4-r11, r14} (4)
    //保存r4~r11和R14这几个寄存器的值
    
    str r0, [r2] (5)
    //将寄存器R0的值写入寄存器R2所保存的地址中去,也就是将新的栈顶保存在任务控制块的第一个字段中。
    //此时的寄存器R0保存着最新的堆栈栈顶指针值,所以要将这个最新的栈顶指针写入到当前任务的任务控制块的
    //第一个字段,而经过(2)和(3)已经获取到了任务控制块,并将任务控制块的首地址写入到了寄存器R2中。
        
    stmdb sp!, {r3,r14} (6)
    //将寄存器R3和R14的值临时压栈,寄存器R3中保存了当前任务的任务控制块,而接下来要调用vTaskSwitchContext(),
    //为了防止R3和R14的值被改写,所以这里临时将R3和R14的值先压栈。
    
    mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY (7)
    msr basepri, r0 (8)
    //关闭终端,进入临界区
        
    dsb
    isb
    bl vTaskSwitchContext (9)
    //调用函数vTaskSwitchContext(),此函数用来获取下一个要执行的任务,并将pxCurrentTCB更新为这个要运行的任务
        
    mov r0, #0 (10)    
    msr basepri, r0 (11)
    //打开中断,退出临界区
        
    ldmia sp!, {r3,r14} (12)
    //刚刚保存的寄存器R3和R14的值出栈,恢复寄存器R和R14的值。注意,经过(12),此时pxCurrentTCB的值已经改变
    //所以读取R3所保存的地址处的数据就会发现其值改变,成为了下一个要运行的任务的任务控制块。
    
    ldr r1, [r3] (13)
    ldr r0, [r1] (14)
    //获取新的要运行的任务的任务堆栈栈顶,并将栈顶保存在寄存器R0中。
        
    ldmia r0!, {r4-r11} (15)
    //R4~R11,R14出栈,也就是即将运行的任务现场。
    
    msr psp, r0 (16)
    //更新进程栈指针PSP的值。
        
    isb
    bx r14 (17)
    //执行此行代码以后硬件自动恢复寄存器 R0~R3、R12、LR、PC 和 xPSR 的值,确定异常返回以后应该进入处理器
    //模式还是进程模式,使用主栈指针(MSP)还是进程栈指针(PSP)。很明显这里会进入进程模式,并且使用进程栈指
    //针(PSP),寄存器 PC 值会被恢复为即将运行的任务的任务函数,新的任务开始运行!至此,任务切换成功。
        
    nop
}

 

标签:FreeRTOS,中断,PendSV,列表,任务,寄存器,SVC
From: https://www.cnblogs.com/wxk1213/p/17422765.html

相关文章

  • 没有按照计划做完任务之思考
    计划总是赶不上变化,作为软件工程师会遇到任务不能按期完成,我觉得可以从以下几方面考虑,抛砖引玉。应该怎样提前规避这些问题?怎样发现这些风险?出现风险之后,怎样争取去协调?争取资源?不导致整体进度不受到影响。 应该怎样提前规避这些问题?设计阶段需要考虑重点案例满足。也要......
  • 2步轻松实现ASP.NET Core托管服务执行定时任务
    最近接到一个新项目,需要在项目里添加一个后台任务,定时去发邮件通知客户;由于是一个比较小型的项目,不希望引入Quartz.Net、Hangfire等太重的框架,同时也没持久化要;寻觅了一下发现ASP.NETCore本身带有托管服务,可以执行定时任务。ASP.NETCore提供了IHostedService接口,它使我们能够创......
  • quartz定时任务时间设置
    每天凌晨2点002**?和每天隔一小时0**/1**?例1:每隔5秒执行一次:*/5****?例2:每隔5分执行一次:0*/5***?在26分、29分、33分执行一次:026,29,33***?例3:每天半夜12点30分执行一次:0300**?(注意日期域为0不是24)每天凌晨1点执行一次:001**?......
  • 系统启动项及定时任务
     用cmd输入regedit启动注册表 如果无法显示,输入cncp437,然后在输入命令 用文件资源管理器打开  用命令行 用cmd运行 ......
  • angular中轮训查任务
    //查状态checkStatus(test_plan_info):void{constselectData=test_plan_info;//过滤出和this.modelData中和test_plan_info中test_application_sample_no相等的数据constmodelDataCopy=this.modelData.filter((v)=>{returnselectData.so......
  • FreeRTOS应用基础(一)
      本系列主要作为自己第一次系统学习RTOS的记录,以正点原子的STM32F103战舰,keil环境编程为例。想要达到以下目标:  1:初步熟悉FreeRTOS的移植和使用,并迁移完成一个小型项目;  2:以FreeRTOS为入门,了解RTOS的本质,并提升阅读源码的能力;  本系列文章主要参考以下资料,本文仅作为......
  • springboot添加@Scheduled定时任务多线程执行
    packagecom.example.demo;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.EnableAutoConfiguration;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.boot.autoc......
  • DolohinScheduler 分布式任务调度框架 代码流程分解
    一、DS-API模块-执行工作流 -定时任务执行 更新schedule参数 -/schedule新增schedule参数做了什么事? 将schedule参数用ScheduleParam类进行解析 有效性校验,而后解析保存到t_ds_schedules表内,更新t_ds_process_definition表 -/onlin......
  • FreeRTOS移植
    一、 二、1.在项目新建文件夹FreeRTOS,把FreeRTOSv202112.00\FreeRTOS\Source所有文件拷贝到新建的文件夹。 2.STM32F40x_FreeRTOS_Test\FreeRTOS\portable,protable中保留如下三个文件,其它删除掉 ......
  • ASP.NET Core 用 Hangfire 实现定时任务周期任务
    前言系统经常会有一些逻辑或任务,需要定时执行或周期性执行,为了实现这个需求,一般有以下几种方式选择:后台任务,即IHostedService开源库:Hangfire开源库:Quartz.net这里,说下Hangfire的使用。官网https://www.hangfire.io/Hangfire–Backgroundjobsandworkersfor.NET......