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

MCU Invaders Part 2

 

This post follows up on the previous one and includes a description of the software involved in MCU Invaders.  Initially this project started out as a C++ project however I found it difficult to get the initialization “glue” sorted out.  It seems that C++ initialization procedures have changes since GCC 4.7 and I didn’t want to get diverted from the main goal figuring this out.  It was easier instead to convert to a C project while retaining the object oriented nature in spirit.

The project is made up of the following layers:

softwarearchitecture

 

The SPI Layer
Communication with the ILI9341 display is carried out over the SPI bus.  This consists of 4 signals:
MOSI (Master Out Slave In).  This carries data from the LPC1114 to the ILI9341
MISO (Master In Slave Out).  This carries data from the ILI9341 to the LPC1114
SCK (Serial ClocK).  This is driven by the master and sets the pace for SPI Communications.
CSn or SSEL.  This line is driven by the LPC1114 and can be used to wake up the ILI9341 by driving it low. The LPC1114 is capable of generating this signal automatically when transfers are taking place.  This proved to be a problem in this case.  The ILI9341 is slow to respond to changes to this signal and this limited data rates to less than 1 Mbit/s.  This was not sufficient for the game.  I noticed however that if the SSEL (CS) line is kept low all of the time the SPI bus could be run at about 24MHz.  This of course means that the SPI bus is dedicated to this display which is not a problem in this case.

The Pixel Layer
This layer “understands” how to control the ILI9341.  It is used to initialize the display and contains the following low level graphical functions:
putPixel
putImage
fillRectangle
putText
It also contains the macro RGBToWord which converts RGB colour values (1 byte per colour) into a 16 bit number that can be used by the ILI9341.

The Sprite Layer

typedef struct {
// Location of top left corner. 
 uint16_t x;
 uint16_t y;
// Dimensions of sprite
 uint16_t width;
 uint16_t height;
// The pixels for the sprite are stored at this location
 const uint16_t *SpriteImage;
 uint8_t visible;
} Sprite;
// Construction based on 2 D pixel array 
void initSprite(Sprite * sp,const uint16_t * InitArray, uint8_t Width, uint8_t Height, uint16_t x, uint16_t y);
// Show/hide the sprite 
void showSprite(Sprite * sp); // Put a sprite on the display
void hideSprite(Sprite * sp); // hide the sprite
void drawSprite(Sprite * sp); // show the sprite
// Move to the specified position
void moveSprite(Sprite * sp,uint16_t x, uint16_t y); // move sprite
// Does the sprite contain the given point
uint8_t withinSprite(Sprite * sp,uint16_t x, uint16_t y);

This layer is all about Sprites : Movable pictures on the display.  A sprite is defined by a data structure that specifies location, dimensions and a pointer to an array of data that contains the actual image.  The remaining functions are used to show/hide and move the sprite.  An additional function ‘withinSprite’ is used to test whether a given point is within the sprite.  This is used to detect whether missiles or invaders have hit a target.

The game layer (main)

This layer contains all of the game logic.  It also defines the shape of the various sprites used.  This layer also makes calls out to  the serial Communications layer allowing the developer to debug game play.  Dynamic memory allocation (new/malloc etc) is not used in this project.  Fixed length arrays are used for the invaders and missiles and these are enabled/disabled as necessary.  One implication of this is that there can be up to 5 missiles in flight at any one time.  There is still sufficient RAM available to increase this number should a developer wish to.

The video below shows the game in operation.

Code is available here on Github

 

MCU Invaders Part 1

Back in the 1980’s computers were a good deal simpler than they are now. This of course limited their capabilities but it also allowed people to fully understand what exactly was going on inside. Today, this is less easy as computers are extremely complex from a software and a hardware perspective. One area that still retains some of the innocence of the early days is embedded systems. Today’s low-end microcontrollers (MCU’s) are similar in specification and complexity to the home computers of the 1980’s. This makes them very attractive for re-inventing some of the classic programs of that time.
In a previous blog post I discussed a simple RPG called “MicroRealms”.
Here you will find a “Simon” lookalike.
This series of posts will describe a game inspired by Space Invaders and implemented on the LPC1114FN28 microcontroller. I will start with a description of the hardware platform and then later delve into the software and any problems encountered.

MCU Invaders hardware

wiring

The hardware consists of an LPC1114 MCU, a 320×240 colour LCD display, some buttons, resistors and finally a USB to serial interface to allow the MCU to be programmed.

The display is driven over the SPI bus and runs at approx 25MHz (this is beyond the official specification but it seems to work fine).  The buttons allow you move a defender left and right at the bottom of the screen and a “fire” button allow you launch missiles at the invader.

The MCU is programmed using ISP or IAP – i.e. programs are downloaded via its UART after it is reset into “ISP mode” (hold down Reset and ISP buttons, release Reset and then release ISP).  The completed circuit is shown in the picture below.

I got the display from dx.com and I note that it is now out of stock.  The original supplier ElecFreaks still has plenty of them available.

The next post will describe the code in detail bit in case you can’t wait that long you can find it over on github.

board

STM32L011 serial, analog and low power examples

Here are some further examples for the STM32L011 Nucleo board. The first is a simple interrupt driven serial communications demonstration. It makes use of the built in serial interface in the Nucleo board and so no additional hardware is required for your host PC to exchange data with the MCU.
The second example builds on the first. It reads ADC channel 0 and outputs the result to the host PC over the serial port.
The third example is a little more elaborate and is aimed at those wishing to develop a very long life battery application. It uses the built-in real-time-clock (RTC) to wake the MCU up about once every second. While awake, the MCU reads the ADC, outputs data to the host PC over the serial port and blinks the LED on the Nucleo board. Current consumption is minimized during sleep by turning off
peripherals, reducing AHB clock speed and putting the Cortex M0+ into a deep sleep. I found it difficult to measure the current flow during this time but it seems to be around 3 to 4 microamps. One thing I did notice is that the CPU will not go to sleep with interrupts disabled – a good way of preventing you from shooting yourself in the foot.

Links to code.

Serial communications over the nucleo serial interface to the host PC
Read the ADC and output the result over the serial port to the host PC
Minimize power consumption and wake using the internal RTC every second

Blinky on the STM32L011 Nucleo

The STM32L011 Nucleo board is an mbed enabled development kit featuring the STM32Lo11K4 MCU. This is a Cortex M0+ device with some interesting features such as hardware accelerated AES encryption (Correction: AES not present on the STM32L011) and a “Firewall” which seems to prevent firmware and other data being offloaded from the device. The board is meant to be programmed with a large IDE or using the mbed online development environment. I was curious to see could it be done from the command line with all code contained in a single directory. Normally I would tackle this with OpenOCD and GDB but this is such a new board that OpenOCD/GDB can’t interface with it properly (yet). All is not lost however! The mbed feature of this board means that it appears as a removable storage device to the operating system. Programming the board is simply a matter of dropping a correctly formatted executable (binary) file into this removable storage device. The utility objcopy can be used to prepare the executable file.
Assuming you have compiled your code to an “elf” file called main.elf, you can generate a binary file as follows:
objcopy -O binary main.elf main.bin
The “bin” file can be dropped on the Nucleo and should run fine.
Code for blinky is available here

Analog pass-through using the MSP432 Launchpad and Energia

This example combines two previous posts regarding the TLV5618 DAC and timer interrupts with the MSP432 Launchpad.  The project reads the A0 analog input at 22050Hz (half CD quality) and outputs the reading  via the TLV5618 Channel A DAC.  Higher sample rates are possible but an error creeps in due to overhead handling the DAC.  This example also corrects an error in the previous post about timer interrupts which assumed that the timer base frequency was 16MHz – it seems to be 12MHz in fact.  The output for a 1kHz sinewave is shown below.

MSP432_PassThrough

The code is here.

// This program does an analog pass through between Analog input A0 (P5.5)
// on the MSP432 Launchpad and a TLV5618 SPI DAC.
// The green LED is used to indicate that the program is still alive :o)
// Further details at ioprog.com

#include <SPI.h>
#include <msp432.h>
#include <driverlib/timer_a.h>
#include <driverlib/interrupt.h>
#define SS_PIN 17
void setup()
{ 
  // put your setup code here, to run once:
  pinMode(SS_PIN,OUTPUT);
  pinMode(15,OUTPUT); // MOSI
  pinMode(7,OUTPUT); // SCK
  SPI.setModule(EUSCI_B0_MODULE); // Select correct SPI interface
  SPI.setDataMode(SPI_MODE2);
  SPI.setBitOrder(MSBFIRST);   
  SPI.setClockDivider(SPI_CLOCK_DIV2); // DIV2 = 8MHz, DIV4 = 4MHz, DIV8 = 2MHz etc. (from measurement)
  UCB0CTLW0 &= ~(BIT0); // Take SPI out of reset   
  pinMode(BLUE_LED,OUTPUT);
  pinMode(GREEN_LED, OUTPUT);
  setupTimer(1000000/22050); // Set sample frequency to 22050 : Period=(1000000 microseconds / desired sample rate)
  analogReadResolution(12); // set the ADC resolution to 12 bits (match the DAC)
}


void loop()
{ // Nothing happens here: it is all interrupt driven - see OnTimer for main processing code.
}
void OnTimer()
{
 
  writeDACA(analogRead(A0)); // write out what is coming in on A0
  // The following simply flashes the green LED to show the program is still running
  static int Count = 0;
  static int state = 0;
  Count++;
  if (Count > 22050)
  {
    Count = 0;
    digitalWrite(GREEN_LED,state);
    if (state)
      state = 0;
    else
      state = 1;
  }

}

void writeDACA(int Value)
{
  Value=Value & 0xfff;
  Value = Value | 0xc000; // Write DAC A
  digitalWrite(SS_PIN,LOW); // Drive CS low
  UCB0TXBUF=( (Value >> 8) & 0xff); // write high byte
  while(UCB0STATW & BIT0); // wait while SPI busy
  UCB0TXBUF=  ( Value  & 0xff); // write low byte
  while(UCB0STATW & BIT0); // wait while SPI busy
  digitalWrite(SS_PIN,HIGH); // Drive CS High
}
void writeDACB(int Value)
{
  Value=Value & 0xfff;
  Value = Value | 0x4000; // Write DAC B value to buffer and update
  digitalWrite(SS_PIN,LOW); // Drive CS low
  UCB0TXBUF=( (Value >> 8) & 0xff); // write high byte
  while(UCB0STATW & BIT0); // wait while SPI busy
  UCB0TXBUF=  ( Value  & 0xff); // write low byte
  while(UCB0STATW & BIT0); // wait while SPI busy
  digitalWrite(SS_PIN,HIGH); // Drive CS High
}
void writeDACs(int AValue, int BValue)
{
  
  // Write both DACs and update outputs simultaneously.
  BValue=BValue & 0xfff;
  BValue = BValue | 0x5000; // Write DAC B value to buffer 
  digitalWrite(SS_PIN,LOW); // Drive CS low
  UCB0TXBUF=( (BValue >> 8) & 0xff); // write high byte
  while(UCB0STATW & BIT0); // wait while SPI busy
  UCB0TXBUF=  ( BValue  & 0xff); // write low byte
  while(UCB0STATW & BIT0); // wait while SPI busy
  digitalWrite(SS_PIN,HIGH); // Drive CS High
  digitalWrite(SS_PIN,LOW); // Drive CS low
  AValue=AValue & 0xfff;
  AValue = AValue | 0xc000; // Write DAC A and update B
  digitalWrite(SS_PIN,LOW); // Drive CS low
  UCB0TXBUF=( (AValue >> 8) & 0xff); // write high byte
  while(UCB0STATW & BIT0); // wait while SPI busy
  UCB0TXBUF=  ( AValue  & 0xff); // write low byte
  while(UCB0STATW & BIT0); // wait while SPI busy
  digitalWrite(SS_PIN,HIGH); // Drive CS High

}
volatile uint32_t millisecondCounter=0;
int count = 0;
volatile int state = HIGH;
volatile int flag = HIGH;
void setupTimer(unsigned Period)
{
 
  // Configuration word
  // Bits 15-10: Unused
  // Bits 9-8: Clock source select: set to SMCLK (12MHz)
  // Bits 7-6: Input divider: set to 4
  // Bits 5-4: Mode control: Count up to TACCRO and reset
  // Bit 3: Unused
  // Bits 2: TACLR : set to initially clear timer system
  // Bit 1: Enable interrupts from TA0
  // Bit 0: Interrupt (pending) flag : set to zero (initially)
  TA3CTL=0b0000001010010110;
  TA3CCR0=Period*3; // Set TACCR0 = Period (3MHz clock)
  TA3CCTL0=BIT4; // Enable interrupts when TAR = TACCR0
// The following places the address of our interrupt service routine in the RAM based interrupt vector table
// The vector number is 14 + 16  = 30 which is represented by the symbol INT_TA3_0
  Interrupt_registerInterrupt(INT_TA3_0,timerA3ISR); 
 
  // according to the datasheet Table 6-12 timer A3 is on ISR 14
  NVIC_ISER0 = (1<<14); // enable this interrupt in the NVIC
 
}
 
void timerA3ISR(void)
{
  TA3CTL &= ~1;         // Acknowledge the interrupt
  TA3CCTL0 &= ~1;       // Acknowledge the interrupt
  NVIC_ICPR0 = (1<<14); // clear interrupt pending flag in NVIC
  millisecondCounter++;
  OnTimer();
}

Speedier DACs with the MSP432 Launchpad

In an earlier post I showed how a TLV5618 dual DAC could be interfaced to an MSP432 Launchpad.  This worked OK but it was a bit slow.  The code below drives the DAC’s much faster as it uses direct register writes to the SPI interface.  The Energia SPI library is used to configure the interface. A single DAC channel can be driven at just over 100kHz; while a pair of channels can be updated at approx 48kHz – good enough for decent audio.

 

// This program drives a TLV5618 dual DAC
// It outputs sawtooth waveforms on both channels
// Output update frequency (both channels) is about 48kHz which
// is sufficient for decent quality audio.
// A single channel can be driven at more than 100kHz.
#include <SPI.h>
#include <msp432.h>
#define SS_PIN 17
void setup()
{
  // put your setup code here, to run once:
  pinMode(SS_PIN,OUTPUT);
  pinMode(15,OUTPUT); // MOSI
  pinMode(7,OUTPUT); // SCK
  SPI.setModule(EUSCI_B0_MODULE); // Select correct SPI interface
  SPI.setDataMode(SPI_MODE2);
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV2); // DIV2 = 8MHz, DIV4 = 4MHz, DIV8 = 2MHz etc. (from measurement)
  UCB0CTLW0 &= ~(BIT0); // Take SPI out of reset
}
void writeDACA(int Value)
{
  Value=Value & 0xfff;
  Value = Value | 0xc000; // Write DAC A
  digitalWrite(SS_PIN,LOW); // Drive CS low
  UCB0TXBUF=( (Value >> 8) & 0xff); // write high byte
  while(UCB0STATW & BIT0); // wait while SPI busy
  UCB0TXBUF=  ( Value  & 0xff); // write low byte
  while(UCB0STATW & BIT0); // wait while SPI busy
  digitalWrite(SS_PIN,HIGH); // Drive CS High
}
void writeDACB(int Value)
{
  Value=Value & 0xfff;
  Value = Value | 0x4000; // Write DAC B value to buffer and update
  digitalWrite(SS_PIN,LOW); // Drive CS low
  UCB0TXBUF=( (Value >> 8) & 0xff); // write high byte
  while(UCB0STATW & BIT0); // wait while SPI busy
  UCB0TXBUF=  ( Value  & 0xff); // write low byte
  while(UCB0STATW & BIT0); // wait while SPI busy
  digitalWrite(SS_PIN,HIGH); // Drive CS High
}
void writeDACs(int AValue, int BValue)
{

  // Write both DACs and update outputs simultaneously.
  BValue=BValue & 0xfff;
  BValue = BValue | 0x5000; // Write DAC B value to buffer
  digitalWrite(SS_PIN,LOW); // Drive CS low
  UCB0TXBUF=( (BValue >> 8) & 0xff); // write high byte
  while(UCB0STATW & BIT0); // wait while SPI busy
  UCB0TXBUF=  ( BValue  & 0xff); // write low byte
  while(UCB0STATW & BIT0); // wait while SPI busy
  digitalWrite(SS_PIN,HIGH); // Drive CS High
  digitalWrite(SS_PIN,LOW); // Drive CS low
  AValue=AValue & 0xfff;
  AValue = AValue | 0xc000; // Write DAC A and update B
  digitalWrite(SS_PIN,LOW); // Drive CS low
  UCB0TXBUF=( (AValue >> 8) & 0xff); // write high byte
  while(UCB0STATW & BIT0); // wait while SPI busy
  UCB0TXBUF=  ( AValue  & 0xff); // write low byte
  while(UCB0STATW & BIT0); // wait while SPI busy
  digitalWrite(SS_PIN,HIGH); // Drive CS High

}
int i=0;
void loop()
{
  // put your main code here, to run repeatedly:
  while(1)
  {
    writeDACs(i,0xfff-i);
    i++;
    if (i > 0xfff)
      i = 0;
  }
}

The output is shown below

dualdacsawtooth