Wait until UART finished transmittion before sleep

Go To Last Post
15 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Good day!

I'm using this UART library, which is interrupt-driven:

http://homepage.hispeed.ch/peter...

I've something like this in my code:

uart_puts_P("Going to sleep...\n");
set_sleep_mode(SLEEP_MODE_PWR_DOWN); 
sleep_enable(); 
sleep_mode(); 	

The problem is that MCU goes to sleep immediately and I get the message only after waking up. The dirty way, I've used is to add a delay:

uart_puts_P("Going to sleep...\n");
set_sleep_mode(SLEEP_MODE_PWR_DOWN); 
_delay_ms(100); // FIXME: Give some time to UART to transmit data
sleep_enable(); 
sleep_mode(); 	

But I feel that this is a wrong way of doing it.

Thanks in advance!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Does this have anything to do with the AVR GCC Forum you are posting to?

Warning: Grumpy Old Chuff. Reading this post may severely damage your mental health.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why not poll the Tx buffer until it is empty? (I guess the relevance to GCC is that the Fleury code is written only for GCC?)

EDIT: checking the Fleury code it appears the way he knows whether there's anything in the Tx buffer is:

    if ( UART_TxHead != UART_TxTail) {

You can perform the same check.

Note however the variables at present are static:

static volatile unsigned char UART_TxHead;
static volatile unsigned char UART_TxTail;

Either write an accessor/test function in the context of uart.c or remove the 'static'

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

> You can perform the same check.

However, keep in mind that this still means there's a character
in the Tx shift register, as well as another one in the transmit
UDRx. So after the software transmit buffer is empty, you have
to await until UDREx is set (i.e. the transmit UDRx would be
able to take the next character), *and* TXCx (transmit complete)
is also set. (Don't forget to clear the latter one.)

Of course, it would equally suffice to wait for about 20 bit clock
periods once the software buffer is empty (plus some overhead for
the ISRs to work).

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The ISR code is:

SIGNAL(UART0_TRANSMIT_INTERRUPT)
/*************************************************************************
Function: UART Data Register Empty interrupt
Purpose:  called when the UART is ready to transmit the next byte
**************************************************************************/
{
    unsigned char tmptail;

    
    if ( UART_TxHead != UART_TxTail) {
        /* calculate and store new buffer index */
        tmptail = (UART_TxTail + 1) & UART_TX_BUFFER_MASK;
        UART_TxTail = tmptail;
        /* get one byte from buffer and write it to UART */
        UART0_DATA = UART_TxBuf[tmptail];  /* start transmission */
    }else{
        /* tx buffer empty, disable UDRE interrupt */
        UART0_CONTROL &= ~_BV(UART0_UDRIE);
    }
}

So it may be possible to simply monitor the UDRIE bit.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Interestingly enough, just yesterday night i faced exactly the same problem. I fixed it as described above: you create a funtion which returns TRUE if the TX buffer is empty. Then you simply code:

while (!IsTXBufferEmpty()) {
  _delay_ms(1);
}
_delay_ms(1);

Of course the time you decide to wait is somewhat depending on your baud rate (i use 57600 Bd).

-- Thilo

Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why even bother delaying? It doesn't hurt to just keep calling the buffer test:

while (!IsTXBufferEmpty());

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

> So it may be possible to simply monitor the UDRIE bit.

In addition, you have to monitor the TXC bit. Otherwise, you'll miss
the last character that just entered the shift register.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've added this to uart library:

unsigned char uart_tx_buffer_empty(void)
{
	return (UART_TxHead == UART_TxTail);
}

When I use it like this:

uart_puts_P("Going to sleep...\n"); 
// Wait before going to sleep
while (!uart_tx_buffer_empty());

// Going to sleep now
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_mode();     

//...
uart_puts_P("Waking up!..\n"); 

The "Going to sleep..." is transmited good, but "Waking up!" is transmited with distortion in first letters. With even a small delay it works:

uart_puts_P("Going to sleep...\n"); 
// Wait before going to sleep
while (!uart_tx_buffer_empty());
_delay_ms(1);

So I need to monitor something more in uart_tx_buffer_empty()?

Thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

So I need to monitor something more in uart_tx_buffer_empty()?

Yes, as Jörg told you above.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the reply!

Here are defines from the library (I've ATmega8):

#elif  defined(__AVR_ATmega8__)  || defined(__AVR_ATmega16__) || defined(__AVR_ATmega32__) \
  || defined(__AVR_ATmega8515__) || defined(__AVR_ATmega8535__) \
  || defined(__AVR_ATmega323__)
  /* ATmega with one USART */
 #define ATMEGA_USART
 #define UART0_RECEIVE_INTERRUPT   SIG_UART_RECV
 #define UART0_TRANSMIT_INTERRUPT  SIG_UART_DATA
 #define UART0_STATUS   UCSRA
 #define UART0_CONTROL  UCSRB
 #define UART0_DATA     UDR
 #define UART0_UDRIE    UDRIE

I've tried the following :

unsigned char uart_tx_buffer_empty(void)
{
	return ( (UART0_STATUS & (1 << UART0_UDRIE )) && (UART0_STATUS & (1 << TXC )) );
}

I still get something like this from UART:

Going to sleep...ЎU¬­Y№ђup!..

The garbage after "..." should be "Waking up!"

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Make this:

unsigned char uart_tx_buffer_empty(void)
{
   return ( (UART0_CONTROL & (1 << UART0_UDRIE )) == 0
            && (UART0_STATUS & (1 << TXC )) );
}

Your buffer is empty iff:

. the UDR empty ISR has eventually turned off the UDR empty interrupt mask bit, AND
. the TX shift register has been clocked out completely

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the reply, I will try it when get to my hardware...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MBedder wrote:
Does this have anything to do with the AVR GCC Forum you are posting to?
Hmmm.
Five lines of code.
The _P suggests at least that someone is naming things the avr-gcc way.
_delay_ms is an avr-gcc-ism,
as are set_sleep_mode, sleep_enable and sleep_mode.
I'd say 80+ percent.

Move the wall.

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can anybody with moderator rights move this topic to appropriate forum? Thanks in advance!

About the code -- that's doesn't work for me, I've got the same results :(