(二)基于STM32L431的Liteos低功耗Runstop模式的实现优化(退出stop2模式后任务相关Tick补偿优化)

26 篇文章 3 订阅
8 篇文章 2 订阅
前言:

使用Liteos的develop版本,Runstop模式由于没有相关代码,如果想要实现uA级功耗的话,Runstop模式需要自己实现,实现的大概过程在我的另一篇(一)基于STM32L431的Liteos低功耗Runstop模式的实现有体现。

上一篇文章实现Runstop模式存在的问题:

①进入stop模式的时间必须大于至少50个tick,否则唤醒之后系统的任务调度会有问题。
②即使进入stop模式的时间大于50个tick,唤醒之后需要执行的任务并没有立刻执行,而是会有时间间隔,这个时间最长达到了312ms,那么系统的实时性就没有了,而且会增加功耗。
在这里插入图片描述

测试下来发现,如果是因为软件定时器唤醒的,软件定时器的回调函数能正常执行,软件定时器因为只需要修改头部节点的值就可以,所以不会有问题;如果是因为任务唤醒的,那么任务的执行会有时间间隔,并且这个间隔时间是不一定的。那么就说明是唤醒之后的任务tick补偿有问题。

正常运行时每次systick中断服务函数中会调用osTaskScan()函数,来扫描有没有要执行的任务,如果有就进行任务切换,关键代码:

    g_stTskSortLink.usCursor = (g_stTskSortLink.usCursor + 1) % OS_TSK_SORTLINK_LEN;
    pstListObject = g_stTskSortLink.pstSortLink + g_stTskSortLink.usCursor;
    if (pstListObject->pstNext == pstListObject)
    {
        return;
    }

    for (pstTaskCB = LOS_DL_LIST_ENTRY((pstListObject)->pstNext, LOS_TASK_CB, stTimerList);&pstTaskCB->stTimerList != (pstListObject);) /*lint !e413*/
    {
        usTempStatus = pstTaskCB->usTaskStatus;
        if (UWROLLNUM(pstTaskCB->uwIdxRollNum) > 0)
        {
            UWROLLNUMDEC(pstTaskCB->uwIdxRollNum);
            break;
        }

        LOS_ListDelete(&pstTaskCB->stTimerList);
    }

可以看到里面有一个关键的变量 g_stTskSortLink.usCursor每次中断都会加1,OS_TSK_SORTLINK_LEN值为32,而且使用osTaskNextSwitchTimeGet()函数来获取最近一个任务延时到来时间时,也用到了g_stTskSortLink.usCursor的值,但是我在之前任务任务的tick补偿的时候并没有关注这个值。在调用LOS_TaskDelay()函数进行任务延时的时候会调用osTaskAdd2TimerList()函数来把任务添加到延时链表中,在这个函数中也用到了g_stTskSortLink.usCursor的值,那么g_stTskSortLink.usCursor的值应该是导致唤醒后任务执行不及时的原因。

分析源码后发现,Liteos是通过一个环状的队列来判断任务延时是否到来的。具体实现如下图(以OS_TSK_SORTLINK_LEN等于10为例),假设当前g_stTskSortLink.usCursor的值为2,一个任务需要延时120ms,每个tick是5ms,也就是一共延时24个tick。每次systick中断g_stTskSortLink.usCursor值加1,那么24个tick过去之后,g_stTskSortLink.usCursor应该在6的位置。因此任务添加到时间链表中的位置应该是6。

g_stTskSortLink.usCursor当前的值为2,执行一轮再到2的时候过去了10个tick,也就是50ms。而对于任务来说,插入位置6之后任务控制块中UWROLLNUM(pstTaskCB->uwIdxRollNum)的值应该是2,因为每一轮是50ms,过两轮就是100ms,g_stTskSortLink.usCursor的值从2变为6需要经过4个tick,就是20ms,加起来就是任务需要延时的总时间120ms。
在这里插入图片描述
理解了这个之后就能对任务进行正确的Tick补偿。osIdxRollNumDec()函数要变为:

LITE_OS_SEC_TEXT_MINOR VOID osIdxRollNumDec(UINT32 Tick)
{
    LOS_TASK_CB *pstTaskCB;
    LOS_DL_LIST *pstListObject;
    UINT32 uwIndex =0;
		UINT16 uwRollTicks = Tick%OS_TSK_SORTLINK_LEN;

    for (uwIndex = 0; uwIndex < OS_TSK_SORTLINK_LEN; uwIndex++)
    {
				UINT32 uwTaskTick = Tick;
        pstListObject = g_stTskSortLink.pstSortLink + (g_stTskSortLink.usCursor + uwIndex)%OS_TSK_SORTLINK_LEN;
        if (pstListObject->pstNext != pstListObject)
        {
            pstTaskCB = LOS_DL_LIST_ENTRY((pstListObject)->pstNext, LOS_TASK_CB, stTimerList);
						uwTaskTick /= OS_TSK_SORTLINK_LEN;
					
						if(uwTaskTick > UWROLLNUM(pstTaskCB->uwIdxRollNum))
						{
							uwTaskTick = UWROLLNUM(pstTaskCB->uwIdxRollNum);
						}
						UWROLLNUMDECMULT(pstTaskCB->uwIdxRollNum,uwTaskTick);
        }
    }
		g_stTskSortLink.usCursor = (g_stTskSortLink.usCursor + uwRollTicks)%OS_TSK_SORTLINK_LEN;
}

任务延时补偿的函数没有问题之后还需要注意的是,通过osStopTicksGet()函数获取到的进入stop模式的tick数需要减去1。之所以这么做是因为进入stop模式前会把systick关掉,唤醒之后打开,然后再产生一次systick中断的时候,在中断服务函数中会执行osTaskScan()和osSwtmrScan()函数,这样才能进行正常的任务切换,否则就错过了要执行的位置,那么就需要等下一轮才能执行,系统也就出了问题。还拿上面的图片举例,g_stTskSortLink.usCursor还是2,任务延时插入后位置是6,如果直接补偿24个tick后g_stTskSortLink.usCursor变为6,唤醒之后的下一次systick中断后,g_stTskSortLink.usCursor的值变为7,任务也就得不到执行,需要下一轮了,这中间就需要耽误45ms。

我移植Liteos的时候LOSCFG_BASE_CORE_TICK_PER_SECOND配置的是200,也就是每个tick5ms。在前一篇文章中提到过唤醒之后初始化时钟和外设等需要的时间是1.91ms,如果项目需求对tick的误差不敏感的话,这部分的误差就可以不算进去,那么就不需要获取lptim定时器的计数值,lptim只用来在需要的时间唤醒MCU。如果在乎这段时间的话,就还可以使用前一篇文章中提到的获取lptim计数值后进行补偿,不过就要注意tick补偿之后g_stTskSortLink.usCursor的位置。

测试:
系统因为软件定时器超时需要唤醒:

进入stop之前获取到tick数0x578(1400*5ms = 7s),那么唤醒时间(补偿tick)为0x577(1399),g_stTskSortLink.usCursor值为1。系统中OS_TSK_SORTLINK_LEN值为32。
在这里插入图片描述
唤醒之后补偿tick后,g_stTskSortLink.usCursor值为0x18。(1399%32 = 23 23+1 = 24(0x18))
在这里插入图片描述
唤醒之后的下一个systick中断,g_stTskSortLink.usCursor在osTaskScan()函数中已经加1(0x19),此时软件定时器的头部节点计数值uwCount为1,减1后为0,执行osSwTmrTimeoutHandle函数。
在这里插入图片描述

系统因为任务延时到达需要唤醒:

下一个要执行的任务为test_task1,进入stop之前获取到tick数0xC8(200*5ms = 1s),那么唤醒时间(补偿tick)为0xC7(199),g_stTskSortLink.usCursor值为0x09。
在这里插入图片描述
唤醒之后补偿tick后,g_stTskSortLink.usCursor值为0x10。(199%32 = 7 7+9 = 16(0x10))
在这里插入图片描述
唤醒之后的下一个systick中断,g_stTskSortLink.usCursor在osTaskScan()函数中加1(0x11),此时可以看到要操作的任务控制块就是test_task1,任务的状态为0x20(OS_TASK_STATUS_DELAY),任务的延时值UWROLLNUM(pstTaskCB->uwIdxRollNum)为0。对于OS_TSK_SORTLINK_LEN值为32来说,pstTaskCB->uwIdxRollNum的值每一个表示32个tick,每个tick5ms就是160ms。
在这里插入图片描述
任务的tick补偿修改好后,通过osStopTicksGet()函数获取到的tick数只要大于2个tick就可以再次进入stop模式,之所以有限制是因为任务调度和唤醒后的初始化都需要时间开销。

唤醒后任务执行情况:
在这里插入图片描述
从唤醒到任务得到执行的时间基本都不到2ms:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值