Fix multiple timing errors in example sketch #6
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This pull request fixes many errors in SimpleEMGFilters.ino that made its timing unreliable.
Use
unsigned long
to get correct arithmetic on timingsmicros()
returns an unsigned 32-bit number (unsigned long
). Its value rolls over roughly every 71.6 minutes. When this happens,timeStamp
is close to 232, whilemicros()
is close to zero. If computed asunsigned long
, the differencewraps modulo 232 to the correct result. If computed as
unsigned long long
, however, the difference wraps modulo 264 and ends up being too large by0xffffffff00000000
. This ends up causing a delay that is way too large.The comment “
micros()
will overflow [...]” has been removed, as it becomes irrelevant once the timing arithmetics are done with the correct type.Update the timestamp with
+= interval
in order to avoid driftThe sketch currently tries to control the sampling rate by having
loop()
execute in exactly 1 ms. However, this is unreliable, as there are a few instruction for which the execution time is not accounted for, including returning fromloop()
and calling it again. This causes the timing to slowly drift, and the average sampling rate to be slightly lower than intended.This is fixed by changing the meaning of
timeStamp
: it is now the time when a sample ought to be taken. The actual sampling can happen a few instructions later but, astimeStamp
is always updated by addinginterval
, these timing errors are non-cumulative, and the average sampling period is exactly 1 ms (non counting for the inaccuracy in the physical oscillator).Avoid printing more that what can be sent between samples
At 115200 b/s, printing the string
"Squared Data: "
takes about 1.215 ms, which is more than the intended sampling period. It is thus impossible to sample at the intended frequency while printing this message for each sample.In debug mode, do not count the cost of printing to the serial port
Again, because of the speed of the serial link, if
_DEBUG
is defined, it is impossible to keep up with the intended sampling rate. Thus, we don't even try, and instead we attempt to give an accurate estimate of the time cost of the filter. This requires flushing the output buffer in order to not count the time cost of the serial communication.Use
delayMicroseconds()
for better resolutionThe required delay is always less than a millisecond. The
delay()
function is thus not appropriate, as its resolution is one millisecond.Only delay if there is some time left
If the program ever gets a bit late for a sample,
interval - timeElapsed
wraps modulo 232 to a very large number, which causes a delay of more than 70 minutes. Avoid this by delaying only if we do have some time to kill.