logo
CODEMBIT
Reliable software engineering partner
Home Blog Tools

Low power mode support for FreeRTOS and TI MSP430FR5739

By Toni Akkala, 13th of August, 2022
img

Overview

After working with the tickless mode using Renesas board, I started to think would it be possible to use tickless mode also with the MSP430. The first question that came to my mind was how to measure current consumption from that MSP-TS430RHA40A board. I downloaded the schematics and had a look. Jumper JP1 can be used to select external power supply. That power supply could be used to measure the current. Unfortunately I don't have such power supply for now. I will still try to make necessary code changes for the tickless mode.

MCU low power modes

MSP430 has low power modes LMP0 through LPM4. These modes has a small differences which clocks are disabled during the low power mode. Wakeup from these modes is possible through all enabled interrupts. And because the control of these modes is through the Status Register (SR), program flow can return previous operating mode if SR content is not changed. Low power modes LPM3.5 and LPM4.5 will also disable voltage regulator in addition to clocks. Wakeup from these modes is more difficult and is possible only through some specific sources like resets.

Implementation

Because I use Digitally Controlled Oscillator (DCO) and ACLK clock for the timers I need to use LPM2 mode where DCO can still source ACLK during low power mode. My demo application is using 16-bit timers with quite fast clock source. This doesn’t matter, but I cannot sleep as long as I could or at least FreeRTOS is expecting which means I won’t get the full benefit of this tickless mode. I started by defining some constants.

// ACLK frequency after configuring it [Hz] #define mainACLK_FREQUENCY_HZ 625000 /// Timer value per each tick #define mainTIMER_TICK_VALUE (mainACLK_FREQUENCY_HZ / configTICK_RATE_HZ) /// Calculate maximum number of ticks with 16-bit timer #define mainTIMER_MAX_TICKS (65536 / mainTIMER_TICK_VALUE)

Here is my clock system setup function, function to setup RTOS tick timer and function to setup wake up timer which compare register value will be written before every start.

static void prvSetupHardware( void ) { // Disable the watchdog. WDTCTL = WDTPW + WDTHOLD; // Write CS password before accessing other registers. CSCTL0_H = 0xA5; // 20 MHz clock, DCPRSEL=1, DCOFSEL=1 CSCTL1 = 0x0082u; // Select DCO clock for ACLK, MCLK and SMCLK CSCTL2 = 0x0333u; // No clock dividers for MCLK and SMCLK. // Divide ACLK by 32 = 625 kHz. CSCTL3 = 0x0500u; // Reset CS password to prevent further access. CSCTL0_H = 0x00u; } static void prvSetWakeTimeInterrupt( void ) { // Ensure the timer is stopped TA1CTL = 0; // Set ACLK clock source TA1CTL = TASSEL__ACLK; // Enable the interrupts TA1CCTL0 = CCIE; } void vApplicationSetupTimerInterrupt( void ) { // Ensure the timer is stopped during configuration. TA0CTL = 0; // Set timer clock source to ACLK. TA0CTL = TASSEL__ACLK; // Set the compare match value according to the tick rate we want. Note that // the timer period will be value + 1 so decrease one from written value. TA0CCR0 = mainTIMER_TICK_VALUE - 1; // Enable the interrupt. Interrupt flag will be set when timer counts to the // value in the compare register. TA0CCTL0 = CCIE; // Up mode. Timer wraps to zero after compare register value. TA0CTL |= MC__UP; }

I configured timer A1 to wake up MCU from the tickless mode after the specified time because I don’t want to reconfigure my RTOS tick timer every time this function gets called. We will also use different mode with this timer to be able to read the timer value before it wraps to zero. After adding the new timer I needed to add also new Interrupt Service Routine (ISR) for that timer. As that will be called only after waking up from the tickless mode ISR will make sure that all low power bits are cleared and interrupts are disabled (GIE=0) in SR, interrupts will be enabled at the end of vApplicationSleep function:

#pragma vector=configLPM_VECTOR interrupt void vLPMWakupISR( void ) { // This interrupt will wakeup us from low power mode __bic_SR_register_on_exit( SCG1 + SCG0 + OSCOFF + CPUOFF + GIE ); }

Then I configured FreeRTOSConfig.h to take tickless mode into use and added a macro to define function name to be called:

#define configUSE_TICKLESS_IDLE 1 #define portSUPPRESS_TICKS_AND_SLEEP( xIdleTime ) vApplicationSleep( xIdleTime )

And finally I added function implementation for the tickless mode function:

void vApplicationSleep( TickType_t xExpectedIdleTime ) { eSleepModeStatus eSleepStatus; // Stop the timer that is generating the tick interrupt TA0CTL &= ~MC__UP; __disable_interrupt(); // ************** Enter a critical section ***************************** // Ensure it is still ok to enter to the sleep mode eSleepStatus = eTaskConfirmSleepModeStatus(); if( eSleepStatus != eAbortSleep ) { if( eSleepStatus != eNoTasksWaitingTimeout ) { // Configure wake up timer. First reset counter value TA1CTL |= TACLR; // Set the compare match value according time to sleep if (xExpectedIdleTime > mainTIMER_MAX_TICKS) { xExpectedIdleTime = mainTIMER_MAX_TICKS; } TA1CCR0 = (mainTIMER_TICK_VALUE * xExpectedIdleTime) - 1; // Set continuous mode so that we can read and calculate // sleep time before the timer wraps to zero. TA1CTL |= MC__CONTINOUS; // Enter the low power state with interrupts enabled __bis_SR_register( LPM2_bits + GIE ); // and stop the wake up timer immediately after wake up TA1CTL &= ~MC__CONTINOUS; // step kernel ticks forward as many ticks we were asleep vTaskStepTick( TA1R / mainTIMER_TICK_VALUE ); } } // ************** Exit the critical section ***************************** __enable_interrupt(); // Restart the timer that is generating the tick interrupt TA0CTL |= MC__UP; }

I also removed idle hook by setting configUSE_IDLE_HOOK to zero and removing vApplicationIdleHook function. There is no point of using that if I want to use tickless mode instead.

Debugging

Now I was able to build the demo and start testing. First I wanted to verify if timings were still correct when sleeping between flashing the LED. After connecting my logic analysator, I noticed that the LED was flashing quite close to every 333 ms as expected.

Verify LED flashing frequency

Conclusion

Unfortunately, I didn't have proper equippment to measure the current consumption after enabling the tickless mode. It was easy to configure FreeRTOS tickless mode on, not so easy as with Renesas FSP, but easy anyway.

References: