This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

LP-MSP430FR2476: Backchannel UART parity bit not working

Part Number: LP-MSP430FR2476


I have a an application that uses eUSCI_A0 at 115200 Bd, 8 data bits, 1 stop bit and odd parity. Some characters do not get through in either direction and it seems this is due to incorrect handling of the parity bit by the built-in USB-serial converter. Using another USB-serial converter (CP2102-based) everything works as expected.

The program sends the string "Initialization done" upon startup. Using picocom on my Linux PC I receive this as "itilitiooe". Investigating further I found that the string "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" is received as "03569cefijloqrtwxABDGHKMNPSUVYZ". Coincidentally all the characters are missing where the parity bit would be '0', I guess this is detected as an invalid stop bit. picocom output and scope trace (at the jumper on the board marked as "TXD>>") are as follows:

picocom v3.1

port is        : /dev/ttyACM1
flowcontrol    : none
baudrate is    : 115200
parity is      : odd
databits are   : 8
stopbits are   : 1
escape is      : C-a
local echo is  : no
noinit is      : no
noreset is     : no
hangup is      : no
nolock is      : no
send_cmd is    : sz -vv
receive_cmd is : rz -vv -E
imap is        : 
omap is        : crlf,
emap is        : crcrlf,delbs,
logfile is     : none
initstring     : none
exit_after is  : not set
exit is        : no

Type [C-a] [C-h] to see available commands
Terminal ready
itilitiooe
          03569cefijloqrtwxABDGHKMNPSUVYZ

Scope shot

With the scope's serial decoder set to parity=none you can see that the characters "I" and "n" which aren't received have an error marker at the end.

The board also doesn't generate the parity bit in the RX direction: Sending "I" with picocom (parity bit set to odd) yields the following scope trace ("RXD<<" on the board; serial decoder set to parity=odd this time!):

Scope trace

As you can see, there is no parity bit generated. The MSP430 program doesn't receive the character (as it should if the parity bit is wrong).

When I don't enable the parity bit on the MSP430 side (not setting/clearing UCPEN of UCA0CTLW0), the communication between the microcontroller and picocom work correctly.

This seems like a bug of the eZ-FET firmware. picocom is directing the driver to enable the parity bit, as evidenced by the fact that it works with a CP2102-based board and also this output from 'stty -F /dev/ttyACM1 -a' (note the "parenb parodd"):

speed 115200 baud; rows 0; columns 0; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W;
lnext = ^V; discard = ^O; min = 1; time = 0;
parenb parodd -cmspar cs8 hupcl -cstopb cread clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany -imaxbel -iutf8
-opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
-isig -icanon -iexten -echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc

Has there been a new firmware release? How would I go about flashing it if there is one?

  • I forgot to mention that I'm using mspdebug, which reports the following when flashing the program:

    MSPDebug version 0.25 - debugging tool for MSP430 MCUs
    Copyright (C) 2009-2017 Daniel Beer <dlbeer@gmail.com>
    This is free software; see the source for copying conditions.  There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    Chip info database from MSP430.dll v3.3.1.4 Copyright (C) 2013 TI, Inc.
    
    Using new (SLAC460L+) API
    MSP430_GetNumberOfUsbIfs
    MSP430_GetNameOfUsbIf
    Found FET: ttyACM0
    MSP430_Initialize: ttyACM0
    Firmware version is 31501001
    MSP430_VCC: 3000 mV
    MSP430_OpenDevice
    MSP430_GetFoundDevice
    Device: MSP430FR2476 (id = 0x0210)
    3 breakpoints available
    MSP430_EEM_Init
    Chip ID data:
      ver_id:         832a
      ver_sub_id:     0000
      revision:       10
      fab:            55
      self:           5555
      config:         10
      fuses:          55
    warning: unknown chip
    Erasing...
    Programming...
    Writing    2 bytes at ffe0 [section: __interrupt_vector_31]...
    Writing    2 bytes at fffe [section: __reset_vector]...
    Writing   87 bytes at 8000 [section: .rodata]...
    Writing    2 bytes at 8058 [section: .upper.data]...
    Writing 1458 bytes at 805a [section: .text]...
    Done, 1551 bytes total
    MSP430_Run
    MSP430_Close

  • How are you initializing the serial port?

    How are you sending and receiving data? For sending are you allowing enough time for the character to clear before sending the third byte?

    Are you using interrupts and serial buffer?

  • As I wrote, it works perfectly fine if I either use a different serial to USB adapter or disable parity bit generation and checking on the MSP430 side. That makes it unlikely that it has anything to do with my code.

    Anyway, I've copied together the relevant parts of my code as a MRE which you should be able to compile and test as is. It works without problems if I replace line 45 with "UCA0CTLW0 |= UCSSEL__SMCLK;" and disable parity checking in picocom, otherwise the behaviour is as described above.

    #include <msp430.h>
    #include <stdbool.h>
    #include <stdlib.h>
    #include <stddef.h>
    #include <stdint.h>
    
    #define MCLK_FREQ_MHZ 8 // MCLK = 8MHz
    #define F_CPU (1000000 * MCLK_FREQ_MHZ)
    #define T_MS (F_CPU / 1000)
    
    #define UART_BAUD_RATE 115200
    #define UART_TX_BUFFER_SIZE 128
    #define UART_TX_BUFFER_INDEX_MASK (UART_TX_BUFFER_SIZE - 1)
    #define UART_RX_BUFFER_SIZE 128
    #define UART_RX_BUFFER_INDEX_MASK (UART_TX_BUFFER_SIZE - 1)
    
    static char uart_tx_buffer[UART_TX_BUFFER_SIZE];
    static size_t uart_tx_buffer_write_index = 0;
    static volatile size_t uart_tx_buffer_read_index = 0;
    static char uart_rx_buffer[UART_RX_BUFFER_SIZE];
    static volatile size_t uart_rx_buffer_write_index = 0;
    static size_t uart_rx_buffer_read_index = 0;
    static volatile bool uart_tx_active = false;
    
    /**
     * P1.4: UCA0TXD (UART to USB)
     * P1.5: UCA0RXD (UART to USB)
     */
    void uart_init_io() {
        P1SEL0 |= BIT4 | BIT5;
    }
    
    void uart_enable() {
        UCA0CTLW0 = UCSWRST;
        // Character length: 8 bit, Stop bits: 1, LSB first, Parity: odd
        UCA0CTLW0 |= UCSSEL__SMCLK | UCPEN;
        UCA0CTLW1 = UCGLIT_3;
        // Configuration for 115200 bd with BRCLK = 8 MHz
        // UCOS16 = 1, UCBRx = 4, UCBRFx = 5, UCBRSx = 0x55
        UCA0BRW = 4;
        UCA0MCTLW = UCOS16 | (5 << 4) | (0x55 << 8);
        UCA0CTLW0 &= ~UCSWRST;
    
        uart_tx_active = false;
    
        UCA0IFG = 0;
        UCA0IE = UCRXIE | UCTXIE;
    }
    
    size_t uart_putc(char c) {
        size_t ret = 0;
        __dint();
        if (uart_tx_active) {
            // Transmission is in progress, character will be taken out of the buffer when the current one has been sent
            size_t read_index = uart_tx_buffer_read_index;
            size_t write_index = uart_tx_buffer_write_index;
            if (((write_index + 1) & UART_TX_BUFFER_INDEX_MASK) == read_index) {
                // Buffer full
                ret = 0;
                goto reenable_interrupts;
            }
            uart_tx_buffer[write_index] = c;
            uart_tx_buffer_write_index = (write_index + 1) & UART_TX_BUFFER_INDEX_MASK;
            ret = 1;
            goto reenable_interrupts;
        } else {
            // Transmission not in progress
            uart_tx_active = true;
            UCA0TXBUF = c;
            ret = 1;
            goto reenable_interrupts;
        }
    reenable_interrupts:
        __eint();
        return ret;
    }
    
    size_t uart_puts(char *str) {
        size_t len = 0;
        while (*str != '\0') {
            size_t transmitted = uart_putc(*str);
            if (transmitted == 0) {
                break;
            }
            str++;
            len++;
        }
        return len;
    }
    
    static size_t uart_tx_buffer_getc(char *c_out) {
        size_t read_index = uart_tx_buffer_read_index;
        if (read_index == uart_tx_buffer_write_index) {
            return 0;
        }
        *c_out = uart_tx_buffer[read_index];
        uart_tx_buffer_read_index = (read_index + 1) & UART_TX_BUFFER_INDEX_MASK;
        return 1;
    }
    
    static size_t uart_rx_buffer_putc(char c) {
        size_t write_index = uart_rx_buffer_write_index;
        if (((write_index + 1) & UART_RX_BUFFER_INDEX_MASK) == uart_rx_buffer_read_index) {
            // Buffer full
            return 0;
        }
        uart_rx_buffer[write_index] = c;
        uart_rx_buffer_write_index = (write_index + 1) & UART_RX_BUFFER_INDEX_MASK;
        return 1;
    }
    
    size_t uart_getc(char *c_out) {
        // No need to __dint() since uart_rx_buffer_write_index is read in a single cycle
        __dint();
        size_t write_index = uart_rx_buffer_write_index;
        __eint();
        size_t read_index = uart_rx_buffer_read_index;
        if (read_index == write_index) {
            return 0;
        }
        *c_out = uart_rx_buffer[read_index];
        uart_rx_buffer_read_index = (read_index + 1) & UART_RX_BUFFER_INDEX_MASK;
        return 1;
    }
    
    __attribute__((interrupt(EUSCI_A0_VECTOR)))
    void isr_USCI_A0(void) {
        uint16_t source = UCA0IV;
        switch (source) {
            case UCIV__UCRXIFG: {
                uint16_t stat = UCA0STATW;
                // Reading from UCAxRXBUF automatically clears any receive error flags
                uint8_t data = UCA0RXBUF;
                // Discard data if any error occurred
                if (!(stat & UCRXERR)) {
                    uart_rx_buffer_putc(data);
                }
    
                __low_power_mode_off_on_exit();
                break;
            }
            case UCIV__UCTXIFG: {
                char next_c;
                size_t l = uart_tx_buffer_getc(&next_c);
                if (l > 0) {
                    UCA0TXBUF = next_c;
                } else {
                    uart_tx_active = false;
                }
                break;
            }
        }
    }
    
    static void Software_Trim()
    {
        unsigned int oldDcoTap = 0xffff;
        unsigned int newDcoTap = 0xffff;
        unsigned int newDcoDelta = 0xffff;
        unsigned int bestDcoDelta = 0xffff;
        unsigned int csCtl0Copy = 0;
        unsigned int csCtl1Copy = 0;
        unsigned int csCtl0Read = 0;
        unsigned int csCtl1Read = 0;
        unsigned int dcoFreqTrim = 3;
        unsigned char endLoop = 0;
    
        do
        {
            CSCTL0 = 0x100;                         // DCO Tap = 256
            do
            {
                CSCTL7 &= ~DCOFFG;                  // Clear DCO fault flag
            }while (CSCTL7 & DCOFFG);               // Test DCO fault flag
    
            __delay_cycles((unsigned int)3000 * MCLK_FREQ_MHZ);// Wait FLL lock status (FLLUNLOCK) to be stable
                                                               // Suggest to wait 24 cycles of divided FLL reference clock
            while((CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1)) && ((CSCTL7 & DCOFFG) == 0));
    
            csCtl0Read = CSCTL0;                   // Read CSCTL0
            csCtl1Read = CSCTL1;                   // Read CSCTL1
    
            oldDcoTap = newDcoTap;                 // Record DCOTAP value of last time
            newDcoTap = csCtl0Read & 0x01ff;       // Get DCOTAP value of this time
            dcoFreqTrim = (csCtl1Read & 0x0070)>>4;// Get DCOFTRIM value
    
            if(newDcoTap < 256)                    // DCOTAP < 256
            {
                newDcoDelta = 256 - newDcoTap;     // Delta value between DCPTAP and 256
                if((oldDcoTap != 0xffff) && (oldDcoTap >= 256)) // DCOTAP cross 256
                    endLoop = 1;                   // Stop while loop
                else
                {
                    dcoFreqTrim--;
                    CSCTL1 = (csCtl1Read & (~DCOFTRIM)) | (dcoFreqTrim<<4);
                }
            }
            else                                   // DCOTAP >= 256
            {
                newDcoDelta = newDcoTap - 256;     // Delta value between DCPTAP and 256
                if(oldDcoTap < 256)                // DCOTAP cross 256
                    endLoop = 1;                   // Stop while loop
                else
                {
                    dcoFreqTrim++;
                    CSCTL1 = (csCtl1Read & (~DCOFTRIM)) | (dcoFreqTrim<<4);
                }
            }
    
            if(newDcoDelta < bestDcoDelta)         // Record DCOTAP closest to 256
            {
                csCtl0Copy = csCtl0Read;
                csCtl1Copy = csCtl1Read;
                bestDcoDelta = newDcoDelta;
            }
    
        }while(endLoop == 0);                      // Poll until endLoop == 1
    
        CSCTL0 = csCtl0Copy;                       // Reload locked DCOTAP
        CSCTL1 = csCtl1Copy;                       // Reload locked DCOFTRIM
        while(CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1)); // Poll until FLL is locked
    }
    
    void init_clocks() {
        __bis_SR_register(SCG0);                // disable FLL
        CSCTL3 = SELREF__REFOCLK;              // Set REFO as FLL reference source
        CSCTL1 = DCOFTRIMEN_1 | DCOFTRIM0 | DCOFTRIM1 | DCORSEL_3;// DCOFTRIM=3, DCO Range = 8MHz
        CSCTL2 = FLLD__1 | (243 & FLLN);                  // DCODIV = 8MHz
        __delay_cycles(3);
        __bic_SR_register(SCG0);                // enable FLL
        Software_Trim();                        // Software Trim to get the best DCOFTRIM value
    
        CSCTL4 = SELMS__DCOCLKDIV | SELA__REFOCLK; // set default REFO(~32768Hz) as ACLK source, DCODIV as MCLK and SMCLK source
    }
    
    static void set_up_io_defaults() {
        // The MSP430FR2xx family user's guide specifies:
        //   - Unused IOs should be configured as outputs and driven low to reduce power consumption
        //   - PxDIR, PxREN, PxOUT and PxIES have undefined values after power up
    
        P1DIR = 0xff;
        P1REN = 0x00;
        P1OUT = 0x00;
        P1IES = 0x00;
    
        P2DIR = 0xff;
        P2REN = 0x00;
        P2OUT = 0x00;
        P2IES = 0x00;
    
        P3DIR = 0xff;
        P3REN = 0x00;
        P3OUT = 0x00;
        P3IES = 0x00;
    
        P4DIR = 0xff;
        P4REN = 0x00;
        P4OUT = 0x00;
        P4IES = 0x00;
    
        P5DIR = 0xff;
        P5REN = 0x00;
        P5OUT = 0x00;
        P5IES = 0x00;
    }
    
    static void enable_port_interrupts() {
        // Clear all PxIFGs to avoid erroneous port interrupts
        P1IFG = 0x00;
        P2IFG = 0x00;
        P3IFG = 0x00;
        P4IFG = 0x00;
        P5IFG = 0x00;
    
        P1IE = 0x00;
        P2IE = 0x00;
        P3IE = 0x00;
        P4IE = 0x00;
        P5IE = 0x00;
    }
    
    int main(void) {
        // Disable the watchdog timer
        WDTCTL = WDTPW | WDTHOLD;
    
        init_clocks();
    
        set_up_io_defaults();
    
        uart_init_io();
    
        // Disable the GPIO power-on default high-impedance mode to activate previously configured port settings
        PM5CTL0 &= ~LOCKLPM5;
    
        uart_enable();
    
        enable_port_interrupts();
    
        __eint();
    
        __delay_cycles(10000);
    
        uart_puts("Initialization done\r\n");
        uart_puts("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n");
    
        while (1) {
            uart_putc('~');
    
            char c;
            size_t len = uart_getc(&c);
    
            if (len > 0) {
                uart_putc(c);
            }
    
            __low_power_mode_0();
        }
    }
    
    

  • It don't support parity by default. You can try the setting bellow to enable it.

    For more details you can refer to this doc:www.ti.com/.../slau647o.pdf

**Attention** This is a public forum