Phase shift theremin sensor on Teensy 4
Trying to build as simple theremin sensor as possible.
In theremin sensor, we have to measure antenna capacitance with VERY high accuracy.
Typical theremin antenna C is 7-8pF.
Hand close to antenna adds ~1.5pF more. Influence of hand reduces very fast with increasing of hand to antenna distance.
C = C_antenna + C_hand
C_hand reduces ~3.5 times each 10cm
Code:
Distance, cm C_hand, pF
1 1.5000000000
11 0.4285714286
21 0.1224489796
31 0.0349854227
41 0.0099958351
51 0.0028559529
61 0.0008159865
71 0.0002331390
81 0.0000666111
Measured C_hand could be converted to distance, then to note or volume, then passed to synthesizer as pitch or volume control value.
We could use LC oscillator with antenna as C, and try to measure its frequency directly, but since frequency is changed by about 5% for all hand distances.
For all ranges > 21cm, we have only about 0.3% of frequency change. It's very hard to measure using MCU.
Some method to zoom in working range is required.
For LC oscillator, we can mix its output F_osc with reference frequency F_ref in heterodyne, to get difference frequency F_ref-F_osc.
Another method is to detect phase shift of constant reference frequency signal passed through LC.
If signal passes LC tank, output depends on how close it is to LC resonant frequency.
At resonance, phase shift is PI/2 and its amplitude has maximum value (much bigger than input signal voltage).
When input frequency is higher than resonant frequency, amplitude decreases, and phase shift is < PI/2.
When input frequency is lower than resonant frequency, amplitude decreases, and phase shift is > PI/2.
Change of phase shift as reaction on LC resonance frequency change (C change) depends on LC tank Q.
The bigger Q is, the more sensitivity (phase change per C change) is visible.
By tuning of LC Q, we can "zoom in" C_antenna corresponding to hand distances far from antenna near PI/2 phase shift.
But we need to keep full range readable (still have usable output for distance close to antenna).
Let's wind air core inductor and use its parameters in LTSpice simulation.
I'm using 32 mm diameter of PPL water pipe frame, 0.1mm copper wire, 175mm winding length.
Got 4.272mH on chinese L/C meter from aliexpress.

Trying to check with Coil64:
Code:
Input:
Inductance L: 4,272 microH
Frequency f: 0.6 MHz
Former diameter D: 32 mm
Wire diameter d: 0.1 mm
Wire diameter with insulation k: 0.109 mm
Winding pitch p: 0.122 mm
Result:
Number of turns of the coil N = 613.288
Length of wire without leads lw = 61.864 m
Length of winding l = 74.930 mm
Weight of wire m = 4.32436 g
Reactance of the coil X = 16,105.061 Ohm
Self capacitance Cs = 1.341 pF
Coil self-resonance frequency Fsr = 1.358 MHz
Coil constructive Q-factor Q = 82
Loss resistance ESR = 196.403 Ohm
LTSpice model: GitHub link
Schematic:

Inductor parameters are set from Coil64 results.
We could just pass square 50% duty cycle reference frequency from Teensy pin to LC, but simulation shows some distortion in signal on antenna introduced by square wave input.
I've added square to sine conversion on LRC bandpass filter and NPN buffer - to feed LC tank with almost pure sine.
Delay (phase shift) introduced by this conversion should be const for const reference frequency.
LC resonant frequency is near 690KHz, so bandpass filter should have center frequency near to this value.
Using online calculator http://sim.okawa-denshi.jp/en/RLCtool.php to get filter component values.
Simulation results for square to sin conversion:

Voltage swing on antenna and sensor output:

Source and output signals of sensor output buffer:

In this simulation, we change C_hand from 0 to 2pF with 0.1pF step.
During calibration phase, MCU should choose such reference frequency value to be near resonance for hand distances far from antenna.
We should use widest 0.1pF step for far distances, and 14 more steps to the right will correspond to shorter (<21cm) distances.
Reference frequency period is 1.359uS, and width of widest interval is 0.057uS which gives us 4.2% of input range for 0.1pF interval corresponding to distances > 21cm
Can we measure this signal with good enough precision using Teensy 4.0?
Let's use FlexPWM module, with channel A generating reference frequency signal with 50% duty cycle (even divider value should be used).
Channel B will work in capture mode, capturing PWM counter values for both input (phase shifted) signal edges.
For 690KHz reference frequency (divider 216, actual f=694444Hz), we will have 216 values in PWM counter cycle.
Captured value gives us 7.75 bits of information about phase shift.
Having two edges measured, we get one more bit - 8.75 bits total.
Seems too low. Is it possible to increase number of bits in measured result?
Sure, we can measure it N times, then average. It will give log2(N) more bits.
E.g., for 1ms window, we have 690 measures, providing log2(690)=9.43 bits
Total bits for 1ms averaging is 8.75+9.43 = 18
Is it enough? Let's check.
For short distances (>21cm where we have 1.4pF C_hand range), it's obviously enough.
Let's check what we have for longer (>21cm) range which corresponds to ~0.1pF region near resonance frequency (PI/2 shift).
4.2% input range limit of middle 0.1pF eats log2(100/4.2)=4.57 bits of input range.
18-4.57 = 13.6 bits
Code:
range bits left
21 13.6
31 11.8
41 10
51 8.2
61 6.4
71 4.6
81 2.8
91 1
101 -0.8
After 61cm distance we see 6.4 bits left in input value.
In general, this means we will have audible quantization of pitch.
But actually, long distances usually correspond to lower frequency notes. And actually, when synthesis of output signal with "dithered" (varying) frequency,
output probably will sound like "average" frequency.
We can as well to increase averaging time interval. Increasing of averaging 4 times, gives additional 10cm of playable distance.
But the bigger averaging interval is, the bigger is latency between hand movement and
I believe 3..4ms averaging interval will still be non-audible.
So, it looks like it's possible to have playable distance 60-90cm of hand from pitch antenna, using only Teensy 4.0 PWM pins, with simple phase shift sensor.
But how will we read measured values? There will be 690000 measure results per second.
We could use interrupt, but at this rate, CPU would waste half of time on interrupt processing (saving/restoring registers, etc).
Fortunately, there is DMA in Teensy 4.0 MCU.
It can be programmed to save results of every capture to ring buffer in RAM.
In Audio ISR, we can do averaging for all values in buffer to get precise enough sensor value per frame.
Summing of 1000 integers once per audio frame doesn't consume a lot of CPU resources unlike interrupt per measure.
Conclusion.
It looks like it's possible to implement phase shift based theremin on Teensy4 with minimal external components.
Pitch antenna max playable range is expected to be 60..80cm.