Part Number:MSP432P401R
I am having problems with the MSP432 eUSCI_A UART losing the UCTXIFG flag in the UCAxIFG register causing the transmitter to hang. The problem only occurs when simultaneously transmitting and receiving characters. If I am not receiving, the transmitter works fine, but when it starts receiving characters, the UCTXIFG flag stops coming on and the transmitter interrupt does not occur. (All this was discovered while trying to implement a data transmission protocol between the micro and a PC that I have implemented without problems many times over the years using various MSP430s and Tiva chips.) I have managed to boil this down to a very small demo program that reliably reproduces the problem. In this program, every time the EUSCI_A_UART_TRANSMIT_INTERRUPT_FLAG interrupt occurs, I transmit a 'c'. Every time a EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG interrupt occurs, I receive the character and ignore it. I connect to HyperTerminal on the PC and receive a stream of 'c' characters which continues as long as I don't try to type characters back to the micro. When I type characters back to the micro (sometimes takes a lot of characters), it will eventually stop sending. When I look at what the program is doing, I find that the UCTXIFG is off. It stays off and the EUSCI_A_UART_TRANSMIT_INTERRUPT_FLAG interrupts are not happening. It is receiving characters OK, though.
I am using the red MSP432 Launchpad with a rev C MSP432 and version 1.50.00.12 of the SDK driverlib.
To reproduce the problem:
1. Compile and link the program using SDK 1.50.00.12 driverlib, and download it to the MSP432 Launchpad.
2. Open HyperTerminal on the PC and establish a connection at 921600 baud, 8 bit, no parity, no flow control to the User UART port of the Launchpad.
3. Start the program on the MSP432. You should see a continuous stream of 'c' characters on HyperTerminal.
4. Type fast on the keyboard to HyperTerminal. You may have to bang on the keyboard for a few seconds. Eventually, the 'c' characters will stop.
5. Look back at the MSP432 program and you will see that UCTXIFG is off and not coming back on.
The program also contains code for a workaround that I have been using. The workaround is compiled if you uncomment the "#define KLUDGEY_FIX" statement. The code then tries to use the EUSCI_A_UART_TRANSMIT_COMPLETE_INTERRUPT to detect the loss of UCTXIFG and output to TXBUF anyway. This causes everything to recover and EUSCI_A_UART_TRANSMIT_INTERRUPT_FLAG interrupts start happening again. Using this code, I have been unable to make the transmitter fail using HyperTerminal.
I have tried the code at slower baud rates. I am still able to make it fail but it takes more typing on HyperTerminal.
This bug seems to be similar but not identical with the many eUSCI bugs reported for the Rev C and Rev D processors in the errata sheet (SLAZ610J). While my workaround seems to be working, it would be nice to have a real fix for the problem since the device I am working on will eventually be a medical device and must meet strict reliability standards.
/*
For baud rate 9600 and SMCLK = 24000000, set BRDIV=156, UCxBRF=4, UCxBRS=0
For baud rate 115200 and SMCLK = 24000000, set BRDIV=13, UCxBRF=0, UCxBRS=37
For baud rate 230400 and SMCLK = 24000000, set BRDIV=6, UCxBRF=8, UCxBRS=32
For baud rate 460800 and SMCLK = 24000000, set BRDIV=3, UCxBRF=4, UCxBRS=2
For baud rate 921600 and SMCLK = 24000000, set BRDIV=1, UCxBRF=10, UCxBRS=0
*/
/* DriverLib Includes */
#include "driverlib.h"
/* Standard Includes */
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#define LAUNCHPAD_ONLY
// RGB LED pins on P2 on the LAUNCHPAD
#define P2_RED_LED_H GPIO_PIN0
#define P2_BLUE_LED_H GPIO_PIN2
#define P2_GREEN_LED_H GPIO_PIN1
#define P2_ALL_LEDS_H (P2_RED_LED_H|P2_BLUE_LED_H|P2_GREEN_LED_H)
// #define KLUDGEY_FIX to implement a workaround for the bug where the eUSCI_A unit loses the UCTXIFG flag in the UCAxIFG register.
//#define KLUDGEY_FIX
// Set baud rate to 921600 assuming SMCLK = 24000000
#define A0_BRDIV 1
#define A0_UCxBRF 10
#define A0_UCxBRS 0
const eUSCI_UART_Config uartConfig =
{
EUSCI_A_UART_CLOCKSOURCE_SMCLK, // SMCLK Clock Source
A0_BRDIV, // BRDIV
A0_UCxBRF, // UCxBRF
A0_UCxBRS, // UCxBRS
EUSCI_A_UART_NO_PARITY, // No Parity
EUSCI_A_UART_LSB_FIRST, // LSB First
EUSCI_A_UART_ONE_STOP_BIT, // One stop bit
EUSCI_A_UART_MODE, // UART mode
EUSCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION // Oversampling
};
void InitUart ()
{
/* Selecting P1.2 and P1.3 in UART mode */
MAP_GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1, GPIO_PIN2 | GPIO_PIN3, GPIO_PRIMARY_MODULE_FUNCTION);
/* Configuring UART Module */
MAP_UART_initModule(EUSCI_A0_BASE, &uartConfig);
/* Enable UART module */
MAP_UART_enableModule(EUSCI_A0_BASE);
/* Enabling interrupts */
#ifdef KLUDGEY_FIX
MAP_UART_enableInterrupt(EUSCI_A0_BASE, EUSCI_A_UART_RECEIVE_INTERRUPT | EUSCI_A_UART_TRANSMIT_INTERRUPT | EUSCI_A_UART_TRANSMIT_COMPLETE_INTERRUPT);
#else
MAP_UART_enableInterrupt(EUSCI_A0_BASE, EUSCI_A_UART_RECEIVE_INTERRUPT | EUSCI_A_UART_TRANSMIT_INTERRUPT);
#endif
MAP_Interrupt_enableInterrupt(INT_EUSCIA0);
// Start transmitting characters
MAP_UART_transmitData(EUSCI_A0_BASE, (uint_fast8_t) 'c');
}
// EUSCI A0 UART receive ISR - Echoes data back to PC host
void EUSCIA0_IRQHandler(void)
{
uint32_t status = MAP_UART_getEnabledInterruptStatus(EUSCI_A0_BASE);
MAP_UART_clearInterruptFlag(EUSCI_A0_BASE, status);
if(status & EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG)
{
// Throw away received data
MAP_UART_receiveData(EUSCI_A0_BASE);
}
if (status & EUSCI_A_UART_TRANSMIT_INTERRUPT_FLAG)
{
// Transmit the next byte here
MAP_UART_transmitData(EUSCI_A0_BASE, (uint_fast8_t) 'c');
}
#ifdef KLUDGEY_FIX
else
{
// Attempt to mitigate the problem where the UCTXIFG flag gets lost. This seems to happen when you transmit at high baud rates
// while also receiving bytes. The bug seems similar to some of the USCI errata items such as USC144, although in this case we
// are using a clock source sychronized to MCLK. In this fix, we are trying to use UCTXCPTIFG (transmit complete) to detect when
// we can send the next character after we lose the UCTXIFG flag. Errata item USCI42 suggests that this might also have a problem,
// but tests so far indicate that this fix works.
uint_fast8_t intEnabled;
// UART_getEnabledInterruptStatus will only return EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG or EUSCI_A_UART_TRANSMIT_INTERRUPT_FLAG so
// we have to get EUSCI_A_UART_TRANSMIT_COMPLETE_INTERRUPT_FLAG this way .
status = UART_getInterruptStatus(EUSCI_A0_BASE, EUSCI_A_UART_TRANSMIT_COMPLETE_INTERRUPT_FLAG);
intEnabled = EUSCI_A_CMSIS(EUSCI_A0_BASE)->IE;
if (intEnabled & status)
{
UART_clearInterruptFlag(EUSCI_A0_BASE, EUSCI_A_UART_TRANSMIT_COMPLETE_INTERRUPT_FLAG);
// Should not be possible to have transmit complete without TXIFG also set, but we get that when the transmitter hangs
if (!BITBAND_PERI(EUSCI_A_CMSIS(EUSCI_A0_BASE)->IFG, EUSCI_A_IFG_TXIFG_OFS))
EUSCI_A_CMSIS(EUSCI_A0_BASE)->TXBUF = 'X'; // Try to restart the transmitter
}
}
#endif // KLUDGEY_FIX
}
int main(void)
{
/* Halting the Watchdog */
MAP_WDT_A_holdTimer();
// Set up core voltage and flash wait states to support running at 48 MHz
MAP_PCM_setPowerState(PCM_AM_DCDC_VCORE1);
MAP_FlashCtl_setWaitState(FLASH_BANK0, 1);
MAP_FlashCtl_setWaitState(FLASH_BANK1, 1);
/* Configuring GPIO as an output */
MAP_GPIO_setAsOutputPin(GPIO_PORT_P1, GPIO_PIN0);
#ifdef LAUNCHPAD_ONLY // Set up the RGB LED output pins
MAP_GPIO_setAsOutputPin(GPIO_PORT_P2, P2_ALL_LEDS_H);
MAP_GPIO_setOutputLowOnPin(GPIO_PORT_P2, P2_ALL_LEDS_H);
#endif
// Set ACLK to VLOCLK (9.4 kHz)
MAP_CS_initClockSignal(CS_ACLK, CS_VLOCLK_SELECT, CS_CLOCK_DIVIDER_1);
/* Set DCO to 48MHz */
MAP_CS_setDCOCenteredFrequency(CS_DCO_FREQUENCY_48);
// Use DCO for MCLK and SMCLK
MAP_CS_initClockSignal(CS_MCLK, CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_1);
MAP_CS_initClockSignal(CS_SMCLK, CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_2);
/* Enabling the FPU with stacking enabled (for use within ISR) */
MAP_FPU_enableModule();
MAP_FPU_enableLazyStacking();
// Initialize the UART and transmit the first character
InitUart();
/* Enabling MASTER interrupts */
MAP_Interrupt_enableMaster();
// All the action is now at interrupt level
while (true)
MAP_PCM_gotoLPM0();
}