Jump to content
43oh

Why the UART TX channel would not empty!


Recommended Posts

This is a recap of a little adventure in the hopes that it helps someone else out there.

 

This application is on an MSP430G2553 MCU. CSS is used as the IDE and the Energia libraries are in play (a good tooling combination, highly recommend J).

 

The app spends most of its time in LPM3. It wakes up periodically to do certain tasks and also wakes up when traffic comes in on the UART RX channel. The ISR for the RX channel detects the arrival of a command and acts on the command during the ISR.

 

Part of servicing a command is sending a response, which is transmitted via the UART TX channel.  Sometimes the app would respond to the command and other times the MCU just hangs. Digging in deeper with the CSS debugger showed it hanging in the Energia libraries waiting for the Serial TX ISR to fire and make space in the TX ring buffer. Why was the TX ISR not firing?

 

Here is what was happening:

 

If the response string was short enough to fit in the ring buffer it would be stored there, the TX ISR would eventually fire and send the response. If the response was too large the write routine would hang waiting for space to become available in the ring buffer.

 

Energia disables GIE when it is in its TX or RX ISR routines. This app is trying to transmit while in the RX ISR.  That will have the effect of filling the TX ring buffer but the TX ISR is not firing because GIE is disabled. So the buffer fills and the app hangs waiting for space to become available.

 

Easy enough to fix: In the app code that is responding to commands and sending the responses do this (remember, this is inside the RX ISR thread of execution)

 

__istate_t iState = __get_interrupt_state();  /* Current state of SR */

__bis_status_register(GIE);           /* Enable GIE */

/* Process the command, send response */

__set_interrupt_state(iState); /* Restore GIE to previous state */

 

What this does is enable interrupts within the interrupt. Now the TX ISR will fire as Energia places bytes in the TX buffer.

 

All is good!

Link to post
Share on other sites

Yeah doing UART TX is not recommended while inside an ISR for the reasons discovered. Beware potential pitfalls from nesting interrupts (setting GIE inside ISRs). Btw it's standard MSP430 hardware behavior to keep GIE cleared by default in ISR context.

I think that can lead to re-entrant ISRs if the interrupts are enabled inside an ISR. Risky business... :(

Link to post
Share on other sites

The replies make good points, thank you. I am going to look into disabling the RX ISR while the command is being processed within the RX ISR. That fits within the use-cases for this app. hopefully the HC-05 BT module has enough buffering to hold any few characters that may come in while RX ISR is disabled.

 

Another option is to place the commands in a queue to be processed "later" when not in the RX ISR.  Without an OS to support queueing, threading and scheduling issues that is just too much trouble, for this project on this little MCU (running out of code space anyway)

 

Thank you for the replies,

Link to post
Share on other sites

The replies make good points, thank you. I am going to look into disabling the RX ISR while the command is being processed within the RX ISR. That fits within the use-cases for this app. hopefully the HC-05 BT module has enough buffering to hold any few characters that may come in while RX ISR is disabled.

 

Another option is to place the commands in a queue to be processed "later" when not in the RX ISR.  Without an OS to support queueing, threading and scheduling issues that is just too much trouble, for this project on this little MCU (running out of code space anyway)

 

Thank you for the replies,

It is typically the case that your ISRs set a "signal semaphore" (global "volatile boolean" variable) and the main loop does the remaining processing.  Not all that hard to do with a while() loop as your make-shift "scheduler" - or just the loop() function in Energia's case.  There would be a set of conditions at the beginning of loop() that would determine whether it's safe to enter sleep() mode or whatever, and if any of those failed it would go through and check each one and run the handler.

Link to post
Share on other sites

Yes, that had occurred to me as well. If this app was awake at all times that would be easier to implement. But this one spends most of its life in LPM3 (it is a battery powered system). I am too lazy to want to complicate the for loop with logic to see if LPM3 should be delayed or not because a human user is awaiting a response. It is just easier to send the response during the RX ISR routine and let the system fall back into LPM3 when the RX ISR terminates.

 

Essentially the for loop is not running most of the time, if that makes sense.

 

Thank you,

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...