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

problem because of absence of interrupt for "uart tx data register ready"

#include "nrf_delay.h"
#include "nrf_gpio.h"

#define  SETB(x,y)   (x|=(1<<y))     //for o/p
#define  CLRB(x,y)   (x&=(~(1<<y)))  //for o/p
#define  TGLB(x,y)   (x^=(1<<y))     //for o/p
#define  CHECKB(x,y) (x&(1<<y))      //for i/p

#define SERIAL_FLOAT_PRECISION 4

#define SERIAL_BUFFER_SIZE 64


typedef struct
{
  unsigned char buffer[SERIAL_BUFFER_SIZE];
  volatile unsigned int head;
  volatile unsigned int tail;
}ring_buffer;



ring_buffer rx_buffer;
ring_buffer tx_buffer;

void UARTE0_UART0_IRQHandler(void)
{
    unsigned char tx,rx;
    unsigned int i;
    
    if(NRF_UART0->EVENTS_TXDRDY == 1)
    {
        NRF_UART0->EVENTS_TXDRDY=0; 
        
        if (tx_buffer.head == tx_buffer.tail)
        {
            SETB(NRF_UART0->INTENCLR,7);         
        }
        else
        {
            tx = tx_buffer.buffer[tx_buffer.tail];
            tx_buffer.tail = (tx_buffer.tail + 1) % SERIAL_BUFFER_SIZE;

            NRF_UART0->TXD = tx;
        }

    }

    if(NRF_UART0->EVENTS_RXDRDY == 1)
    {
        NRF_UART0->EVENTS_RXDRDY = 0; 
        
        i = (unsigned int)(rx_buffer.head + 1) % SERIAL_BUFFER_SIZE;

        if(CHECKB(NRF_UART0->ERRORSRC,1)==0)
        {
            rx = NRF_UART0->RXD;
            
            if (i != rx_buffer.tail)
            {
                    rx_buffer.buffer[rx_buffer.head] = rx;
                    rx_buffer.head = i;
            }
        }
        else
        {
            rx = NRF_UART0->RXD;
        }   
        
    }                                   
    
}


void uart_init()
{
   
    NRF_UART0->BAUDRATE = 0x01D7E000;
    //NRF_UART0->CONFIG = 0x00000001;

    NRF_UART0->PSELRTS = 5;
    NRF_UART0->PSELTXD = 6;
    NRF_UART0->PSELCTS = 7;
    NRF_UART0->PSELRXD = 8;
    
    nrf_gpio_cfg_output(6);
    nrf_gpio_cfg_input(8, GPIO_PIN_CNF_PULL_Disabled);

    nrf_gpio_cfg_output(5);
    nrf_gpio_cfg_input(7, GPIO_PIN_CNF_PULL_Disabled);

    
    NRF_UART0->TASKS_STARTTX = 1;
    NRF_UART0->TASKS_STARTRX = 1;
    
    NRF_UART0->ENABLE = 0x00000004;

    NRF_UART0->INTENSET=0x84;
    
    NVIC_EnableIRQ(UARTE0_UART0_IRQn);
}



int serial_available(void)
{
  return (unsigned int)(SERIAL_BUFFER_SIZE + rx_buffer.head - rx_buffer.tail) % SERIAL_BUFFER_SIZE;
}


unsigned char serial_read(void)
{
  unsigned char c;
  
  if (rx_buffer.head == rx_buffer.tail)
  {
    return -1;
  } 
  else 
  {
    c = rx_buffer.buffer[rx_buffer.tail];
    rx_buffer.tail = (unsigned int)(rx_buffer.tail + 1) % SERIAL_BUFFER_SIZE;
    return c;
  }
}


void serial_write(unsigned char c)
{
  int i = (tx_buffer.head + 1) % SERIAL_BUFFER_SIZE;
	
  while (i == tx_buffer.tail){    NRF_P0->OUT^=0x00020000;  }
	
  tx_buffer.buffer[tx_buffer.head] = c;
  tx_buffer.head = i;
	
  SETB(NRF_UART0->INTENSET,7); 
  NRF_UART0->EVENTS_TXDRDY = 0;      

}

void serial_write_string(char *pointer)
{
  while(*pointer!='\0')
  {
    serial_write(*pointer);
    pointer++;
  }
}

int main(void)
{
  NRF_P0->DIR |= 0x000E0000;
  NRF_P0->OUTSET |= 0x000E0000;
  
  uart_init();
  
  unsigned int i=0;

  while(1) 
  {
 
    NRF_P0->OUT^=0x00040000;
    
    //NRF_UART0->TXD = '$';  // after comment out this line, code works normally !!!!
    serial_write_string("Hello World i=");
    serial_no(i++);
    serial_write_string("\n\r");
    
    
    nrf_delay_ms(50);

  }
}

/NRF_UART0->TXD = '$'; // after comment out this line, code works normally !!!! When I comment out this line from main function only then code prints "Hello World" on serial terminal.

This is arduino logic. I think due to unavailability of interrupt for "uart tx data register ready" it is behaving like this. I have implement same logic on STM32F407 & there it works smoothly.

  • Hi,

    What nRF5x chip is this? If you set the UART INTEN register, you can enable interrupt for all types of UART events, including EVENTS_TXSTARTED (UART transmitter has started) and EVENTS_ENDTX ( Last TX byte transmitted). Have you considered using the UART driver provided in the SDK ?

  • I am using nRF52832 microcontroller.

    www.google.co.in/url

    As per this datasheet, I did not found anything like EVENTS_TXSTARTED under UART module (page no. 529)

    I don't use SDK examples since they very complicated & I prefer to write own microcontroller drivers.

  • You are using an old version of the PS. Current version is v1.4, you can find it here.

    The UART peripheral is considered deprecated. I recommend using the UARTE instead. (page 333 in PS v1.4)

  • #include "nrf_delay.h" #include "nrf_gpio.h"

    #define  SETB(x,y)   (x|=(1<<y))     //for o/p
    #define  CLRB(x,y)   (x&=(~(1<<y)))  //for o/p
    #define  TGLB(x,y)   (x^=(1<<y))     //for o/p
    #define  CHECKB(x,y) (x&(1<<y))      //for i/p
    
    
    #define SERIAL_BUFFER_SIZE 64
    
    
    typedef struct
    {
      volatile unsigned char buffer[SERIAL_BUFFER_SIZE];
      volatile unsigned int head;
    }ring_buffer;
    
    
    
    ring_buffer rx_buffer;
    ring_buffer tx_buffer;
    
    
    void UARTE0_UART0_IRQHandler(void)
    {
      int boo;
    
      if(NRF_UARTE0->EVENTS_ENDTX == 1)
      {
        NRF_UARTE0->EVENTS_ENDTX = 0;    
      }
    
      if(NRF_UARTE0->EVENTS_TXDRDY == 1)
      {
    
        NRF_UARTE0->EVENTS_TXDRDY=0;
      }
    
      if(NRF_UARTE0->EVENTS_TXSTARTED == 1)
      {
        NRF_UARTE0->EVENTS_TXSTARTED = 0;
    
        //NRF_UARTE0->TXD.PTR=(uint32_t)&tx_buffer.buffer[0];
     
      }
    
    }
    
    
    void uart_init()
    {
       
        NRF_UARTE0->BAUDRATE = 0x01D60000;
        //NRF_UARTE0->CONFIG = 0x00000001;
    
        NRF_UARTE0->PSEL.RTS = 5;
        NRF_UARTE0->PSEL.TXD = 6;
        NRF_UARTE0->PSEL.CTS = 7;
        NRF_UARTE0->PSEL.RXD = 8;
        
        nrf_gpio_cfg_output(6);
        nrf_gpio_cfg_input(8, GPIO_PIN_CNF_PULL_Disabled);
    
        nrf_gpio_cfg_output(5);
        nrf_gpio_cfg_input(7, GPIO_PIN_CNF_PULL_Disabled);
         
        NRF_UARTE0->EVENTS_ENDTX=0;
        NRF_UARTE0->EVENTS_TXDRDY=0;
        NRF_UARTE0->EVENTS_TXSTARTED=0;
    
        //NRF_UARTE0->TASKS_STARTTX = 1;
        //NRF_UARTE0->TASKS_STARTRX = 1;
    
        NRF_UARTE0->INTENSET |= (1<<20)|(1<<8)|(1<<7)|(1<<2);
      
        NRF_UARTE0->ENABLE = 0x00000008;
        
        NVIC_EnableIRQ(UARTE0_UART0_IRQn);
        
    }
    
    
    
    
    void serial_write_irq(unsigned char c)
    {
      int i = (tx_buffer.head + 1) % SERIAL_BUFFER_SIZE;
     
      tx_buffer.buffer[tx_buffer.head] = c;
      tx_buffer.head = i;
    
    }
    
    void serial_write_string(char *pointer)
    {
    
      tx_buffer.head = 0;
    
      while(*pointer!='\0')
      {
        serial_write_irq(*pointer);
        pointer++;
      }
    
      NRF_UARTE0->TXD.PTR=(uint32_t)&tx_buffer.buffer[0];
      NRF_UARTE0->TXD.MAXCNT=tx_buffer.head;
    
      
      NRF_UARTE0->TASKS_STARTTX = 1;
      nrf_delay_ms(1);
    
    }
    
    void serial_no(long int n) 
    {
      char c;
      char buf[8 * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte.
      char *str = &buf[sizeof(buf) - 1];
      unsigned long int m;
    
      *str = '\0';
      
      if(n<0)
      {
         n=-n;
         serial_write_string("-");
      }
    
      do 
      {
        m = n;
        n /= 10;
        c = m - 10 * n;
        
        *--str = c < 10 ? c + '0' : c + 'A' - 10;
        
      } while(n);
    
      serial_write_string(str);
    }
    
    int main(void)
    {
      int foo=-1000;
    
      NRF_P0->DIR |= 0x0001E000;
      NRF_P0->OUTSET |= 0x0001E000;
    
      
      uart_init();
    
    
      while(1) 
      {
     
        NRF_P0->OUT^=0x00002000;
     
        serial_write_string("Hello World ");
        serial_no(foo++);
        serial_write_string("\n\r");
        //nrf_delay_ms(100);
    
      }
    
      return(0);
    
    }
    

    I write my own driver for UARTE. But I don't know why nrf_delay_ms(1) is required in serial_write_string() at the end to work it properly.

  • So from the UARTE Transmission chapter in the PS we have that:

    When all bytes in the TXD buffer, as specified in the TXD.MAXCNT register, have been transmitted, the UARTE transmission will end automatically and an ENDTX event will be generated.

    A UARTE transmission sequence is stopped by triggering the STOPTX task, a TXSTOPPED event will be generated when the UARTE transmitter has stopped.

    So if you look at how this is handled by the UART(E) driver in SDK, you can see that after we trigger the TASKS_STARTTX, we are checking and waiting for the either the ENDTX event or the TXSTOPPED to be triggered, before we continue. This is how we do it in the SDK, in the function nrf_drv_uart_tx_for_uarte():

    __STATIC_INLINE ret_code_t nrf_drv_uart_tx_for_uarte(const nrf_drv_uart_t * p_instance)
    {
        uart_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
        ret_code_t err_code = NRF_SUCCESS;
    
        nrf_uarte_event_clear(p_instance->reg.p_uarte, NRF_UARTE_EVENT_ENDTX);
        nrf_uarte_event_clear(p_instance->reg.p_uarte, NRF_UARTE_EVENT_TXSTOPPED);
        nrf_uarte_tx_buffer_set(p_instance->reg.p_uarte, p_cb->p_tx_buffer, p_cb->tx_buffer_length);
        nrf_uarte_task_trigger(p_instance->reg.p_uarte, NRF_UARTE_TASK_STARTTX);
    
        if (p_cb->handler == NULL)
        {
            bool endtx;
            bool txstopped;
            do
            {
                endtx     = nrf_uarte_event_check(p_instance->reg.p_uarte, NRF_UARTE_EVENT_ENDTX);
                txstopped = nrf_uarte_event_check(p_instance->reg.p_uarte, NRF_UARTE_EVENT_TXSTOPPED);
            }
            while ((!endtx) && (!txstopped));
    
            if (txstopped)
            {
                err_code = NRF_ERROR_FORBIDDEN;
            }
            p_cb->tx_buffer_length = 0;
        }
        return err_code;
    }
    
Related