This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Handling the UART when sleeping

I'm using simple_uart_init to enable UART I/O from my target. This all works fine but I'm not sure what to do with the UART before I sleep (by calling sd_app_event_wait) so that it does not consume power.

I'm especially concerned that the GPIO line associated with TX not leak current into the serial terminator.

I'm also slightly concerned that the UART remains "enabled" during sleep and I'm not sure whether it consumes power in that state, even when the CPU is sleeping.

Whatever I do for sleeping, I presume I can call simple_uart_init again when I wake up to set things up again for operation. Is this correct?

  • Hi Bret,

    simple_uart_init() enables both UART TX and RX (see NRF_UART0->TASKS_STARTTX and NRF_UART0->TASKS_STARTTX). This causes the UART to keep power-consuming HW resources enabled all the time. If you don't need to be able to receive UART data while sleeping, both TX and RX can be disabled before sleeping.

    Note that sd_app_event_wait() only puts the CPU in sleep. Peripherals are kept as you left them, consuming power according to their state. Thus it's not required to run simple_uart_init() once CPU wakes up and execution continues from sd_app_event_wait(). Only stuff explicitly disabled needs to be re-enabled.

    If you are concerned the UART GPIO lines are in an state that can cause leaking, you need to disable the UART (so that it releases the GPIOs) and manually set the pin state. See code example below:

    // Disable UART transmission and reception
    NRF_UART0->TASKS_STOPRX = 1;
    NRF_UART0->TASKS_STOPTX = 1;
    
    // Disable UART to get control of GPIOs
    NRF_UART0->ENABLE = UART_ENABLE_ENABLE_Disabled << UART_ENABLE_ENABLE_Pos;
    // Set TXD, RXD, CTS, RTS as high outputs
    // Alternatively use NRF_GPIO->PIN_CNF to set pull resistors
    NRF_GPIO->DIRSET = (1 << UART_TXD_GPIO) | (1 << UART_RXD_GPIO) | (1 << UART_RTS_GPIO) | (1 << UART_CTS_GPIO);
    NRF_GPIO->OUTSET = (1 << UART_TXD_GPIO) | (1 << UART_RXD_GPIO) | (1 << UART_RTS_GPIO) | (1 << UART_CTS_GPIO);
    
    // Enter sleep
    // Note: Still in System ON state. Upon wakeup execution continues from here, and not from reset.
    err_code = sd_app_event_wait();
    // Todo: Check error
    
    // Re-enable UART
    NRF_UART0->ENABLE = UART_ENABLE_ENABLE_Enabled << UART_ENABLE_ENABLE_Pos;
    
    // Enable UART transmission and reception
    NRF_UART0->TASKS_STARTRX = 1;
    NRF_UART0->TASKS_STARTTX = 1;
    
  • The code above works quite well for disabling and enabling the UART and I've confirmed that it achieves a substantial sleep current reduction.

    One remaining issue is that disabling the TX shortly after a call to simple_uart_putstring causes the last string to never be output on the UART and also results in the UART not getting successfully re-enabled.

    simple_uart_putstring repeatedly calls simple_uart_put, which is spinning on (NRF_UART0->EVENTS_TXDRDY!=1)

    If I add my own spin right after simple_uart_putstring by doing this: for( int i = 0 ; i < 1000000 ; i++ ) {} then everything works properly. I see the last string come out of the UART before sleeping and the re-enable works too. Without my additional spin delay, it doesn't work.

    So I guess that NRF_UART0->EVENTS_TXDRDY does not actually indicate that the UART is through sending data. Is there a flag I can use that will indicate that the UART TX is complete so that I can safely stop the TX and disable the UART?

  • NRF_UART0->EVENTS_TXDRDY indicates when a byte transmission has completed, so it should be safe to turn TX off once that happens. If STOPTX task has been called, the TXDRDY event will never happen, causing simple_uart_put() to spin as you described.

    I modified simple_uart_putstring() to only enable TX when needed. Could this modification work for you? (This is probably easier than what I suggested in the previous post)

    void simple_uart_putstring(const uint8_t *str)
    {
      uint_fast8_t i = 0;
      uint8_t ch = str[i++];
        
      NRF_UART0->TASKS_STARTTX = 1;
        
      while (ch != '\0')
      {
        simple_uart_put(ch);
        ch = str[i++];
      }
      
      NRF_UART0->TASKS_STOPTX = 1;
    }
    
Related