当前位置: 首页 > article >正文

FreeRTOS从入门到精通 第十一章(FreeRTOS时间管理)

参考教程:【正点原子】手把手教你学FreeRTOS实时系统_哔哩哔哩_bilibili

一、延时函数

1、相对延时与绝对延时

(1)函数概览:

函数

描述

vTaskDelay

相对延时,函数参数为阻塞时间

xTaskDelayUntil

绝对延时,函数参数依次为指向上一次任务唤醒时间的指针pxPreviousWakeTime、绝对延时时间xTimeIncrement

(2)相对延时是指每次延时都是从执行函数vTaskDelay开始,直到延时指定的时间结束,一旦调用则阻塞任务自身。

(3)绝对延时往往将整个任务的运行周期看成一个整体,适用于需要按照一定频率运行的任务,如果任务中调用绝对延时,那么从最近一次开始执行到阻塞过程中任务本身耗费的时间、调用绝对延时所耗费的时间以及任务阻塞时间,三者之和即为绝对延时的时间,如下图所示。(调用绝对延时函数时,绝对延时函数会通过指向上一次任务唤醒时间的指针更新任务最近一次的唤醒时间)

2、相对延时函数源码剖析

(1)void vTaskDelay函数源码:

void vTaskDelay(const TickType_t xTicksToDelay)
{
    BaseType_t xAlreadyYielded = pdFALSE;
    traceENTER_vTaskDelay(xTicksToDelay);
    if(xTicksToDelay > (TickType_t) 0U)   //函数参数需要大于0,否则无效
    {
        vTaskSuspendAll();                    //用户可选择性地实现该函数
        {
            configASSERT(uxSchedulerSuspended == 1U);

            traceTASK_DELAY();
            prvAddCurrentTaskToDelayedList(xTicksToDelay, pdFALSE);  //将当前任务移至阻塞列表(第二个参数是pdTRUE则移动至挂起列表)
        }
        xAlreadyYielded = xTaskResumeAll();    //恢复任务调度器
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    if( xAlreadyYielded == pdFALSE )           //判断是否需要任务切换
    {
        taskYIELD_WITHIN_API();
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    traceRETURN_vTaskDelay();
}

(2)prvAddCurrentTaskToDelayedList函数源码:

static void prvAddCurrentTaskToDelayedList(TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely)
{
    TickType_t xTimeToWake;
    const TickType_t xConstTickCount = xTickCount;    //记录当前系统的时钟节拍
    List_t * const pxDelayedList = pxDelayedTaskList;
    List_t * const pxOverflowDelayedList = pxOverflowDelayedTaskList;

    #if ( INCLUDE_xTaskAbortDelay == 1 )
    {
        pxCurrentTCB->ucDelayAborted = ( uint8_t ) pdFALSE;
    }
    #endif
    if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
    {
        portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    #if ( INCLUDE_vTaskSuspend == 1 )
    {
        if((xTicksToWait == portMAX_DELAY) &&(xCanBlockIndefinitely != pdFALSE))
        {
            /* xCanBlockIndefinitely为pdFALSE时,不支持直接将任务挂到挂起列表,走else分支 */
            listINSERT_END(&xSuspendedTaskList, &(pxCurrentTCB->xStateListItem) );
        }
        else
        {
            //解除阻塞时间(唤醒时间) = 当前系统时间 + 阻塞时间
            xTimeToWake = xConstTickCount + xTicksToWait;  
            //按唤醒时间顺序将任务插入阻塞列表
            listSET_LIST_ITEM_VALUE(&(pxCurrentTCB->xStateListItem),xTimeToWake);
            if(xTimeToWake <xConstTickCount) //阻塞时间溢出,任务挂到溢出阻塞列表
            {
                traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST();
                vListInsert(pxOverflowDelayedList, &(pxCurrentTCB->xStateListItem));
            }
            else                            //阻塞时间未溢出,任务挂到阻塞列表
            {
                traceMOVED_TASK_TO_DELAYED_LIST();
                vListInsert(pxDelayedList, &(pxCurrentTCB->xStateListItem));
                if( xTimeToWake < xNextTaskUnblockTime )
                {
//更新阻塞列表中距当前时刻最近唤醒的任务的唤醒时间
                    xNextTaskUnblockTime = xTimeToWake;
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        }
    }
    #else /* INCLUDE_vTaskSuspend */
    {
        xTimeToWake = xConstTickCount + xTicksToWait;
        listSET_LIST_ITEM_VALUE(&(pxCurrentTCB->xStateListItem), xTimeToWake);
        if( xTimeToWake < xConstTickCount )
        {
            traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST();
            vListInsert( pxOverflowDelayedList, &( pxCurrentTCB->xStateListItem ) );
        }
        else
        {
            traceMOVED_TASK_TO_DELAYED_LIST();
            vListInsert( pxDelayedList, &( pxCurrentTCB->xStateListItem ) );
            if( xTimeToWake < xNextTaskUnblockTime )
            {
                xNextTaskUnblockTime = xTimeToWake;
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        ( void ) xCanBlockIndefinitely;
    }
    #endif /* INCLUDE_vTaskSuspend */
}

二、延时函数演示实验

1、原理图与实验目标

(1)原理图:

(2)实验目标:

①设计3个任务——start_task、task1、task2:

[1]start_task:用于创建其它两个任务,然后删除自身。

[2]task1:用于展示相对延时函数vTaskDelay的使用。

[3]task2:用于展示绝对延时函数vTaskDelayUntil的使用。

②预期实验现象:两个LED灯不同步闪烁。

2、实验步骤

(1)将上一章中“任务时间统计API函数实验”的工程文件夹复制一份,在拷贝版中进行实验。

(2)移除task3任务的相关内容,并更改task1和task2的实现,task1中死等100ms+相对延时500ms,task2中死等100ms+绝对延时500ms。

void task1(void)
{
	while(1)
	{
		LED1_Turn();      //LED1状态翻转
		Delay_ms(100);    //死等100ms
		vTaskDelay(500);  //相对延时500ms
	}
}

void task2(void)
{
	TickType_t xLastWakeTime;
	xLastWakeTime = xTaskGetTickCount();   //获取任务上一次唤醒的时间
	while(1)
	{
		LED2_Turn();      //LED2状态翻转
		Delay_ms(100);    //死等100ms
		vTaskDelayUntil(&xLastWakeTime, 500);  //绝对延时500ms
	}
}

(3)程序完善好后点击“编译”,然后将程序下载到开发板上。

3、程序执行流程

(1)main函数全流程(省略OLED屏显示字符串部分):

①初始化OLED模块、按键模块、LED模块(还有串口模块,下图未示出)。

②调用FreeRTOS_Test函数。

(2)测试函数全流程:

①创建任务start_task。

②开启任务调度器。

(3)多任务调度执行阶段(发生在开启任务调度器以后):

①start_task任务函数首先进入临界区,在临界区中start_task任务不会被其它任务打断,接着start_task任务依次创建任务task1、task2,然后删除自身,接着退出临界区,让出CPU资源。

②task2的优先级较高,优先执行task2,如下所示,在不考虑task1的情况下(或者说task1运行时task2一直处于自我阻塞状态),task2依次执行LED2状态翻转、死等100ms及绝对延时500ms,这些操作加上阻塞时间共500ms。

③task2阻塞时task1可以执行,task1依次执行LED1状态翻转、死等100ms及相对延时500ms,这些操作加上阻塞时间共约600ms。

④随着时间的推移,task1在进行死等的时候很可能task2的阻塞时间正好结束,由于task2的优先级较高,task1会直接被打断,从而引发task1非预期的等待,这就导致LED1在这次闪烁轮回中的闪烁频率会产生细微的变化。


http://www.kler.cn/a/524124.html

相关文章:

  • 不背单词快捷键(不背单词键盘快捷键)
  • LeetCode热题100中 17. 20. 53. 78. 215.
  • Mac m1,m2,m3芯片使用nvm安装node14报错
  • 构建 QA 系统:基于文档和模型的问答
  • ReactNative react-devtools 夜神模拟器连调
  • linux通过deb包安装(命令模式)
  • doris:JSON
  • LLM架构与优化:从理论到实践的关键技术
  • [MySQL]事务的理论、属性与常见操作
  • Web实训项目-ToDoSystem项目
  • 区块链在能源行业的应用场景
  • 基于FPGA的BT656解码
  • Elasticsearch+kibana安装(简单易上手)
  • 几种K8s运维管理平台对比说明
  • SQL注入漏洞之 提交方式类型注入 Get分类 Post分类 Cookie分类 请求数据位置分类 请求行 请求头 请求数据分类 靶场练习
  • 【Leetcode刷题记录】45. 跳跃游戏 II--贪心算法
  • 【算法】经典博弈论问题——斐波那契博弈 + Zeckendorf 定理 python
  • 中文输入法方案
  • CH32V303RCT6使用RTOS的选择对比
  • 深入理解 C 语言函数指针的高级用法:(void (*) (void *)) _IO_funlockfile
  • 对游戏宣发的粗浅思考
  • LabVIEW开发故障诊断
  • 虚幻基础06:cast to
  • 讲解QoS队列调度算法
  • 【算法应用】基于A*-蚁群算法求解无人机城市多任务点配送路径问题
  • 用HTML、CSS和JavaScript实现庆祝2025蛇年大吉(附源码)