My explanation for this issue is that the last pulse generated by the
tone() function is interrupted while the output is on a high level. This results in the last pulse being shorter than it should be to correspond to the desired sound frequency.
While the pulse length doesn't matter for a continuous output (where the pulse frequency determines the sound frequency), a single short pulse can be interpreted as a pulse of a higher frequency.
According to Fourier theory at least its fundamental frequency will be higher than the one of the longer pulses.
Observations that might substantiate my theory:
- the issue happens randomly in ~50% of the cases (for a 50% duty cycle signal).
- the frequency of the erronous tone varies and is always higher than the desired frequency, depending on how short the trailing pulse is.
These seem to be the case for the simulation.
a) provide the
tone() function with the optional
duration argument and hope the library itself addresses this problem (which it actually might not as the issue is even in the simulation)
b) make sure to only use multiples of the square wave period for
duration, so the wave always stops on a low level
According to my theory, a low-pass filter will not work as a fix because the fault frequency can be close to the desired frequency and is the "primary signal", not just some low amplitude harmonic which would be easier to filter from a (much stronger) fundamental.