Energia, MSP432 delays and serial data corruption.

Here is my Energia MSP432 Code:


// the setup routine runs once when you press reset:
void setup() {
  Serial.begin(38400);
}

// the loop routine runs over and over again forever:
void loop() {
  Serial.println("Hello World");
  delay(500);
}

 
I expected this to produce a stream of “Hello World” strings. It didn’t! I got this instead:

Hello World��
Hello World��
Hello World��
Hello World��
Hello World��
Hello World��

I discovered that if I change the delay to 501, I got an output like this:

Hello World
Hello World
Hello World
Hello World
Hello World
Hello World

How could a millisecond make such a difference? The answer lies in the way delay is implemented in the MSP432 wiring library. Sections of this library are shown below (file is wiring.c). The short answer to my problem is this:
If the delay in milliseconds is a divisible by 250 the library puts the MSP432 into a deep sleep and lets the watchdog timer wake it up 250ms later. When in such a sleep, the state of the TX pin is high impedance so it is susceptible to noise (hence the junk after Hello World).
If the delay is not divisible by 250 then the CPU is not put into a deep sleep and the TX pin is maintained at a known state.
This “feature” was found in Energia 1.6.10E18 and on a Rev 1.0 MSP432 board. It could probably be fixed by using a pull-up resistor on the TX line.
So, if you see this effect in your program just add one to your delays 🙂

void delay(uint32_t milliseconds)
{
    if (milliseconds == 0) {
        Task_yield();
        return;
    }

    switch (delayMode) {
        /* using Timer_A, check for opportunity to transition to WDT */
        case 0:
            if ( (milliseconds >= 250) && (milliseconds % 250) == 0) {
                delayMode = 1;
                switchToWatchdogTimer();
            }
            else {
                delayMode = 2;
                switchToTimerA();
            }
            break;
        /* using WDT, check for need to transition to Timer_A */
        case 1:
            if ( (milliseconds >= 250) && (milliseconds % 250) == 0) {
                /* stay in mode 1 */
            }
            else {
                /* switch to Timer_A and never look back */
                delayMode = 2;
                switchToTimerA();
            }
            break;
        /* always using Timer_A */
        case 2:
            break;
    }

    /* timeout is always in milliseconds so that Clock_workFunc() behaves properly */
    Task_sleep(milliseconds);
}

/*
 *  ======== switchToWatchdogTimer ========
 *
 *  Use 250ms watchdog timer interrupt to drive the Clock tick
 *  Stop the default Timer_A then start the watchdog timer.
 */
static void switchToWatchdogTimer()
{
    Clock_TimerProxy_Handle clockTimer;
    static Hwi_Handle wdtHwi = NULL;

    /* Stop Timer_A currrently being used by Clock */
    clockTimer = Clock_getTimerHandle();
    Clock_TimerProxy_stop(clockTimer);

    MAP_WDT_A_holdTimer();

    if (wdtHwi == NULL) {
        /* Create watchdog Timer Hwi */
        wdtHwi = Hwi_create(19, clockTickFxn, NULL, NULL);
        
        /* set WDT to use 32KHz input, 250ms period */
        MAP_WDT_A_initIntervalTimer(WDT_A_CLOCKSOURCE_XCLK, WDT_A_CLOCKITERATIONS_8192);
    }

    /* remove DEEPSLEEP0 constraint left from TimerA usage */
    Power_releaseConstraint(PowerMSP432_DISALLOW_DEEPSLEEP_0);

    /* don't allow deeper than DEEPSLEEP1 */
    Power_setConstraint(PowerMSP432_DISALLOW_DEEPSLEEP_1);

    /* Start watchdog Timer */
    MAP_WDT_A_clearTimer();
    MAP_WDT_A_startTimer();

    /* hence, Clock_tick() will be called from 250ms watchdog timer interrupt */
}

/*
 *  ======== switchToTimerA ========
 *
 *  Use 1ms Timer_A interrupt to drive the Clock tick
 *  By default, the Timer_A Hwi object has already been
 *  statically created and configured to call Clock_tick().
 *  Simply stop the watchdog timer and restart the Timer_A.
 */
static void switchToTimerA()
{
    Clock_TimerProxy_Handle clockTimer;

    /* Stop watchdog Timer */
    MAP_WDT_A_holdTimer();

    /* remove DEEPSLEEP1 constraint left from watchdog usage */
    Power_releaseConstraint(PowerMSP432_DISALLOW_DEEPSLEEP_1);

    /* don't all the power to be cut in deep sleep */
    Power_setConstraint(PowerMSP432_DISALLOW_DEEPSLEEP_0);

    /* Re-start Timer_A */
    clockTimer = Clock_getTimerHandle();
    Clock_TimerProxy_start(clockTimer);

    /* hence, Clock_tick() will be called from 1ms Timer_A interrupt */
}

&nbsp

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s