Tinkering TI MSP430F5529

USCI – UART

UART/serial communication in MSP430s is achieved using USCI modules. In MSP430F5529, USCI A modules can be used for both SPI and UART. 

The block diagram of USCI in UART mode is shown above. As can be seen, UART module can be clocked with four clock sources. These clock sources can be fine-tuned for desired baud rates. There are separate buffers and state machines for transmission and reception parts. The UART module can use used for IrDA communication and there are optional internal encoder and decoder for so. Lastly, there are flags for events and errors at various points.

Eltima Soft

Eltima software is a US-based company that make some useful computer communication software interfaces. Eltima has been kind enough to supply me their Serial Port Monitor.

In embedded-system world, we all use some kind of serial port monitor but Eltima’s serial port monitor has the following features that are not usually available in other software:

  • COM data logging in real-time and saving for later uses
  • Multi-port monitoring in one session
  • Five different visualization modes
  • Emulation of port
  • Sniffing of MODBUS RTU and ASCII data
  • Comparison of sessions
  • Easy to use user-interface
  • Changing of serial port parameters on-the-fly
  • Digitally signed drivers
  • Advanced filtering

Of the many features, I particularly liked the logging and MOBUS features. I have no other similar software to compare with it. I highly recommend readers to use it and see for themselves.  

In this tutorial post, I have exclusively used this software.

Code Example

#include "driverlib.h"
#include "delay.h"
#include "lcd.h"
#include "lcd_print.h"

char RX_Data = 0;
char TX_Data = 0;

void clock_init(void);
void GPIO_init(void);
void USCI_UART_init(void);

#pragma vector = USCI_A1_VECTOR
__interrupt void UART_ISR(void)
{
    switch(__even_in_range(UCA1IV, 4))
    {
        case 0x00:      // None
        {
            break;
        }

        case 0x02:      //Data RX
        {
            RX_Data = USCI_A_UART_receiveData(USCI_A1_BASE);

            GPIO_toggleOutputOnPin(GPIO_PORT_P4,
                                   GPIO_PIN7);

            break;
        }

        case 0x04:      //TX Buffer Empty
        {
            break;
        }
    }
}

void main(void)
{
    WDT_A_hold(WDT_A_BASE);

    clock_init();

    USCI_UART_init();

    GPIO_init();

    LCD_init();
    LCD_clear_home();

    LCD_goto(0, 0);
    LCD_putstr("MSP430 USCI UART");
    LCD_goto(0, 1);
    LCD_putstr("TXD: ");
    LCD_goto(10, 1);
    LCD_putstr("RXD: ");

    while(1)
    {
        TX_Data = RX_Data;

        GPIO_toggleOutputOnPin(GPIO_PORT_P1,
                               GPIO_PIN0);

        USCI_A_UART_transmitData(USCI_A1_BASE,
                                 TX_Data);

        while(USCI_A_UART_getInterruptStatus(USCI_A1_BASE,                                                                                                               USCI_A_UART_TRANSMIT_INTERRUPT_FLAG) == 0);

        LCD_goto(5, 1);
        LCD_putchar(TX_Data);

        LCD_goto(15, 1);
        LCD_putchar(RX_Data);

        delay_ms(900);
    };
}

void clock_init(void)
{
    PMM_setVCore(PMM_CORE_LEVEL_3);

    GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P5,
                                               (GPIO_PIN4 | GPIO_PIN2));

    GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P5,
                                                (GPIO_PIN5 | GPIO_PIN3));

    UCS_setExternalClockSource(XT1_FREQ,
                               XT2_FREQ);

    UCS_turnOnXT2(UCS_XT2_DRIVE_4MHZ_8MHZ);

    UCS_turnOnLFXT1(UCS_XT1_DRIVE_3,
                    UCS_XCAP_3);

    UCS_initClockSignal(UCS_FLLREF,
                        UCS_XT2CLK_SELECT,
                        UCS_CLOCK_DIVIDER_4);

    UCS_initFLLSettle(MCLK_KHZ,
                      MCLK_FLLREF_RATIO);

    UCS_initClockSignal(UCS_SMCLK,
                        UCS_XT2CLK_SELECT,
                        UCS_CLOCK_DIVIDER_4);

    UCS_initClockSignal(UCS_ACLK,
                        UCS_XT1CLK_SELECT,
                        UCS_CLOCK_DIVIDER_1);
}

void GPIO_init(void)
{
    GPIO_setAsOutputPin(GPIO_PORT_P1,
                        GPIO_PIN0);

    GPIO_setDriveStrength(GPIO_PORT_P1,
                          GPIO_PIN0,
                          GPIO_FULL_OUTPUT_DRIVE_STRENGTH);

    GPIO_setAsOutputPin(GPIO_PORT_P4,
                        GPIO_PIN7);

    GPIO_setDriveStrength(GPIO_PORT_P4,
                          GPIO_PIN7,
                          GPIO_FULL_OUTPUT_DRIVE_STRENGTH);

    GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P4,
                                               GPIO_PIN5);

    GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P4,
                                                GPIO_PIN4);
}

void USCI_UART_init(void)
{
    USCI_A_UART_initParam UART_Param = {0};

    UART_Param.selectClockSource = USCI_A_UART_CLOCKSOURCE_ACLK;
    UART_Param.clockPrescalar = 3;
    UART_Param.firstModReg = 0;
    UART_Param.secondModReg = 3;
    UART_Param.msborLsbFirst = USCI_A_UART_LSB_FIRST;
    UART_Param.parity = USCI_A_UART_NO_PARITY;
    UART_Param.numberofStopBits = USCI_A_UART_ONE_STOP_BIT;
    UART_Param.uartMode = USCI_A_UART_MODE;
    UART_Param.overSampling = USCI_A_UART_LOW_FREQUENCY_BAUDRATE_GENERATION;

    USCI_A_UART_init(USCI_A1_BASE,
                     &UART_Param);

    USCI_A_UART_resetDormant(USCI_A1_BASE);

    USCI_A_UART_enable(USCI_A1_BASE);

    USCI_A_UART_enableInterrupt(USCI_A1_BASE,
                                USCI_A_UART_RECEIVE_INTERRUPT);

    __enable_interrupt();
}

Hardware Setup

Explanation

Firstly, GPIO pins are initialized as secondary function pins.

GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P4, GPIO_PIN5);

GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P4, GPIO_PIN4);

The following function initializes USCI A1 module in UART mode.

void USCI_UART_init(void)
{
    USCI_A_UART_initParam UART_Param = {0};

    UART_Param.selectClockSource = USCI_A_UART_CLOCKSOURCE_ACLK;
    UART_Param.clockPrescalar = 3;
    UART_Param.firstModReg = 0;
    UART_Param.secondModReg = 3;
    UART_Param.msborLsbFirst = USCI_A_UART_LSB_FIRST;
    UART_Param.parity = USCI_A_UART_NO_PARITY;
    UART_Param.numberofStopBits = USCI_A_UART_ONE_STOP_BIT;
    UART_Param.uartMode = USCI_A_UART_MODE;
    UART_Param.overSampling = USCI_A_UART_LOW_FREQUENCY_BAUDRATE_GENERATION;

    USCI_A_UART_init(USCI_A1_BASE, &UART_Param);
    USCI_A_UART_resetDormant(USCI_A1_BASE);
    USCI_A_UART_enable(USCI_A1_BASE);
    USCI_A_UART_enableInterrupt(USCI_A1_BASE, USCI_A_UART_RECEIVE_INTERRUPT);
    __enable_interrupt();
}

In serial communication, baud rate selection is very important because two serial communication devices must negotiate under a common data transaction rate or else data is unrecognized or treated as garbage. Since baud rate clock-sensitive, we have to carefully chose clock and prescalar settings and this is reflected in the first setting values – clockPrescalar, firstModReg and secondModReg. Now let’s see how we came to these values. Refer to section 36.3.13 Typical Baud Rates and Errors of MSP430x5xx and MSP430x6xx Family User’s Guide.

We have used ACLK as the clock source for UART. ACLK happens to be derived from XT1 oscillator and so its frequency is 32.768 kHz.

UCS_initClockSignal(UCS_ACLK, UCS_XT1CLK_SELECT, UCS_CLOCK_DIVIDER_1);

Therefore, to achieve a baud rate of 9600, we have to put the values as shown in the highlighted section of the table above. With these settings the TX-RX errors are high but still we will go with these just to see that even after such high error rates, the serial communication remains smooth and steady.

The rest of the settings describe other properties of our MSP430’s serial port. These include number of stop bits, parity, USCI mode and others.

We will be using reception interrupt and so it is also needed to be enabled.

USCI_A_UART_enableInterrupt(USCI_A1_BASE, USCI_A_UART_RECEIVE_INTERRUPT);

Sending data is very easy. It is just like what we did in SPI examples. We write the data to be sent and wait for the transmit buffer to get empty.

USCI_A_UART_transmitData(USCI_A1_BASE, TX_Data);
while(USCI_A_UART_getInterruptStatus(USCI_A1_BASE, 
                                     USCI_A_UART_TRANSMIT_INTERRUPT_FLAG) == 0);

Data is received in the interrupt. It is a very simple process. Once a reception interrupt is triggered, we just have to read the reception buffer. Interrupt flag is automatically cleared.

#pragma vector = USCI_A1_VECTOR
__interrupt void UART_ISR(void)
{
    switch(__even_in_range(UCA1IV, 4))
    {
        case 0x00:      // None
        {
            break;
        }

        case 0x02:      //Data RX
        {
            RX_Data = USCI_A_UART_receiveData(USCI_A1_BASE);

            GPIO_toggleOutputOnPin(GPIO_PORT_P4, GPIO_PIN7);

            break;
        }

        case 0x04:      //TX Buffer Empty
        {
            break;
        }
    }
}

We chose interrupt method for reception because we don’t know for sure when a data will be sent to the MSP430 and as such so we don’t want to miss any data sent to the micro.

The demo here simply echoes any character data that has been sent to it from a host PC.

Demo

Pages: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

Related Posts

14 comments

Leave a Reply to Vasile Guta-Ciucur Cancel reply

Your email address will not be published. Required fields are marked *