Last night, I decided to explore an idea that I have been formulating over the past several months, but haven't actually taken the time to explore. I had a thought that I could emulate a Lunetta circuit (a CMOS 40106 integrated circuit with a potentiometer and a capacitor to create an oscillator) using my FPGA and Verilog.
After all, the 40106 is simply a hex inverter (6 inverter circuits on one chip), so the code was this simple:
assign pin1 = ~pin0;
That simply set the value of pin 1 to be the opposite of pin 2 (inverted signal) at every clock cycle. I plugged in the capacitor and potentiometer according to the regular "Lunetta" arrangement (as seen in the schematic below), but it didn't work.
Replace the U1A IC in the schematic with the FPGA and you'll understand how I set it up.
I think I have a fundamental misunderstanding of the nature of CMOS and FPGA's. While you can emulate the functionality of an inverter chip using an FPGA, I don't think it's possible to emulate the physics of the chip itself. The Lunettas work off of the actual physical reaction and timings of the chip itself, not off of a clock cycle such as the one that the FPGA is running under. Emulating a physical feedback circuit will not render the same results when using an FPGA. I looked at the datasheet for the 40106 and saw that it has ~140ns delay. I'm not sure if I can emulate that kind of timing.
I enjoyed the few hours I spent hooking the circuit up and playing around, though, and it got me into Pulse Width Modulation and other audio generation techniques for FPGAs. I'll be exploring more about audio processing in the future, but I've put away the idea of emulating discreet components for their "out of band" uses.
It's fun to play around with this stuff, but when the theory gets too thick, I lose interest and move onto more playful aspects of the technology. I think it's why I have a cursory understanding of a lot of technology, but only a deep understanding of a limited number of topics (like C#, HTML, and other Web Development).
In past blog entries (Controlling a 40106 Oscillator from Raspberry Pi), I have covered my adventures using the 40106 CMOS chip as an oscillator to create DIY synthesizers. Since I am a beginning electronics hobbyists (and I had limited supply of parts), my circuits were imperfect and completely wrong.
My main goal for now is to control the output of 40106 oscillators using a microcontroller (like the Raspberry Pi or the Arduino).
The main problem I had in the previous attempt was that I was using a NAND gate with the pinout of the Raspberry Pi on one side of the gate and the oscillator output on the other side.
Here is a pseudo-schematic of my incorrect approach:
At first glance, this seemed OK, and my circuit appeared to work when I set Pin 7 on the Raspberry Pi to HIGH. However, I was making a mistake in two places.
Mixed input voltages to the NAND gate
A layman's explanation of how the oscillator makes sound (because I am a layman)
The 40106, when setup with a capacitor and a resistor in a "feedback loop", creates a square wave by cycling on and off at a particular frequency. If the capacitor and resistor values are chosen carefully, the frequency of the oscillation is within human hearing (~20Hz to ~20kHz). You can then hookup the output of the oscillator to an audio amplifier and hear the sound that is generated. You can also use the same setup for a low frequency oscillator (LFO) outside of the human hearing range that can be used to control other aspects of your circuit.
I read over the math behind how this works and watched a few videos on YouTube, but it makes my brain melt, so I just hack away at it. Here is a much more detailed explanation (and some great plans for a finished Lunetta oscillator bank).
Here is a picture of my oscilloscope measuring an oscillator with a square wave within an audible range.
When the square wave hits the high point (1), it sends a HIGH signal to the output. When the square wave hits the low point, it turns the signal off.
In my original setup, I used a 470nF capacitor and a 1MΩ resistor.
Since I was using a NAND gate, the signal went HIGH when the oscillator went low. So, I was actually "hearing" the bottom of the square wave!
The fix for this was simple, I simply bought some AND gates (CD4081B - datasheet). Then the output was HIGH when both inputs were HIGH. But, there was still a mismatch in the input voltages...
Mixed Input Voltages
In the schematic, notice that pin 14 (VDD/VCC) on both ICs is connected to the +5v output of the Raspberry Pi. The entire audio circuit is running at +5v. However, when the GPIO pin on the Raspberry Pi is HIGH, it is only supplying +3.3v to the logic gate (NAND in this example). So, one side of the logic gate is getting +5v, while the other is getting +3.3v. Like I said, the circuit was working, so I was happy. Upon further study I read that for the NAND gate to register a HIGH signal, the minimum voltage at the pin must be ~5v (since our source voltage was +5v). Also, it wasn't a good idea to connect the 3.3v GPIO pins directly to a circuit running at 5v.
Logic Level Shifter
I looked around and immediately found a logic-level shifter that I could buy that would convert my 3.3v outputs to a 5v output. I decided that they were too expensive for my application (I'm going to add many more 40106 oscillators to my setup). I may end up getting a few of these down the road for more passive and simplistic applications, but they don't suit my current needs.
Update: I still need a logic level shifter. Read more details below.
Enter the i2c Port Expander
After a little more digging, I found exactly what I was looking for. The MCP23017 i2c 16 input/output port Expander allows me to communicate with the gates using i2c (there is a similar version that uses the SPI interface if that's what you prefer).
Each chip can control up to 16 oscillators by triggering the gate. Since the MCP23017 can run at 5v with the rest of the audio circuit, it will send a 5v HIGH signal to the gate when it is triggered. This solves my problem of worrying about GPIO pins having the wrong voltages.
Here is a link to an article on Adafruit explaining how to use the MCP23017 with the Pi. While the article is a bit dated, the concepts still hold true.
Since these io expander chips use i2c to communicate, you can power them from 5V while still connecting the i2c data lines to a 3.3V device like the pi. That's because the Pi has two i2c resistors that pull up SDA/SCL to 3.3V.
Update: It turns out that the i2c also requires level-shifting from 3.3v to 5v. The great thing is that I only need one level-shifter for all of my i2c communications.
According to the MCP23017 datasheet (pg 28), the input pins need to have a minimum voltage of 0.8 VDD, which in my case is 5v.
So, I need to be sending at least 4v to the port expander. The fact that it's working with only 3.3v is a convenient quirk. As people have pointed out in the forums, this could lead to inconsistent results and failure when using other components.
Here is my final schematic showing the MCP23017 port expander connected to the oscillator and the AND gate:
You can see that there are only two wires coming from the Raspberry Pi. These are the i2c communication wires.
Here is a picture of this circuit on a breadboard:
The ICs (from left to right):
MCP23017 - i2c I/O Port Expander
CD4081BE - AND Gate
CD40106BE - Hex inverting Schmitt trigger (used as an oscillator)
The i2c lines can be seen at the bottom left of the screen (yellow and green wire pair).
Platform, OS, and Development environment
You can use any microcontroller that supports i2c to make this work. I am using a Raspberry Pi with Windows 10 IoT edition. My code is written using Visual Studio 2015 and C# using the Adafruit .NET Class Library. This library has a class that supports communication with the MCP23017.
Note: I found the following resources necessary to get my Raspberry Pi running Windows 10 in developer mode and making the required settings:
Windows Device Portal - This helped me set up developer mode and set the debug PIN. It also allows you to access the settings of the device.
Windows IoT Remote Display - This is the same thing as a remote desktop client for your Windows 10 IoT devices. It allows for autodiscovery of Windows 10 IoT devices on your network. I use it when debugging my app from Visual Studio on my PC. It displays my UI on the remote client. Combined with remote debugging in Visual Studio, it ticks all the boxes for being able to effectively debug and monitor a UWP app for Windows IoT.
Most of the information above can be found from the home page for Windows 10 IoT. When all else fails, use your favorite search engine (and then follow the link it gives you to the answer on StackOverflow...)
Here is the main source code that runs the oscillator gate. The first section is the initialization logic. It runs when the application starts up:
private async void Init()
// prepare the I2C for communication
_mcp = new MCP23017(0x20); // 0x20 (decimal 32) is the default address
for (int outputPin = 0; outputPin < 16; outputPin++)
catch (Exception ex)
string a = ex.Message;
Once the initialization has occurred, the user interface loads up toggle switches. When the state of a toggle switch changes, it fires an event that determines the output pin based on the number of the toggle switch. Here is the toggle event handler:
The way I've implemented the handler, I have to take care to ensure that my toggle switch naming convention is handled correctly. In my case, I defined a constant value in a static utility class that I use to determine the name of the toggle switch.
Here is what my user interface looks like on the Raspberry Pi:
When the user toggles the switch, the associated pin goes HIGH on the MCP23017, which causes the AND gate to toggle its output (pin 3 in this case) to HIGH each time the 40106 square wave goes HIGH. This output is then sent to the output pin (SIGNAL_OUT on the right side of the schematic), where it can be used by another circuit (an audio amplifier in this case).
After a few false starts, I believe that I have the gating mechanism that I can use to control most of my synth circuits. I anticipate that I'll also use this approach to turn effects on and off, as well as other applications (enabling/disabling control voltages (CV signals)).
Windows 10 IoT is Ready for Prime Time
I used Linux (Raspbian) and Mono .NET in my previous attempts at this effort. While it worked, it didn't feel comfortable. Things were off somehow.
I decided to switch to Windows 10 IoT. The Windows IoT ecosystem is absolutely brilliant. It was a little bit of a technical challenge getting things to work the first time, but everything performed exactly as it should have. The learning curve should be easy for most .NET developers and slightly challenging for non .NET developers. There were no strange errors or undocumented secrets to getting things to work.
The tools they provide "just work". I was able to get a remote shell on my Pi using PowerShell. I easily accessed my Pi using the IoT remote desktop app. It's easy to setup your custom app to always launch as the default app when the device starts. Add to that the fact that I can use Visual Studio 2015 (the IDE and development environment I use at work each day) to develop these apps and you have a great end-to-end solution. I haven't yet integrated with Azure IoT hub or any of the cloud features yet, but I have read up on them and know they could come in handy later for home automation and other applications.
Another great discovery was that Adafruit has created the .NET NuGet package (and related GitHub project). This made it very easy to interface with the MCP23017
Thanks go out to Rick Lesniak for his excellent work on this library. He saved me hours of work.
Footnote: I made videos describing each of the topics discussed in this post. I have not yet had the time to combine them into one video. Once I get it created and edited, I will embed it here and post a link to it on my social media sites.