Passing data into interrupt service routine


I have been developing a control software (based on FreeRTOS) for field oriented control of three phase induction motor. My plan is to calculate the whole torque control loop in the interrupt service routine (ISR) associated with the “end of the A/D conversion” interrupt which will be invoked regularly with 100 us period. Besides the torque control loop the associated ISR will be also responsible for implementation of software oscilloscope. Operation of the software oscilloscope will be configurable in such a manner that it will be possible to set how many samples are reserved for the pre-trigger, to set the trigger variable and trigger condition (given variable greater than or less than the specified value).

My question is how to pass the oscilloscope configuration into the ISR (more precisely into the oscilloscope module which is invoked from the ISR) from a FreeRTOS task in such a manner that it is ensured that in all cases the oscilloscope works with consistent configuration i.e. always “new” pre-trigger setting, “new” trigger variable and “new” trigger condition are used together (it is not allowed to use let´s say “new” pre-trigger setting, “new” trigger variable with “old” trigger condition).

My first idea was to exploit the FreeRTOS queues for data exchange between ISR and FreeRTOS task. I have found that the functions xQueueSendToBack and xQueueReceive disabling interrupts for some not negligible time. I am afraid of deterioration of torque control loop parameters due to transport delay insertion resulting from interrupt disabling.

Could anybody recommend me some proven solution for exchanging data between ISR and FreeRTOS task suitable for my purposes? Thanks in advance for any suggestions.

You could use 2 structs containing the trigger settings and 1 shared pointer to one of them. Setup the config in the shadowed struct and atomically switch the current pointer e.g. by using taskDISABLE/ENABLE_INTERRUPTS.
That’s the fastest way in the sense of minimal interrupt disable time.
Alternatively you can also directly modify shared trigger config data enclosed by taskDISABLE/ENABLE_INTERRUPTS.

1 Like

Hello Hartmut Schaefer, thank you for elegant solution.

If the writing of a pointer value is atomic on the processor (and it often is), you don’t even need the taskDISABLE_INTERRUPTS.

I think you dont have to pause the interrupt. I’m also working on a similar project where I need to pass a set of PID params into an ISR that controls the PFC. The PID values have to be consistent like in your case.

My solution would be to have two copies of PID params (namely active and snapshot) and one flag (global volatile variable) that guards the second set. The active variables can be safely accessed (r/w) by the task. In my case, is the Modbus codes.

When the task detects a change (in the active variables) or there is a forced update request (by application logic, from the current task or another one), the task sets the flag, then performs active->snapshot copy, and finally clears the flag. If control ISR preempts the CPU and the ISR service code sees the flag has been set, it knows the snapshot is not clean yet (the task was doing active->snapshot copying when it got interrupted) and gives up access to the snapshot for this time. If the ISR service code finds that the flag has not been net, the snapshot contains valid data, and the ISR service code can safely access them.

Additionally, you can add another flag to the guarded region (between set and reset of the abovementioned flag) as the change indicator so the ISR can know when the settings change.

This approach does not delay the ISR servicing but at the cost of triple memory usage (the active one for the task, the snapshot one for the intercommunication, and one for the ISR code itself). It cannot guarantee that the ISR sees each snapshot, but as the control ISR is periodic, it will always pick up the latest valid one.

Also, I think it is better to modify the enter/exit critical region functions so that it does not pause the control ISR. It is safe to do so as long as the control ISR does not call ANY FreeRTOS-related functions, which should be the case in 99.99% of scenarios.

It would be even better if the CPU supports ISR nesting so that the control ISR can preempt the tick ISR used by FreeRTOS, which guarantees minimum latency. According to FreeRTOS documentation for Arm Cortex-M core, this is possible for Arm M3 and M4. Nesting is not available on M0. Ti’s C2000 DSP can also “emulate” nesting with some simple changes to the ISR code.