Arduino-FreeRTOS-SAMD21
Arduino-FreeRTOS-SAMD21 copied to clipboard
Changing configTICK_RATE_HZ to 100
Hello,
I am working on a project that has some atypical constraints that keep popping up (reading sensors at high cycle rates, and running tasks for many hours on end). And I've realized that having the configTICK_RATE_HZ set to 1000 may be both cutting into my available cycle times and limiting how long my tasks can run (due to overflow of uint).
The FreeRTOS documentation recommends configTICK_RATE_HZ to be set somewhere in the 100's (yet their documentation is often in 1000).
If I change the configTICK_RATE_HZ to 100 in the FreeRTOSConfig.h it would seem to work fine, except the Arduino function millis()
is now counting in centi-seconds (aka 10x faster than milliseconds).
My guess is that this is a split-brain conflict between how Arduino thinks the MPU timer interrupts are configured and how the FreeRTOS actually configures the interrupts.
I dug through the arduino core and found where millis()
is defined:
https://github.com/arduino/ArduinoCore-samd/blob/9f91accecc8298976670234e4d6ac0afef5c7a39/cores/arduino/delay.c
And that seems to bubble up to a SysTick_Handler
.
And interestingly enough the FreeRTOSConfig.h has reference to this function: // #define xPortSysTickHandler SysTick_Handler
But sadly, when I uncomment it I get a build error:
/tmp/arduino-sketch-8207F96DB73DDE6C846722181CBF0D7B/../arduino-core-cache/core_P1AM-100_samd_P1AM-100_native_9b4df8df00aa424bbcc46d14ec2deaea.a(cortex_handlers.c.o): In function `SysTick_Handler':
/home/andy/.arduino15/packages/P1AM-100/hardware/samd/1.6.20/cores/arduino/cortex_handlers.c:171: multiple definition of `SysTick_Handler'
/tmp/arduino-sketch-8207F96DB73DDE6C846722181CBF0D7B/libraries/FreeRTOS_SAMD21/port.c.o:/home/andy/Arduino/libraries/FreeRTOS_SAMD21/src/port.c:402: first defined here
collect2: error: ld returned 1 exit status
Error during build: exit status 1
I'm guessing that I need to either modify my board port (the P1AM-100) to not define the SysTick_Handler and let FreeRTOS do that (which I'll try next) but wanted to ask/seek guidance on this issue since I am quite over my head in this area.
Let me know if you have any sage advice in approaching this matter.
Thanks.
Hello centerorbit,
Hmmm this is a interesting pickle to be in. The original atmel port code I found for this chip just so happened to use the same hardware timer as the arduino core system tick uses in delay. I figured why beat them, join them, and thats why I chose the 1000 to match the arduino timer setup.
I have never tried changing this, but I think port.c in the library is where you want to focus your attention about the hardware timer interupts. When porting FreeRtos to a microcontroller, port.c defines some low level micrcontroller specific functions. It appears prvSetupTimerInterrupt() is what is changing the register that changes the hardware timer interrupt tick speed, and portNVIC_SYSTICK_LOAD is the register address this timer uses to change the tick rate?
I would look into how to setup and configure your own hardware timer, and point your FreeRtos port to use that one instead. Its convenient arduino has this timer setup and running already in milliseconds, but you can setup your own to custom tick speeds.
Also not having seen your code, below I have some random ideas that may or may not be useful at all.
Just for arguments sake, do you really need the millis() function? Assuming your not using any librarys that irreversibly depend on the millis() functions, you can and should use the rtos delays instead of poling for time expired in your rtos loops. The rtos delays are actually way more desirable, as to not create threads that are hogging the cpu, or starving other threads. Maybe not an ideal work around in your case, but would make it easier to allow the Rtos to set the interrupt timer interval and use the rtos for all timing.
Your problem does make my spidey sense tingle that you may have one or more threads that are hogging the cpu. Not seeing how you have your threads or code written, you may want to revisit them and make sure you are using the FreeRtos delays pretty much exclusively, and eliminate all arduino delays() or millis() while loops to make sure the thread blocks as much as possible to allow for smooth scheduling. Also think about your threads priorities, this could have a huge impact on how the cpu prioritizes what should be run and when.
You could be running into a case where the first part of a thread should be a super high priority and serviced immediately, but the actual handling of the data can be a way lower priority and can wait for awhile to actually be dealt with. These cases you can split into two threads of different priority, or more likely a hardware interrupt that services the sensor, and then a lower priority thread to get around to doing something with the data. You may need to take a step back and think about your software architecture to see where things could be going wrong.
Hopefully something here will help you find a solution, Good Luck!
Hey @BriscoeTech
Thank you for the reply!
I will look into setting up a hardware clock different than the general one. I came across this great article about SAMD21 clocks that will prove invaluable: https://blog.thea.codes/understanding-the-sam-d21-clocks/
I recently implemented this watchdog code, which uses the 2nd clock (but SAMD21 has 6 clocks, so I should have more than enough!): https://forum.arduino.cc/t/wdt-watchdog-timer-code/353610/11
I appreciate your general random ideas, and to help clarify:
-
"do you really need millis()". The product I'm working on is taking scientific measurements from various sensors, and so I use millis() to timestamp these measurements when they are taken. For all other loops and delays I use RTOS's built-in functions.
-
You are right to have spidey senses tingle. Under normal circumstances one wouldn't need to ever bother with this level of control. I happen to have a whole plethora of sensors I'm dealing with (including 6 thermistors), of which some of the science needs are/were to sample them in a window less than 21ms apart. I was able to close to the ~20 ms range, and so I was looking at ways to push this even further. I suspect I may be able to get to 10 or 15 ms by changing that tickrate. (A 150% to 200% increase).
-
I do have a fair bit of thread priority juggling going on. I do like your bootstrapping task idea, I will have to consider that in the future. I use Queues to pass sensor data between tasks, I use a queue "owner" methodology where the Task that will be pulling data off of the Queue is the one to initialize it. So I set all of these owners with a high initial priority, to preempt any task from writing to an uninitialized queue, and after Queue creation is complete the task lowers it's own priority down to "Normal" levels.
Thank you for the tips on where to look in port.c, etc. This feature has been lowered in terms of development priority, so I'm not sure I'll get to it, but I'd love to have the background knowledge at some point. It may come in handy for future projects.