freertos的核心---调度(RISC_V为例)

FreeRTOS时间片 在 RTOS 中,最小的时间单位为一个 tick,即 SysTick 的中断周期, RT-Thread 和 μC/OS 可以指定时间片的大小为多个 tick,但是 FreeRTOS 不一样,时间片只能是一个 tick。在SysTick中断函数中并不是直接执行任务切换,而是将xTickCount进行加1操作,xTickCount是FreeRTOS的系统时钟节拍数,具体实现函数则是xTaskIncrementTick(),该函数在中断函数中被调用。FreeRTOS支持时间片的功能。最后返回任务切换信息。 阅读详情

前言.这篇文章可能稍长,需要你花点时间去理解.

这也是freertos核心.理解了它,其他的就不在话下了.

 

一.任务的启动

void vTaskStartScheduler( void )
{
BaseType_t xReturn;


        xReturn = xTaskCreate(    prvIdleTask,
                                configIDLE_TASK_NAME,
                                configMINIMAL_STACK_SIZE,
                                ( void * ) NULL,
                                portPRIVILEGE_BIT, 
                                &xIdleTaskHandle ); 
    }

    if( xReturn == pdPASS )
    {
        portDISABLE_INTERRUPTS();

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


        portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();
        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(). */
        }
    }

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

1.首先创建IDLE任务.IDLE任务优先级最低.

2.初始化变量

xNextTaskUnblockTime = portMAX_DELAY;/*下一次任务启动的时间*/
xSchedulerRunning = pdTRUE;/*调度器标志开启*/
xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;/*时间戳置位*/

3.运行xPortStartScheduler函数,正常函数不会返回.

 

二.开启调度

xPortStartScheduler需要由移植层提供,这里以RISC_V为例说明

/*
 * See header file for description.
 */
BaseType_t xPortStartScheduler( void )
{

    __disable_irq();
    vPortSetupTimerInterrupt();

    /* Start the first task. */
    prvPortStartFirstTask();


    vTaskSwitchContext();
    prvTaskExitError();

    /* Should not get here! */
    return 0;
}

1.禁止中断

2.vPortSetupTimerInterrupt函数-使能mtimer寄存器并且配置mtimer为非向量中断(系统时间的节拍器,即tick多久产生一次中断)

使能mtimer的软件中断并配置为向量中断,立即切换上下文

3.prvPortStartFirstTask函数,开启第一个任务,进行上下文的切换

prvPortStartFirstTask:
    la t0, __stack_end__
    csrw CSR_MSCRATCH, t0
    LOAD sp, pxCurrentTCB          
    LOAD sp, 0x0(sp)                


    LOAD t0,  0  * REGBYTES(sp)
    csrw CSR_MEPC, t0
   
    LOAD t0,  (portRegNum - 1)  * REGBYTES(sp)
    csrw CSR_MSTATUS, t0
   
    LOAD x1,  1  * REGBYTES(sp)   
    LOAD x5,  2  * REGBYTES(sp)
    LOAD x6,  3  * REGBYTES(sp)
    LOAD x7,  4  * REGBYTES(sp)
    LOAD x8,  5  * REGBYTES(sp)
    LOAD x9,  6  * REGBYTES(sp)
    LOAD x10, 7  * REGBYTES(sp)
    LOAD x11, 8  * REGBYTES(sp)
    LOAD x12, 9  * REGBYTES(sp)
    LOAD x13, 10 * REGBYTES(sp)
    LOAD x14, 11 * REGBYTES(sp)
    LOAD x15, 12 * REGBYTES(sp)
#ifndef __riscv_32e
    LOAD x16, 13 * REGBYTES(sp)
    LOAD x17, 14 * REGBYTES(sp)
    LOAD x18, 15 * REGBYTES(sp)
    LOAD x19, 16 * REGBYTES(sp)
    LOAD x20, 17 * REGBYTES(sp)
    LOAD x21, 18 * REGBYTES(sp)
    LOAD x22, 19 * REGBYTES(sp)
    LOAD x23, 20 * REGBYTES(sp)
    LOAD x24, 21 * REGBYTES(sp)
    LOAD x25, 22 * REGBYTES(sp)
    LOAD x26, 23 * REGBYTES(sp)
    LOAD x27, 24 * REGBYTES(sp)
    LOAD x28, 25 * REGBYTES(sp)
    LOAD x29, 26 * REGBYTES(sp)
    LOAD x30, 27 * REGBYTES(sp)
    LOAD x31, 28 * REGBYTES(sp)
#endif

    addi sp, sp, portCONTEXT_SIZE

    mret

逐句解释.

la t0, __stack_end__

csrw CSR_MSCRATCH, t0

1.把工程的桟底写入to寄存器

2.然后通过csrw指令写入内核暂存寄存器CSR_MSCRATCH

LOAD sp, pxCurrentTCB           
LOAD sp, 0x0(sp)                

1.把pxCurrentTCB赋予桟指正sp,而pxCurrentTCB就是任务,而任务结构体的第一项就是桟顶.这就对应起来了

2.把sp桟的第一项出站,第一项就是PC地址

后边的指令依次出栈.这就和任务创建时桟初始化对应起来了

PC指针赋予了任务的函数地址,此时跳转到任务函数运行啦.至此第一个任务运行起来了.

 

三.任务的节拍器

来看看mtimer中断都干了什么事情

void xPortSysTickHandler( void )
{
    portDISABLE_INTERRUPTS();
    {
        SysTick_Config(SYSTICK_TICK_CONST);
        if( xTaskIncrementTick() != pdFALSE )
        {
            portYIELD();
        }
    }
    portENABLE_INTERRUPTS();
}

1.禁止中断

2.重新配置mtimer下次的触发时间

3.xTaskIncrementTick函数.增加系统的tick数值

4.如果有必要使用portYIELD进行切换.

portYIELD触发一次mtimer软件中断,保存当前任务上下文,切换运行任务上下文.切换的过程和prvPortStartFirstTask基本一直,只是需要二次切换

 

这里重点来看看xTaskIncrementTick干了些什么事情.

BaseType_t xTaskIncrementTick( void )
{
TCB_t * pxTCB;
TickType_t xItemValue;
BaseType_t xSwitchRequired = pdFALSE;


    if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
    {
       /*调度器没有挂起*/
        const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1;
        xTickCount = xConstTickCount;

        if( xConstTickCount == ( TickType_t ) 0U ) /*lint !e774 'if' does not always evaluate to false as it is looking for an overflow. */
        {
            taskSWITCH_DELAYED_LISTS();
        }

       /*判断延时列表是否超时*/
        if( xConstTickCount >= xNextTaskUnblockTime )
        {
            for( ;; )
            {

               /*获取正确的下一次延时时间,延时时间在状态列表项中*/
                if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
                {
                    xNextTaskUnblockTime = portMAX_DELAY;
                    break;
                }
                else
                {
                    pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
                    xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );

                    if( xConstTickCount < xItemValue )
                    {
                        xNextTaskUnblockTime = xItemValue;
                        break;
                    }

                    /*延时到了,把状态列表项从延时列表中删除*/
                    ( void ) uxListRemove( &( pxTCB->xStateListItem ) );

                    /*  如果有事件等待要处理,则事件列表项也删除*/
                    if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
                    {
                        ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
                    }

                    /*  添加任务到就绪列表*/
                    prvAddTaskToReadyList( pxTCB );

                  
                    #if (  configUSE_PREEMPTION == 1 )
                    {
                        /*任务切换的判断*/
                        if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
                        {
                            xSwitchRequired = pdTRUE;
                        }
                    }
                    #endif /* configUSE_PREEMPTION */
                }
            }
        }

        #if ( configUSE_TICK_HOOK == 1 )
        {
            if( xPendedTicks == ( TickType_t ) 0 )
            {
                vApplicationTickHook();
            }
        }
        #endif /* configUSE_TICK_HOOK */

        #if ( configUSE_PREEMPTION == 1 )
        {
            if( xYieldPending != pdFALSE )
            {
                xSwitchRequired = pdTRUE;
            }
        }
        #endif /* configUSE_PREEMPTION */
    }
    else
    {

        /*调度器挂起了,记录挂起的时间,等调度器唤醒后处理.*/
        ++xPendedTicks;


        #if ( configUSE_TICK_HOOK == 1 )
        {
            vApplicationTickHook();
        }
        #endif
    }

    return xSwitchRequired;
}

重要的节点已经备注了.

xTaskIncrementTick函数增加tic数值,判断延时超时.如果是,则处理,并且做任务的切换.

思路还是很清晰的.

这里xPendedTicks记录在调度器挂起的情况下,中断了多少次,然后等调度器恢复后,做tick补偿等等工作.

调度器挂起只是不能切换上下文,中断还是可以响应的.挂起列表,用处就在这里

 

四.思考与总结

从任务的第一次启动,任务的运行,系统节拍器,软中断切换上下文.

如果你已经理解了上面的内容,那么恭喜你,你对freertos的理解更近的一步.

此时系统已经具备了运行的基础和核心.

 

FREERTOS任务调度和切换 我们已经学会了FreeRTOS的任务创建和删除,挂起和恢复等基本操作,并且也学习了分析FreeRTOS源码所必须掌握的知识:列表和列表项。但是任务究竟如何被创建、删除、挂起和恢复的?系统是怎么启动的等等这些我们还不了解,一个操作系统最核心的内容就是多任务管理,所以我们非常有必要去学习一下FreeRTOS的任务创建、删除、挂起、恢复和系统启动等,这样才能对FreeRTOS有一个更深入的了解。本章和下一章要讲解的内容和Cortex-M处理器的内核架构联系非常紧密! 阅读详情

相关推荐

FreeRTOS任务调度与任务切换 | FreeRTOS

FreeRTOS任务调度与任务切换

qq_57663276的博客 2984

ESP32-C3 RISC-V核心FreeRTOS任务调度观察

ESP32-C3的FreeRTOS移植展示了RISC-V架构在嵌入式RTOS领域的成熟度。单核设计虽然简化了SMP的复杂性,但也带来了资源共享和实时性保证的新挑战。核心结论上下文切换开销:约1.5μs @160MHz,与Cortex-M4相当临界区机制:全局关中断,不适合长临界区Wi-Fi共存:单核下协议栈抢占不可避免,需通过优先级和任务设计缓解调试便利性:内置USB-JTAG使RISC-V调试体验优于Xtensa未来展望。

从事移动开发八年,csdn博客专家,阿里云特邀专家,华为云云享专家,高级网络信息安全工程师,大数据工程师(高级),python技术开发(高级),HarmonyOS 应用开发者高级认证,现在某学校任教。 1万+

从ARM到RISC-V:FreeRTOS在不同芯片架构下的中断与任务切换机制对比

本文对比了FreeRTOS在ARM Cortex-M与RISC-V(以ESP32-S3为)两种芯片架构下的中断处理与任务切换机制。重点分析了SVC与ECALL异常触发、硬件与软件上下文保存策略、PendSV与直接切换差异,并探讨了其对系统实时性和性能的影响,为嵌入式开发者在不同平台进行FreeRTOS移植和优化提供实践指导。

fff88的博客 915

FreeRTOS开启任务调度函数xPortStartScheduler详解

比如系统正在执行一个受FreeRTOS管理的中断且正在对一个信号量进行操作,此时产生了一个不受FreeRTOS管理的中断,在中断中也调用FreeRTOS信号量函数,那么有可能会使得之前中断保存的上下文并不是实际的上下文,从而产生错误。所以,上面的代码就是取出中断优先级为0的,即第一个用户中断优先级配置寄存器,然后往里面写0xFF来测试8位优先级是几位有效的。如果设置不对,可能导致本不能调用FreeRTOS函数的某优先级的中断可以调用FreeRTOS函数,从而导致某些错误,如信号量的不统一。

主要分享嵌入式软件、人工智能部分知识 3935

1266_FreeRTOS调度器启动代码实现分析

全部学习汇总: GreyZhang/g_FreeRTOS: learning notes about FreeRTOS. (github.com)这一次看一下FreeRTOS的启动都执行了什么操作。这个是CubeIDE生成的代码中内核启动的接口,这个并不是FreeRTOS最原始的接口而是进行了一定封装的结果。看一下这个接口的实现。这种封装的意义看起来不是很大,因为没有实质的变化,只是增加了一个无效的返回值。然而,增加的这个无效返回值在调用的时候又被丢弃了。因此,这个其实可以直接调用FreeRTOS的接口即可

小灰笔记 1435

[RISCV]为RISC-V移植FreeRTOS系列之三 -- 时基

前言 书接上回,上回说到我们已经做好了准备,所谓万事具备,就差一场东风,而能吹动FreeRTOS这条大船的是什么呢?没错,聪明的你已经猜到了,是时基。 有过其他MCU移植经验的小伙伴应该知道,时基是操作系统的心跳,所有的操作,包括任务切换,抢占等等都是基于时基,在STM32中,我们一般用systick(滴答计时器)作为时基,而在riscv中,我们用**mechine timer(以下简称mtime)**作为时基。 作者:wangyijieonline 链接:https://blog.csdn.net/wa

wangyijieonline的博客 4630

从0到1打造RISC-V智能家居中控:硬件+固件+通信全链路实战

本文将详细介绍如何从零开始构建基于RISC-V架构的智能家居中控系统,涵盖硬件选型、固件开发、通信协议设计等全链路实现方案。文章首先分析了RISC-V架构在物联网领域的优势,然后系统性地阐述了智能家居中控的硬件搭建(以ESP32-C3为)、裸机与RTOS固件开发、MQTT/BLE通信协议实现,以及安全机制设计。通过具体的代码示(如DHT11传感器驱动、I2C通信等)和架构图,帮助开发者快速掌握RISC-V在智能家居领域的实践应用。全文约15分钟可读完,适合嵌入式开发者和物联网技术爱好者参考。

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

FreeRTOS在ESP32-S3上的实现差异:一场从ARM到RISC-V的跨架构嵌入式设计思辨

本文深入探讨了FreeRTOS在ESP32-S3(基于RISC-V架构)与ARM Cortex-M平台上的实现差异,重点分析了中断处理、上下文切换、多核调度核心机制。通过对比两种架构的设计哲学和具体实现,为嵌入式开发者提供了跨平台移植FreeRTOS的实用指南和优化建议,并整合了espidf相关开发信息。

z5a6b的博客 812

ESP-IDF中FreeRTOSRISC-V实现机制解析

本文深入解析ESP-IDF中FreeRTOSRISC-V架构下的实现机制,重点对比与ARM Cortex-M的差异,涵盖任务启动、上下文切换、定时器中断等核心实现,帮助开发者解决从ARM迁移到ESP32-S3时遇到的任务调度和中断处理问题。

oauth7security的博客 478

FreeRTOSRISC-V架构的移植:QEMU virt平台GCC编译流程

### 1.1 核心文件路径 - **RISC-V移植代码**:FreeRTOS/Source/portable/GCC/RISC-V - **QEMU virt平台Makefile**:[FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/Makefile](https://link.gitcode.com/i/ef861a2152b5119f047b6d4031623ecc)

gitblog_00929的博客 1020

RISC-V多核FreeRTOS SMP移植实战:从核心挑战到调试优化

实时操作系统(RTOS)是嵌入式开发的核心,负责管理任务调度、资源分配和中断响应。其多核对称多处理(SMP)扩展旨在充分利用多核处理器性能,通过核间协同实现真正的并行计算。这一技术的关键价值在于提升复杂嵌入式系统的实时性与吞吐量,广泛应用于物联网网关、工业控制和边缘计算等场景。实现SMP需攻克核间中断(IPI)、原子操作与内存屏障等底层挑战,尤其在RISC-V这类开放指令集架构上,其模块化特性要求开发者深入理解硬件与操作系统的交互细节。本文聚焦于RISC-V平台适配FreeRTOS SMP的完整实践,针对移

weixin_33860553的博客 275

FreeRTOS时基选型:为什么RISC-V偏爱mtime而非Systick?从硬件机制聊透

本文深入探讨了FreeRTOSRISC-V架构下选择mtime而非Systick作为时基的技术原因。从硬件机制角度对比了两者的差异,分析了RISC-V采用mtime的简洁性、多核支持和灵活性优势,并提供了FreeRTOSRISC-V上的具体实现方法和实践挑战解决方案。

weixin_30896511的博客 638

从零开始:手把手教你为RISC-V芯片(以Andes N25为)搭建FreeRTOS工程目录

本文详细指导如何为RISC-V芯片(以Andes N25为)搭建FreeRTOS工程目录,从源码包解析到模块化结构设计,涵盖核心组件识别、目录组织、关键文件配置及构建系统集成。特别针对RISC-V架构的移植层适配和内存管理方案选择提供实用建议,帮助开发者高效构建嵌入式实时系统。

weixin_30443731的博客 618

RISC-V定时器中断在FreeRTOS中的应用实战

深入讲解RISC-V架构下定时器中断的配置与触发机制,结合FreeRTOS实现多任务调度的实际应用,展现risc-v在实时系统中的高效响应能力。

weixin_34725745的博客 477

FreeRTOS入门笔记——FreeRTOS介绍

FreeRTOS是一个迷你的实时操作系统内核。作为一个轻量级的操作系统,功能包括:任务管理、时间管理、信号量、消息队列、内存管理、记录功能、软件定时器、协程等,可基本满足较小系统的需要。

ljt2333的博客 2898

CH32V208 RISC-V MCU移植FreeRTOS实战:从环境搭建到多任务调试

实时操作系统(RTOS)是嵌入式开发中管理多任务、确保系统实时性的核心软件架构,其通过任务调度、中断管理和进程间通信等机制,为复杂应用提供清晰的结构与及时的响应。FreeRTOS作为一款开源、轻量级的RTOS内核,凭借其可裁剪性和可移植性,成为资源受限微控制器(MCU)项目的热门选择。在RISC-V架构的国产芯片如沁恒CH32V208上移植FreeRTOS,能充分发挥其硬件性能,构建高效可靠的应用系统。本文聚焦于**FreeRTOS移植**与**RISC-V MCU**开发,详细解析从开发环境搭建、工程配置

weixin_34357436的博客 333

FreeRTOS】中断机制

FreeRTOS中,中断是实现实时性必要的操作。一款芯片的中断涉及到硬件触发,软件触发,软件中断处理。所以FreeRTOS的中断机制其实不好单独拿出来看。FreeRTOS关于中断能做到的是提供一套专门在中断服务函数中使用的API,比如:对应 注意:下文有对于指令集的区分,主要以ESP-IDF(RISC-V为)中断处理主要包括硬件处理部分和软件处理部分(不同的指令集架构有不同)情景假设:用户在系统正在运行Task1时按下按键,此时中断的处理流程如下。以写队列为。用pxHigherPriorityTask

佳大先生的博客 6654
上一篇: freertos的核心---线程与调度
下一篇: freertos的核心-其他调度手段
vircun
博客等级 码龄12年 10粉丝 10原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值