Controlling a 40106 Oscillator - Part 2

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).
Note: Information about using 40106 and other CMOS chips to create DIY synthesizers can be found here: Intro to "Lunetta" CMOS Synths.
You can also find an enormous amount of information on the electro-music.com DIY Synth forum
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.
- Inverted output
- Mixed input voltages to the NAND gate
Inverted Output
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.
MCP230xx GPIO Expander on the Raspberry Pi
From the article:
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.
I found a great article on this very topic: Raspberry Pi and I2C devices of different voltage
Here is the circuit that I need to level shift my i2c interface:

There is a much better description of the level shifting application at Adafruit's website. Application Note AN97055 - Bi-Directional Level Shifter for I²C-bus and other systems
Update 2016-02-15: I found the perfect bi-directional logic level shifter on Amazon. These come in a 5 pack for around $9.

XCSOURCE 5PCS IIC I2C Logic Level Converter Bi-Directional Module 5V to 3.3V TE291
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).
Overview video
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 10 IoT Start Page
- Windows 10 IoT Core Dashboard -For setting up your Raspberry Pi to run Windows 10. Also allows for software project demo installs.
- 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.
- Writing Apps
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()
{
try
{
// prepare the I2C for communication
_mcp = new MCP23017(0x20); // 0x20 (decimal 32) is the default address
await _mcp.InitMCP23017Async(i2cBase.i2cSpeed.i2c_400kHz);
for (int outputPin = 0; outputPin < 16; outputPin++)
{
_mcp.pinMode(outputPin, MCP23017.Direction.OUTPUT);
}
PerformOutputTest();
}
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:
private async void toggleSwitch_Toggled(object sender, RoutedEventArgs e)
{
var toggle = e.OriginalSource as ToggleSwitch;
int outputPin = Int32.Parse(toggle.Name.Substring(Utility.TOGGLE_SWITCH_PREFIX.Length));
MCP23017.Level toggleState = toggle.IsOn ? MCP23017.Level.HIGH : MCP23017.Level.LOW;
try
{
_mcp.digitalWrite(outputPin, toggleState);
}
catch (Exception ex)
{
string a = ex.Message;
}
}
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.
Related: MCP23017 Class reference on Adafruit.com
Here is the definition of the prefix constant that is in my Utility class:
public const string TOGGLE_SWITCH_PREFIX = "toggleSwitch";
I have also named my toggle switches using this prefix and the target output pin that I want to flip when the switch state changes:
<ToggleSwitch
x:Name="toggleSwitch0"
Header="Gate 1"
HorizontalAlignment="Left"
Margin="10,65,0,0"
VerticalAlignment="Top"
Toggled="toggleSwitch_Toggled"
IsDoubleTapEnabled="False"
IsHoldingEnabled="False"
/>
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).
Conclusion
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.