SLAU533D September 2013 – April 2017
The following code sample shows the main loop.
while(1)
{
// Receive backchannel UART bytes, send over USB
rxByteCount = bcUartReceiveBytesInBuffer(buf_bcuartToUsb);
if(rxByteCount)
{
cdcSendDataInBackground(buf_bcuartToUsb, rxByteCount, CDC0_INTFNUM, 1000);
//hidSendDataInBackground(buf_bcuartToUsb, rxByteCount, HID0_INTFNUM, 1000);
}
// Receive USB bytes, send over backchannel UART
rxByteCount = cdcReceiveDataInBuffer(buf_usbToBcuart,
sizeof(buf_usbToBcuart),
CDC0_INTFNUM);
/*rxByteCount = hidReceiveDataInBuffer(buf_usbToBcuart,
sizeof(buf_usbToBcuart),
HID0_INTFNUM); */
if(rxByteCount)
{
bcUartSend(buf_usbToBcuart, rxByteCount);
}
}
The main loop does the following actions for both the backchannel UART and USB CDC interface:
When data arrives at the USCI_A1 backchannel UART, it is immediately copied to the receive buffer, bcUartRcvBuf. Then, because this main loop never sleeps, it frequently checks if any bytes are waiting in the receive buffer using bcUartReceiveBytesInBuffer(). If bytes are waiting, the bytes are copied into buf_bcuartToUsb. Then cdcSendDataInBackground() sends them over the application's CDC interface to the host PC.
The same happens in the other direction. When data arrives over the USB CDC interface, the USB hardware module places them into the USB endpoint buffers. The main loop calls cdcReceiveDataInBuffer(), which checks if any bytes have been received; if so, they are copied into buf_usbtoBcuart. Then, bcUartSend() sends them over the backchannel UART.
When data is sent over a UART, communication generally happens quickly, because it is low-level and has essentially no overhead. After a byte is written to the TXBUF register, the time is very brief before TXBUF is ready to send the next byte. Therefore, bcUartSend() does not return until all bytes are sent. Hardware flow control could theoretically keep execution here indefinitely, but it is usually a safe assumption that the eZ-FET lite's MSP430, which is at the other end of this hardware UART connection, will quickly de-assert flow control and be ready to receive data.
Sending data over a USB interface is very different. Multiple communication layers, both hardware and software, exist between the MSP430 application and the bus. There is a higher potential for the host and bus to respond slowly. So, sending data over USB is an interrupt-driven process, involving multiple interrupts over time. Polling in one place until all data is sent is possible but carries some risk of blocking execution.
The USB API provides two construct functions for CDC interfaces. cdcSendDataWaitTilDone() waits until the sending is complete before proceeding to the next line of code. cdcSendDataInBackground() only initiates the sending operation and returns immediately while data is sent in the background behind subsequent lines of code. However, cdcSendDataInBackground() always checks to ensure there is not a previous send operation still open and polls until that operation is complete. Both functions have a retry parameter, so they can only block for a limited amount of time.
This example of USB sending and receiving is sufficient for simple situations, but its handling of events like surprise removal of the USB cable is simplistic. If USB is present, it sends data. If not, then it simply returns an error (which is not even checked) and moves forward. More sophisticated applications may need to pay attention to return codes and consider USB surprise removals. The emulStorageKeyboard example in Section 3.5 demonstrates this.