历史上的今天
返回首页

历史上的今天

今天是:2025年01月11日(星期六)

2021年01月11日 | FreeRTOS 8.2.1在MSP430 上的低功耗实现

2021-01-11 来源:eefocus

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



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


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


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


void vApplicationIdleHook( void )

{

/* Called on each iteration of the idle task.  In this case the idle task

just 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_YIELD 0

#define configUSE_TICKLESS_IDLE                 1

#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波特率上工作没有误码。


推荐阅读

史海拾趣

Global Connector Technology公司的发展小趣事
电源系统中的电压波动或电磁干扰可能干扰继电器的正常工作。
Geyer Electronic E K公司的发展小趣事
检查继电器内部元件是否损坏或老化,必要时进行更换。
Amperite Co公司的发展小趣事
安装位置不正确或连接线路错误,导致继电器无法正常检测到断相故障。
Calex Mfg Co Inc公司的发展小趣事

在国内市场取得一定成绩后,Calex Mfg. Co., Inc.开始积极拓展海外市场。公司制定了全球化战略,通过参加国际展会、建立海外销售网络等方式,不断开拓国际市场。同时,公司还根据不同地区的市场需求,推出定制化的产品和服务,进一步提升了其在国际市场中的竞争力。

DCCOM [ DC COMPONENTS ]公司的发展小趣事

在快速发展的过程中,DCCOM公司始终坚持品质至上的原则。公司建立了一套严格的质量控制体系,从原材料采购到产品生产的每一个环节都严格把关。这种对品质的执着追求,使得DCCOM的产品在市场上赢得了良好的口碑,客户回头率逐年攀升。

Advanced Semiconductor, Inc.公司的发展小趣事

Advanced Semiconductor, Inc. (简称ASI) 成立于XXXX年,由一群热衷于半导体技术创新的工程师和科学家创立。在创立初期,ASI专注于研发高性能的半导体芯片,以满足当时市场对更快、更稳定电子产品的需求。公司凭借其独特的技术和创新的理念,很快在半导体行业中崭露头角,吸引了众多投资者的关注。

问答坊 | AI 解惑

浅析变频器迅速发展之原因

我们通常所说的变频器 电动机变频器GKC800 就是运动控制系统中的功率变换器。现今运动控制系统包含有多种学科。技术领域总的发展趋势就是:驱动的交流化,功率变换器的高频化,控制的数字化、智能化和网络化。所以,变频器作为系统的重要功率变换部 ...…

查看全部问答>

keil学习(一)

Keil 工程文件的建立、设置与目标文件的获得 单片机开发中除必要的硬件外,同样离不开软件,我们写的汇编语言源程序要变为 CPU 可以执行的机器码有两种方法,一种是手工汇编,另一种是机器汇编,目前已极少使用手工汇编的方法了。机器汇编是通过汇 ...…

查看全部问答>

altium designer 和Protel 99se哪个更好?

我只用过protel 99se 但是听说altium designer 很不错,就有打算把protel换成altium designer ,大家说觉得有必要吗,有用过的大侠现身说法一下吧 呵呵多谢了 比如说,altium designer 好不好上手 资料好不好找  呵呵…

查看全部问答>

专为狗设计的MP3。超贵

这款MP3售价要2000美元,是狗专用的。 用料方面毫无悬念,白的是白金、黄的是黄金、闪亮的是钻石。据说给狗听音乐能够使够的精神放松。不过人听的音乐够听了能放松吗?另一方面,人怎么知道狗放没放松?…

查看全部问答>

WINCE启动运行程序LCD无显示

各位大侠: 我修改了shell.reg里面的内容如下: [HKEY_LOCAL_MACHINE\\init] ;\"Launch50\"=\"explorer.exe\" ;\"Depend50\"=hex:14,00, 1e,00 \"Launch200\"=\"player.exe\" \"Depend200\"=hex:14,00, 1e,00 本意是想开机就运行我的player ...…

查看全部问答>

新建SENSOR调试群

在论坛讨论太慢 强烈要求大家进去一起讨论学习:QQ群号:22280050…

查看全部问答>

驱动AT080TN52液晶

最近在学校用了一个试验箱,芯片是CYCLONE III,上面有个AT080TN52的TFT液晶,想用VHDL直接驱动显示一副图像,不知道如何实现,请用过的大哥指教一下,谢谢…

查看全部问答>

LM3S811 画简单板子出现问题 焊接好之后不能下载

   自己画了一个做课设的板子  主控芯片是TI的LM3S811, 自己原来有一块811的开发板, 板子回来之后焊接了第一块,不能下载,以为是自己焊得不是很好,又仔细焊接了第二块,结果还是一样,自己测试电源打压正常、晶振起振、复位键正 ...…

查看全部问答>

关于MSP430f4152读写操作的问题

void flash_read_kuai(unsigned char * Ptr)    //*ptr为flash地址指针;{  unsigned char i;  for(i=0;i<10;i++)  {    RamCode=*Ptr;         &nb ...…

查看全部问答>