Jump to content
43oh

Full-duplex hardware UART for launchpad


Recommended Posts

Now that I have a freshly minted uniarch version of msp430-gcc, I wanted to test it out and make sure the newly added support for the msp430g2553 chip really worked. I took my softserial full duplex code and twisted it around some to take advantage of gcc. I also exercised some of my favorite features of gcc, specifically templates, just to make sure they work in the uniarch version.

 

Yeah, as I get further into the msp430 world I've decided I don't really like TI's version of eclipse + CDT too much. Along my path to enlightenment I have left the CCS blanket and wrapped myself in the latest Eclipse+CDT (Helios) with msp430-gcc running on Ubuntu. Unfortunately, the code below probably won't compile with CCS. However, I'm sure at some point someone will port the uniarch version of mspgcc over to cygwin.

 

One of the nice thing about the 2553 vs the 2231 is the 2553 chip has a hardware UART builtin. No more stolen TimerA cycles for me. I'm doing the serial dance with hardware now. Attached you will find the code and a Makefile.

 

Notes about hardware USCI UART: You have to flip your TX/RX jumpers on your launchpad. This has been talked about elsewhere on the forums. I'm just using a couple of F-M jumper wires going to my msp430g2553 on a breadboard.

 

You can read the announcement about uniarch and support for the newer chips over on the mailing list:

http://sourceforge.net/mailarchive/foru ... pgcc-users

 

The latest version of the following source code can be found here: https://gist.github.com/969447

 

I took a snapshot of the code:

/**
* usci_serial.cpp - simple example using USCI UART + ringbuffer + command line monitor
*
* This code was written to test the new uniarch version of msp430-gcc. I wanted
* to make sure it could generate code for the msp430g2553 running on the TI
* launchpad.
*
* This code mplements a command line interface over the USB-CDC serial at
* 9600-8-N-1. To test you must swap the RX/TX pins on your launchpad to use
* the hardware USCI UART.  I disconnected those jumpers on my launchpad and used
* a couple of F-M jumper wires to run the msp430g2553 from a breadboard.
* P1.1=RXD and P1.2=TXD ( this is the opposite of the msp430g2231 default setup )
*
* * License: Do with this code what you want. However, don't blame
* me if you connect it to a heart pump and it stops.  This source
* is provided as is with no warranties. It probably has bugs!!
* You have been warned!
*
* Author: Rick Kimball
* email: rick@kimballsoftware.com
* Version: 1.00 Initial version 05-12-2011
*/

#include 
#include 
#include 
#include "config.h"
#include "ringbuffer.h"
#include "usci_serial.h"

/**
*  create a ring buffer that holds up to 16 uint8_t bytes
*
*  Note: you could change the ringbuffer_ui8_16 typedef to
*  make it smaller or larger, just be consistent with what
*  you provide the Serial template
*/

ringbuffer_ui8_16 usci_buffer = { 0, 0, { 0 } };

Serial usci0 = { usci_buffer };

/**
* USCI0RX_ISR - USCI UART receive character ISR handler
*
* we get an interrupt when a new character arrives via
* the USCI UART receive pin. We just stuff it into
* our buffer and let the main routine consume it.
*/

interrupt(USCIAB0RX_VECTOR) USCI0RX_ISR(void) {
   /**
    * Note: a side effect of reading UCA0RXBUF
    * is the rx interrupt flag is cleared
    */

   usci_buffer.push_back(UCA0RXBUF);
}

/**
* main - echo back to the user whatever they type
*/
int main(void) {
   WDTCTL = WDTPW + WDTHOLD; // Stop WDT

   BCSCTL1 = CALBC1_16MHZ; // set DCO clock for MCLK and SMCLK
   DCOCTL = CALDCO_16MHZ;

   usci0.init();

   __bis_SR_register(GIE); // interrupts enabled

   usci0.xmit("\r\nMSP430G2553 Monitor\r\n$ ");

   while (true) {
       while (!usci0.empty()) {
           volatile int c;

           c = usci0.recv();
           usci0.xmit((uint8_t) c);
       }
   }
}

 

/**
* config.h - configure baud rates, MCLK frequency
*
* License: Do with this code what you want. However, don't blame
* me if you connect it to a heart pump and it stops.  This source
* is provided as is with no warranties. It probably has bugs!!
* You have been warned!
*
* Author: Rick Kimball
* email: rick@kimballsoftware.com
* Version: 1.00 Initial version 05-12-2011
*/

#ifndef CONFIG_H_
#define CONFIG_H_

#define F_CPU  16000000     // use calibrated 16MHZ clock

#ifdef __MSPGCC__
#define _enable_interrupts() __bis_status_register(GIE)
#define _disable_interrupts() __bic_status_register(GIE)
#endif

#endif

 

/*
* ringbuffer.h - template for a circular buffer
*
* License: Do with this code what you want. However, don't blame
* me if you connect it to a heart pump and it stops.  This source
* is provided as is with no warranties. It probably has bugs!!
* You have been warned!
*
* Author: Rick Kimball
* email: rick@kimballsoftware.com
* Version: 1.00 Initial version 05-12-2011
*/

#ifndef RINGBUFFER_H_
#define RINGBUFFER_H_

/**
* ringbuffer - a template based interrupt safe circular buffer structure with functions
*/

template
struct ringbuffer {
   volatile int head;
   volatile int tail;
   volatile T buffer[MAX_ITEMS];

   /**
    * empty() - checks the buffer for data
    *
    * returns true if empty, false if there is data
    */
   inline bool empty() {
       bool isEmpty;

       _disable_interrupts(); // prevent inconsistent reads
       isEmpty = (head == tail);
       _enable_interrupts();

       return isEmpty;
   }

   /**
    * push_back() - append a byte to the buffer is possible
    *               assumed to be called from the recv interrupt
    */
   inline void push_back(T c) {
       int i = (unsigned int) (head + 1) % MAX_ITEMS;
       if (i != tail) {
           buffer[head] = c;
           head = i;
       }
   }

   /**
    * pop_front() - remove a value from front of ring buffer
    */
   inline T pop_front() {
       T c = -1;

       _disable_interrupts(); // disable interrupts to protect head and tail values
       // This prevents the RX_ISR from modifying them
       // while we are trying to read and modify

       // if the head isn't ahead of the tail, we don't have any characters
       if (head != tail) {
           c = (T) buffer[tail];
           tail = (unsigned int) (tail + 1) % MAX_ITEMS;
       }

       _enable_interrupts(); // ok .. let everyone at them

       return c;
   }
};

typedef ringbuffer ringbuffer_ui8_16; // ringbuffer, max of 16 uint8_t values
typedef ringbuffer Ringbuffer_uint8_32; // ringbuffer, max of 32 uint8_t values

#endif /* RINGBUFFER_H_ */

/**
* usci_serial.cpp - simple example using USCI UART + ringbuffer + command line monitor
*
* This code was written to test the new uniarch version of msp430-gcc. I wanted
* to make sure it could generate code for the msp430g2553 running on the TI
* launchpad.
*
* This code mplements a command line interface over the USB-CDC serial at
* 9600-8-N-1. To test you must swap the RX/TX pins on your launchpad to use
* the hardware USCI UART.  I disconnected those jumpers on my launchpad and used
* a couple of F-M jumper wires to run the msp430g2553 from a breadboard.
* P1.1=RXD and P1.2=TXD ( this is the opposite of the msp430g2231 default setup )
*
* * License: Do with this code what you want. However, don't blame
* me if you connect it to a heart pump and it stops.  This source
* is provided as is with no warranties. It probably has bugs!!
* You have been warned!
*
* Author: Rick Kimball
* email: rick@kimballsoftware.com
* Version: 1.00 Initial version 05-12-2011
*/

#include 
#include 
#include 
#include "config.h"
#include "ringbuffer.h"
#include "usci_serial.h"

/**
*  create a ring buffer that holds up to 16 uint8_t bytes
*
*  Note: you could change the ringbuffer_ui8_16 typedef to
*  make it smaller or larger, just be consistent with what
*  you provide the Serial template
*/

ringbuffer_ui8_16 usci_buffer = { 0, 0, { 0 } };

Serial usci0 = { usci_buffer };

/**
* USCI0RX_ISR - USCI UART receive character ISR handler
*
* we get an interrupt when a new character arrives via
* the USCI UART receive pin. We just stuff it into
* our buffer and let the main routine consume it.
*/

interrupt(USCIAB0RX_VECTOR) USCI0RX_ISR(void) {
   /**
    * Note: a side effect of reading UCA0RXBUF
    * is the rx interrupt flag is cleared
    */

   usci_buffer.push_back(UCA0RXBUF);
}

/**
* main - echo back to the user whatever they type
*/
int main(void) {
   WDTCTL = WDTPW + WDTHOLD; // Stop WDT

   BCSCTL1 = CALBC1_16MHZ; // set DCO clock for MCLK and SMCLK
   DCOCTL = CALDCO_16MHZ;

   usci0.init();

   __bis_SR_register(GIE); // interrupts enabled

   usci0.xmit("\r\nMSP430G2553 Monitor\r\n$ ");

   while (true) {
       while (!usci0.empty()) {
           volatile int c;

           c = usci0.recv();
           usci0.xmit((uint8_t) c);
       }
   }
}

#ifndef HW_SERIAL_H
#define HW_SERIAL_H

/**
* Serial - simple access to USCI UART hardware
*          code implements interrupt driven input
*          and poll driven output.
*
* License: Do with this code what you want. However, don't blame
* me if you connect it to a heart pump and it stops.  This source
* is provided as is with no warranties. It probably has bugs!!
* You have been warned!
*
* Author: Rick Kimball
* email: rick@kimballsoftware.com
* Version: 1.00 Initial version 05-12-2011
*/

template
struct Serial {
   T_STORAGE &_recv_buffer;

   /**
    * init - setup the USCI UART hardware for 9600-8-N-1
    *        P1.1 = RX PIN, P1.2 = TX PIN
    */
   inline void init() {
       P1SEL = BIT1 + BIT2; // P1.1=RXD, P1.2=TXD
       P1SEL2 = BIT1 + BIT2; // P1.1=RXD, P1.2=TXD

       UCA0CTL1 |= UCSSEL_2; // use SMCLK for USCI clock
       UCA0BR0 = 130; // 16MHz 9600
       UCA0BR1 = 6; // 16MHz 9600
       UCA0MCTL = UCBRS1 + UCBRS0; // Modulation UCBRSx = 3
       UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
       IE2 |= UCA0RXIE; // Enable USCI0RX_ISR interrupt
   }

   inline bool empty() {
       return _recv_buffer.empty();
   }

   inline int recv() {
       return _recv_buffer.pop_front();
   }

   void xmit(uint8_t c) {
       while (!(IFG2 & UCA0TXIFG))
           ; // USCI_A0 TX buffer ready?

       UCA0TXBUF = (uint8_t) c; // TX -> RXed character
   }

   void xmit(const char *s) {
       while (*s) {
           xmit((uint8_t) *s);
           ++s;
       }
   }

};

#endif /* HW_SERIAL_H */

#
# Makefile - usci_serial
#
# License: Do with this code what you want. However, don't blame
# me if you connect it to a heart pump and it stops.  This source
# is provided as is with no warranties. It probably has bugs!!
# You have been warned!
#
# Author: Rick Kimball
# email: rick@kimballsoftware.com
# Version: 1.00 Initial version 05-12-2011

CC=msp430-gcc
CXX=msp430-g++
MCU=msp430g2553

CFLAGS=-mmcu=$(MCU) -O2 -g -Wall

APP=usci_serial
TARGET=Debug

all: $(TARGET)/$(APP).elf

$(TARGET)/$(APP).elf: $(TARGET)/$(APP).o
$(CC) $(CFLAGS) -o $(TARGET)/$(APP).elf $(TARGET)/$(APP).o
msp430-objdump -DS $(TARGET)/$(APP).elf >$(TARGET)/$(APP).lst
msp430-size $(TARGET)/$(APP).elf

$(TARGET)/$(APP).o:	config.h ringbuffer.h $(APP).cpp $(APP).h 
$(CC) $(CFLAGS) -c -o $(TARGET)/$(APP).o $(APP).cpp

install:
mspdebug -q --force-reset rf2500 "prog $(TARGET)/$(APP).elf"

clean:
rm -f $(TARGET)/$(APP).o $(TARGET)/$(APP).elf $(TARGET)/$(APP).lst
mkdir -p $(TARGET)/

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...