This is the fourth entry in a series on my Tiny Telco project. I’m making a compact, modular, hobbyist-friendly telephone exchange that will allow my vintage computers to communicate with each other via their modems.

Here’s what I have, so far:

Picture of the prototype circuit, featuring several breadboards, an Arduino, a telephone, and a custom circuit board.

Click on the picture to see it in detail. There is a lot going on here.

Arduino and Data Bus

The schematic below shows how the Arduino interfaces with the rest of the circuit:

The entire system is built around a bastardization of the classic Intel bidirectional address/data bus. Here, the bus is 4 bits wide, but it actually uses 10 Arduino pins. Here are the lines that make up the bus:

  • An active-low “write” pin, WR.
  • An active-low “read” pin, RD.
  • An active-high “register bank select” pin, RS0.
  • 4 bidirectional data lines, D0-D3
  • An active-low “chip select” pin, SEL.

Three pins from the Arduino route into a 74HC138 3-to-8 decoder with inverted outputs. This decoder chip generates eight SEL pins from just three Arduino pins, labeled ADDR0-ADDR2 in this schematic:

The Arduino’s reset pin is connected to the 74HC138’s E2 pin. That way, all the SEL pins get de-selected while the Arduino is held in reset.

To read from the bus, the Arduino first selects a device by changing the ADDR pins. That causes one of the eight SEL pins to be driven low, “selecting” a device on the bus. Then the Arduino drives the RD pin low, reads from D0-D3, then drives the RD pin high again when it’s done.

Writing is a similar process. The Arduino again selects a device by writing to the ADDR pins. Then, it configures the D0-D3 pins as outputs, and writes the desired value to the D0-D3 pins. Then, it drives WR low, waits a few hundred nanoseconds, then drives WR high again. After another brief delay, it changes D0-D3 back to being inputs.

RS0 is an odd bird. RS0 is used to swap register banks on the targeted device before doing a read or write. There are two RS lines on a real Intel address/data bus, but the MT8888 only uses one, so I didn’t bother with a second RS line. If I do a bus write to the MT8888 while RS0 is low, that changes one register inside the MT8888, and if I write while RS0 is high, that changes a different register.

The MT8888 is the entire reason I’m using this bus architecture. It was specifically designed to interface with contemporary Intel chips; its datasheet includes schematics that show how to connect an MT8888 to an 8080 or 8051.

DTMF Decoder

Since the MT8888’s bus interface dictates so much of the rest of the design, I figure that’s a good place to start. The MT8888 is a DTMF tone decoder chip. SEL0 from the 74HC138 is used to select the MT8888 DTMF decoder chip:

You can see the bus in the bottom left of the diagram.

Has anyone else never heard of a 3.579545 MHz crystal before? I hadn’t, but both the MT8888 and its little brother, the MT8870, use this weird crystal. Wikipedia actually has a page about this; apparently 3.579545 MHz crystals were originally meant for NTSC signal processing. Other devices started using them because they were cheap and plentiful.

R28 and C16 set some internal timing behaviors for the decoder. By changing the values of these parts, you can adjust how long it takes for the MT8888 to pick up on a phone’s key press. I took the 300k/100n values directly from Gadget Reboot’s SLIM board design. His design uses the MT8870, but according to the datasheet, both chips have the same timing characteristics. I’ve had no trouble at all with these part values.

The two 68k resistors set the gain of the MT8888’s internal amplifier. The datasheet provides a formula for calculating the gain based on the resistor values. AUDIO_IN is, naturally, where the audio signal from a SimpleSLIC enters the MT8888. But, it doesn’t connect directly to the SLIC. First, the audio signal must pass through some filters and switches.

DTMF Audio Filtering

Before the audio from a SimpleSLIC reaches the MT8888, it first passes through this filter circuit:

The first stage is a Sallen-Key low pass filter, which provides a hard frequency cutoff at 5000 Hz. This is higher than necessary - technically I could bring it down to ~1700 Hz without messing with the MT8888’s ability to decode touch tones. However, there aren’t any downsides to having the cutoff in the 5000 Hz range, since the major sources of noise in the circuit sit much higher, at 33 kHz or above.

The second stage is an RC high pass filter, with an (approximate) 250 Hz cutoff frequency. Notice that the “R” in the RC filter is actually two resistors. To get the approximate cutoff frequency of this filter, think of those two resistors as being in parallel. By pulling one of the resistors up to 5 Volts, I add a DC offset to the audio signal. This bias is necessary because the MT8888 can only accept signals in the 0-5 Volt range.

Right at the end of the filter, there’s a 5.1 Volt Zener diode with its cathode pulled up to 5V. This adds a “hard floor” to the signal before it reaches the MT8888, just in case the signal still reaches below the 0 Volts.

This circuit has its problems. Compared to the MFB filter I’m using for the tone generator, the Sallen-Key filter is inherently worse at damping high-frequency noise. If you ignore the op amp for a moment, you can see a direct path from input to output through one 6.8k resistor, and two capacitors. That just isn’t much. The MFB filter from the second post in this series has more resistance separating the input from the output.

The Sallen-Key filter’s biggest upside is economy. It has fewer parts than the MFB filter. It has good part reuse, too: 20k, 9.1k, and 6.8k resistors are also used by the MFB filter, as are 100nF and 4.7nF capacitors. I gain even more economy by making the RC high-pass filter serve double duty as a biasing circuit.

Audio Switching

The input to the DTMF filtering circuit doesn’t go straight to a SimpleSLIC’s audio output pin. Instead, it has to pass through some switching circuitry. The switches route the DTMF decoder/filter to whichever SimpleSLIC needs key presses to be decoded at the moment.

Note that this switching circuit actually serves both the DTMF decoder and the tone generator. This circuit serves four SimpleSLICs, so there are eight audio switches: four switches connect the SimpleSLIC “audio out” pins to the DTMF decoder, and four more switches connect the SimpleSLIC “audio in” pins to the tone generator.

Let’s work from left to right. A single CD4555 chip serves both sides of the audio switch. A 4555 contains two 2-to-4 decoders. The Arduino selects which SimpleSLIC should have access to the DTMF decoder and tone generator by setting the A0/A1 pins of the two decoders. Then, the EN pins of the decoders can be used to toggle that SimpleSLIC’s connections to the tone generator and decoder on and off.

Let’s skip over all those op-amp looking things in the middle. We’ll come back to that shortly.

The actual switching is performed by a CD4066. A single CD4066 chip has four independent, bidirectional, analog switches. When the control pin of one of those switches is driven high, the signal is allowed to pass through. But, there’s a hitch.

The 4066 chips are powered from the -5 Volt and +5 Volt supply rails. This is necessary because the audio signals coming from the SimpleSLICs are centered around zero - the signals can go negative. We give the 4066 a negative supply rail so that the input signals don’t fry the switches, but that means the 4066 chips’ logic levels are based on that full 10-Volt swing.

From the CD4066’s point of view, a logic “high” is at least 2 Volts, and a logic “low” is -2 Volts. The Arduino’s logic “low” is 0 Volts, not -2, so we need more circuitry to translate Arduino logic levels to CD4066 logic levels.

That’s where the LM339 comparators come in. These look like op amps in the schematic. They output 5V when their input signal is above ~1.6V, and they output -5V when the input signal is below 1.6V. Woo-hoo! Level shifting achieved.

Conveniently, an LM339 has four comparators, which matches the four switches inside a single CD4066. Inconveniently, the LM339’s outputs are single-ended, so I had to add all those pull-up resistors to make the circuit work.

Comparator Shenanigans

The audio mux circuit isn’t fully implemented in the breadboard prototype, partly because it’s rather silly. Instead of doing all this level-shifting lunacy, I could just shift the audio signals up into the 0-5V range before passing them to the 4066 switches. That would let me power the 4066 chips from a 0 - 5V swing, meaning they’d take normal CMOS logic levels.

This design allows me to experiment, and that’s the whole point of the breadboard prototype. You see, I’m thinking about connecting calls together the “wrong” way – basically just connecting the “audio out” of one SimpleSLIC to the “audio in” of another SimpleSLIC. This would eliminate a bunch of large, expensive transformers and relays from the final design.

If I’m going to get modem signals to pass through the SimpleSLIC audio channels unharmed, I need to minimize the noise that pops up on those audio lines. Biasing the audio signals would require me to couple them to the 5V supply rail, which is definitely carrying some noise.

I have to shift the audio signal into the 0-5V range either way, so that I can pass it into the DTMF decoder. But, the comparator shenanigans mean I can move the RC level shifter past the 4066 switches, where they can be decoupled from the audio signal once a call is actually connected.

Hook Status Register

Finally, something simple. Here’s the hook status register:

It’s as basic as it looks. A NOR gate combines SEL2 and RD together into an active-high activation signal for some 74HC126 tri-state buffers. When the Arduino drives SEL2 and and RD low, the buffers drive the bus’s data lines with the on/off-hook state for each phone line.

Ringer Register

The ringer register is a bit more complex than the hook status register, but not by much:

We have the same NOR gate turning the two active-low signals into a single active-high one. When the Arduino drives SEL1 and WR low, the 74HC174 will latch the bus data lines onto its outputs, which then get fed into two CD4555 2-to-4 decoders. The upper decoder (U6A in the above picture) routes to the “ring mode” pins on the SimpleSLICs. The lower decoder (U6B) routes to the “forward/reverse” pins on the SimpleSLICs.

This is a fun little circuit. Here’s a truth table that maps the 74HC174’s outputs to ringing behaviors:

Q2 Q1 Q0 Action
0 X X No phones ringing
1 0 0 Ring line 1
1 0 1 Ring line 2
1 1 0 Ring line 3
1 1 1 Ring line 4

You’ll notice that the only material difference between the two 4555 decoders is on their EN lines. When Q2 goes high, that enables U6A directly, causing one of its four outputs to go high. That’s how the “ring mode” pin gets toggled on the SimpleSLIC. Q2 also enables the 20 Hz square wave generator, whose output then toggles U6B’s EN line, causing one of its four output lines to toggle at 20 Hz. That’s how the 20 Hz “F/R” signal gets generated.

For the sake of completeness, here’s the 20 Hz square wave generator circuit. It’s the same as the 20 Hz 555 timer circuit from the second post in this series:

If you read the second post, you’ll notice that the slower 0.2 Hz timer is missing. The 0.2 Hz timer was responsible for making the phone ring for a second, then pause for a few seconds, then ring again. You know, like how a phone normally rings. In this prototype, the Arduino is responsible for making those long pauses between rings. It simplifies the circuit, and the added complexity in code is minimal.

Next Steps

This post took longer than I wanted it to. I had some personal matters to tend to, so the prototype has sat unchanged for almost a month.

You’ll notice a glaring omission from this prototype: it can’t actually connect a call. There are no facilities for connecting two SimpleSLICs together. All it does is connect tone generators and DTMF decoders to the SimpleSLICs as if it were going to connect a call. There are many things to do, but the next step seems clear to me.