I wanted to create a 9600 asynchronous serial connection with my TI LaunchPad.
I started my development from the source code of the example program that I found in it (temp. sensor with serial connection and leds modulation) but I wanted three more things: a faster link (9600 which is four times greater), a mspgcc4 compatibility (the free-software compiler that I’m using) and a CPU working at 16 MHz (instead of 1 MHz of the example).
The task is not very difficult and I carried it in a completely free environment (Ubuntu + MSPGCC + mspDebug).
Introduction
First of all, the asynchronous serial communication is a way to send and receive data over a link, without the need of a clock signal. In its simplest implementation we have the transmission of 10 symbols: a starting 0 (start bit), eight bits of data and an ending 1 (stop bit).
From this we can add a parity bit (and other things…) to aim to a more strong/error-free connection but that’s not our case of study.
To allow a communication between two units we have to set the same “communication speed” parameter, which impose the time duration of each bit. Its unit is the BAUD: the maximal number of symbols each second.
For example 9600 BAUD means that every symbol needs to be 1/9600 seconds long (otherwise we cannot meet the desired top speed).
void Transmission(void)
As in the software example of TI, the serial transmission is made by putting the 8 bits to send in a variable (TXByte) and then calling the function void transmission().
This function prepares the transmission adding a Start Bit and a Stop Bit to the data that we want to send. It also actives the interrupt of the TimerA, the timing device inside the MSP430 that is used to create a good and compliant serial signal.
With Bitime we set the number of cycles of the timer between each interruption. This is done by adding this value to the TimerA configuration register CCR0.
The main program is blocked inside this function until the transmission is completed.
interrupt(TIMERA0_VECTOR) uart(void)
This function is responsible for changing the output of the UART according to the data bit to send. It checks the number of the bits already sent (BitCnt) and if the communication is not terminated.
Configurations
As stated before, we set up the internal oscillator at 16 MHz (=MSCKL = SMCLK).
The TimerA is working in continuous mode, incremented at the frequency of SMCLK divided by 8 (Timer Clock).
Bitime is derived from the Timer Clock speed and the Communication speed.
The relation is:
BAUD = Timer Clock / Bitime
In the example we have Bitime = 52, which is ( Timer Clock / BAUD ) = ( (1 MHz / 8 ) / 2400).
In our case I changed both the Clock and the BAUD, so the new Bitime is (16 MHz / 8 ) / 9600 = 209.
-
–> Actually 209 didn’t worked… I changed to 250, but I don’t know why…
Source code
#include
#include
// leds
#define LED_R BIT0
#define LED_G BIT6
// software UART
#define TXD BIT1
#define RXD BIT2
// BAUD * Bitime = TimerA adding frequency
// @1Mhz Bitime = ( 1MHz / 8 ) / 2400 = 52
// @16Mhz Bitime = ( 16MHz / 8 ) / 9600 = 209
#define Bitime 250
unsigned int TXByte; // byte to send
unsigned char BitCnt; // bit counter
void initClocks(void);
void initIO(void);
void initTimerUart(void);
void Transmit();
static void __inline__ delay(register unsigned int n);
int main(void)
{
WDTCTL = WDTPW|WDTHOLD; // Stop watchdog timer
initClocks();
initIO();
initTimerUart();
__bis_SR_register(GIE); // General Interrupts Enable
while(1){
TXByte = 0xAA; // let's send 10101010
Transmit();
TXByte = 0x48; // let's send H
Transmit();
TXByte = 0x49; // let's send I
Transmit();
delay(30000);
}
return 0;
}
void initTimerUart(){
CCTL0 = OUT; // TXD Idle as Mark (OUT = 1)
TACTL = TASSEL_2 + MC_2 + ID_3; // SMCLK/8, continuos mode
}
interrupt(TIMERA0_VECTOR) uart(void)
{
CCR0 += Bitime; // Add Transmission Offset to CCR0
if (TACCTL0 & CCIS0){ // TX on CCI0B?
if ( BitCnt == 0){
TACCTL0 &= ~ CCIE; // All bits TXed, disable interrupt
} else {
CCTL0 |= OUTMOD2; // TX Space
if (TXByte & 0x01) CCTL0 &= ~ OUTMOD2; // TX Mark
TXByte = TXByte >> 1; // next bit
BitCnt --;
}
}
}
// Function Transmits Character from TXByte (Mark Bit = "1" and Space Bit = "0")
void Transmit()
{
BitCnt = 0xA; // 10 bits (8 data + start bit + stop bit)
if (CCR0 != TAR) CCR0 = TAR; // synch CCR0 to TAR
CCR0 += Bitime; // Some time till first bit
TXByte |= 0x100; // Add mark stop bit to TXByte
TXByte = TXByte << 1; // Add space start bit
CCTL0 = CCIS0 + OUTMOD0 + CCIE; // TXD = mark = idle
while ( CCTL0 & CCIE ) delay(5); // Wait for TX completion
}
void initClocks(void)
{
BCSCTL1 = CALBC1_16MHZ; // Set range (MSCKL = SMCLK = 16MHz)
DCOCTL = CALDCO_16MHZ;
}
void initIO(void)
{
P1SEL |= TXD + RXD; // use pins for the UART
P1DIR |= TXD + LED_R; // set TXD as output
P1OUT = LED_R; // all outputs to 0, except Red led
}
// Delay Routine from mspgcc help file
static void __inline__ delay(register unsigned int n)
{
__asm__ __volatile__ (
"1: \n"
" dec %[n] \n"
" jne 1b \n"
: [n] "+r"(n));
}
