What is the relationship between FreeRTOS and interrupts?
The Arduino PORTENTA Pro C33 implementation of FreeRTOS is an interrupt driven process with a 1 ms time between ticks. FreeRTOS is but one of many Interrupt Service Routines (ISR) running on the C33. We face a challenge when those ISRs compete for microcontroller resources in a real-time system. Let’s frame this problem in the form of a project.
This engineering brief is a continuation of an earlier introduction to FreeRTOS. Recall that the Arduino PORTENTA Pro C33 was programmed with FreeRTOS to include a variety of tasks including blinking an LED, sending data to the Arduino serial monitor, and communication over wired ethernet. To this mix we will add a quadrature encoder as described in this article. Also, recall that the quadrature encoder has a moderately high speed of 11.2 k counts per second requiring careful ISR integration to prevent a missed count. The quadrature encoder code included a state machine to identify common faults such as a miscount from position 00 to 11.
Tech Tip: Always remember that a quadrature encoder system is worse than useless if even a single counter transition is missed. The physical mechanism will flounder and become increasingly lost if the microcontroller is too busy to track the encoder.
Figure 1 presents an overly simplistic representation of the problem. Here we see the A and B outputs of the quadrature encoder. The third line is a pulse indicating that the ISR has handled the quadrature encoder event. Notice that the circled transition is delayed by the higher priority FreeRTOS ISR. Also note that the 12 VDC motor is moving very slowly with 1.5 VDC applied. If we speed up the motor, the FreeRTOS-associated delay period will be longer than the quadrature encoder period. The result is a missed transition and a lost mechanical system.
Please do not misunderstand. In no way am I suggesting that the Arduino and the default FreeRTOS or interrupt settings are at fault. To the contrary, the settings are perfectly appropriate for the majority of projects. Instead, this project with its fast real-time operation of the quadrature encoder is an exception that demands other than default settings.
Figure 1: The logic analyzer shows the quadrature encoder A and B outputs along with a tick indicating the time spent in the associated ISR. Observe the jump when FreeRTOS has priority.
Tech Tip: If you have not already done so, you are encouraged to review the previously mentioned quadrature encoder article. At first glance the Finite State Machine (FSM) based code seems overly complex, especially when we consider that the ideal quadrature encoder code could be written in a few lines. However, when we consider code development and the complexities of coordinating multiple competing ISRs, it’s good to know that a time-critical section has a built-in failsafe mechanism. The FSM will not catch all errors, but it will easily catch a missed transition. This is especially important for a system operating at over 11,000 interrupt per second.
How does FreeRTOS conflict with other high-speed interrupts such as a quadrature encoder?
FreeRTOS is lightweight Operating System (OS) purpose-built for microcontrollers. It is designed to coordinate and control the activities of multiple tasks so that they appear to operate seamlessly. Recall that a microcontroller is a limited resource with the ability to run one and only one task at any given time. For now, we will ignore multiple core processors and enhanced peripherals such as Direct Memory Access (DMA).
Tech Tip: The task is the fundamental building block for FreeRTOS. The objective is to divide a larger microcontroller program into several meaningful tasks and then used FreeRTOS mechanisms to coordinate those tasks. These tasks may be divided by function / hardware they may also be divided by priority. For example, a simple robot may include tasks to control the motors. It may then have higher level albeit slower tasks to control the robot behavior. In some respects, this reflects the difference between the human autonomic and reflexive nervous systems.
Priorities within priorities with software and hardware
The purpose of FreeRTOS is to select and direct the running task and then swap out tasks based on the programmer’s assigned priority for each task. This priority is assigned by the programmer as part of the task instantiation. For example, a responsive Proportional Integral Derivative (PID) control task will be assigned a higher priority than the background communications task. In this example, the PID foreground task must be completed at regular intervals to maintain stability of the real-time process. Generally, the background communication with the user is not time critical.
This FreeRTOS-assigned priority should not be confused with the priorities of the microcontroller’s various ISRs. In this context, FreeRTOS tick is one of many event (timer) driven ISRs competing for microcontroller resources. For example, FreeRTOS context swapping mechanism itself exists largely as an ISR driven by a hardware timer. For every tick of the hardware timer, the FreeRTOS ISR is swapped in and performs its high-level magic such as the activating the PID activating the communications. Like all ISRs, FreeRTOS is written as small as possible so that it does not hog the microcontroller resources.
At this low-level analysis, the FreeRTOS ISR competes with other ISR such as a change on pin:
attachInterrupt(digitalPinToInterrupt(QUAD1_A_PIN), ISR_QUAD1, CHANGE);
attachInterrupt(digitalPinToInterrupt(QUAD1_B_PIN), ISR_QUAD1, CHANGE);
To summarize, don’t confuse the high-level FreeRTOS task priorities with the low-level ISR priorities. The FreeRTOS task take place at the software level while the ISR priorities are a hardware configuration.
Hardware based ISR priorities and the ARM NVIC
The PORTENTA Pro C33 features a sophisticated Renesas Arm Cortex processor. This microcontroller is designed to operate in a complex real-time environment. Consequently, it features a robust interrupt vectoring hardware mechanism that allows priority assignments for the associated ISRs. Even better, it features a Nested Vectored Interrupt Controller (NVIC) where a high priority ISR can preempt a lower priority ISR. As a simple example, we can assign the FreeRTOS ticking ISR one priority and the Arduino attachInterrupt another.
That brings us to the core of the article.
These hardware-based priorities must be correctly assigned so that the high-speed quadrature encoder operates in the foreground. In fact, it must have a priority higher than FreeRTOS to prevent missed pulses. Stated another way, the NVIC priority for the quadrature encoder is selected so that it preempts FreeRTOS.
How can we change the NVIC ISR priorities for the Arduino PORTENTA Pro ARM processor?
While there are several ways to change the behavior of the NVIC, we will focus on a simple single-line-change solution. This requires us to explore the inner workings of the Arduino IDE and locate the IRQManager.cpp file from within the renesas_portenta folder:
C:\Users\your_name\AppData\Local\Arduino15\packages\arduino\hardware\renesas_portenta\1.1.0\cores\arduino\IRQManager.cpp
Once you locate the file, change the priority. In this example, the default Arduino was changed from 12 to 5 (smaller numbers have greater priority). In relative terms, the external interrupts associated with the quadrature encoder now have the highest priority.
#define EXTERNAL_PIN_PRIORITY 5 // default is 12
What are the limitations for changing Arduino ARM NVIC priorities?
Personally, I don’t like this solution as we expose sensitive files deep within the Arduino IDE making it easy to break Arduino. Also, an update to the IDE would require the new file to be modified. Needless to say, this solution is not portable, requiring every programmer to make the underlying changes. Finally, there is also no guarantee that the Arduino team wouldn’t change the underlying structure.
If you are skilled in the Arduino IDE and the ARM NVIC operations, please comment or provide a better solution. It would be nice if we could overload the attachInterrupt( ) function with the desired ARM NVIC priority. An alternative, perhaps attachInterrupt( ) could return the NVIC number. This would allow the use of functions such as NVIC_SetPriority( ) .
What are the results when we configure the ARM NVIC so that the quadrature encoder has a higher priority?
For this demonstration, we changed the interrupt priorities so that the quadrature encoder has top priority. This was done by changing the NCIV priority from a default of 12 to 5. The results show significant improvement over Figure 1. The circled delay is completely absent, and motor now operated at maximum speed without loss of count. Note that this example is a continuation of an earlier project. The C33 is programmed with a heavy load including streaming “the quick brown fox jumps over the lazy dog” via a wired Ethernet connection and streaming the position count over a serial connection.
You can download the code here: FreeRTOSISR.zip (7.3 KB)
Recall that the quadrature encoder includes a robust state-based error detection mechanism. No errors were detected while operating the system.
Parting thoughts
The Arduino PORTENTA C33, with its ARM Cortex microcontroller, features a versatile interrupt control via the ARM NVIC mechanism. When combined with FreeRTOS, we must be carefully assign priorities both with in the FreeRTOS software and the hardware-based interrupts. The quadrature encoder mechanism featured in this article provides a good starting point to explore the complexity. You are encouraged to continue learning as we have only begun to explore the rich topic.
Please leave your comments and suggestions below. As previously mentioned, this is a project in need of an elegant solution for setting the NVIC priorities from the Arduino .ino setup ( ) function.
Best wishes,
APDahlen
Please follow this link for related Arduino education content.
About the author
Aaron Dahlen, LCDR USCG (Ret.), serves as an application engineer at DigiKey. He has a unique electronics and automation foundation built over a 27-year military career as a technician and engineer which was further enhanced by 12 years of teaching (interwoven). With an MSEE degree from Minnesota State University, Mankato, Dahlen has taught in an ABET accredited EE program, served as the program coordinator for an EET program, and taught component-level repair to military electronics technicians. Dahlen has returned to his Northern Minnesota home and thoroughly enjoys researching and writing articles such as this.