Search code examples
embeddeduartmsp430texas-instruments

UART Serial Bridge using MSP430 FR5994


I am trying to create a UART bridge using MSP430. I have a sensor sending strings to the MSP430 which I intend to send to my PC. Additionally, the sensor responds to commands which I intend to send using my PC through the MSP430 bridge. The commands I am sending to the sensor reach it without any flaw. However, the messages sent by the sensor reach the TXBUF of the UART connected to my PC but does not appear on the terminal. On checking the registers I see 0x000A on the TXBUF but it appears to recieve all the chahracters. But nothing is printed.

I am using the following code:

#include <msp430.h> 

unsigned char *msg;

unsigned char i=0 , j=0;

int main(void)

{

 WDTCTL = WDTPW | WDTHOLD;      // stop watchdog timer

// Pin Initialization

P6SEL1 |= BIT1;
P6SEL0 &= ~BIT1;
P6SEL1 |= BIT0;
P6SEL0 &= ~BIT0;
P2SEL1 |= BIT5;
P2SEL0 &= ~BIT5;
P2SEL1 |= BIT6;
P2SEL0 &= ~BIT6;

PM5CTL0 &= ~LOCKLPM5;

// UART Initialization
UCA1CTLW0 |= UCSWRST;
UCA1CTLW0 |= UCSSEL__SMCLK;     // Using 1 MHZ clock
UCA3CTLW0 |= UCSWRST;
UCA3CTLW0 |= UCSSEL__SMCLK;
UCA3BRW = 6;                    // Baud Rate set to 9600
UCA3MCTLW = UCOS16 | UCBRF_8 | 0x2000;
UCA1BRW = 6;
UCA1MCTLW = UCOS16 | UCBRF_8 | 0x2000;

UCA3CTLW0 &= ~UCSWRST;
UCA1CTLW0 &= ~UCSWRST;
UCA3IE |= UCRXIE;
UCA1IE |= UCRXIE;
__enable_interrupt();           // Interrupt enable
while (1)
{}
}

// UART A3 connected to the PC.
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=EUSCI_A3_VECTOR
__interrupt void USCI_A3_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(EUSCI_A3_VECTOR))) USCI_A3_ISR (void)
#else
#error Compiler not supported!
#endif
{
switch(__even_in_range(UCA3IV, USCI_UART_UCTXCPTIFG))
{
    case USCI_NONE: break;
    case USCI_UART_UCRXIFG:
        while(!(UCA3IFG&UCTXIFG));
        UCA1TXBUF = UCA3RXBUF;
        __no_operation();
        break;
    case USCI_UART_UCTXIFG: break;
    case USCI_UART_UCSTTIFG: break;
    case USCI_UART_UCTXCPTIFG: break;
    default: break;
   }
 }


 // UART A1 connected to the sensor.
 #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
 #pragma vector=EUSCI_A1_VECTOR
 __interrupt void USCI_A1_ISR(void)
 #elif defined(__GNUC__)
 void __attribute__ ((interrupt(EUSCI_A1_VECTOR))) USCI_A1_ISR (void)
 #else
 #error Compiler not supported!
 #endif
{
switch(__even_in_range(UCA1IV, USCI_UART_UCTXCPTIFG))
{
    case USCI_NONE: break;
    case USCI_UART_UCRXIFG:
        while(!(UCA1IFG&UCTXIFG));          //Trying to read a string
        {
            *(msg+i) = UCA1RXBUF;
            j = *(msg+i);
            UCA3TXBUF = j;
            i++;
        }
        break;
    case USCI_UART_UCTXIFG: break;
    case USCI_UART_UCSTTIFG: break;
    case USCI_UART_UCTXCPTIFG: break;
    default: break;
}

}

Please help. Thanks in advance.


Solution

  • First, the problems that I see with your listing:

    (p1) Even though the baud rates of both UARTs are the same, your design does not make use of proper (see problem 3 below) buffering in the event that both the PC and the sensor is sending data at the same time. To make matters worse, both your ISRs contain blocking while loops that don't buffer and only waste time until the interrupt flags clears.

    (p2) Your source (shown below) is likely coded in error:

    while(!(UCA1IFG&UCTXIFG));          //Trying to read a string
    {
      *(msg+i) = UCA1RXBUF;
      j = *(msg+i);
      UCA3TXBUF = j;
      i++;
    }
    

    because the body of the while loop is actually empty due to the trailing ";" so the code within the open/closing brackets that follows is not part of the while loop.

    (p3) The pointer variable msg was never initialized. Most likely it points to random heap memory or unused portion of the stack, so the program doesn't crash right away. Eventually, it would because the variable i is incremented but never decremented, so memory is "one time used" by the sensor ISR.

    My suggestions:

    (s1) Declare two buffers, one for data arriving from the PC and the other for data arriving from the sensor. Remove the "unsigned char *msg" and replace with something like this:

    unsigned char pc_data[256];
    unsigned char sensor_data[256];
    

    The size 256 is on purpose to create a poor-mans circular buffer when used with an 8-bit index variable. When the variable reaches 255 and is incremented, it will simply roll back to 0. In this case both i and j as you already declared can be used, but maybe pc_data_index and sensor_data_index would be better understood. You also need two more variables for the size of the buffer, maybe pc_data_count and sensor_data_count. If your procssor cannot afford this much buffer space, then decrease to a modular amount (i.e., 2^BUFSIZE, where BUFSIZE = 32) and use the modular operator when updating the index like this:

    pc_data_index = (pc_data_index + 1) % BUFSIZE;
    

    (s2) Change both ISR routines to process both the USCI_UART_UCRXIFG and USCI_UART_UCTXIFG interrupt events. The ISRs should not contain any loops, simply buffer data or write data out from buffer. Here is an example:

    switch(__even_in_range(UCA1IV, USCI_UART_UCTXCPTIFG))
    {
        case USCI_NONE: break;
        case USCI_UART_UCRXIFG:
            // Byte was received from sensor, so buffer it
            sensor_data[sensor_data_count++] = UCA1RXBUF;
            sensor_data_index = (sensor_data_index + 1) % BUFSIZE;
            // Enable the TX interrupts to send the buffered data
            UCA1IE |= UCTXIE;
            break;
        case USCI_UART_UCTXIFG: 
            // Sensor UART is ready to send next byte
            UCA3TXBUF = sensor_data[sensor_data_index];
            sensor_data_count--;
            // Disable the TX interrupt if no more data to send
            if (sensor_data_count == 0) UCA1IE &= ~UCTXIE;
            break;