Hi Nerds here,
You may want to discuss the results of my comparison between two methods of timing:
- timing interrupt driven
- timing millis() driven
I am using a quite extensive sketch that needs processing at three timing levels:
a) every 10 ms gathering data and integrating averages, processing the serial menu.
b) every 50 ms computing 10mS averages, calculating maximum and minimum, doing some maths, integrating averages for the next level.
c) every second computing 50 ms averages, doing some maths, more computing, and printing out some results ( which is a part of the process that takes the most time ).
a) Timing interrupt driven.
Since the processing involved can be quite extensive, I do not process it directly within the interrupt, but just set a flag, and process the sketch whenever the flag is set.
I used to use 2 interrupt timers.
-one at 500Hz to set the flags for 10 ms and 50 ms, flags are reset once the job is done.
-another one at 1Hz that sets the flag for one second, flag it is reset once the job is done as well.
As a part of the reporting, the number of times each step is processed within one second is displayed.
Here is the result using the method with interrupts:
22:39:11.721 -> Vcc=4.94 | Vbat=12.50 | 22°C | A0Raw=425 | 10ms=94 | 50mS=20 | Avg=28.08 | Min=25.57 | Max=30.08
22:39:12.724 -> Vcc=4.94 | Vbat=12.56 | 22°C | A0Raw=421 | 10ms=94 | 50mS=20 | Avg=27.70 | Min=25.07 | Max=29.96
22:39:13.727 -> Vcc=4.94 | Vbat=12.48 | 26°C | A0Raw=427 | 10ms=94 | 50mS=20 | Avg=29.33 | Min=27.20 | Max=31.71
22:39:14.731 -> Vcc=4.94 | Vbat=12.48 | 26°C | A0Raw=450 | 10ms=94 | 50mS=20 | Avg=28.95 | Min=27.07 | Max=31.21
22:39:15.734 -> Vcc=4.94 | Vbat=12.40 | 23°C | A0Raw=426 | 10ms=94 | 50mS=20 | Avg=26.95 | Min=24.69 | Max=29.21
The timestamp in the first column of the display shows that:
- The 1 Sec timing is quite precise with a bit of jitter.
- The: 10 mS column shows that ( probably during printing) the 10 ms process cannot be 100% granted.
- The: 50 mS column is executed precisely.
B) timing using indirectly millis()
I made a second version of the program, and eliminating the interrupt and the flags replacing them by the runEvery macro (millis()-based used @10mS, 50mS and 1000mS.
The behavior of the program was roughly the same. The details in reporting gave following results:
22:53:13.147 -> Vcc=4.89 | Vbat=5.32 | 31°C | A0Raw=644 | 10ms=100 | 50mS=19 | Avg=53.77 | Min=51.77 | Max=56.40
22:53:14.151 -> Vcc=4.87 | Vbat=6.64 | 31°C | A0Raw=685 | 10ms=100 | 50mS=20 | Avg=58.53 | Min=56.03 | Max=60.91
22:53:15.154 -> Vcc=4.87 | Vbat=5.56 | 31°C | A0Raw=705 | 10ms=101 | 50mS=21 | Avg=62.79 | Min=60.91 | Max=64.30
22:53:16.158 -> Vcc=4.87 | Vbat=5.64 | 31°C | A0Raw=693 | 10ms=99 | 50mS=19 | Avg=63.05 | Min=60.54 | Max=65.43
22:53:17.161 -> Vcc=4.87 | Vbat=7.00 | 31°C | A0Raw=727 | 10ms=100 | 50mS=20 | Avg=63.67 | Min=61.54 | Max=66.05
22:53:18.164 -> Vcc=4.87 | Vbat=5.76 | 29°C | A0Raw=708 | 10ms=100 | 50mS=21 | Avg=60.91 | Min=59.29 | Max=63.92
Compared to the message with interrupts, we can notice following differences:
The one second cycle has a bit more jitter, but there is no huge difference between the two methods.
The 10 ms cycles are all executed with a jitter, if the cycle could not happen, we will get one cycle more in the next second
The 50mS cycles are also executed with a jitter, if the cycle could not happen, we will get one cycle more in the next second.
My conclusions: both methods are roughly equivalent,
-the interrupt timer driven processing keeps the precise timing, and skips cycles which cannot be processed.
-the millis() driven processing keeps the amount of total cycles, but brings a strong jitter on every level.
Summary: There is no clear performance winner, in every case you need to count the number of really executed cycles until the next level, if you are doing averages.
From the point of view of the user, there is no noticeable difference.
Regarding programming: The runEvery Macro is closer to natural language and is more portable.
The interrupts method must be adapted to the different processors, and requires quite tricky compiler directives if you want a code to be half way portable.
I will give the code used in the next message...