Embedded Freaks..

September 23, 2008

AVR Uart Library with Auto Power Saving Mode

Filed under: avr — Tags: — kunilkuda @ 3:42 pm

I like Peter Fleury’s UART library (here). His library is quite simple, yet it’s perfect for my need since it has circular buffer to hold the receiving/transmitted data. But I need some kind of UART library with greater control over its on/off mode to preserve power.

So, I modified his source code to include auto shutdown for UART TX (the AVR’s TX will only ON if there is data inside the circular buffer), and manual RX on/off. By applying RX manual ON/OFF, I can have periodic UART receiver (which will save power, since I can switch to lower power mode if it’s not in receiving mode).

If you interested in my work, you can download it here.

Anyway, here is the brief explanation of how to achieve auto TX ON/OFF and the sample of how to use the library to conserve power.

Auto TX ON/OFF

This is very easy. You just need to turn on the UART TX (TXEN and UDRIE, since the buffer is transmitted using UDRIE) if there’s data inside the buffer. Since I’m very lazy, I just turn ON the TX if the uart_put() is called.


void uart_putc(unsigned char data)
{
    unsigned char tmphead;

    tmphead  = (UART_TxHead + 1) & UART_TX_BUFFER_MASK;

    while ( tmphead == UART_TxTail ){
        ;/* wait for free space in buffer */
    }

    UART_TxBuf[tmphead] = data;
    UART_TxHead = tmphead;

    uart_enableTx();

    /* enable UDRE interrupt */
    UART0_CONTROL    |= _BV(UART0_UDRIE);
} /* uart_putc */

Actually, the correct method is to check whether there’s data inside the circular buffer or not before overwriting the TXEN. But since my experiment on overwriting TXEN didn’t result in any defect on UART’s TX, I guess it’s ok to enable the already enabled UART TX.

To shut it down, just wait until the buffer is empty and the TXC bit is set (means that all data has been transmitted out of serial shifter). If you don’t check the TXC bit, some of the data bit may still stuck inside the serial shifter after you turn off the UART TX.

SIGNAL(UART0_TRANSMIT_INTERRUPT)
{
    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);
        /* wait until the transmission is complete */
        while(!bit_is_set(UART0_STATUS, UART0_TXC));
        /* disable the transmitter */
        uart_disableTx();
    }
}

Not much magic inside, huh ?

Periodic RX ON/OFF

The AVR’s UART can only be run in NORMAL and IDLE mode (refer to AVR’s datasheet, in power management and sleep modes section). In IDLE mode, the MCU consumes around 6mA (with 3.3V and 7.3728MHz xtal).

To reduce the current consumption, we can enable the UART’s RX periodically. Thus, enable the MCU to go down for deeper sleeping mode (up to several uA) for most of the time.

This is the simple method to do that. To do this, you need the RTC xtal (or other async clock) running in your system.

First, init the timer to shoot periodically using async mode (refer to this post). According to AVR’s datasheet, async clock is enabled until POWER_SAVE mode. So, everytime the timer shoot, we can switch from IDLE mode to POWER_SAVE mode (and vice versa), like this:

static uint8_t currentSleepMode = SLEEP_MODE_IDLE;
SIGNAL(SIG_OUTPUT_COMPARE0)
{
  if (currentSleepMode == SLEEP_MODE_IDLE) {
    currentSleepMode = SLEEP_MODE_PWR_SAVE;
    uart_disableRx();
  }
  else {
    currentSleepMode = SLEEP_MODE_IDLE;
    uart_enableRx();
  }
}

Then, execute the sleep mode (and SLEEP command) in the main loop. Like this:

while(1)
{
  set_sleep_mode(currentSleepMode);
  sleep_mode();
}

There. This one will reduce the power consumption until 50%. But it’s not good enough.

There are two problems in this kind of code: you can only reduce 50% of the power consumption, and what if there are more bytes to come while the time out. Actually, the solution is simple. Reset the timeout everytime new byte is received, and add some counter inside the timer interrupt to enable 1%, 10%, 20% or any duty cycle that you want.

I’ll come back with the periodic UART RX demo after I finished my project.

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: