In my “#RaspberryPi Pico – let’s make an atrocious synthesiser“, I said that I wasn’t particularly happy with the results. The ADC readings were prone to significant fluctuations. I’ve asked about it on the Pi Forums, and apparently that’s to be expected.
Averaging several readings is one way to ameliorate the effect, but with some of the experiments I did, I still wasn’t satisfied.
I also said that there were likely to be synchronisation issues between the time counter and an update. I haven’t investigates a means to resolve this situation, although I expect that there is one.
I wondered if there is another approach. The typical way of creating an oscillator is through a combination of a resistor, capacitor, and inverted Schmitt trigger. The Schmitt triggers are cheap. I ordered some off of ebay recently. You can get 5 chips for less than £3. Each chip has 6 triggers on them, so they’re cheap.
Whilst I’m waiting for the chips to arrive, I thought I’d see if I could create a Schmitt trigger inverter using the Raspberry Pi Pico.
What is a Schmitt trigger, I hear you ask? According to Wikipedia:
a Schmitt trigger is a comparator circuit with hysteresis implemented by applying positive feedback to the noninverting input of a comparator or differential amplifier. It is an active circuit which converts an analog input signal to a digital output signal. The circuit is named a “trigger” because the output retains its value until the input changes sufficiently to trigger a change.
Huh? Think of it as having two input levels: a low and a high. When the input voltage is lower than the low level, the output from the trigger is low. When the input voltage is higher than the high level, the output goes low. At an intermediate input voltage, the output voltage doesn’t change. As the input voltage varies continuously, the output therefore swings from high to low abruptly according to the level of the input voltage.
An inverted Schmitt trigger does the opposite: it turns the output on when the input is low, and vice versa. Using a resistor and a capacitor, you can build up charge until a critical point that will switch the trigger off. This feeds back into the circuit, discharging the capacitor, and sending the switch on again. So you can achieve oscillations. Here is a typical circuit:
In place of the inverter, I’m going to use a Pico. It reads the voltage level using an ADC (pin GP28). When the voltage gets above a certain level, we send output pin GP16 low, and low when the ADC reading is below a certain the output pin is sent high. Voila, instant Schmitt inverter.
Actually, we also set GP15 to have the same value as GP16, so that we can drive a small speaker. Here’s the circuit:
I chose a 0.1uF capacitor, and used a 100K ohm variable resistor. That way, by varying the resistor, I can change the frequency. Here’s the uPython (MicroPython) code:
from machine import Pin, PWM import machine pot = machine.ADC(28) spk = Pin(15, Pin.OUT) out1 = Pin(16, Pin.OUT) def on(): out1.value(1) spk.value(0) def off(): out1.value(0) spk.value(1) while True: v = pot.read_u16() if(v<21845 and out1.value() == 0): on()# 65535/3 if(v>43690 and out1.value() == 1): off()# 65535/3*2
Pretty straightforward. Have a go, make some noise.
I found that I could generate frequencies in the range 46Hz – 7.2kHz using the values as described above.
I’m still not entirely happy. The signal sounds if it is not entirely clean. I suspect you could generate lower frequencies by adjusting the threshold values in the code. I simply decided to divide the max. ADC reading of 65535 into three lots. It was a fairly arbitrary choice. Try lowering the lower threshold, and raising the upper threshold.
That should take care of the lower frequency. 46Hz is fairly low anyway for audio purposes. Typical ranges for human hearing are 20Hz – 20kHz.
The upper range is a little disappointing. I’m wondering if I’m reaching the performance limits of uPython. I did some benchmarking of ADCs on an STM32. Each analogue read took about 7us, for an implied sampling frequency of 143kHz.
I haven’t done a similar benchmark for the Pico. Assuming it’s in the same ballpark, the ADC conversion shouldn’t be the problem. My suspicions are on uPython itself. I was playing around with some audio ideas some time ago, and IIRC 8kHz was stretching what the MCU could handle using uPython.
I am hopeful that porting the code to C is likely to yield better results. I think I won’t bother, though. I’ll just wait for my Schmitt triggers to arrive.
I’ve been thinking about the Pi0 again recently, and the possibility of doing some digital signal processing on it. So I’m undecided as to which way to jump: should I go the DSP route, or should I got the oscillator route?
Hmmm, decisions decisions.