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.

UART Tx ISR Problem

I am using an MSP430 (MSP430x5xx family) MCU.  The code base currently has incoming and outgoing message handling code using UART0.  This works most of the time, but I just noticed that occasionally comms appears to go down.  

After debugging I found that we are able to receive data with no problem, but at some point the Tx ISR stops triggering so no data ever gets sent.  If I manually set the UCA0IFG Tx bit in the debugger, things will start to work again until the error condition pops up again.  When I am in the bad state, the Tx interrupt is enabled.

Does anyone have any idea what would stop the Tx ISR from firing?  

The following is my Tx ISR code:

 UCA0IE &= ~UCTXIE;					// Disable tx interrupt

if(numBytesInTxQueue == 0)
{ // Clear interrupt if nothing left in queue
UCA0IFG &= ~UCTXIFG;
}
else
{
UCA0TXBUF= txByteQueue[readIndex++]; // Add next byte in queue to buffer
numBytesInTxQueue--;
}

UCA0IE |= UCTXIE;
  • Hi,

    Any chance that the numBytesInTxQueue is rolling over to zero as you are adding bytes to the txByteQueue?

    Regards,

    Barry

  • Hi Kevin,

    It looks like you might be misusing the UCTXIFG.

    You shouldn't clear UCTXIFG manually if you have nothing left to transmit.  Instead, just mask the interrupt with the IE register.  That leaves the IFG bit set as it should be.

    Also note that in the 5xx family, if you read the Interrupt Vector (UCA0IV) register and it shows a TX interrupt, it also automatically clears TXIFG for you.  In that case, you need to manually set the flag back to 1 if you see you have nothing to transmit.  So in the case of nothing left to transmit, mask the interrupt *and* set TXIFG.

    Your existing masking and unmasking statements (at the beginning and end of the posted code) are unnecessary.

    Jeff

  • I don't think so.  Even it that could happen, I would see numBytesInTxQueue == 0 and just clear the interrupt.  The interrupt should keep firing in that case also.

    Also, there is a snippet of code I didn't include that detects when the index hits the end of the buffer and wraps it back to the beginning

  • Jeff Tenney said:

    Hi Kevin,

    It looks like you might be misusing the UCTXIFG.

    You shouldn't clear UCTXIFG manually if you have nothing left to transmit.  Instead, just mask the interrupt with the IE register.  That leaves the IFG bit set as it should be.

    Also note that in the 5xx family, if you read the Interrupt Vector (UCA0IV) register and it shows a TX interrupt, it also automatically clears TXIFG for you.  In that case, you need to manually set the flag back to 1 if you see you have nothing to transmit.  So in the case of nothing left to transmit, mask the interrupt *and* set TXIFG.

    Your existing masking and unmasking statements (at the beginning and end of the posted code) are unnecessary.

    Jeff

    Interesting.  So I am using this completely wrong by clearing TXIFG.  

    In addition to masking the interrupt when nothing is left to transmit, I also need to unmask the interrupt when something is ready to transmit, correct?

  • Kevin Alden said:
    I also need to unmask the interrupt when something is ready to transmit, correct?

    Yep.  Preferably in the "right" order -- insert tx char into output queue, then enable interrupt.

    Jeff

  • Hi,

    That is interesting way of handling a transmit buffer. I've always left the interrupt enabled, filled the transmit queue and then kick start the transmission by sending the first byte to the TXBUF.  I can see advantages to doing it your way. You just fill up the transmit queue , set the TXIFG flag, enable the interrupt and away it goes.

    I believe my point about numBytesInTxQueue rolling to zero is still valid.  If you add a byte to the TX Queue and numBytesInTxQueue rolls over to zero, then when you come into the ISR, the TX queue is full, but it appears to be empty, because numBytesInTxQueue is zero. The TXIFG flag is cleared and the ISR is exited with nothing be transmitted. The TXIFG gets set when the TX Buffer is ready for more data, if nothing has been transmitted, the flag will not get set again.  You stated that if you set TXIFG via the debugger that it would start working again.  Probably not very likely, especially if numBytesInTxQueue is a 16 bit value, but still a possibility.

    Regards,

    Barry

  • blb said:

    Hi,

    That is interesting way of handling a transmit buffer. I've always left the interrupt enabled, filled the transmit queue and then kick start the transmission by sending the first byte to the TXBUF.  I can see advantages to doing it your way. You just fill up the transmit queue , set the TXIFG flag, enable the interrupt and away it goes.

    I believe my point about numBytesInTxQueue rolling to zero is still valid.  If you add a byte to the TX Queue and numBytesInTxQueue rolls over to zero, then when you come into the ISR, the TX queue is full, but it appears to be empty, because numBytesInTxQueue is zero. The TXIFG flag is cleared and the ISR is exited with nothing be transmitted. The TXIFG gets set when the TX Buffer is ready for more data, if nothing has been transmitted, the flag will not get set again.  You stated that if you set TXIFG via the debugger that it would start working again.  Probably not very likely, especially if numBytesInTxQueue is a 16 bit value, but still a possibility.

    Regards,

    Barry

    Actually this is how I am doing it now.  I currently leave the interrupt enabled and then kick start the process by putting a byte into TXBUF to start the transmission.  I can now see some issues with the way the code is written though, one of them being the wrapping issue.  I am going to do some more investigation tomorrow and see what I can find.  Thanks for the help so far.

  • Barry, Kevin,

    Using the "kick-start" approach to transmit handling, how do you know that it is safe for the non-ISR code to put the first char into TXBUF?  I can't immediately think of a safe way to do it, so I'm curious how you do it.  How do you know there's not something already in TXBUF?

    Jeff

  • Currently, I queue bytes one at a time into my local buffer when I want to transmit data.  If the local buffer is empty at the time of queueing, the byte is immediately put into TXBUF and never queued and I set a flag indicating this.  From this point on, all data will be queued into the buffer until the ISR has emptied it and my flag is cleared.  Then the process repeats.

    As I was typing that, something feels wrong.  I can't quite see where the problem would be, but the whole process seems overly complicated.

  • I can see how adding the extra flag you mentioned would help to address the problem.  But I can also see danger in handling the flag as well as one nasty potential race condition that would be hard (impossible) to debug.  Sounds like you have a good sixth sense for these weaknesses.

    Jeff

  • Kevin,

    In my implementations, it is a command / response scenario. So I know that the transmitter is not in use when I start to build a message to be transmitted. 

    I can see how your method of using the IE to start the transfer would be superior when there is a constant stream of data to be transmitted.  Thanks for sharing. :)

    Regards,

    Barry

  • Whoops I meant Jeff.  Dang it.  Sorry...

  • So I tried making the mods you recommended and it didnt seem to matter.  One thing I noticed is if I take out my code that reads from an ADC module over i2c, my problems go away.  Basically, every half a second a set a flag to read from the ADC in my main loop.  I just commented out the call to my adc read function.

    Is there any way UCB0 (the i2c port im using) could interfere with UCA0 operation?

  • The i2c issue was a separate problem.  Fixing both that problem and moving to your suggested interrupt handling scheme seems to have resolved my problem.  Thanks!

**Attention** This is a public forum