How to use critical region and app_uart

I implemented UART communication as follows, but occasionally the same data is sent twice.

  1. Enter the critical region (app_util_critical_region_enter).
  2. Store the transmission data in the TX FIFO until it is full (app_uart_put).
  3. Exit the critical region (app_util_critical_region_exit).
  4. When the APP_UART_TX_EMPTY event occurs, if there is any remaining transmission data, store it in the TX FIFO (app_uart_put). (If there is no remaining data, the transmission ends.)

I intend to stop the event occurrence between steps 1 and 3, but in reality, it seems that the event in step 4 is running during the process in step 2, causing the same data to be stored in the TX FIFO.

In the above design, is it not possible to stop the event occurrence?

SDK:nRF5 SDK 15.3.0

SoftDevice:s132_nrf52_6.1.1

Parents
  • Hello,

    I am not sure I understand why you need this critical section at all. 

    Also, I don't follow your flow. You are using app_uart_put() to store data (but not send it)? And then you do something when the FIFO is full? What do you do?

    When the APP_UART_TX_EMPTY event occurs, if there is any remaining transmission data

    This event only occurs when you get the NRF_DRV_UART_EVT_TX_DONE, and there are no more elements in your FIFO buffer. 

    if there is any remaining transmission data, store it in the TX FIFO

    remaining data where? In your FIFO buffer? It is already there, isn't it? Or do I misunderstand? And why was it not transmitted? How did you transmit the rest of the fifo buffer?

    I am sorry that there are a lot of questions, but I struggle to understand the question, I think.

    Best regards,

    Edvin

  • Hello, Thank you for your reply.

    What I want to do is wait for the UART communication to complete after storing the transmission data in the FIFO. Therefore, I am waiting for the APP_UART_TX_EMPTY event to occur after storing the data in the FIFO using app_uart_put().

    Additionally, the transmission data is stored in another large array, and I am sequentially storing it in the FIFO using app_uart_put() from the beginning. Therefore, I am also considering the case where app_uart_put() returns NRF_ERROR_NO_MEM within the critical section. In that case, I store the remaining data using app_uart_put() after the APP_UART_TX_EMPTY event occurs and the FIFO becomes empty.

    I am attaching the sample. The caller waits for the communication to complete with IsEndUART() after calling SendUART().

    uint16_t g_usSendLength = 0u;
    uint16_t g_usSendCount = 0u;
    
    uint8_t	g_pucSendBuffer[512];
    
    bool	g_bSendEnd = false;
    bool	g_bSendError = false;
    
    /**
     * Function for Send UART
     */
    bool	SendUART(const uint8_t * const pucData, 
    				 const uint16_t usDataLength)
    {
    	bool bSuccess = true;
    
    	uint8_t ucIsNested = 0u;
    	
    	// 1.Enter Critical Region
    	app_util_critical_region_enter(&ucIsNested);
    
    	
    	g_usSendLength = usDataLength;
    	g_usSendCount = 0u;
    
    	
    	memcpy(g_pucSendBuffer, pucData, usDataLength);
    
    	g_bSendEnd = false;
    	g_bSendError = false;
    	
    	
    	// 2.Put TX FIFO
    	PutUART();
    	
    	
    	// 3.Exit Critical Region
    	app_util_critical_region_exit(0u);
    
    	return bSuccess;
    }
    
    bool IsEndUART(void)
    {
    	return g_bSendEnd;
    }
    
    
    /**
     * Function for Put UART
     */
    void	PutUART(void)
    {
    	uint32_t ulErrCode = 0u;
    
    	while (1)
    	{
    		// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v12.2.0%2Fgroup__app__uart.html
    		ulErrCode = app_uart_put(g_pucSendBuffer[g_usSendCount]);
    
    		if (NRF_SUCCESS == ulErrCode)
    		{
    			++g_usSendCount;
    
    			if (g_usSendLength <= g_usSendCount)
    			{
    				break;
    			}
    			else
    			{
    				// DO NOTHING
    			}
    		}
    		else if (NRF_ERROR_NO_MEM == ulErrCode)
    		{
    			// FIFO Full
    			break;
    		}
    		else
    		{
    			// UART Error
    			g_bSendEnd = true;
    			g_bSendError = true;
    
    			break;
    		}
    	}
    }
    
    void uart_event_handle(app_uart_evt_t * p_event)
    {
    	switch (p_event->evt_type)
    	{
    		// 4.An event indicating that UART has completed transmission of all available data in the TX FIFO.
    	case APP_UART_TX_EMPTY:
    		if (g_usSendLength <= g_usSendCount)
    		{
    			// complete
    			g_bSendEnd = true;
    		}
    		else
    		{
    			// Put TX FIFO(rest data)
    			PutUART();
    		}
    		break;
    
    	default:
    		// DO NOTHING
    		break;
    	}
    }
    

  • Hello, Thank you for your reply.

    Actually, as you mentioned, the issue was resolved by sending the contents of another buffer via app_uart_put() and taking measures not to call app_uart_put() when the APP_UART_TX_EMPTY event occurs.

    What I am concerned about now is whether there are any issues with the use of the critical region. Based on the premise that the critical region can suppress the occurrence of all events, we are using it for exclusive access to buffers and other resources in other communications as well. If there are issues with the use of the critical region, we believe it will be necessary to review other designs as well.

    If it's only a problem with UART, then there is no issue...

  • Hello,

    I still don't see why you need to have the critical region enter/exit, but if it works, I guess it is fine. 

    Are you doing anything else in the application that you want to stop interrupts from, other than UART? Any BLE activity, for instance?

    Best regards,

    Edvin

  • Hello, Thank you for your reply.

    For example, if an event occurs and modifies the shared buffer while it is being copied in the main loop, the atomicity of the buffer could be compromised. Therefore, to ensure atomicity for shared resources (such as buffers) that are accessed by both the main loop and events, I use a critical region when accessing from the main loop.

    The same concept applies to BLE and SPI data reception events. The received data is copied to the shared buffer on the event side. In the main loop, a critical region is used when accessing the shared buffer.

    In the above case, does the event suppression by the critical region also not function properly?

  • Ok, I guess that makes sense. At least if you risk the events changing the part of the buffer that is being transmitted. If you use some sort of ring buffer, or a double buffer, it can be avoided, though. Also, I don't know how much time you spend actually transmitting this buffer. It depends on the buffer size and the baud rate. If it takes a really long time, you risk loosing data other places, e.g. if the SPI data events are suppressed for too long, you may loose bytes there.

    BR,
    Edvin

  • I understand your response.

    If it takes a significant amount of time, such as when the buffer size is large, I will consider methods other than critical regions.

    Thank you for your response.

Reply Children
No Data
Related