Page 1 of 1

CAN: Implementing a minimum TX delay with µs-Granularity for consecutive frames

Posted: Thu Aug 31, 2023 2:33 pm
by DrMickeyLauer
I know that there are a bunch of threads about such a topic, but I don't think that they are applying to my problem.

Short background: I'm working on a CAN-Bus adapter based on the ESP32C6 (later ESP32S3). I'm sending a lot of data to the CAN-Bus, however I'm not allowed to send the can frames without any delay. The shortest delay I can achieve using FreeRTOS is 1ms (I have set `CONFIG_FREERTOS_HZ=1000` and it works fine so far), which is way to long, since I need e.g., 350µs.

I tried using a busy waiting loop with code such as

Code: Select all

static inline void delayMicrosecondsBusyWaiting(uint32_t us) {

		uint32_t m = (unsigned long)esp_timer_get_time();
		if (us) {
			uint32_t e = (m + us);
			if (m > e) { // overflow
				while ((unsigned long)esp_timer_get_time() > e) {
					asm volatile("nop");
				}
			}
			while ((unsigned long)esp_timer_get_time()) {
				asm volatile("nop");
			}
		}
	}
However, if I call that (instead of `vTaskDelay`) during my CAN send packages loop, I'm getting:

Code: Select all

E (784675) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:
E (784675) task_wdt:  - IDLE (CPU 0)
E (784675) task_wdt: Tasks currently running:
E (784675) task_wdt: CPU 0: TwaiReceiver
E (784675) task_wdt: Print CPU 0 (current core) registers
and the system hangs.

Is there another way to achieve a delay with microsecond granularity when sending out CAN frames? Or will I have to patch the Twai driver itself?

Re: CAN: Delay with Microsecond-Granularity vs. Watchdog

Posted: Thu Aug 31, 2023 2:56 pm
by DrMickeyLauer
Ok, obviously I had a bug in the code which was missing a `< e` in the loop. That said, the question is still valid whether there is a better way to achieve the µs granularity for sending consecutive CAN frames.

Moreover... would it perhaps be better to call taskYield() instead of using an asm NOP?

Re: CAN: Delay with Microsecond-Granularity vs. Watchdog

Posted: Fri Sep 01, 2023 1:48 am
by ESP_Sprite
You could use esp_timer for this. Create a timer with a callback function that sets a semaphore or task notification or something, then when you need to wait, start this timer with a given timeout and wait for that semaphore or task notification.

Note that taskYIELD likely does nothing in this case as it simply will cause the RTOS to schedule the highest priority unblocked task. Unless you have another task with the same prio that is unblocked, that will probably be the task that called taskYIELD in the first place and as such there's no effect.

Re: CAN: Delay with Microsecond-Granularity vs. Watchdog

Posted: Fri Sep 01, 2023 9:27 am
by MicroController
Besides the clean/"CPU-friendly" solution Sprite described, you can also use

Code: Select all

#include "rom/ets_sys.h"
ets_delay_us(x);	
which does the exact same thing your delayMicrosecondsBusyWaiting does.

Re: CAN: Delay with Microsecond-Granularity vs. Watchdog

Posted: Mon Sep 04, 2023 7:26 am
by DrMickeyLauer
Thanks @ESP_Sprite and @Microcontroller for answering.

This works ­– somewhat. While setting a delay 330µs leads to roughly 3 CAN frames being sent per millisecond, the timing is all but reliable. Sometimes it's 100µs between consecutive frames, sometimes it's 700µs. This is not the precision I need for my task. I guess the reason is because of the TWAI TX queue. At the end of the day, the actual transmission task reads the packages from a queue and sends them 'as they are coming'.

I'm afraid the way to really tackle this problem is to adjust the TWAI driver itself, i.e.:

1. Enhance twai_obj_t with a minimum_delay value that holds the number of µs and a timestamp (for last transmission).
2. In twai_handle_tx_buffer_frame: Check how much time has passed between the last transmission, if we're "too fast", call ets_delay_us and wait.

Do you think that approach could work?

Re: CAN: Delay with Microsecond-Granularity vs. Watchdog

Posted: Mon Sep 04, 2023 9:34 am
by MicroController
Do you think that approach could work?
Could probably be made to work somewhat; but twai_handle_tx_buffer_frame(...) runs from the TWAI ISR, and you don't really want to delay inside an ISR...

Also, the time at which a CAN frame is actually successfully put on the wire may be non-deterministic due to arbitration. So what you actually want is to delay a certain amount of time not between the start of two sends, but from the end of one transmission to the start of the next. Maybe in response to the TX_IDLE alert.

Re: CAN: Delay with Microsecond-Granularity vs. Watchdog

Posted: Thu Sep 21, 2023 2:49 pm
by DrMickeyLauer

Code: Select all

TX_IDLE
is too late, since it occurs only when the queue on the TWAI side has been emptied.

One approach would be hooking into

Code: Select all

twai_handle_tx_buffer_frame
, where it says:

Code: Select all

    if (p_twai_obj->tx_msg_count > 0 && p_twai_obj->tx_queue != NULL) {
        twai_hal_frame_t frame;
        int res = xQueueReceiveFromISR(p_twai_obj->tx_queue, &frame, task_woken);
        if (res == pdTRUE) {
            twai_hal_set_tx_buffer_and_transmit(&twai_context, &frame);
        } else {
            assert(false && "failed to get a frame from TX queue");
        }
    } else {
        //No more frames to transmit
        twai_alert_handler(TWAI_ALERT_TX_IDLE, alert_req);
    }
Instead of directly calling

Code: Select all

twai_hal_set_tx_buffer_and_transmit
, I'd like to delay that for a certain time -- usual values are ECU specific, in the range of about 350µs to 2ms.

Now

Code: Select all

ets_delay_us
is most likely a bad idea, since we are inside an ISR. I tried it anyways and somehow the delays started to pile up and the timing was completely erradic.

So, do you have any idea how I could do this? Is there a hardware timer that could trigger the TWAI IRQ with an event like

Code: Select all

TWAI_HAL_EVENT_TX_RESUME
(which I would have to create) that leads to

Code: Select all

twai_intr_handler_main
being run, which could finally call

Code: Select all

twai_hal_set_tx_buffer_and_transmit
then?

Excuse me if that all doesn't make sense, in driver code, I'm a bit out of my depth here...

Re: CAN: Implementing a minimum TX delay with µs-Granularity for consecutive frames

Posted: Thu Sep 21, 2023 10:46 pm
by MicroController
TX_IDLE

is too late, since it occurs only when the queue on the TWAI side has been emptied.
Huh? I don't get that. What's the difference between having a message sit in the queue for 300us vs. putting it into the queue after 300us?

Re: CAN: Implementing a minimum TX delay with µs-Granularity for consecutive frames

Posted: Fri Sep 22, 2023 8:14 am
by DrMickeyLauer
What's the difference between having a message sit in the queue for 300us vs. putting it into the queue after 300us?
Ok, so you would vote to do this outside the driver. I don't think that's the correct place to implement such a functionality, in particular because every TWAI client would then need to handle that (there might be multiple, since we might talk to multiple devices on the bus in parallel). It would also render the TX queue basically useless. If you implement protocols such as ISOTP, a design where the actual driver handles STMIN would IMHO be better.