2. Comments
2.1. ISR vs. Task code
It is (always) best to make your ISR code as short as possible. Only include the actually time-critical actions and leave the rest to be computed later (by a Task). |
Next value of OCR1A
// Original ISR snippet:
OCR1A = signal_synth(dco_phase >> 8);
dco_phase += phase_increment;
The problem is that the function signal_synth()
takes a variable amount of time to complete.
This means that the register gets updated every (tperiod + ???) seconds.
Such jitter in the sample rate/period is generally unacceptable.
/*
* Better version:
*/
static uint8_t signal_dutycycle; // global-ish variable
OCR1A = signal_dutycycle;
dco_phase += phase_increment;
signal_dutycycle = signal_synth(dco_phase >> 8);
This update means that:
-
The price is the output duty cycle changes one tick later than the original version.
-
The benefit is the output duty cycle changes at exactly the same time interval.
In DSP world terminology, this is a \(z^{-1}\) delay.
Should signal_synth()
be in the ISR?
Or can it be computed in a Task that runs at the Scheduler’s leisure?
→ What are the reasons to choose ISR vs. Task?
3. ShortSquawker output features
-
Signal is not just a sin(). At lower frequencies, the output signal adds harmonics to make the tone more “sharp” but be the same perceived pitch.
-
If the input voltage is high enough to be considered an open-circuit at the probes, then the output is silent. There are several possible ways to be silent!
-
If in the alternate tone mode, then the output frequency alternates between the live value and the stored value at some duty cycle.
4. Porting ShortSquawker to an RTOS
-
Figure out the the original version!
-
Convert to tasks that operate independently of each other. Maybe add some communication between each task.
FreeRTOS documentation
[handout of the ISR code]
The Entire runtime is tick()'d by the TIMER0_COMPA interrupt which happens at 10 kHz or every 100 μs. That ISR needs to always finish in less than 100 μs!
-
OCR1A
sets the output signal’s duty cycle using the Timer1 hardware connected to the speaker pin. -
timer_count
is a static variable that counts how many times the ISR has run since reset.-
Difference between
static
variable inside a function vs. a "global" variable in C.
-
-
The ADC is handled every
ADC_PERIOD
ticks, ⇒
Resulting period / frequency ⇒ -
The input button is handled every
INPUT_PERIOD
ticks, ⇒
Resulting period / frequency ⇒
4.1. ISRs and sample rates
General principle: code for an ISR should be as short (time) as possible. It should only handle exactly the parts that need that level of deterministic and precise timing. Be prepared to justify why you may choose to violate this principle! → Document this decision in the code itself. |
Therefore, the Arduino version of ShortSquawker is horrible :(
Output sample rate must be smooth with no timing jitter.
Input sample rate (ADC) should have small timing jitter.
4.2. Improvements
-
Move the sound output ON/OFF decision out of the ADC handling block.
-
Also better handle the alternating tone feature.
-
-
Use a double-buffer for updating the
OCR1A
value.-
DO NOT call
signal_synth()
from the ISR (why?)
-
-
Is there a better way to handle user button input detection?