We now have all the tools to build a UART transmitter on the MSP430. We have an accurate clock, which operates at a frequency that gives us the best accuracy on standard transmission rates with the DCO. The best way to control the timing of our serial transmission will be to use the Timer_A peripheral, so let's look at how to configure it.
First, we need to ensure we have our DCO set to our custom calibration. The way I've chosen to do this is to use definitions in the header:
#define CALDCO_UART *(char *)0x10BE
#define CALBC1_UART *(char *)0x10BF
Then in our code, we can set the DCO just as we would for the 1 MHz calibration:
BCSCTL1 = CALBC1_UART;
DCOCTL = CALDCO_UART;
To make this even easier, you can put the #defines in a header file (I've called mine calibrations.h), and then include it in any program where you need to use the custom DCO calibration.
Now to set up our timer. First, we need to choose our transmission rate. The commonly selected rate is 9600 baud, and if you use only the LaunchPad's USB connection it may be the only rate that works. (It's a little unclear still how the USB connection is set up-- the MSP430 UART connects to the MSP430 in the emulator, which has separate pins to forward the data to the TUSB chip interfacing with the USB port.) This DCO frequency can reliably do transmission up to a rate of 921,600 baud. Any faster, and there may not be enough time to run the transmission code between bits. The code you can download at the end of the tutorial is configured for 9600 baud.
The next thing we need is to know how many clock cycles occur during the time to transmit one bit. We'll transmit at 9600 bits per second, and our clock oscillates at 7,372,800 times per second, so there are 7,372,800/9600 = 768 clock cycles in each bit. (Note that this is exact; there's no fractional part being truncated in this division, which is why we chose this DCO frequency to start with.) This period is often called the bit time.
bit_time = 24576; // bit time for 300 baud
bit_time = 768; // bit time for 9600 baud
bit_time = 64; // bit time for 115,200 baud
The LaunchPad is set up to send the transmission over P1.1. Looking in the datasheet, we see that this pin can be used as the TA0.0 output
–using Timer_A, we can toggle this output when the TAR counter reaches the value stored in TACCR0. We have two options for this: we can use the timer's up-mode so that the counter resets after reaching TACCR0 of bit_time, or we can use continuous mode and reset TACCR0 to be bit_time cycles beyond the current value of TAR when it triggers an interrupt. (Doing this automatically accounts for roll-over.) The first would be an easy solution for our transmitter, but as we're working towards a full-duplex receiver/transmitter, and the receiver looks at P1.2 (using TA0.1, based on TACCR1 for timing, of course), it makes more sense to use the second method in anticipation of setting up the receiver next time so that we don't have conflicts in using the timer for both tasks.
The idle state for UART is logic high. When we configure the timer, we'll ensure that the TA0.0 defaults to setting P1.1. There's no need to start the timer until we're ready to transmit, but on the other hand there's no need to stop it unless you're concerned about power consumption. We'll go ahead and let the timer run for now, since as a peripheral it runs independently and won't interfere with any code running. We will, however, wait to enable interrupts until we're ready to transmit.
Once we're ready to transmit a character, we need to enable interrupts, and configure Timer_A to reset the TA0.0 output to signal the start of a character. TACCR0 is incremented to bit_time counts later, so the next interrupt occurs at the timing necessary for our chosen transmission rate. We then step through the bits being sent and tell the MSP430 whether the next bit should be set or reset, depending on our encoding. We'll use the standard 8N1, little-endian encoding, so it is determined by the lsb of the character we're sending. When all 8 bits have been sent, we then set TA0.0 to mark a stop bit, making a total of 10 bits for each character. Interrupts are then disabled until we have another character to send.
Take a look at the code in
uartTXG2231.c. Note first of all the routine DCO_init(). This includes a trap–since we're accessing the flash memory directly, we run the risk of setting the DCO to its highest setting if we use a device that hasn't had the DCO calibration written to that memory address! This type of check will prevent that, and is not a bad thing to include even when using the factory calibrations. I've created an LED signal specifically to indicate a DCO calibration error: three flashes of LED1, repeated with a pause between sets. If you try this code with an uncalibrated G2231, it should immediately indicate this error. Note that it cannot tell if a written code is correct or not, only if the addresses we're accessing have been erased.
If you've jumped right in and tried to run the code after calibrating your DCO, you may still encounter the error. CCS by default erases both the main memory and the information memory segments (except for INFO_A) when you program the chip; so your calibrations in INFO_B have been erased and the code won't run. Rerun the
UART calibration code, and then open the project properties. (When uartTXG2231 is the [Active -Debug] project, you can find the properties in the File menu, or right-clicking the project name in the Project Explorer tab.) In the menu on the left, select Debug. In the window that comes up, select MSP430 Properties. Here you'll see a list of Download Options; be sure to choose "Erase Main Memory Only" and close the properties window. Now when you load this project into your device, it will only erase the Main Memory and leave the information memory segments alone.
This code is set up to demonstrate a few ways to transmit data. Each loop is initiated by pressing the button on P1.3. First, a string is stepped through and sent over UART character by character. Individual characters are also sent, as is the value of a variable count, which is set to repeatedly count from 1 through 9. Single digits can be sent as characters easily as they appear in order in ASCII-- the digit 0 is ascii code 48 = 0 + 48, 1 is 49 = 1 + 48, and so on. A multiple digit number could be pushed through using code similar to what we used for the LCM. These are some simple ways of putting data and messages through UART that take very little code space in your device.
The real magic happens in the
tx_byte() and
CCR0_ISR() routines. When
tx_byte is called, it first polls the UART_FG flag and waits for the TXbit to clear. The byte being sent is loaded into a buffer and formatted to include the stop bit. The TXbit is set in UART_FG, an LED turned on to indicate transmission, and the timer is configured to reset at the next interrupt. A character cannot be transmitted until at least bit_time after the previous transmission; since the MSP430 clock is so much faster than the data rate, you could potentially run into a problem of initiating a new character send microseconds after the last, rather than leaving at least one bit_time space between. To prevent this, any present interrupt flag is cleared and the next interrupt is set for one bit_time after the current time. This configuration leaves just slightly more than one bit time between characters. The interrupts run the
CCR0_ISR routine, which sets the next interrupt to be bit_time later, configures TA0.0 to the next bit state for the next interrupt, and shifts the transmission buffer down to pull the next bit to the front.
To see all of this happen, you'll need a serial terminal, such as
RealTerm or
PuTTY. When your terminal is directed to watch the port your LaunchPad is on at the 9600 baud rate, you should see the message display every time you push the button.
Next time we'll use Timer_A to receive data, getting ready for a complete UART transceiver.