Introducing the MSP430G2553 Hardware

Turning on an LED

This section introduces you to programming and using the hardware that is available on the MSP430 and is typically used in monitoring and control of systems in c. Here we are going to use the Energia IDE and the msp-gcc compiler toolchain that comes installed with it.

This tutorial assumes:

  1. You have installed Energia. Instructions Here.
  2. You have tested the install by uploading the blink code. File>Examples>1.Basics>Blink and observed a blinking Red LED.

To program in c with Energia you simply delete the Energia code that appears when you open a new project and then open a new tab using the drop down arrow in the Energia toolbar. Click here for more detailed instructions.

The chip that we are going to use on the MSP430 Launchpad is the 20 pin MSP430G2553. To program this device, and any micro controller for that matter, it is essential that you have the MSP430G2553 data sheet and the MSP430 Family User Guide on hand.

For reference figure 1 shows 20 pin device pin functionality.

Figure 1 20 pin device pin functionality

Figure 1 20 pin device pin functionality

Step 1 Turning an LED on

Turning and LED requires three steps:

  1. Determine LED voltage and current requirements. If the LED requires less voltage than the micro controller output then a current limiting resistor in series with the LED will be required. If the LED current is larger than the current that the micro controller pin can source (give out) or sink (take in) then you will need to use an external MOSFET to switch current to the LED from an external source.
  2. Configure the pin as an output.
  3. Set the pin to high.

Turn on LED example code

#include "msp430G2553.h";

void main(void) {
 WDTCTL = WDTPW | WDTHOLD; // Disable Watch Dog Timer

 P1DIR |= BIT0;  // Set bit0 in the I/O direction register to define as an output
 P1OUT |= BIT0;  // Set the bit0 output to high
}

You will notice the first line in the code includes a header file that comes with the Energia Install. This header file is developed by the chip manufacturer and defines all the hardware addresses in the chip and control bits with easy to remember mnemonics such as P1OUT which contains the address of the 8 bit port 1 output register which is directly attached to pins labelled P1.0 to P1.7 on the MSP430 Launchpad. All of the register and bit names that are defined in the header file match up with the names in the MSP430G2553 Data Sheet and MSP430x2 User Guide.

The WDTCTL = WDTPW | WDTHOLD; line is required to disable the watch dog timer (WDT). If we do not do this then when the WDT times out and creates an interrupt if we fail to service the interrupt the WDT will reset the program counter to 0 and hence your program will start executing from the start. A useful feature if your code has crashed but not so useful if you don’t implement a WDT interrupt to restart the WDT, when the code is working properly.

Note the use of the |= (or assignment operator) where a |= b; implements a = a | b; This is used to only set bit0 in the P1OUT register leaving all other bits unchanged. If we had used P1OUT = BIT0; then all bits except BIT0 would have been cleared (set to 0) and only BIT0 is set i.e. the contents of the register would be 00000001.

Step 2 Blinking an LED the efficient way

Blinking an LED requires three elements:

  1. Initialisation code to configure the pin that the LED is attached to as an output
  2. Code that switches the output high to turn the LED on and low to switch it off.
  3. A timer or delay to control the blink rate.

Most simple versions of the blink demonstration use a delay block that is based on making the CPU run many times through a loop doing nothing.

For Example

#include "msp430G2553.h";

unsigned int i = 0;

void main(void) {
  WDTCTL = WDTPW + WDTHOLD; // Stop WDT
  P1DIR |= BIT0;  // Set bit0 in the I/O direction register to define as an output
  P1OUT |= BIT0;  // Set the bit0 output to high

  while(1){
    /* Delay Block */
    i = 40000;           // Initialise count
    while (i-- > 0){     //post decrement count and loop until count == 0
    P1OUT = P1OUT;       //Do something that doesn't change anything
    }
  P1OUT ^= BIT0;         // Toggle bit0
  }
}

The line P1OUT = P1OUT which clearly does nothing is required because if you simply had while(count– > 0); a good compiler would simply reduce this line to count = 0; and you would get no delay. The disadvantage with the approach is that while the CPU is running through the while loop just to provide a delay it cannot be doing something else of indeed sleeping to to save power.

You can achieve the same result as above more elegantly using the delay function that is included in the msp430.h header file

__delay_cycles(1000);

which will provide a delay in clock cycles. Again this function is convenient for creating short delays especially when initiating peripherals but should not be used for controlling program flow as it is very power inefficient and blocks the CPU.

A better approach is to use a dedicated timer. A timer is just a hardware counter, much like the clickers that you often see bouncers using to count people as they enter a nightclub. You can use the system clock to increment the counter. The MSP430G2553 has two 16 bit timers i.e. the maximum count is 216-1. You can set the upper count value and get the timer to assert an interrupt when the maximum count is reached.

To use the timer you need to:

  1. Choose the timer clock source, clock divide ratio and set the maximum count.
  2. Enable the timer interrupt when the maximum count is reached.
  3. Within the interrupt service routine (ISR) toggle the LED.
  4. Put CPU to sleep.

#include "msp430G2553.h";

void main(void) {
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
/* Load calibrated DCO vlues to set CPU clock to 1MHz */
BCSCTL1 = CALBC1_1MHZ; // Set DCO to 1MHz
DCOCTL = CALDCO_1MHZ; // Set DCO to 1MHz

P1DIR |= BIT0; // Set bit0 in the I/O direction register to define as an output
P1OUT |= BIT0; // Set the bit0 output to high

/* Configure timer A as a millisecond interval counter */
TA0CTL = TASSEL_2 + MC_1 + ID_1; // SMCLK as input clock, count up to TA0CCR0, clock/2
TA0CCR0 = 40000; // Set maximum count value to determine count frequency = SMCLK/ClockDivide/TACCR0 (1MHz/2/40000 = 12.5Hz)
TA0CCTL0 = OUTMOD_0 + CCIE; // Set out mode 0, enable CCR0 interrupt

__bis_SR_register(CPUOFF + GIE); // Put CPU to sleep in LPM0 with interrupts enabled
}

#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A0_ISR (void)
{
P1OUT ^= BIT0; // Toggle bit0
__bic_SR_register(CPUOFF + GIE); // Put CPU to sleep in LPM0 with interrupts enabled
}

Figure 2 Timer A block diagram

Figure 2 Timer A block diagram

Step 3 Analogue Output: Pulse Width Modulation (PWM)

Often we would like to control the amplitude of an output signal to vary for example the brightness of an LED or the control the speed of a motor. The MSP430 microcontrollers being digital devices don’t have analog outputs for such applications. We could get an external digital to analogue converter (DAC), however, for most applications we can actually just use the timer to create a Pulse Width Modulated Signal (PWM). A PWM signal is a pulsed signal with a period that is chosen to be much faster tun the response time of the device of system that you want to drive. In this way the system doesn’t respond to the fast pulses only the average value of the pulse train. We can readily change the average value of the pulse train by varying the duty cycle (or pulse width).


#include "msp430G2553.h";

void main(void) {
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
/* Load calibrated DCO vlues to set CPU clock to 1MHz */
BCSCTL1 = CALBC1_1MHZ; // Set DCO to 1MHz
DCOCTL = CALDCO_1MHZ; // Set DCO to 1MHz

P1DIR |= BIT0; // Set bit0 in the I/O direction register to define as an output
P1OUT |= BIT0; // Set the bit0 output to high

P1DIR |= BIT6; // Set P1.6 as output
P1SEL |= BIT6; // Select output P1.6 to be TA0.1
P1SEL2 &= ~BIT6; // Select output P1.6 to be TA0.1

/* Configure timer A as a PWM */
TA0CTL = TASSEL_2 + MC_1 + ID_0; // SMCLK as input clock, count up to TA0CCR0, clock/1
TA0CCR0 = 1000; // Set maximum count value to determine PWM frequency = SMCLK/ClockDivide/TACCR0 (1MHz/1/1000 = 1kHz)
TA0CCR1 = 100; // Initialise counter compare value 1 to control Duty Cycle = TACCR1/TACCR0 (100/1000 = 10%)
TA0CCTL1 = OUTMOD_7; // Set output to on when counter resets and off when counter equals TA0CCR1. Normal PWM.
__bis_SR_register(CPUOFF); // Put CPU to sleep in LPM0 with interrupts enabled
}

Step 4 Analogue Input: Analogue to Digital Conversion (ADC)

The 20pin MSP430G2553 has a single 10bit ADC with a maximum sample rate of 200kS/s. The input to the ADC is multiplexed and 8 of the ADC inputs are routed to the external pins on the 20 pin package. In this example we are going to configure P1.4 (or A4) as a 1.5V output reference voltage and use P1.5 (or A5) as the input to the ADC. To test the system we will attach a potentiometer between P1.4 and ground and then attach the wiper input to P1.5. In this way we can vary the input voltage on P1.5 from 0 to Vref=1.5V.

MSP430G2553 10bit ADC

Figure 3 MSP430G2553 10bit ADC

To use the ADC you need to:


#include "msp430G2553.h";

void main(void) {
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
/* Load calibrated DCO vlues to set CPU clock to 1MHz */
BCSCTL1 = CALBC1_1MHZ; // Set DCO to 1MHz
DCOCTL = CALDCO_1MHZ; // Set DCO to 1MHz

/* Configure ADC*/
ADC10CTL0=SREF_1 + REFON + REFOUT + ADC10ON + ADC10SHT_3 + ADC10IE ; //1.5V ref, Ref on, Enable Ref Output, 64 clocks for sample
ADC10CTL1=ADC10DIV_3; //temp sensor is at 10 and clock/4
ADC10AE0 |= BIT5 + BIT4; // Set P1.4 and P1.5 as ADC inputs

/* Configure Output pin for LED*/
P1DIR |= BIT6; // Set P1.6 as output
P1SEL |= BIT6; // Select output P1.6 to be timer output TA0.1
P1SEL2 &= ~BIT6; // Select output P1.6 to be timer output TA0.1

/* Configure timer A as a PWM */
TA0CTL = TASSEL_2 + MC_1 + ID_0; // SMCLK as input clock, count up to TA0CCR0, clock/1
TA0CCR0 = 1024; // Set maximum count value to determine count frequency = SMCLK/ClockDivide/TACCR0 (1MHz/1/1024 = 0.97kHz)
TA0CCTL0 = OUTMOD_0 + CCIE; // Set out mode 0, enable CCR0 interrupt
TA0CCR1 = 10; // Initialise counter compare value 1 to control Duty Cycle = TACCR1/TACCR0 (100/1000 = 10%)
TA0CCTL1 = OUTMOD_7; // Set output to on when counter resets and off when counter equals TACCR1. Normal PWM.
__bis_SR_register(CPUOFF+GIE); // Put CPU to sleep in LPM0 with interrupts enabled
}

// ADC10 interrupt service routine
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR (void)
{
TA0CCR1 = ADC10MEM; // read ADC value (note this is a 10bit value stored in a 16 bit register)
ADC10CTL0 &= ~ENC;
}

#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A0_ISR (void)
{
ADC10CTL1 = INCH_5; // Select ADC Channel
ADC10CTL0 |= ENC + ADC10SC; // Start ADC Acquisition
}

Step 5 Serial Communication

The MSP430G2553 has a hardware UART (Universal Asynchronous Receiver Transmitter) that can be used to send characters to a computer terminal using the USB connection on the MSP430 Launchpad. Remember characters are simply 8 bit binary numbers that are interpreted as characters using the mapping defined in the ASCII table.

The following example implements both the transmitter and the receiver. The receiver is interrupt based and assumes that a carriage return terminated string is transmitted from that computer. When the return character is received a flag ” is set indicating the number of characters that were received that you can check in the main function to indicate when a terminated string has been received.


/* Configure UART to transmit and receive charcters over the serial port at 9600 baud
This uses pins P1.1 and P1.2. On the Launchpad these are connected to a USB bridge chip to allow
for communcation between the board and a Computer using a USB connection.
The code transmitts "Hello world" and then waits to receive strings terminated with a carriage return.
The received strings are echoed back
*/

#include "msp430G2553.h";

void UARTConfigure(void);

volatile char UARTRxData[20];
volatile char UARTRxFlag = 0;
volatile char i=0;

void main(void) {
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
/* Load calibrated DCO vlues to set CPU clock to 1MHz */
BCSCTL1 = CALBC1_1MHZ; // Set DCO to 1MHz
DCOCTL = CALDCO_1MHZ; // Set DCO to 1MHz

UARTConfigure();
UARTSendString("Hello World");
UARTSendString("\r\n");

while(1){
if (UARTRxFlag){
UARTSendArray(UARTRxData,UARTRxFlag);
UARTSendString("\r\n");
}
__bis_SR_register(CPUOFF+GIE); // Put CPU to sleep in LPM0 with interrupts enabled
}
}

void UARTConfigure(void){
/* Configure hardware UART */
P1SEL = BIT1 + BIT2 ; // P1.1 = RXD, P1.2=TXD
P1SEL2 = BIT1 + BIT2 ; // P1.1 = RXD, P1.2=TXD
UCA0CTL1 |= UCSSEL_2; // Use SMCLK
UCA0BR0 = 104; // Set baud rate to 9600 with 1MHz clock (Data Sheet 15.3.13)
UCA0BR1 = 0; // Set baud rate to 9600 with 1MHz clock
UCA0MCTL = UCBRS0; // Modulation UCBRSx = 1
UCA0CTL1 &= ~UCSWRST; // Initialize USCI state machine
IE2 |= UCA0RXIE; // Enable USCI_A0 RX interrupt
}

void UARTSendChar(unsigned char character){
// Send a single byte using the hardware UART 0
// Example usage: UARTSendChar('a');

while(!(IFG2 & UCA0TXIFG)); // Wait for TX buffer to be ready for new data
UCA0TXBUF = character; //Write the character at the location specified by the pointer and increment pointer
}

void UARTSendArray(unsigned char *TxArray, unsigned char ArrayLength){
// Send number of bytes Specified in ArrayLength in the array at using the hardware UART 0
// Example usage: UARTSendArray("Hello", 5);
// int data[2]={1023, 235};
// UARTSendArray(data, 4); // Note because the UART transmits bytes it is necessary to send two bytes for each integer hence the data length is twice the array length

while(ArrayLength--){ // Loop until StringLength == 0 and post decrement
while(!(IFG2 & UCA0TXIFG)); // Wait for TX buffer to be ready for new data
UCA0TXBUF = *TxArray++; //Write the character at the location specified by the pointer and increment pointer
}
}

void UARTSendString(char *TxArray){
// Send number of bytes Specified in ArrayLength in the array at using the hardware UART 0
// Example usage: UARTSendArray("Hello", 5);
// int data[2]={1023, 235};
// UARTSendArray(data, 4); // Note because the UART transmits bytes it is necessary to send two bytes for each integer hence the data length is twice the array length

while(*TxArray)
{
while(!(IFG2 & UCA0TXIFG)); // Wait for TX buffer to be ready for new data
UCA0TXBUF = *TxArray++; //Write the character at the location specified by the pointer and increment pointer
}
}

// Echo back RXed character, confirm TX buffer is ready first
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
UARTRxData[i] = UCA0RXBUF;//Read USART data register

if(UARTRxData[i++]=='\r'){
UARTRxFlag=--i; //Set String received flag to length of received string
i=0; //Reset buffer index
__bic_SR_register_on_exit(CPUOFF);
}
}

Step 6 Printing Numbers to the serial terminal.

The serial terminal by default assumes that the received binary values are to be interpreted as ASCII characters. However if you wish to send binary values that represent a numerical quantity such as the reading from an ADC then you either need to use a serial terminal program that lets you select the data representation, or send the numerical value using ascii characters.

To convert binary numbers into formatted ASCII strings on the MSP430 you can use the following module. To use this simply add #include ”

print.h Header file


/* Formats binary values into ascii strings using the standard c format syntax
E.g. to format the value stored in variable data as an integer at least three characters wide.
printformat("%3i" , data);
*/

#ifndef MSP430G2553
#define MSP430G2553
#include <msp430g2553.h>
#endif
#include "stdarg.h"

void printformat(char *format, ...);

Print.c module source code

#include "print.h"

void putcharacter(unsigned character);
void putstring(char *s);

static const unsigned long dv[] = {
// 4294967296 // 32 bit unsigned max
 1000000000, // +0
 100000000, // +1
 10000000, // +2
 1000000, // +3
 100000, // +4
// 65535 // 16 bit unsigned max
 10000, // +5
 1000, // +6
 100, // +7
 10, // +8
 1, // +9
};

static const int dw[] = { // Width of integer
 // 4294967296 // 32 bit unsigned max
 10, // +0
 9, // +1
 8, // +2
 7, // +3
 6, // +4
 // 65535 // 16 bit unsigned max
 5, // +5
 4, // +6
 3, // +7
 2, // +8
 1, // +9
};

static void xtoa(unsigned long x, int width, const int *wp, const unsigned long *dp)
{
 char c;
 unsigned long d;

 if(x) {
 while(x < *dp) {
 ++dp;
 if (width >= *wp) putcharacter('0');
 ++wp;
 }
 do {
 d = *dp++;
 c = '0';
 while(x >= d) ++c, x -= d;
 putcharacter(c);
 } while(!(d & 1));
 }
 else {
 while (width--) putcharacter('0');
 }
}

static void puth(unsigned n)
{
 static const char hex[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
 putcharacter(hex[n & 15]);
}

void printformat(char *format, ...)
{
 char formatChar;
 int i;
 long n;
 int width = 1;

 va_list a;
 va_start(a, format);
 while(formatChar = *format++) {
 if(formatChar == '%') {
 formatChar = *format++;
 if ((formatChar > 48) && (formatChar < 58) ) {
 width = formatChar-48;
 formatChar = *format++;
 }
 switch(formatChar) {
 case 's': // String
 putstring(va_arg(a, char*));
 break;
 case 'c': // Char
 putcharacter(va_arg(a, unsigned));
 break;
 case 'i': // 16 bit Integer
 case 'u': // 16 bit Unsigned
 i = va_arg(a, int);
 if(formatChar == 'i' && i < 0) i = -i, putcharacter('-');
 xtoa((unsigned)i, width, dw + 5, dv + 5); // Send 16 bit integer to xtoa and point to +5 element of dv
 break;
 case 'l': // 32 bit Long
 case 'n': // 32 bit unsigned long
 n = va_arg(a, long);
 if(formatChar == 'l' && n < 0) n = -n, putcharacter('-');
 xtoa((unsigned long)n, width, dw, dv);
 break;
 case 'x': // 16 bit hexadecimal
 i = va_arg(a, int);
 puth(i >> 12);
 puth(i >> 8);
 puth(i >> 4);
 puth(i);
 break;
 case 0: return;
 default: goto bad_fmt;
 }
 } else
 bad_fmt: putcharacter(formatChar);
 }
 va_end(a);
}

void putstring(char *s)
{
 while(*s) putcharacter(*s++);
}

/**
 * Sends a single byte out through UART
 **/
void putcharacter(unsigned character)
{
 while (!(IFG2 & UCA0TXIFG)); // USCI_A0 TX buffer ready?
 UCA0TXBUF = character; // TX -> RXed character
}

Example code to test print.c using the UART


/* Configure UART to transmit and receive charcters over the serial port at 9600 baud
This uses pins P1.1 and P1.2. On the Launchpad these are connected to a USB bridge chip to allow
for communcation between the board and a Computer using a USB connection.
The code transmitts ascii formatted number strings using the print.c module.
*/

#include "msp430G2553.h";
#include "print.h"

void UARTConfigure(void);

int value = 672;

void main(void) {
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
/* Load calibrated DCO vlues to set CPU clock to 1MHz */
BCSCTL1 = CALBC1_1MHZ; // Set DCO to 1MHz
DCOCTL = CALDCO_1MHZ; // Set DCO to 1MHz

UARTConfigure();
printformat("Standard Width: %i\r\n",value);
printformat("Fixed Width: %5i\r\n",value);

__bis_SR_register(CPUOFF+GIE); // Put CPU to sleep in LPM0 with interrupts enabled
}

void UARTConfigure(void){
/* Configure hardware UART */
P1SEL = BIT1 + BIT2 ; // P1.1 = RXD, P1.2=TXD
P1SEL2 = BIT1 + BIT2 ; // P1.1 = RXD, P1.2=TXD
UCA0CTL1 |= UCSSEL_2; // Use SMCLK
UCA0BR0 = 104; // Set baud rate to 9600 with 1MHz clock (Data Sheet 15.3.13)
UCA0BR1 = 0; // Set baud rate to 9600 with 1MHz clock
UCA0MCTL = UCBRS0; // Modulation UCBRSx = 1
UCA0CTL1 &= ~UCSWRST; // Initialize USCI state machine
IE2 |= UCA0RXIE; // Enable USCI_A0 RX interrupt
}

 

Step 7 Try things for yourself

  1. Develop a program that keeps track of time in seconds and sends the current time back to the computer via the serial port every 5 seconds.
  2. Modify this code to sample the analogue input every 5 seconds and send the result back over the serial terminal as an ascii encoded string.
Advertisements

One thought on “Introducing the MSP430G2553 Hardware

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