Sending encrypted data from an MSP432 to a python script

This post shows how you might send encrypted data over as serial communications port. The example was written for the MSP432 using the Energia environment. It uses AES128 ECB encryption using a pre-shared, hard coded (bad practice) key. The example generates a simple text message with a counter that counts from 0 to 255 repeatedly. The message is encrypted and sent as a HEX string (so I can read it) over a serial port. The receiver is a python script that decrypts the data and displays it to the screen. The code was developed in a Linux environment but should work fine in other operating systems – you will need to change the path for the serial port.
This example does not deal with the thorny issue of key distribution.
Energia code



#include <msp432.h>
#include <stdio.h>
/*   Serial communications with AES 128 ECB encryption
     Data sent in ASCII hex (e.g. the value 0x02 will 
     be sent as the string "02".  Transmitting in this 
     way allows us monitor the messages using a simple 
     serial monitor.  It also allows us use the 
     Serial.print and Serial.println methods without 
     worrying about data values that are zero (which 
     will be interpreted as end of string markers by the
     Serial library routines.  Of course this is less 
     efficient but it will hopefully be instructive.
*/

// Data block size (in bytes)
#define BLOCKSIZE 16

const char key[] = "8d2e60365f17c7df1040d7501b4a7b5a";
char plaintext[BLOCKSIZE]; 
///const char MSG[] = "Mary had a little lamb"; //  "Mary had a little lamb";
// the setup routine runs once when you press reset:
void setup() {
  Serial.begin(38400);
}
int Counter;
// the loop routine runs over and over again forever:
void loop() {
  sprintf(plaintext,"Counter=%d",Counter);
  Counter++;
  if (Counter > 255)
    Counter = 0;
  SendEncryptedMessage(plaintext, sizeof(plaintext) );
  delay(1000);
}

uint32_t ByteStringToNumber(const char *Str)
{
  uint32_t UpperNibble = 0;
  uint32_t LowerNibble = 0;
  if (Str[0] > '9')
    UpperNibble = (  Str[0] | 32 ) - 'a' + 10; // ensure lower case, remove hex 'a' and offset by 10
  else
    UpperNibble = ( Str[0] - '0');
  if (Str[1] > '9')
    LowerNibble = (  Str[1] | 32 ) - 'a' + 10; // ensure lower case, remove hex 'a' and offset by 10
  else
    LowerNibble = ( Str[1] - '0');
  return (UpperNibble << 4) + LowerNibble;
}
void setKey(const char * keystr)
{
  // The key will take the form of a 32 character ASCII string produced
  // by a tool like openssl.
  // This routine will extract the bytes from the string and store them
  // in the AES accelerator
  int Index;
  uint32_t KeySection;
  uint8_t *Ptr = (uint8_t *)(&AESAKEY);
  for (Index = 0; Index < 16; Index++)
  {
    KeySection = ByteStringToNumber(keystr + Index * 2);
    *Ptr = (uint8_t) KeySection;
  }
}

void SendEncryptedMessage(const char *Payload, unsigned int len)
{
  // Send the payload over the encrypted channel
  // Message is padded with zeros if it is not a multiple of BLOCKSIZE
  unsigned int BlockIndex = 0;
  unsigned int TotalByteCount = 0;
  uint8_t PlainTextBuffer[BLOCKSIZE];
  uint8_t CryptoText[BLOCKSIZE];
  volatile uint8_t *InputDataRegister;
  volatile uint8_t *OutputDataRegister;

  AESACTL0 = 0; // Set the AES engine into encryption mode
  setKey(key);  // Must set key after changing mode

  while (TotalByteCount < len)
  {
    BlockIndex = 0;
    while (BlockIndex < BLOCKSIZE)
    {
      if (TotalByteCount < len)
        PlainTextBuffer[BlockIndex] = Payload[TotalByteCount];
      else
        PlainTextBuffer[BlockIndex] = 0;
      BlockIndex++;
      TotalByteCount++;
    }
    InputDataRegister = (uint8_t *)&AESADIN;
    for (BlockIndex = 0; BlockIndex < BLOCKSIZE; BlockIndex++)
      *InputDataRegister = PlainTextBuffer[BlockIndex];
    delay(1);
    
    OutputDataRegister = (uint8_t *)&AESADOUT;
    for (BlockIndex = 0; BlockIndex < 16; BlockIndex++)
      CryptoText[BlockIndex] = *OutputDataRegister;

    for (BlockIndex = 0; BlockIndex < BLOCKSIZE; BlockIndex++)
    {
      if (CryptoText[BlockIndex] < 16)
        Serial.print("0"); // send leading zeros for values < 16
      Serial.print(int(CryptoText[BlockIndex]), HEX);
    }
      
    Serial.println(""); // Send a line feed after each block
  }

}

Python code

# This python script receives encrypted data over the serial port
# The encryption method is AES128, ECB and the key is hardcoded into
# sender and receiver
# Expected received data format:
# ASCII-HEX 32 characters (representing 16 bytes or 128 bits) followed by 
# CR LF (\r\n)

from Crypto.Cipher import AES
import serial as ser;
import binascii 
port=ser.serial_for_url("/dev/ttyACM0") # CHANGE THIS TO SUIT YOUR SITUATION!!!
port.baudrate=38400
key = binascii.unhexlify('8d2e60365f17c7df1040d7501b4a7b5a')
IV = 16 * '\x00' # set inital vector (or state) to 0
mode = AES.MODE_ECB # Electronic Code Book encryption used
encryptor = AES.new(key, mode, IV=IV)
while (1):
    ciphertext=port.read_until()
    if len(ciphertext)==34:      # got a full packet?
        ciphertext=ciphertext[:32] # trim off CR LF
        # convert to actual values rather than hex string
        ciphertext=binascii.unhexlify(ciphertext) 
        plaintext = encryptor.decrypt(ciphertext) # decrypt 
        print(plaintext) 

Energia and the MSP432 AES encryption engine

I’ve been trying to get matching results between the TI-MSP432’s encryption engine and a python equivalent on my laptop. It has been a bit frustrating but I think I’ve got it. The main cause of my frustration is that the AES registers are declared as if they represented 16 bit quantities and so the compiler was generating 2 byte writes to the registers. For example, the AESAKEY register is declared in msp432p401r_classic.h as follows:

#define AESAKEY (HWREG16(0x40003C06)) 

This messed up the engine’s state when I wanted to write bytes. Anyway, it works now and here is some Energia code that outputs the same answer as a python script (which is included in the comments). The code needs a little work and I hope to set up an encrypted link between the MSP432 and the PC over the UART.

#include <msp432.h>
/*key and message obtained from NIST test vectors in http://csrc.nist.gov/groups/STM/cavp/documents/aes/AESAVS.pdf */
/* Using AES 128 ECB Encryption */
/* Tested against the following python code:

# This produces the same answer as http://aes.online-domain-tools.com/
# but not the same as NIST test vector for ECB
from Crypto.Cipher import AES
import binascii
key = binascii.unhexlify('8d2e60365f17c7df1040d7501b4a7b5a')
IV = 16 * '\x00'
mode = AES.MODE_ECB
encryptor = AES.new(key, mode, IV=IV)
text = binascii.unhexlify('59b5088e6dadc3ad5f27a460872d5929')
#text=b'Plain text msg  '
ciphertext = encryptor.encrypt(text)
print(binascii.hexlify(ciphertext))

 */
const char key[]="8d2e60365f17c7df1040d7501b4a7b5a";
const char testmsg[]="59b5088e6dadc3ad5f27a460872d5929";
uint32_t ByteStringToNumber(const char *Str)
{
  uint32_t UpperNibble = 0;
  uint32_t LowerNibble = 0;
  if (Str[0] > '9')
    UpperNibble = (  Str[0] | 32 ) - 'a' + 10; // ensure lower case, remove hex 'a' and offset by 10
  else
    UpperNibble = ( Str[0] - '0');
  if (Str[1] > '9')
    LowerNibble = (  Str[1] | 32 ) - 'a' + 10; // ensure lower case, remove hex 'a' and offset by 10
  else
    LowerNibble = ( Str[1] - '0');
  return (UpperNibble << 4) + LowerNibble;
}
void setKey(const char * keystr)
{
  // The key will take the form of a 32 character ASCII string produced
  // by a tool like openssl.
  // This routine will extract the bytes from the string and store them
  // in the AES accelerator
  int Index;
  uint32_t KeySection;
  uint8_t *Ptr = (uint8_t *)(&AESAKEY);
  Serial.println("Setting the following key: ");
  for (Index = 0; Index < 16; Index++)
  {
    
    KeySection = ByteStringToNumber(keystr+Index*2);
    Serial.print(KeySection,HEX);
    Serial.print(" ");
    *Ptr = (uint8_t) KeySection;   
    
  }
  Serial.println("");
  printAESRegisters();
}
void encryptBlock(const char *plain_text, char *crypto_text, uint32_t len)
{
  // Encrypts a 128 bit (16 byte) block of text
  int Index = 0;
  uint8_t Dummy = 0;
  uint32_t Section;
  volatile void *Ptr;
  // select encrypt mode  
  AESACTL0 = 0;//0xfffc;
  setKey(key); 
  Ptr = &AESADIN;
  Serial.println("Encrypting the following:" );
  for (Index = 0; Index < 16; Index++)
  {

    Section = ByteStringToNumber(plain_text+Index*2);
    Serial.print(Section,HEX);
    Serial.print(" ");
    *((uint8_t *)Ptr) = (uint8_t)Section;   
  }
  Serial.println(" ");
  delay(4);
  //while ( (AESACTL0 & (1 << 8) )==0); // wait for ready flag
  Ptr = &AESADOUT;
  for (Index = 0; Index < 16; Index ++)
  {
      crypto_text[Index] =  *((uint8_t *)Ptr);      
  } 
}


void printAESRegisters()
{
  Serial.print("AESASTAT: ");
  Serial.print(String(AESASTAT,16));
  Serial.print(", AESACTL0: ");
  Serial.print(String(AESACTL0,16));
  Serial.print(",  AESACTL1: ");
  Serial.println(String(AESACTL1,16));
}


// the setup routine runs once when you press reset:
void setup() {
  Serial.begin(38400);
}
char CryptoText[17];  
// the loop routine runs over and over again forever:
void loop() {
  int Index;
  CryptoText[16]=0;
  AESACTL0 = 0;
    
  encryptBlock(testmsg,CryptoText,16);  
  Serial.println("Just encrypted");
  printAESRegisters();
  Serial.println("Crypto Text:");
  
  for (Index = 0 ; Index < 16; Index ++ )
  {
    Serial.print(int(CryptoText[Index]),HEX);    
    Serial.print(" ");
  }
  Serial.println(" ");
  Serial.println(CryptoText);
  Serial.println("--------------------------------");

  delay(501);
}

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

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

Periodic interrupts with Energia and the MSP432 Launchpad

Correction on 25th July 2016: Base clock for timer was wrong in original – now fixed.

In a previous post I showed how a periodic interrupt could be generated with the MSP430 Launchpad.  This post shows how it can be done using Energia and the MSP432 (ARM Cortex M4F) Launchpad.  The example uses Timer A3 to generate a periodic (1 kHz) interrupt.  There appears to be some interplay between this and the built-in delay routine however the delayMicroseconds function seems to work fine.  A global counter variable is updated at each interrupt so software can use this to perform additional time delays etc.

Some use is made of lower level library functions that are included with the Energia environment.  This example was compiled with Energia 0101E0017.

/* Periodic interrupt example for the MSP432 development board and the energia development environment */
/* The function OnTimer is called every millisecond and flashes the green LED every second
   The main loop flashes the blue LED every second
   Timer A3 is used to generate the interrupt
*/
 
#include <msp432.h>;
#include <driverlib/timer_a.h>
#include <driverlib/interrupt.h>
 
/*
General notes: serial comms uses eusci_a
PWM uses Timer A0 (at least I think so)
The built-in delay function appears not to work if it expires at the same time as a timer interrupt
The delayMicroseconds appears not to be so affected (so far)
*/
volatile uint32_t millisecondCounter=0;
int count = 0;
volatile int state = HIGH;
volatile int flag = HIGH;
 
void setup()
{
  Serial.begin(9600);
 
  pinMode(BLUE_LED,OUTPUT);
  pinMode(GREEN_LED, OUTPUT);
  setupTimer(1000); // set timer period to 1000 micro seconds
}
 
void OnTimer()
{
 
  static int Count = 0;
  static int state = 0;
  Count++;
  if (Count > 1000)
  {
    Count = 0;
    digitalWrite(GREEN_LED,state);
    if (state)
      state = 0;
    else
      state = 1;
  }
}
void loop()
{
 
  digitalWrite(BLUE_LED,HIGH);
  delayMicroseconds(500000);
  digitalWrite(BLUE_LED,LOW);
  delayMicroseconds(500000);
  Serial.println(millisecondCounter);
}
 
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();
}

 

Adding a DAC to the MSP432 Launchpad

The MSP432 Launchpad features quite a powerful low energy processor (the MSP432P401R).  This should make it ideal for a little DSP but for one problem: there is no DAC.  I decided that I should remedy this by adding an SPI DAC in the form of a TLV5618A. This 12-bit IC has two DAC outputs and can be driven at a speed sufficient for some pretty decent audio.  Wiring is as shown below:

wiring

Code was developed in Energia – an experience that left me wondering about the completeness and documentation of the MSP432 port to this environment.

 

#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);
  SPI.setDataMode(SPI_MODE1);
  SPI.setBitOrder(MSBFIRST);   //
  SPI.setClockDivider(SPI_CLOCK_DIV2); // DIV2 = 8MHz, DIV4 = 4MHz, DIV8 = 2MHz etc. (from measurement)
}
void writeDACA(int Value)
{
  Value=Value & 0xfff;
  Value = Value | 0xc000; // Write DAC A
  digitalWrite(SS_PIN,LOW);
  SPI.transfer( (Value >> 8) & 0xff);
  SPI.transfer( Value  & 0xff);
  digitalWrite(SS_PIN,HIGH);
}
int i=0;
void loop()
{
  // put your main code here, to run repeatedly:
  while(1)
  {
    writeDACA(i);
    i++;
    if (i > 0xfff)
      i = 0;
  }

}

This produces the following output waveform:

waveform

This is a little noisy but more to the point : SLOW!  The pair of SPI writes are taking more than 70 microseconds which makes me wonder about the efficiency of the underlying libraries.  I hope to remedy this in a later post (Edit: you can read about this here: https://ioprog.com/2016/07/02/speedier-dacs-with-the-msp432-launchpad/)