In this post I present a broken down version of the LaunchPad's example project which only contains the software UART transmit functionality. Note: the code I present can not receive any UART data. The next post will discuss how to use this code with an FTDI breakout board from Spark Fun (which I just received in the mail) for faster communication.
For this post you will need a terminal software. HyperTerminal which comes with Windows will do fine, but I don't like it very much. I use Realterm.
A Little About UART Basics
What is UART? Universal Asynchronous Receiver/Transmitter; UART is a communication protocol. As I already mentioned this post will only implement the transmitter part.
Asynchronous means that the devices which are connected via UART do not need to be synchronized together (aka, share a clock signal). This is very useful and this is the main benefit of using UART compared to other protocols such as SPI or I2C, the downside of this though is that you sacrifice speed when running asynchronously. The UART connection can be either be full duplex, which means the device can transmit and receive at the same time, or half duplex which means the uC can not send and receive as the same time.
The serial port is one implementation of UART and RS-232 was (is) very common. The problem with a serial port on a computer is that the voltages used to transmit the bits are waaaaaay out of the acceptable input range for a typical microcomputer. Why do they do this? They do this to make the transmission more resilient to noise and interference when transmitted over longer distances. If you are hooking your device into something like the image above (from Wikipedia), make sure you have level converters! This is why I like to use USB chips like FTDI's, or the one built into the 5xx. Plus you can achieve faster speeds with those chips than you can with a typical RS-232 interface.
Last bit of theory for UART. UARTs all have some basic properties which can be changed depending on the application. The main property which can be changed is Baud Rate, we will not discuss the others here since it is beyond the scope of this post. Baud Rate directly corresponds to how fast a bite will be transmitted. The larger the baud rate, the faster a byte is sent.
Note: When sending a byte over UART your device needs to send 10 bits total, one start bit and one stop bit in addition to the 8 bit byte.
The LaunchPad and Terminal Software
The LaunchPad has the hardware built into the emulator to provide a very slow UART connection to the PC for debugging purposes. It's maximum baud rate is 9600, which I have verified. The drivers that are (hopefully) automatically installed create a virtual COM port (VCP) which a terminal program or any computer application can access. In my case it is COM9, if you don't know which one it is you can check the device manager. If you want faster communication you have to use a seperate USB device. I typicaly have used FTDI chips and will be using an MSP330F5xx chip in the future.
Sorry for all that rambling, here comes the code.
The Code
Code is also posted here. Sorry for the poor comments. Forgot to clean them up before I posted.
About the Code
I will not be going over how I condensed the example project to just contain the UART since that would require explaining how the example project works. I might do that eventually in a separate blog, but not yet. I also changed a bit of their code because there were a few things I really didn't like, or that caused problems; I tried to keep the code as similar as possible to the example code provided by TI. I would have done this a bit differently. I don't claim though that I wrote this code and I state in the comments that I only modified the code. Please keep this in mind. I also don't ever claim that any of my code will ever be perfect, though I will never post code that I haven't tested and validated.
About the Code
I will not be going over how I condensed the example project to just contain the UART since that would require explaining how the example project works. I might do that eventually in a separate blog, but not yet. I also changed a bit of their code because there were a few things I really didn't like, or that caused problems; I tried to keep the code as similar as possible to the example code provided by TI. I would have done this a bit differently. I don't claim though that I wrote this code and I state in the comments that I only modified the code. Please keep this in mind. I also don't ever claim that any of my code will ever be perfect, though I will never post code that I haven't tested and validated.
Analysis of the Code
#define Bitime 104 //9600 Baud, SMCLK=1MHz (1MHz/9600)=104
Bitime is the number of clock ticks per bit, in the original code the clock was divided down by 8, and they used a baud rate of 2400; this code uses a baud rate of 9600. (I think they should have called it BitTime).
BCSCTL1 = CALBC1_1MHZ; // Set range
DCOCTL = CALDCO_1MHZ; // SMCLK = DCO = 1MHz
The two lines above set the internal DCO (Digitally Controlled Oscillator) to [almost] exactly 1MHz. The way this works is that CALBC1_1MHZ and CALDCO_1MHZ point to locations in memory that have been written with calibration information that is device specific. Each type of MSP430 will have different values for calibration. I was skeptical about how exact this could be, but after measuring it with my Logic Analyzer, the frequency was almost exactly 1MHz.
P1SEL |= TXD;
P1DIR |= TXD;
These two lines set the mode of the pin selected for TXD. The important thing to note here is that you can not pick a pin that does not have a Timer terminal function. For example P1.5 also can be set to be TA0.0, so you can change TXD to BIT5 and not change any other code. This is because, like in our PWM example, the Timer automatically changes the value of the TA0.0 pin when a CCR0 event happens.
The loop in main should be pretty self explanatory. Make sure you put the value you want to send into TXByte before calling Transmit().
CCTL0 = OUT;
...
CCTL0 = CCIS0 + OUTMOD0 + CCIE; // Set signal, intial value, enable interrupts
...
CCTL0 &= ~ OUTMOD2; // TX Mark`
The three lines above go together, they all handle how the output pin is set. The OUTMODX determines how the signal changes when a CCR0 event happens. We should already be familiar with OUTMOD7 (Set/Reset). When OUTMOD0 is selected, the output signal will be set to whatever value the OUT bit in CCTL0 is set to. The first line above sets this bit to 1, so the output pin will be changed to 1. When OUTMOD2 is set, whenever a CCR0 event happens the output is cleared (set to 0). You can see this in the timer interrupt function, if the bit to be sent is equal to one, OUTMOD0 is set otherwise OUTMOD2 is set. This is a fancy way of saying set the output to 1 when the bit being sent is 1, and set the output to 0 when the bit being sent is 0.
CCR0 = TAR;
The above command sets the compare register to the current value of the timer (TAR). This allows the compare register to start from the correct reference point when additional bitimes are added to the compare register.
I'm now going to explain how it all works and hopefully you can peace things together without me elaborating on every line.
Transmit() is called, this initializes the timer and compare registers, formats the byte to be sent, then starts the timer. Once the interrupt happens, it is time to change the bit. This is done in the interrupt function, which adds the needed offset to the counter first so that the next bit will transition on time even though there is processing being done within the interrupt function. The bit counter is then decremented once each interrupt; this is done until it reaches 0, which means that it is done transmitting the byte which then disables the interrupt. Meanwhile the Transmit() function is still running and is in a loop waiting for the byte to be done. It knows it is done when the timer interrupt is disabled. This loop makes it so that you can't try to send another byte while one is already being sent. Note that this is very wasteful as far as power goes, and that more care should be taken if you are worried about power consumption.
Seeing our Data on the Computer
Go to your terminal program and set the COM port, set the baud rate to 9600, make sure the display is in HEX and reconnect the terminal software to allow the changed parameters to go into effect. Run your code and you should now see values counting upward being sent to the terminal! Cool!
What Next?
Play around with some baud rates and have some fun. If you want to try and use what you've learned so far from previous posts, use the transmit function with a push button interrupt. It's important to see how code pieces together to make cool projects. Use the push button code I mentioned in a previous blog (or a version with debouncing, but this time also transmit a some value (a counter maybe) every button press.
Next post I will be going over how to achieve higher data speeds using FTDI's chip, and the problems I ran into using this code while trying to send data at 115200 baud (the code breaks when bitime is lower then about 50).
As always, comment away. Having any problems? Any cool code modifications you'd like to share? Also feel free to start threads on any of the websites I mentioned in my previous post, I will be watching them.
Have fun!
-NJC