cancel
Showing results for 
Search instead for 
Did you mean: 

Single shot timer for peripheral timeout check

regjoe
Senior

Hello,

I'm here on H75x. I'm looking for portable way to do a timeout check via timer.

Here, I want a timeout IRQ if UART receive DMA hangs up.

First I setup a OnePulse timer in Cube. It generates the following code:

if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
{
    Error_Handler();
}
if (HAL_TIM_OnePulse_Init(&htim6, TIM_OPMODE_SINGLE) != HAL_OK)
{
    Error_Handler();
}

The second init function does almost the same as the first but activates the OneShot mode which stops the counter immediately if IRQ is fired.

regjoe_0-1752772838108.png

I was looking for a suitable HAL function to setup the timeout value but failed. Instead I use

__HAL_TIM_SetCounter(htim, 1);		// set start value
__HAL_TIM_SetAutoreload(htim, ulDelay);	// set end value
__HAL_TIM_ENABLE(htim);

The last macro is required because after the RX DMA is done in time, the timer has to be stopped to prevent from false timeout IRQs.

Is this the way to go? Is there a HAL-compatible solution which I didn't get?

Thanks

16 REPLIES 16
TDK
Super User

Use HAL_UARTEx_ReceiveToIdle_DMA to receive data and get notified when an IDLE condition happens.

STM32CubeF4/Projects/STM32446E-Nucleo/Examples/UART/UART_ReceptionToIdle_CircularDMA/readme.txt at 5c556f15a2b074ff2d60461be34bfb6766f4df8c · STMicroelectronics/STM32CubeF4

If you feel a post has answered your question, please click "Accept as Solution".

Hi @TDK,

I already do, but I'm unsure if this function solves all sources of trouble. What happens if the sender does not respond at all / not in time? Are there any chances that the DMA blocks forever? I'll have to check multiple different error situations. This timer timeout shall handle all error situations which are probably not handled by ReceiveToIdleDMA, I hope.

Nevertheless, I want also other things do with the timer, e.g. wait a certain time and then start an action when the timer IRQ is fired e.g. insert a short delay between switching the RS484 driver IC to transmit mode before starting the transmit DMA.

Look at this video on using a timer callback. https://www.youtube.com/watch?v=o0qhmXR5LD0

 

You set the timer callback to start a timer. If you receive some data within a time frame, you can disable the timer. If no data within a time frame, you can have the timer callback call a function. 

If you FIFO doesn't work, then it's called GIGO.
TimerCallback tutorial! | UART and DMA Idle with multiple UART instances tutorial!

If you find my solution useful, please click the Accept as Solution so others see the solution.

If the sender doesn't respond, the DMA doesn't do anything since no data shows up and the line remains idle. It would not block since there is nothing for it to do. You would not be notified at all in this case and would need some other timer mechanism.

If you feel a post has answered your question, please click "Accept as Solution".
Karl Yamashita
Principal

@regjoe 

You can look at this project I just added to Github. https://github.com/karlyamashita/Nucleo-G071RB_UART_Timeout/wiki

 

I set a timeout for 5 seconds when you send a message "are you there?". It will start printing the count down value until it receives a message "Hello".

If it doesn't by the time it counts down to 0, it will print a "Failed" message.

If it does receive "Hello" in time, it'll stop the count down timer and print  a "Good" message.

 

But you can substitute the Good message with some other routine of your choice.

 

 

 

If you FIFO doesn't work, then it's called GIGO.
TimerCallback tutorial! | UART and DMA Idle with multiple UART instances tutorial!

If you find my solution useful, please click the Accept as Solution so others see the solution.

@TDK wrote:

If the sender doesn't respond, the DMA doesn't do anything since no data shows up and the line remains idle. It would not block since there is nothing for it to do. You would not be notified at all in this case and would need some other timer mechanism.


This is what I expected. This would cause that my statemachine is endless waiting for data. I want to notify the upper SW layer that the "read data from UART" request failed. The higher layer may retry and abort after certain retries.

Therefore I need kind of timeout check mechanism. I checked the reference manual for h75x family and found this note here:

48.5.16 USART receiver timeout
The receiver timeout feature is enabled by setting the RTOEN bit in the USART_CR2
control register.
The timeout duration is programmed using the RTO bitfields in the USART_RTOR register.
The receiver timeout counter starts counting:
• from the end of the stop bit if STOP = ‘00’ or STOP = ‘11’
• from the end of the second stop bit if STOP = ‘10’.
• from the beginning of the stop bit if STOP = ‘01’.
When the timeout duration has elapsed, the RTOF flag in the USART_ISR register is set. A
timeout is generated if RTOIE bit in USART_CR1 register is set.

I stepped thru the code of HAL_UARTEx_ReceiveToIdle_DMA:

regjoe_0-1752844731232.png

It seems that CR2.RTOEN and RTOR.RTO are not configured. Nevertheless, I get an DMA done event via HAL_UARTEx_RxEventCallback after data received.

I found a HAL function HAL_UART_EnableReceiverTimeout(). It seems that it is used to specify an idle time, but I'm unsure if this does affect the same idle time used by HAL_UARTEx_ReceiveToIdle_DMA.

Have I mixed up two different things, Idle Time and Receive Timeout?

HAL_UARTEx_ReceiveToIdle_DMA uses the IDLE event, which happens immediately after characters are no longer sent. It is not configurable.

HAL_UART_EnableReceiverTimeout seems like what you want. 

If you feel a post has answered your question, please click "Accept as Solution".

 


@TDK wrote:

HAL_UARTEx_ReceiveToIdle_DMA uses the IDLE event, which happens immediately after characters are no longer sent. It is not configurable.


I already tested and it works here for me. What puzzled me is the wording here. What is the difference between "Idle line" and "idle frame"? RM0433 says:

 

Bit 4 IDLE: Idle line detected
This bit is set by hardware when an Idle Line is detected. An interrupt is generated if
IDLEIE = 1 in the USART_CR1 register.

Seems there are 2 different IDLE detection modes implemented in H75x. First mode is used by HAL_UARTEx_ReceiveToIdle_DMA and is triggered by "Idle line" detection.

Second mode is used for MODBUS/RTC but I guess it can be used for other purpose than MODBUS only.

It is triggered if 2 or more "idle frames" are detected:

In this mode, the end of one block is recognized by a “silence” (idle line) for more than 2 character times. This function is implemented through the programmable timeout function.
Bit 23 RTOEN: Receiver timeout enable
This bit is set and cleared by software.
0: Receiver timeout feature disabled.
1: Receiver timeout feature enabled.
When this feature is enabled, the RTOF flag in the USART_ISR register is set if the RX line is idle (no reception) for the duration programmed in the RTOR (receiver timeout register).
When an idle frame is detected, it is handled in the same way as a data character reception except that an interrupt is generated if the IDLEIE bit is set.
An Idle character is interpreted as an entire frame of “1”s. (The number of “1” ‘s includes the number of stop bits).

 

I guess that the only difference is that in the first "Standard" mode the idle timeout is fixed to 1 character = 6-9bits.

In the latter "MODBUS/RTC" mode the idle timeout is configurable from 2 to x characters.

First mode is configured via CR1:IDLEIE, second mode via CR2:RTOE plus RTOR:RTO.

 

MODBUS/RTC mode is configured via HAL_UART_ReceiverTimeout_Config() and enabled via HAL_UART_EnableReceiverTimeout(). 

In both modes, receive is started by HAL_UARTEx_ReceiveToIdle_DMA() and HAL_UARTEx_RxEventCallback() is run after "idle line" detection.

If UART_Start_Receive_DMA() is used, HAL_UART_RxCpltCallback() is run after reception of requested data bytes.

Also, the wording "ReceiverTimeout" is confusing me. It seems is not used to specify a data receive timeout, which means that "no/not enough data has been received after a certain time. Here it is used to specify the time for which the RX line is idle, e.g. the "idle time".

Hi @Karl Yamashita,

I guess the TimerCallback stuff is your implementation and not ST HAL?

I did not find out if this code can be used for a one shot timer, e.g. which stops automatically and does not repeat and where a different timeout can be set before it is called.

I wonder why there is no timer configured in .ioc, you are not using the CubeMx code generator?