embOS tickless mode and MSP430
By Toni Akkala, 14th of August, 2022
Overview
After successfully using tickless mode with FreeRTOS, I wanted to try it out also with the embOS. So the idea is the same, disable tick timer interrupt to remain in low power mode as long as possible. embOS documentation has code examples how to enable tickless mode with three functions: OS_GetNumIdleTicks, OS_AdjustTime and OS_StartTicklessMode. The first one is used to get number of ticks we should sleep. The second one will be used to adjust RTOS time after sleeping and the last one to start the tickless mode and to specify a callback called to end it.
Implementation
I first added some definitions.
/// Timer value per each tick
#define TIMER_TICK_VALUE (OS_PCLK_TIMER / OS_TICK_FREQ)
/// Calculate maximum number of ticks with 16-bit timer
#define TIMER1_MAX_TICKS (65536 / TIMER_TICK_VALUE)
Then the code to initialize clock system and the tick timer.
/* 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;
/* Ensure the timer is stopped. */
TA0CTL = 0;
/* Run the timer of the ACLK. */
TA0CTL = TASSEL__ACLK;
/* Set the compare match value according to the tick rate we want. */
TA0CCR0 = TIMER_TICK_VALUE - 1;
/* Enable the interrupts. */
TA0CCTL0 = CCIE;
/* Up mode. */
TA0CTL |= MC__UP;
When testing tickless mode with FreeRTOS, I used smaller timer clock frequency to be able to adjust longer delay for sleep mode. In this trial, I wanted to use higher 20 MHz DCO frequency even though I could not get very long delay as the maximum divider for the ACLK is 32. I created this function for the callback.
/**
* Calculate how long MCU was in low power mode and
* adjust RTOS system time accordingly.
*/
static void _EndTicklessMode(void)
{
OS_U32 NumTicks;
// adjust the time spent on the low power mode
if (OS_Global.TicklessExpired) {
NumTicks = OS_Global.TicklessFactor;
}
else {
NumTicks = (TA0R / TIMER_TICK_VALUE);
}
OS_AdjustTime(NumTicks);
// and reset value for 1 tick
TA0CCR0 = TIMER_TICK_VALUE - 1;
}
And finally the implementation of the OS_Idle. I used LPM2 low power mode as that still works with the DCO and my board doesn't have an external oscillator to use.
void OS_Idle(void) {
OS_TIME IdleTicks;
// Make sure there won't be any timer interrupts
// during configuration
OS_DI();
// Check how many idle ticks to sleep, we require more than one
IdleTicks = OS_GetNumIdleTicks();
if (IdleTicks > 1) {
// Limit maximum idle tick count based on the timer in use.
// RTOS will call idle mode again if longer sleep is required.
if ((OS_U32)IdleTicks > TIMER1_MAX_TICKS) {
IdleTicks = TIMER1_MAX_TICKS;
}
OS_StartTicklessMode(IdleTicks, &_EndTicklessMode);
// Reset the timer and set new compare value to sleep longer
TA0CTL |= TACLR;
TA0CCR0 = (TIMER_TICK_VALUE * IdleTicks) - 1;
}
OS_EI();
while (1) {
// Enable LPM2 low power mode
_BIS_SR(LPM2_bits + GIE);
}
}
Conclusion
I tried to use another timer for the low power mode but could not get embOS to call callback function. After re-configuring the system tick timer to wake up MCU from low power mode, embOS made a call to callback function and everything was working as expected. Fortunately embOS provides good documentation what needs to be implemented to get tickless mode working so implementation was easy to do.
References: