top of page
Search
filip

Raspberry Pi Pico Top Octave Generator

Updated: 6 days ago





I recently acquired a Hammond X-5 "portable" organ. It included the leslie and foot pedals, but only half of the keys worked. I bought it from another tech who just wanted it gone.




Hammond X-5

These organs use what are known as "Top Octave Generators". A chip (or in this particular case, two chips) generates the 12 highest square wave notes required by the organ or synthesizer. Simple square wave dividers are used to divide by 2 to get each lower subsequent octave. This allows the organ to be fully polyphonic, but also allows for blending of tones for the drawbars.


This diagram from Electric Druid shows how this arrangement works:



MK50240 Diagram from Electric Druid

On my Hammond X-5, it's clear one of these TOGs is malfunctioning. All of my C#s, Ds, D#s, Es, Fs, and F#s do not work, no matter where on the organ you play (upper, lower, or bass pedals). The X-5 uses two Top Octave Generators - the MM5833 and MM5832, each responsible for half the notes - IC3, and IC2 in the schematic. On mine IC3 is faulty.



TOG section of X-5 Service manual

Of course, the year is 2024 and top octave generators are a relic. There are no MM5833 chips left in the world, and without one my Hammond is dead. Flatkeys makes a replacement but it is quite expensive with shipping, and requires modification of the X-5. I thought I'd try to make my own.



The problematic Top Octave Generators


It can't be that hard can it?


The TOG is pretty straightforward, it takes in a 2MHz (ish) signal and outputs 6 or 7 divided down square waves. The Datasheet even states the specific dividers it uses and the expected frequencies. We can use these values to write some code and even confirm that our output is correct.



divider table





I immediately tried the naive approach. Write some arduino code on the fastest microcontroller I have around, and flip some bits. I even took this a set further and set up the input 2MHz clock as an interrupt and counted pulses in an IRQ.



It did not work


To divide down one output is not a big deal, but 6 at a time caused the processor to run out of time to process the incoming clock. Keeping one huge counter ticking up, or keeping 6 medium counters and checking them against arbitrary dividers takes too long. This method would work up until about 1MHz or 1.5MHz with some optimization, but not much beyond that.


I started looking into PIO on the Raspberry Pi Pico (RP2040). This is a feature of this chip that I had never used before.




My oh, Pio


Pio on the RP2040 allows you to write very small, very simple "state machines". These state machines can take in variables, output variables, read pins, and toggle pins. They run independently of the main processor and can execute assembly code in parallel with the other state machines, of which there are 8.


By piecing together example code from around the web, I wrote a PIO that can divide down an incoming pulse train to an arbitrary amount and output it on a pin. I wrote this in micropython.


@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def divide():
    
    pull(noblock)
    mov(y,osr)
    
    wrap_target()
    label("start")
    mov(x, y)
    set(pins,0)    
    label("loop1")
    wait(0,pin,0)
    jmp(x_dec,"d1")
    jmp("part2")
    label("d1")
    wait(1,pin,0)
    jmp(x_dec,"loop1")
    label("part2") # second half of the output wave
    
    set(pins,1)
    mov(x, y)
    label("loop2")
    wait(0,pin,0)
    jmp(x_dec,"d2")
    jmp("start")
    label("d2")
    wait(1,pin,0)
    jmp(x_dec,"loop2")
    
    
    wrap()
    
 ##-------------------------------------------

This state machine can be initialized with an input and output pin and the divider amount is passed through to the input shift register like this:


pin0 = Pin(26, Pin.IN, Pin.IN)
sm1 = rp2.StateMachine(0, divide, set_base=Pin(27), in_base=pin0)
sm1.put(478)
sm1.active(1)

The divider cannot be changed once the state machine is started, so it does not waste time checking for additional variables being passed.



This worked perfectly


No jitter, stable up to 4MHz and all 7 dividers working great. I even had a spare state machine to run an RGB LED. Whoop. I can move onto hardware.



Let's get into hardware


The input and output voltages on the MM5833 are w e i r d . The chip runs on GND, -33V, -25V and -18V, and the output signal swings between ground and -18V. Yes, negative eightteen volts.


I decided to use -33V as a "ground" and GND as a "+33V". Then I can generate a +5V supply to run my RP2040 on, and use transistors to shift the signal in and out of this voltage reference.


This was the input circuit I found worked for the X-5. The 2MHz signal is passed through series capacitor and then a 1M bias resistor to shift the signal down around -33V. I then divided down 1:3 to get a 3.3V peak to peak signal for the RP2040.


Input Circuit

I tried to implement diodes to clamp this clock signal, but the diodes caused the signal to disappear. I also tried a few transistor circuits which also did not work. I suspect this signal is extremely high impedance, or the high frequency is causing issues. I am not well-experienced in designing circuits in the MHz range, a smarter designer than me could probably tell me why my signals were disappearing.



My outputs were passed through MMBT3904 NPN BJT transistors and implemented a voltage divider in the "ON" state. This gives us an output that swings from GND to negative 16.5V. One of these circuits is used for each output.


Output Circuit

mid-solder



Initial design using transistors


Working version with passive front-end



pcb bottom


pcb top


Full Schematic


Chip Installed

New board installed



I added a solder jumper so that you can select between MM5832 and MM5833. I also set the RGB LED to indicate which mode it's in - cyan for '33 and purple for '32. I will eventually replace the 5832 on my organ, but I want to confirm my hardware revisions first.


Debrief

All in all, this is a fantastic use-case for the RP2040 and an amazing application for micropython. Since the PIOs run independently on their own tiny little cores, the usual overhead hit of micrpython doesn't affect us. The PIO assembly could easily be adapted to other top octave generators, and in fact the new Pico 2 (RP2350) has 12 PIOs instead of 8, so it could replace other top octave generators.


As usual, all my code and hardware is on my github here.




14 views

Recent Posts

See All

Comments


bottom of page