25 May 2012

Tutorial 18: Finishing the UART Transceiver

This last tutorial in this series will be fairly short, because there's very little to be done! If we combine tutorial 16f and tutorial 17, we're nearly done building a full-duplex UART transceiver.  There are a few little details that need to be explained in order to get it to work properly, however.

  • A bit counter will be needed for both transmitting and receiving, if you're to be able to do them simultaneously.  This takes another 8 bytes of memory, of course.
  • While not necessary for the UART aspect, this code uses interrupts on two pins of P1. Any output on GPIO, even if interrupts are disabled for the pin, will set the corresponding bit of P1IFG on a rising or falling edge, depending on the setting of the corresponding bit in P1IES. Because the two LEDs are being used, any time the LED is turned on we have a rising edge on that GPIO, which sets the bit in P1IFG. The method used in this code to deal with P1 interrupts fails in those cases, since P1IFG would not be exactly P1RX or BTN1.  To fix this, a mask is introduced so the switch statement in the P1 ISR only considers the bits for P1RX and BTN1, ignoring any others set bits of P1IFG.  There is one disadvantage to this particular scheme: if the UART receives a start bit at the same time as a button press occurs, one or both will be ignored (depending on the order and timing of the two events).
  • A new function is introduced to encapsulate sending a string of characters over UART.  This function makes use of a pointer, allowing us to write new values to the variable message. Be aware that other values in RAM could be overwritten if you're not careful.  Also, the function assumes a message length of exactly 16 characters, and will print garbage (or remnants of the last message) if you write a shorter message.  A longer message will be truncated.
  • Because the DCO is executing commands at a faster rate than the transmission, a short delay of bit_time is added to tx_byte().  This delay lets the transmission start before returning control to the main() function. Without it, the code could call tx_byte() again before transmitting actually starts, changing the value of TXBuffer before it has a chance to get sent.  
The transceiver code can be found in uartTXCVRG2231.c.  I've added a few features to demonstrate the full-duplex capability of the transceiver.  The commands 'r', 'g', and 'z' are retained from the receiver tutorial. To this has been added 'c', which prints a continuous message until another command (eg. 'z' to sleep) is sent.  Note that the command can be sent in the midst of a byte transmission and still work, since the receiver is operating on a separate timer/interrupt configuration.  I verified this to be the case using my logic analyzer, and found that a command was correctly received even while starting in the middle of a transmission.

In addition to the 'c' command, another command is set from a button press (using a non-keyboard ASCII character 0xAF), and can be done at any time, showing that the code can also work with physical interface interrupts as well as the serial transmission of commands.  The button will also stop the continual printing of a 'c' command as well, as it also resets the command variable to 'z'.

Remember to use 9600 baud transmission and to configure CCS to build and link to the header file with the UART calibration, as well as not erase the information memory segment containing the UART DCO calibration.

And with that, we've reached the conclusion of what I'll term as the "basic" tutorials for the MSP430.  A big "Thank you!" to all of you; I've received numerous notes of encouragement from many of my readers, and it's been very helpful.  This project and blog have been a great deal of fun, and wer a key addition to the training I received as a Ph.D. student. This isn't the end of the blog, of course.  I'll be back with more advanced tutorials, and, more importantly, examples of scientific measurements and experiments done with the MSP430.  In fact, my next post will start describing a project I've had in mind for a while.  In the mean time, I will start recompiling these first 18 tutorials into a single volume.  I have as-yet to decide just how it would be distributed, and exactly what form it would take. In any case, I hope all of you stay with me and enjoy exploring the MSP430.

6 comments:

Pietro said...

Hi David thank you for this code, I've some problems when sending a character like 'r' from RealTerm, the code return and blocksì on the initial delay of the code, I've tryed anything but nothing seems work.

Pietro from Italy

mat said...

I have played with the Uart transceiver code and tried to use different transmission rates but they didn't work.

I tried to use:
300 bauds - doesn't work
2400 to 9600 - work fine
19200 and higher - don't work

My question is if somebody tried to use different rates than 9600 bauds with uartTXCVRG2231.c code and where something can be wrong?

Unknown said...

I just tried my code here again, and I was able to get reliable operation from 300 baud up to 57600 baud. 115200 reads ok, but the commands get garbled. In theory, the clock rate this is operating with ought to be able to handle it, so I'm not sure why those rates give trouble. Something to look into later!

As for your troubles, you'll need to answer a few questions:

Are you trying to transmit through the LaunchPad USB? That's pre-set to work at 9600 baud, and is not changeable. To use the other rates, you'll need another USB adapter, such as the FTDI chips.

If you are using an FTDI or similar, are you remembering to remove the jumpers between the emulation side and development side for the Tx/Rx pins? That can cause weird behavior at times, and it's best to remove the jumpers if you're not using the LaunchPad USB for the serial communication.

How are you changing the bit_time and halfbit_time values? You'll need to be certain that the numbers you put here are correct. bit_time = 7372800/baud rate, halfbit_time is half of that.

Are you certain that your DCO calibration is correct? Be sure to review that tutorial if you're not.

Hope one or more of these questions is helpful to you!

mat said...

Firstly, thanks for the reply.

I must admit that after reading your answer my question was stupid. The problem was that I used the launchpad's USB, I could have known that as you have written about it in one of the previous tutorials.

But looking for a reason I thought of the precision with which DCO is calibrated to. To make it better I decided to increase the number of cristal oscillations before comparing SMCLK difference with delta in Set_DCO function but the result was the same. Here is changed code anyway:

#define DELTA 22500 // 100 * 7372800 Hz / ( 32768 Hz) - number of oscillations in 100 osc. of cristal

...

void Set_DCO(unsigned int Delta) { // Set DCO to selected frequency
unsigned int Compare, Oldcapture = 0;

unsigned int CristalOsc = 0;

//BCSCTL1 |= DIVA_3; // ACLK = LFXT1CLK/8

BCSCTL1 &= ~DIVA_3; // ACLK = LFXT1CLK

TACCTL0 = CM_1 + CCIS_1 + CAP; // CAP, ACLK
TACTL = TASSEL_2 + MC_2 + TACLR; // SMCLK, cont-mode, clear

while (1) {

CristalOsc = 0;

while(CristalOsc < 100)
{
while (!(CCIFG & TACCTL0)); // Wait until capture occured
TACCTL0 &= ~CCIFG; // Capture occured, clear flag
CristalOsc++;
}


Compare = TACCR0; // Get current captured SMCLK
Compare = Compare - Oldcapture; // SMCLK difference
Oldcapture = TACCR0; // Save current captured SMCLK

if (Delta == Compare)
break; // If equal, leave "while(1)"

else if (Delta < Compare) {
DCOCTL--; // DCO is too fast, slow it down
if (DCOCTL == 0xFF) // Did DCO roll under?
if (BCSCTL1 & 0x0f)
BCSCTL1--; // Select lower RSEL
}
else {
DCOCTL++; // DCO is too slow, speed it up
if (DCOCTL == 0x00) // Did DCO roll over?
if ((BCSCTL1 & 0x0f) != 0x0f)
BCSCTL1++; // Sel higher RSEL
}
}
TACCTL0 = 0; // Stop TACCR0
TACTL = 0; // Stop Timer_A
BCSCTL1 &= ~DIVA_3; // ACLK = LFXT1CLK
} // Set_DCO

Unknown said...

No such thing as a dumb question! Thanks for the code contribution, too. Have you tested it? Have you compared the results you get between this calibration and the one I provided? I'm curious to know if you've gotten any improvement out of it!

Timing calibration is a really tricky thing; a lot of bigger scientific experiments require precision beyond even a temperature-controlled crystal!

mat said...

I have programmed my microcontroller but the result was the same (just for comparison: 8D87), so therbe better if there were even more than 100 cristal oscillations but then there ise is no improvement.

Maybe it could a problem with overflow of TACRR0 register.