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

Reply
  • 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

Children
  • 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, and sorry for the late reply.

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

    Are you saying that you don't want the APP_UART_TX_EMPTY event when you are between 1 and 3? 

    Is it not possible to store the data in another buffer, without using app_uart_put() between 1 and 3? app_uart_put() will push the data out through the UART, so if that is not what you want to do, perhaps you can store it in some other buffer, and when that buffer is full, you can send it out via app_uart_put()?

    Or did I misunderstand something?

    Best regards,

    Edvin

  • 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?

Related