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.

Problem in USB-CDC-UART bidirectional bridge

Other Parts Discussed in Thread: MSP430F5508

Hi,

I am trying to build a FT232R like USB-UART IC with MSP430F5508. What it does is, collect data from the UART RXD pin on MSP430F5508, relay data to PC via USB-CDC; and also send data received from USB-CDC to the UART TXD pin on MSP430F5508. Something like the following d

[Data Acquisition Module] <-UART-> [MSP430F5508] <-USB-> [PC]

So far I was able to get data flow from Data Acquisiton Module --> UART --> USB --> PC work. But not the other way around.If I try to send some data to MSP430F5508 from a terminal program, the whole MSP430 program crashes and becomes non-responsive.

The following is the code I put together, largely modified from USB-CDC lookback example

#include <string.h>

#include "driverlib.h"

#include "USB_config/descriptors.h"
#include "USB_API/USB_Common/device.h"
#include "USB_API/USB_Common/usb.h"                 // USB-specific functions
#include "USB_API/USB_CDC_API/UsbCdc.h"
#include "USB_app/usbConstructs.h"

#include "hal.h"

// Global flags set by events
volatile uint8_t bCDCDataReceived_event = FALSE;  // Flag set by event handler to indicate data has been received into USB buffer

#define BUFFER_SIZE 512
char dataBuffer[BUFFER_SIZE] = "";
char nl[2] = "\n";
uint16_t count;                    

//Select Baud rate of UART
//
//*****************************************************************************
#define BAUD_RATE                               9600
//*****************************************************************************

uint8_t uartReceivedData[2];

char temp = 'R';

uint8_t ReceiveError = 0, SendError = 0;

/*----------------------------------------------------------------------------+
 | Main Routine                                                                |
 +----------------------------------------------------------------------------*/
void main (void)
{
    WDT_A_hold(WDT_A_BASE); // Stop watchdog timer

    // Minimum Vcore setting required for the USB API is PMM_CORE_LEVEL_2 .
    PMM_setVCore(PMM_CORE_LEVEL_2);

    USBHAL_initPorts();           // Config GPIOS for low-power (output low)
    USBHAL_initClocks(8000000);   // Config clocks. MCLK=SMCLK=FLL=8MHz; ACLK=REFO=32kHz
    USB_setup(TRUE, TRUE); // Init USB & events; if a host is present, connect

    // Configure UART module reset pin P4.3 and set it high
    GPIO_setAsOutputPin(GPIO_PORT_P4, GPIO_PIN3);
    GPIO_setOutputHighOnPin(GPIO_PORT_P4, GPIO_PIN3);

    // COnfigure UART module CTS pin P4.2 as input
    GPIO_setAsInputPin(GPIO_PORT_P4, GPIO_PIN2);

    //P3.4,5 = USCI_A1 TXD/RXD
    GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P4, GPIO_PIN4 + GPIO_PIN5);

    //Baudrate = 9600, clock freq = 8MHz
    //UCBRx = 109, UCBRFx = 0, UCBRSx = 2, UCOS16 = 0
    USCI_A_UART_initParam param = {0};
    param.selectClockSource = USCI_A_UART_CLOCKSOURCE_ACLK;
    param.clockPrescalar = 3;
    param.firstModReg = 0;
    param.secondModReg = 3;
    param.parity = USCI_A_UART_NO_PARITY;
    param.msborLsbFirst = USCI_A_UART_LSB_FIRST;
    param.numberofStopBits = USCI_A_UART_ONE_STOP_BIT;
    param.uartMode = USCI_A_UART_MODE;
    param.overSampling = USCI_A_UART_LOW_FREQUENCY_BAUDRATE_GENERATION;

    if(STATUS_FAIL == USCI_A_UART_init(USCI_A1_BASE, &param))
    {
        return;
    }

    //Enable UART module for operation
    USCI_A_UART_enable(USCI_A1_BASE);

    //Enable Receive Interrupt
    USCI_A_UART_clearInterrupt(USCI_A1_BASE,
                               USCI_A_UART_RECEIVE_INTERRUPT);
    USCI_A_UART_enableInterrupt(USCI_A1_BASE,
                                USCI_A_UART_RECEIVE_INTERRUPT);
    uint16_t i = 0;

    __enable_interrupt();  // Enable interrupts globally

    while (1)
    {
        // uint8_t ReceiveError = 0, SendError = 0;
        uint16_t count;
        
        // Check the USB state and directly main loop accordingly
        switch (USB_getConnectionState())
        {
            // This case is executed while your device is enumerated on the USB host
            case ST_ENUM_ACTIVE:
            
                // Sleep if there are no bytes to process.
                __disable_interrupt();
                if (!USBCDC_getBytesInUSBBuffer(CDC0_INTFNUM)) {
                
                    // Enter LPM0 until awakened by an event handler
                    __bis_SR_register(LPM0_bits + GIE);
                }

                __enable_interrupt();

                // Exit LPM because of a data-receive event, and
                // fetch the received data
                if (bCDCDataReceived_event){
                
                    // Clear flag early -- just in case execution breaks
                    // below because of an error
                    bCDCDataReceived_event = FALSE;

                    // Count has the number of bytes received into dataBuffer
                 count = USBCDC_receiveDataInBuffer((uint8_t*)dataBuffer, BUFFER_SIZE, CDC0_INTFNUM);

                    // Send the bytes received from USB into UART port
                    for (i = 0; i < count; i++) {
                    	// Wait for UART module CTS pin ready to take new input first
                    	// while(GPIO_getInputPinValue(GPIO_PORT_P4, GPIO_PIN2));
                        USCI_A_UART_transmitData(USCI_A1_BASE, temp);
                    }
                }
                break;
                
            // These cases are executed while your device is disconnected from
            // the host (meaning, not enumerated); enumerated but suspended
            // by the host, or connected to a powered hub without a USB host
            // present.
            case ST_PHYS_DISCONNECTED:
            case ST_ENUM_SUSPENDED:
            case ST_PHYS_CONNECTED_NOENUM_SUSP:
                __bis_SR_register(LPM3_bits + GIE);
                _NOP();
                break;

            // The default is executed for the momentary state
            // ST_ENUM_IN_PROGRESS.  Usually, this state only last a few
            // seconds.  Be sure not to enter LPM3 in this state; USB
            // communication is taking place here, and therefore the mode must
            // be LPM0 or active-CPU.
            case ST_ENUM_IN_PROGRESS:
            default:;
        }

        if (ReceiveError || SendError){
            // TO DO: User can place code here to handle error
        }
    }  //while(1)
}                               // main()

/*  
 * ======== UNMI_ISR ========
 */
#if defined(__TI_COMPILER_VERSION__) || (__IAR_SYSTEMS_ICC__)
#pragma vector = UNMI_VECTOR
__interrupt void UNMI_ISR (void)
#elif defined(__GNUC__) && (__MSP430__)
void __attribute__ ((interrupt(UNMI_VECTOR))) UNMI_ISR (void)
#else
#error Compiler not found!
#endif
{
    switch (__even_in_range(SYSUNIV, SYSUNIV_BUSIFG ))
    {
        case SYSUNIV_NONE:
            __no_operation();
            break;
        case SYSUNIV_NMIIFG:
            __no_operation();
            break;
        case SYSUNIV_OFIFG:
            UCS_clearFaultFlag(UCS_XT2OFFG);
            UCS_clearFaultFlag(UCS_DCOFFG);
            SFR_clearInterrupt(SFR_OSCILLATOR_FAULT_INTERRUPT);
            break;
        case SYSUNIV_ACCVIFG:
            __no_operation();
            break;
        case SYSUNIV_BUSIFG:
            // If the CPU accesses USB memory while the USB module is
            // suspended, a "bus error" can occur.  This generates an NMI.  If
            // USB is automatically disconnecting in your software, set a
            // breakpoint here and see if execution hits it.  See the
            // Programmer's Guide for more information.
            SYSBERRIV = 0; // clear bus error flag
            USB_disable(); // Disable
    }
}

//******************************************************************************
//
//	USCI_A1 interrupt vector service routine: transfer whatever received byte directly to USB
//	TO DO: add a ring buffer here, but probably not necessary. USB is much faster than UART
//
//******************************************************************************
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=USCI_A1_VECTOR
__interrupt
#elif defined(__GNUC__)
__attribute__((interrupt(USCI_A1_VECTOR)))
#endif
void USCI_A1_ISR(void)
{
    switch(__even_in_range(UCA1IV,4))
    {
    //Vector 2 - RXIFG
    case 2:
   	uartReceivedData[0] = USCI_A_UART_receiveData(USCI_A1_BASE);
        // Send received byte to USB port
        if (USBCDC_sendDataInBackground((uint8_t*)uartReceivedData, 1, CDC0_INTFNUM, 1)){
            // Exit if something went wrong.
            SendError = 0x01;
            break;
        }
        break;
    default: break;
    }
}

//Released_Version_5_00_01

The code that relays data from USB-CDC to UART are line 110 and 125. If data is received from USB, then the data will be first moved to a buffer, then written to UART port byte by byte

                if (bCDCDataReceived_event){
                
                    // Clear flag early -- just in case execution breaks
                    // below because of an error
                    bCDCDataReceived_event = FALSE;

                    // Count has the number of bytes received into dataBuffer
                    count = USBCDC_receiveDataInBuffer((uint8_t*)dataBuffer, BUFFER_SIZE, CDC0_INTFNUM);

                    // Send the bytes received from USB into UART port
                    for (i = 0; i < count; i++) {
                    	// Wait for UART module CTS pin ready to take new input first
                    	// while(GPIO_getInputPinValue(GPIO_PORT_P4, GPIO_PIN2));
                        USCI_A_UART_transmitData(USCI_A1_BASE, temp);
                    }
                }

As  mentioned in the another post, sending data to USB in ISR probably is not a good idea. But this seems not a problem in my case. My problem is, I cannot relay data from USB to UART. 

One thing I can think of is, USB is much faster than UART, so I included a CTS pin, but that doesn't help.

The other possible problem is in timing of UART. The curreent setting is, UART clock is sourced from ACLK(REO, 32768Hz). UCBRx, UCBRFx, and UCOS16 values were calculated by the tool in CCS. I also tried to source UART clock from SMCK(8MHz), and that didn't help. Although if I simply let UART pin lookback, that seems work just fine.

What else am I doing wrong here? Please advise. And thanks in advance.

**Attention** This is a public forum