I am attempting to program a sort of bridge between UCA0 and UCA1 in UART mode on the MSP430F5529 using the MSP-EXP430F5529LP development board. I am using an interrupt for receiving data but for transmitting I am just loading the UCAxTXBUF directly (no TX interrupts are enabled). I say UCAxTXBUF because the bridge should be bidirectional, so it can either be UCA0TXBUF or UCA1TXBUF.
My interrupt service routine is based on TI's UART examples available in the MSP430Ware packages. The examples use a busy wait inside the ISR to wait for the TX buffer to become available (UCTXIFG goes high) before loading UCAxTXBUF with the data. I hate the idea of putting a busy-wait inside an interrupt but for the sake of argument I'm following the examples. Anyway, the problem is that, while UCTXIFG is high when I'm preparing to transmit the first byte, as soon as I load the UCAxTXBUF with the data the UCTXIFG bit clears (as expected), but then is never set again. This suggests that for reason unknown, the data is not being transmitted so UCBUSY remains set and UCTXIFG doesn't go high indicating the buffer is ready for more data. What might be the issue? The ISRs for UCA0 and UCA1 are shown below:
UCA0 ISR:
#pragma vector = USCI_A0_VECTOR
__interrupt void USCI_A0_ISR(void) {
__no_operation();
switch(__even_in_range(UCA0IV,4)) {
case 0: // No interrupt
break;
case 2: // RX interrupt
__disable_interrupt(); // Disable interrupts
UCA0_rxByte = UCA0RXBUF;
while (!(UCA1IFG & UCTXIFG)); // Wait for UCA1 TX buffer to be ready
// Bad practice - should not have busy
// wait in ISR
UCA1TXBUF = UCA0_rxByte; // Pass through to 485
UCA0_rxString[UCA0_idx] = UCA0_rxByte; // Save current byte in buffer
UCA0_idx++;
TA0_clearTimer(); // Restart timer
__enable_interrupt(); // Re-enable interrupts
break;
case 4: // TX interrupt
break;
}
}
UCA1 ISR:
#pragma vector=USCI_A1_VECTOR // UCA0 RX/TX interrupt vector
__interrupt void USCI_A1_ISR(void) {
switch(__even_in_range(UCA1IV,4)) {
case 0: // No interrupt
break;
case 2: // RX interrupt
__disable_interrupt(); // Disable interrupts
UCA1_rxByte = UCA1RXBUF; // Get current byte
UCA1_rxString[UCA1_idx] = UCA1_rxByte; // Save current byte in buffer
while (!(UCA0IFG & UCTXIFG)); // Wait for UCA0 TX buffer to be ready
// Bad practice - should not have busy
// wait in ISR
UCA0TXBUF = UCA1_rxByte; // Pass through to TTL
UCA1_idx++;
TA0_clearTimer(); // Restart timer
__enable_interrupt(); // Re-enable interrupts
break;
case 4: // TX interrupt
break;
}
}
The __disable_interrupt()
and __enable_interrupt()
lines were added in after this problem first appeared, so I am fairly confident they are not the problem.
When I attempt to transmit the first character from UCA0 the test while(!(UCA1IFG & UCTXIFG))
falls straight through, as I expect it to since the UCA1TXBUF should be empty and ready for data. However, when I attempt to transmit the second character from UCA0 the program hangs at that same test. Checking the registers in CCS I see that UCBUSY is set and UCTXIFG is still clear.
What could cause this sort of behavior? I have found multiple questions asked here and on other sites that describe a similar issue, but they all seem to be for I2C or SPI, and the solutions do not seem to apply to my current problem with the UART.
The actual ISR from TI's UCA0 example is shown below. The added bits above are modifications to variables I use for my own purposes.
switch(__even_in_range(UCA0IV,4)) {
case 0:break; // Vector 0 - no interrupt
case 2: // Vector 2 - RXIFG
while (!(UCA0IFG&UCTXIFG)); // USCI_A0 TX buffer ready?
UCA0TXBUF = UCA0RXBUF; // TX -> RXed character
break;
case 4:break; // Vector 4 - TXIFG
default: break;
}
UPDATE: For sake of completeness, here is the initialization code for UCA1 (effectively identical to the initialization for UCA0):
void UCA1_init(uint32_t smclk, uint32_t baudrate) {
P4SEL |= BIT4 + BIT5; // Select alternate function for P4.4, 4.5 (UCA1 TXD, RXD)
UCA1CTL1 |= UCSWRST; // Reset USCI state machine
UCA0CTL1 |= UCSSEL_2; // Set clock to SMCLK
UCA1BR0 = 52; // Low byte of clock prescaler (9600 bps)
UCA1BR1 = 0; // High byte of clock prescaler
UCA1MCTL = UCBRS_0 + UCBRF_1 + UCOS16; // Modulation stages; oversampling mode
UCA1CTL1 &= ~UCSWRST; // Restart USCI state machine
UCA1IE |= UCRXIE; // Enable RX interrupt
}
And the port initialization for UCA1 (note that UCA1 is port-mapped):
// PORT4
P4SEL = (BIT4 + BIT5); // Set P4.4 as UCA1TXD, P4.5 as UCA1RXD, rest as I/O
PMAPKEYID = 0x2D52; // Unlock port mapping register configuration
PMAPCTL |= PMAPRECFG; // Allow reconfiguration of mapping
P4MAP4 = 12; // Map P4.4 to PM_UCA1
P4DIR |= BIT4; // Set P4.4 as input (UCA1TXD)
P4MAP5 = 11; // Map P4.5 to PM_UCA1
P4MAP5 &= ~(BIT5); // Set P4.5 as input (UCA1RXD)
P4DIR |= (BIT7 + BIT6 + BIT3 + BIT2 + BIT1 + BIT0); // Set rest of PORT4 as outputs
P4OUT &= ~(GPIO_ALL); // Clear PORT4 outputs
I am beginning to wonder if there may be an issue with my mapping of the ports. I should have included this from the start. Perhaps someone can identify an error?