FreeRTOS 8.2.1在MSP430 上的低功耗实现

来源:互联网 发布:java如何不输出字符串 编辑:程序博客网 时间:2024/05/16 00:52

应用FreeRTOS在MSP430上做项目已经很久了,如何配合MSP430这样的低功耗芯片让系统尽可能的工作在低功耗模式是本文的议题。

(本文原创,转载请注明出处RickLeaf CSDNhttp://blog.csdn.net/rickleaf)

一、IDLE线程作为低功耗入口

对于现在流行的大部分RTOS来说启动系统调度环境以后,都会创建出一个IDLE线程,而这个线程又往往运行在最低的优先级,这个线程让系统的调度环境继续工作。

在FreeRTOS的网站上会建议大家引用这个线程让系统进入MSP430的低功耗模式,事实上从官方拿到的代码就有下面这一句(本文以MSP430F5438A的主芯片为例子,代码跑在EXP-MSP5438A演示板上):

void vApplicationIdleHook( void ){/* Called on each iteration of the idle task.  In this case the idle taskjust enters a low power mode. */__bis_SR_register( LPM3_bits + GIE );}

__bis_SR_register( LPM3_bits + GIE );

这个语句会让MSP430进入LPM3模式,这个模式会关闭CPU,SCG1和SCG0,在进入这个模式以后PC指针会停到下一跳指令那里不动,只有在有中断唤醒以后CPU开始执行中断的代码,在中断的代码中加入下面的语句:

__bic_SR_register_on_exit( SCG1 + SCG0 + OSCOFF + CPUOFF );

中断返回以后CPU才会退出LPM3模式,从__bis_SR_register( LPM3_bits + GIE );之后的指令开始往后执行。

看到这里,或许我们已经觉得这个接口很简单哦。我曾经也是这么以为,直到我发现在这样进入LPM3以后,我的串口经常丢数,花费很多资源在校验重发上,直到我发现CPU的功耗一直很难满足硬件工程师的需求。让我们再看一下FreeRTOS的配置文件,或者说去了解你的系统的时钟滴答,下面是我的FreeRTOS的时钟滴答设置

#define configCPU_CLOCK_HZ    ( 25000000UL )#define configLFXT_CLOCK_HZ   ( 32768L )#define configTICK_RATE_HZ    ( ( TickType_t ) 1000 )

可以看到我用一个低速的晶振把系统的工作时钟PLL到25M,并且我把FreeRTOS的时钟滴答设置成了1毫秒(1ms),现在我们设想一下如果我其它任务都已经挂起,只有我IDLE这个线程一直在工作,我们看一下系统的ticker中断

#pragma vector=configTICK_VECTOR__interrupt __raw void vTickISREntry( void ){extern void vPortTickISR( void );__bic_SR_register_on_exit( SCG1 + SCG0 + OSCOFF + CPUOFF );vPortTickISR();}

每隔1个毫秒系统会唤醒一次CPU,然后CPU再进入LPM3。暂时不考虑其他因素,我们单单从功耗的角度去看就会发现系统大部分时间还是在唤醒状态。而且,LPM3本来进入和退出就需要一定的时间,而且这个时间都是10us级的,这无疑会给中断响应增加负担。事实上,这个是我串口丢数据的直接原因。那么我们该如何去实现低功耗模式呢?我们马上继续讨论。

二、FreeRTOS Tickless低功耗模式在MSP430上的应用

在FreeRTOS网站上很容易找到一种低功耗的解决方案,并且大量的应用CortexM的内核上。所有tickless就是指,在系统进入低功耗模式以后,系统的ticker不会继续工作,听上去挺吓人吧,不是休眠是假死,因为没有心跳了。对于我来说,我只是找到一种方法让系统在没有一些event的时候系统能够停留在CPU的某个低功耗模式,当有中断发生以后再让系统的ticker继续工作。

其实,即使ticker不在,系统还是在运行的只是我们不希望在ticker每个中断都要让系统退出低功耗模式,只要在需要的时候才退出低功耗模式。而FreeRTOS的tickless功能本身就提供了这样的接口。

功能总是以耗费资源为代价的,就像如果我们想得到FreeRTOS的runtime states,我们必须要有一个额外的timer,我们为了实现tickless也必须有个额外的timer。大家可以先去看看FreeRTOS的官方文档。

http://www.freertos.org/low-power-tickless-rtos.html

当我们的系统进入IDLE以后,FreeRTOS内核会估算出下一个时间触发事件的到来的时刻,我们假设那个时刻和当前时刻的差值是T。

这时候我们配置要一个和FreeRTOS的ticker同样频率的timer, 并且希望这个tickless定制器在超时T时刻以后,唤醒并且让系统退出低功耗模式LPM3。然后我们停止系统ticker,系统就有T这么长的时间在LPM3中,等到tickless定时器唤醒并且退出LPM3以后,我们在重新启动ticker并且要把时间T的流逝告诉FreeRTOS,老大我们沉睡了1万年现在要到阿凡达了。

当然我们必须要考虑其它的中断唤醒并且退出LPM3的情况,假想一下沉睡的飞船也要能抵御敌人吧。所以如果其它中断唤醒系统并让系统退出LPM3以后,我们要告诉FreeRTOS你睡了几个月,需要紧急处理一些特别任务。

在FreeRTOS的配置文件中其实已经有了这样的接口,只是在MSP430上可能用的人不多。下面是内核配置:

#define configIDLE_SHOULD_YIELD0#define configUSE_TICKLESS_IDLE                 1
</pre><pre name="code" class="cpp">#ifdef __ICC430__    extern void vApplicationSleep( uint32_t xExpectedIdleTime );#endif /* __ICCARM__ */#define portSUPPRESS_TICKS_AND_SLEEP( xIdleTime ) vApplicationSleep( xIdleTime )

我们用timer0 a0作为系统的tickless时钟,用timer0 b0作为系统的ticker,因为EXP-MSP5438的板子timer0 a1是lcd的backlight pwm信号。下面是我的tickless,希望对大家有用,欢迎交流:


/* FreeRTOS includes. */#include "FreeRTOS.h"#include "task.h"/* Hardware includes. */#include "msp430.h"volatile unsigned long ulTicklessCount = 0;volatile unsigned long ulExpectedIdleTime = 0;volatile unsigned char TickLessEnable = 0;/*-----------------------------------------------------------*/void vConfigureTimerForTickless( void ){const unsigned short usACLK_Frequency_Hz = 32768;/* Ensure the timer is stopped. */TA0CTL = 0;/* Run the timer from the ACLK. */TA0CTL = TASSEL_1;/* Clear everything to start with. */TA0CTL |= TACLR;/* Set the compare match value according to the tick rate we want. */TA0CCR0 = usACLK_Frequency_Hz / configTICK_RATE_HZ;/* Enable the interrupts. */TA0CCTL0 = CCIE;/* Start up clean. */TA0CTL |= TACLR;/* Up mode. */TA0CTL |= MC_1;}/*-----------------------------------------------------------*/unsigned long ulGetExternalTime(void){   return ulTicklessCount;}/*-----------------------------------------------------------*/void vSetWakeTimeInterrupt(unsigned long xExpectedIdleTime){    ulExpectedIdleTime = xExpectedIdleTime;    ulTicklessCount = 0;    TickLessEnable = 1;}#pragma vector=TIMER0_A0_VECTOR__interrupt void prvTIMER0_A0_ISR( void ){ulTicklessCount++;    if((ulTicklessCount >= ulExpectedIdleTime) && TickLessEnable )    {        __bic_SR_register_on_exit( SCG1 + SCG0 + OSCOFF + CPUOFF );        TickLessEnable = 0;    }}void vApplicationSleep( TickType_t xExpectedIdleTime ){    unsigned long ulLowPowerTimeAfterSleep;    eSleepModeStatus eSleepStatus;    /* Read the current time from a time source that will remain operational    while the microcontroller is in a low power state. */    //ulLowPowerTimeBeforeSleep = ulGetExternalTime();        /* Stop the timer that is generating the tick interrupt. */    TB0CTL &= ~MC_1;        /* Enter a critical section that will not effect interrupts bringing the MCU    out of sleep mode. */    __disable_interrupt();        /* Ensure it is still ok to enter the sleep mode. */    eSleepStatus = eTaskConfirmSleepModeStatus();    if( eSleepStatus == eAbortSleep )    {        /* A task has been moved out of the Blocked state since this macro was        executed, or a context siwth is being held pending.  Do not enter a        sleep state.  Restart the tick and exit the critical section. */        TB0CTL |= MC_1;        __enable_interrupt();    }    else    {        if( eSleepStatus == eNoTasksWaitingTimeout )        {            /* It is not necessary to configure an interrupt to bring the            microcontroller out of its low power state at a fixed time in the            future. */            __bis_SR_register( LPM3_bits + GIE );        }        else        {            /* Configure an interrupt to bring the microcontroller out of its low            power state at the time the kernel next needs to execute.  The            interrupt must be generated from a source that remains operational            when the microcontroller is in a low power state. */            vSetWakeTimeInterrupt(xExpectedIdleTime);            /* Enter the low power state. */            __bis_SR_register( LPM3_bits + GIE );                        /* Determine how long the microcontroller was actually in a low power            state for, which will be less than xExpectedIdleTime if the            microcontroller was brought out of low power mode by an interrupt            other than that configured by the vSetWakeTimeInterrupt() call.            Note that the scheduler is suspended before            portSUPPRESS_TICKS_AND_SLEEP() is called, and resumed when            portSUPPRESS_TICKS_AND_SLEEP() returns.  Therefore no other tasks will            execute until this function completes. */            ulLowPowerTimeAfterSleep = ulGetExternalTime();                        /* Correct the kernels tick count to account for the time the            microcontroller spent in its low power state. */            vTaskStepTick( ulLowPowerTimeAfterSleep );        }        __enable_interrupt();        TB0CTL |= MC_1;    }}


在成功应用了FreeRTOS的tickless以后,我的系统大部分时间都在LPM3中,而且我的串口在115200波特率上工作没有误码。



0 0
原创粉丝点击