Let's Design and Build a (mostly) Digital Theremin!

Posted: 7/19/2025 9:40:15 PM
Buggins

From: Porto, Portugal

Joined: 3/16/2017


'...because it is known that the major loss in coils occurs at the ends, and Butterworth (ref 6) says "if the coil
is divided into four equal sections the distribution of loss is such that approximately 93% of the loss occurs in
the outer two sections‟.'

Hmm... if there are N sections, will the loss be concentrated in first and last section?

Posted: 7/24/2025 7:10:46 PM
dewster

From: Northern NJ, USA

Joined: 2/17/2012

"Hmm... if there are N sections, will the loss be concentrated in first and last section?"  - Buggins

I don't think the sectioning is physical, it's just saying the losses are concentrated in the ends.  Kinda like skin depth being the point where most of the conduction is in the outer portion of the wire.

Posted: 8/6/2025 11:38:36 AM
dewster

From: Northern NJ, USA

Joined: 2/17/2012

Technical Accounting

Spent the last couple of weeks tending to "technical debt" in the D-Lev software, particularly the user interface sections that service the encoders and push buttons.  Software features that are the last to go in tend to be "hackier" than the rest of the code base because they don't have the virtue of being reexamined as many times and thus better integrated into the whole.  The user interface is on thread 7, which is particularly tricky to work on because it has real-time interrupt service routine (ISR) code running at the 48kHz audio rate (or some fraction of it) as well as non-real-time duties, and these form separate "clocking domains" as it were, so transferring data between them requires extra thought and care in the form of atomic reads / writes and other techniques.  Since the interface itself is being operated on, bugs tend to derail the very mechanisms that convey error information and other health indicators.

A scary proportion of the activity boils down renaming variables and subroutines to bring clarity and uniformity to the nomenclature.  Changes here tend to bring new insight into the operation, so it's not just a cosmetic effort but a back and forth with real implications.  The saying goes "when code is written only the coder and god understand it; six months later only god" so unambiguously naming things is a gift to future you.

Anyway, one section that's been slowly evolving is the parallelism of the encoder section.  It initially started life as fully parallel and able to track the user twisting all of the knobs at once - which as it "turns" out never actually happens.  Because we only have two hands only two encoders at most would be changing simultaneously - and even that never really happens in practice, though there are mitigations we can implement to mask the fact that all of the encoders now share a single velocity speed-up.  The old code processed each encoder change sequentially at a 48kHz / 10 rate, which is entirely adequate to catch single detent changes even if the encoder is equipped with a small diameter knob and spun as fast as possible, where the switching rate can approach 1kHz.  The new code only addresses changes to the most significant encoder at a 48kHz / 8 rate, which might seem like a single point of failure, but the FPGA firmware contains a state machine for each encoder, so the only changes that make it to the processor register are full detent events.  

Sharing the velocity code among all of the encoders is only potentially problematic when quickly switching from one encoder to another - you don't want a velocity "hangover" transferring from one to the other, so we zero out the velocity when a switch is detected.  The velocity should be similarly zeroed out when changing the direction of a single encoder.  As before, the encoder twist delta is transferred from the ISR domain to the non-real-time domain via modulo subtraction - delta equals the old value subtracted from the new.

The push buttons integrated into the encoders are similarly addressed in a most significant precedence manner, because the user will likely never press more than one at a time.  And even if they do, single point of failure is avoided by only tracking press events of the push buttons, with the tracking for all refreshed each time the code implements the specific button press event.  So only in the extremely unlikely scenarios where two or more buttons are pressed within 167 microseconds (8 / 48kHz) of each other could a button press event be missed.

The GPIO port is dealt with in a parallel fashion, so nothing can be missed there.  I took the opportunity to implement two new inputs here, grounding one input increments the preset, grounding another decrements it.  So the four inputs are now: ACAL, Mute, Pre++, and Pre--, which provides for a fairly full-featured footswitch arrangement.  For more advanced remote control, one could implement a footswitch that talks to the serial port, where the sky is the limit as to what is possible.

Speaking of which, the serial command "wk" that writes to the knobs has been reworked so that only the knob being written to is updated system-wide.  Previously all of the knobs were updated every time a single knob was written to, which introduced a noticeable lag when performing editor commands that write to multiple knobs, such as the "morph" feature.  To further facilitate this, the "wk" command doesn't update the knob values on the LCD, a new "lcd" command has been provided to do this explicitly, and would normally be invoked at the end of a string of one or more "wk" commands in order to speed things up.

All of the knob and push button code now pretty much run a single gauntlet of actions, which has simplified and clarified the functionality, and as a side benefit has freed-up some memory, so I might be able to experiment with my "DC scanning" technique proposed a while back, where the encoder wiring is condensed and all of the debouncing and decoding and stuff could happen purely in software, and ideally by a real processor rather than a soft processor instantiated in an FPGA.

Posted: 8/7/2025 7:44:22 PM
ILYA

From: Theremin Motherland

Joined: 11/13/2005

dewster,
are you by any chance planning to replace the FPGA with a modern uC?”

Posted: 8/8/2025 8:42:35 AM
dewster

From: Northern NJ, USA

Joined: 2/17/2012

"are you by any chance planning to replace the FPGA with a modern uC?” - ILYA

I would love to do that!  I suppose it's the logical end-game.  It would need: 

1. A small external FPGA to handle field generation and acquisition (or at least acquisition if an analog oscillator is used) or
2. Very high speed internal peripherals at the pins (currently have 2.5ns timing resolution) or
3. High speed ADC (I would guess 10M samples/sec or better).  

And the platform would need some longevity as the project tends to move at a snail's pace.  Any ideas?

Posted: 8/18/2025 3:30:33 PM
Buggins

From: Porto, Portugal

Joined: 3/16/2017


I would love to do that!  I suppose it's the logical end-game.  It would need:

1. A small external FPGA to handle field generation and acquisition (or at least acquisition if an analog oscillator is used) or
2. Very high speed internal peripherals at the pins (currently have 2.5ns timing resolution) or
3. High speed ADC (I would guess 10M samples/sec or better). 

And the platform would need some longevity as the project tends to move at a snail's pace.  Any ideas?


What about implementing the sensor part as a separate board, with some MCU friendly interface?

It could be one board for Pitch, and one board for Volume sensors.
Inexpensive FPGA can be used as a heart of the sensor, taking care all the DSP stuff.
With external DAC and ADC, it could be possible to offer the best ever possible sensor.
As a cheap alternative, with the same MCU interface, could be a D-lev like approach, with square drive and sense (or use good but expensive sensor for pitch, and cheaper one for volume).

FPGA based DDS will output sine wave drive signal via DAC.
Current sensing the drive current via ADC will feed FPGA with a current waveform.
FPGA will calculate phase offset between drive voltage (sin and cos outputs of DDS) and current. (Single ADC solution is possible, with zero phase delay setting as a calibration parameter).
FPGA may either provide raw frequency values or pre-calculated axis value (if all the frequency to axis conversion and filtering logic is done on FPGA side).

Possible main components selection based on current JLCPCB stock and prices, for FPGA+DAC+ADC:


Code:
AD9235BCPZ-40  LFCSP-32(5x5) 12-bit ADC 40MHz JLCPCB C653327  $8.7135 - $7.5375 stock 135
AD9744ACPZRL7  LFCSP-32(5x5) 14-bit DAC 210MHz JLCPCB C514295  $7.9995 - $6.9270 stock 136
ICE40UP5K-SG48I QFN-48-EP(7x7) FPGA 39io 8DSP     JLCPCB C2678152 $7.1760 - $6.1095 stock 909

Estimation for assembled sensor board cost is $35..$50

So, the pair of sensor boards would cost $70..$100


Posted: 8/21/2025 8:09:59 PM
dewster

From: Northern NJ, USA

Joined: 2/17/2012

"What about implementing the sensor part as a separate board, with some MCU friendly interface?"  - Buggins

Yes, probably the best solution as it keeps timing critical information from needing to move any distance in an analog manner.  Makes things modular too, which is nice.  For the processor use a serial audio connection, like 32 bits @ 48kHz to transfer the info.  Not sure who the timing master would be.

AD9235BCPZ-40  LFCSP-32(5x5) 12-bit ADC 40MHz JLCPCB C653327  $8.7135 - $7.5375 stock 135

So this seems to be the overall resolution bottleneck?  If the operating point is kept < 1MHz then we're looking at 40 samples per cycle minimum, or 5.3 bits.  The 12 bit amplitude resolution is an adder to this, but probably not a simple 5.3 + 12 = 17.3 bits, correct?  Anyway, filtering it ultimately down to 100Hz or so gestural bandwidth increases the resolution further: Operating F / gestural F = 1MHz /100 Hz = 10,000 or 13.3 bits, only 1/2 or so are realized, so 6.6 bits.  17.3 + 6.6 = 24 bits.

Currently things are square wave, sampled with DDR @ 400 MHz, so 400MHz / 1MHz = 400 or 8.6 bits.  Filtering down to 100 Hz gives the same 6.6 bits as above, for a total of 15.2 bits.  So the ADC solution seems much better, and would almost certainly reject interference much better too.

ICE40UP5K-SG48I QFN-48-EP(7x7) FPGA 39io 8DSP     JLCPCB C2678152 $7.1760 - $6.1095 stock 909

The FPGA seems a bit slow internally (low power FPGAs tend to be slower) but the 40MHz operation of the ADC might also be the top speed of the NCO needed to generate the drive, so we could perhaps use a slower, less expensive DAC here.  It looks like the FPGA has an internal configurator, which is nice.

"(Single ADC solution is possible, with zero phase delay setting as a calibration parameter)."

This scares me a little!  Though I suppose it's better if it's all going on in an FPGA which is physically close to the coil.  The phase detector would require some thought.

Coil drive is more complex than a simple square wave buffer, but it being a sine wave keeps the bandwidth requirements reasonably low.  Sine drive would prevent the chance of harmonic locking too.

Posted: 9/16/2025 3:42:44 PM
Buggins

From: Porto, Portugal

Joined: 3/16/2017

Crossposting my message from D-Lev discord here.

Do we really need DAC+ADC+FPGA to get a low noise high sensitive theremin sensor?

I'm close to completion of design of hi-end current sensing LC oscillator, with pure sine drive signal and fully analog PLL.
I believe moving of PLL from FPGA to analog front end may cheap but sensitive and precise theremin sensor, suitable for MCU-only digital theremins.

Power: Input 5V DC, analog part 4.5V (linear regulator), digital output part 3.3V.
Drive signal: pure sine wave (-60dB for biggest harmonic), 2.8Vpp, up to 60mA drive strength
VCO: based on State Variable Filter on 3 OTAs, tunable arbitrary frequency range, two 90 degrees phase shift signals (COS + SIN) of the same amplitude, stable amplitude for the full VCO frequency range
Current sensor: based on current sensing resistor, BJT differential pair, with differential current output
Phase detector: analog multiplier on 2 OTAs, with integrator
Output: sine to square converter on unbuffered inverters, optional LVDS driver(s) if differential output is needed
In the analog loop there is only pure sine signal on oscillator frequency
Main parts: 3 x LM13700 OTAs, 2 x LMH6642 opamps, 2 x MCP6021 opamps, 2-4 unbuffered inverters, 1-2 LVDS drivers, 4 BJT matched pairs, 2 regulators (4.5V, 3.3V)

Estimated cost per board: $5 .. $10.

Options: power enable input to allow stopping of oscillator, dual phase outputs (adds one more bit of precision when sampled and measured by MCU), differential outputs to minimize noise received by transmission line.

Posted: 9/16/2025 4:35:16 PM
ILYA

From: Theremin Motherland

Joined: 11/13/2005


"Main parts: 3 x LM13700 OTAs, 2 x LMH6642 opamps, 2 x MCP6021 opamps, 2-4 unbuffered inverters, 1-2 LVDS drivers, 4 BJT matched pairs, 2 regulators (4.5V, 3.3V)" - Buggins

Too modest. At a minimum, you should replace the LM13700 with an analog equivalent built from discrete components.

Posted: 9/18/2025 9:28:35 AM
dewster

From: Northern NJ, USA

Joined: 2/17/2012

"Input 5V DC, analog part 4.5V (linear regulator)..." - Buggins

I'd be a bit scared of maintaining only 0.5V headroom.

"Main parts: 3 x LM13700 OTAs..."

Don't those require 10V minimum for their supply?

You must be logged in to post a reply. Please log in or register for a new account.