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

STOPRX task of UARTE during reception of bytes

Hi all

Previously, I used the nRF52832 with the interrupt-driven UART at 1Mbaud. But due to SoftDevice interrupts that blocked the UART-RX-IRQ for more than 6 bytes, I tried to use the UARTE with EasyDMA.

I need to receive an arbitrary amount of bytes and can therefore not wait until the RX buffer is filled. So, I tried the suggestion from this thread, by enabling the shortcut between ENDRX and STARTRX and by triggering the STOPRX task in a certain interval.

Now, I check in a certain interval if the RXDRDY event occurred. If yes, I set the second RX buffer as the active buffer and trigger afterwards the STOPRX task. After the ENDRX event occurred, I copy the amount of received bytes.

My code looks like this:

void UART_init(void)
{
   // Disable UART
   NRF_UARTE0->ENABLE = (UARTE_ENABLE_ENABLE_Disabled << UARTE_ENABLE_ENABLE_Pos);

   // Set baudrate
   NRF_UARTE0->BAUDRATE = UARTE_BAUDRATE_BAUDRATE_Baud1M;

   // Disable flow control and parity check
   NRF_UARTE0->CONFIG = (UART_DISABLE_HW_FLOW | UART_EXCLUDE_PARITY);

   // Set TX and RX pin
   NRF_UARTE0->PSEL.TXD = HWH_HIF_MISO_TX;
   NRF_UARTE0->PSEL.RXD = HWH_HIF_MOSI_RX;

   // Set the TX and RX buffers
   gRX_buf_1_active = true;
   NRF_UARTE0->RXD.PTR = (uint32_t)gRX_buf_1;
   NRF_UARTE0->TXD.PTR = (uint32_t)gTX_buf;
   NRF_UARTE0->RXD.MAXCNT = 255;
   NRF_UARTE0->TXD.MAXCNT = 0;

   // Clear all possible pending events
   NRF_UARTE0->EVENTS_RXDRDY = 0;
   NRF_UARTE0->EVENTS_ENDRX = 0;
   NRF_UARTE0->EVENTS_TXDRDY = 0;
   NRF_UARTE0->EVENTS_ENDTX = 0;
   NRF_UARTE0->EVENTS_ERROR = 0;
   NRF_UARTE0->EVENTS_RXTO = 0;
   NRF_UARTE0->EVENTS_RXSTARTED = 0;
   NRF_UARTE0->EVENTS_TXSTARTED = 0;
   NRF_UARTE0->EVENTS_TXSTOPPED = 0;

   // Clear Interrupts
   NRF_UARTE0->INTENCLR = 0xFFFFFFFFUL;

   // Enable shortcut bwetwen ENDRX and STARTRX
   NRF_UARTE0->SHORTS = UARTE_SHORTS_ENDRX_STARTRX_Enabled << UARTE_SHORTS_ENDRX_STARTRX_Pos;

   // Enable UART
   NRF_UARTE0->ENABLE = (UARTE_ENABLE_ENABLE_Enabled << UARTE_ENABLE_ENABLE_Pos);

   // Start RX
   NRF_UARTE0->TASKS_STARTRX = 1;
}

void UART_handle(uint8_t * const pData, size_t const data_size)
{
   uint32_t rx_length;
   uint32_t i;

   if (NRF_UARTE0->EVENTS_RXDRDY)
   {
      // Switch the RX buffer
      if (gRX_buf_1_active)
      {
         NRF_UARTE0->RXD.PTR = (uint32_t)gRX_buf_2;
         NRF_UARTE0->RXD.MAXCNT = 255;
         gRX_buf_1_active = false;
      }
      else
      {
         NRF_UARTE0->RXD.PTR = (uint32_t)gRX_buf_1;
         NRF_UARTE0->RXD.MAXCNT = 255;
         gRX_buf_1_active = true;
      }

      // Stop the reception
      // RX is restarted after the ENDRX event due to shortcut
      NRF_UARTE0->EVENTS_ENDRX = 0;
      NRF_UARTE0->TASKS_STOPRX = 1;
      while (NRF_UARTE0->EVENTS_ENDRX == 0) {}
      NRF_UARTE0->EVENTS_ENDRX = 0;
      NRF_UARTE0->EVENTS_RXDRDY = 0;

      // Copy the received bytes from the used RX buffer
      rx_length = NRF_UARTE0->RXD.AMOUNT;
      for (i = 0; i < rx_length; i++)
      {
         if (i < data_size)
         {
            if (gRX_buf_1_active) {pData[i] = gRX_buf_2[i];}
            else                  {pData[i] = gRX_buf_1[i];}
         }
      }
   }
}

I assumed that due to the enabled shortcut the reception should immediately switch to the second buffer. But it seems that I lose some bytes (or get corrupted bytes) when the STOPRX task is executed during the reception of bytes. E.g. the byte stream "00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF" is sent to the nRF52, but I receive in RX_BUF_1 the bytes "00 11 22 33 44 55 66" and in RX_BUF_2 the bytes "2C DB DD EE FF". Here is a trace from the flow:

Should the STOPRX task not be triggered during the reception of bytes or am I missing something? Do you know this problem?

Many thanks in advance.

Best regards

Parents
  • Hi,

    I have seen this method been attempted before, but unfortunately it is not possible to start and stop the UART asynchronously like this without concern for the incoming data stream. When you randomly start and stop the UART midstream like this, you will stop the logic, reset DMA channels, etc. at random times and you are bound to lose some bytes.

    In SDK 15 we have what we call the Command Line Interface library where one of the available transport layers are the UARTE. If you look in the source code for that ("SDK_folder\components\libraries\experimental_libuarte\nrf_libuarte.c") you can see how the SDK team has chosen to implement something similar. They don't start and stop the UART like this, but use PPI and a counter to keep track of how many bytes are available in the buffer. Maybe you can try something similar.

    Here is a relevant thread: https://devzone.nordicsemi.com/f/nordic-q-a/28420/uarte-in-circular-mode

     

Reply
  • Hi,

    I have seen this method been attempted before, but unfortunately it is not possible to start and stop the UART asynchronously like this without concern for the incoming data stream. When you randomly start and stop the UART midstream like this, you will stop the logic, reset DMA channels, etc. at random times and you are bound to lose some bytes.

    In SDK 15 we have what we call the Command Line Interface library where one of the available transport layers are the UARTE. If you look in the source code for that ("SDK_folder\components\libraries\experimental_libuarte\nrf_libuarte.c") you can see how the SDK team has chosen to implement something similar. They don't start and stop the UART like this, but use PPI and a counter to keep track of how many bytes are available in the buffer. Maybe you can try something similar.

    Here is a relevant thread: https://devzone.nordicsemi.com/f/nordic-q-a/28420/uarte-in-circular-mode

     

Children
  • Thanks for your answer. I assumed that due to the shortcut, the reception would seamless continue in the second RX buffer. Seems that I was wrong. The solution with the PPI channel and the counter sounds promising.

    But I don't quite understand why in the nrf_libuarte.c the capture value of the timer is only retrieved when the ENDRX event occurred. Wouldn't it be easier in this case to check the AMOUNTRX value? Is that correct that the capture value could also be retrieved when the reception is ongoing?

    In the thread that you've linked they are talking about the number of clock cycles it takes for a received byte to be written from the FIFO to the buffer. I didn't find this information in the product specification. Is there a maximum time that you can state?

  • Hi,

    Yes, you are right. If you are only going to check the amounts of received bytes at ENDRX events you may just as well check RXD.AMOUNT rather than using a counter. 

    The libUARTE library is still in experimental mode, meaning it is still a work in progress (or at least it's not finished). I have asked around and it seems like libUARTE is put on hold for the time being. I imagine that there is/was a thought behind the counter, but it just hasn't been put to full use yet. 

  • Thank you for the clarification.

    Is it possible that you can make a generous statement about the maximum duration that is necessary to put a byte from the FIFO to the RAM via EasyDMA?

  • Hi,

    As my colleague run_ar says in that thread, there is no exact answer to that because it depends on whether or not other, higher priority, peripherals are using the same DMA channel. However, the system architect says it will occur "within spec". That is, data should be moved out of the FIFO before the next data arrives, so moving one byte should take less time than it takes to receive one byte at whatever baud rate you are using. 

Related