A while ago a friend asked me if I could take a look on something. He got a clavinova YP-40 (world’s first digital piano) from a newspaper insertion. The former owner obviously wanted to get rid of the heavy chunky keyboard thingy, so my friend eventually got it for free.
The weighted keys have a superb touch, while the sound … well … it sounds like an early digital music instrument. Quite fake and lifeless. Digital vintage fanboys may beg to differ and protest that we are spoiled children of the 90s. So to not speak of a sacrilege, let us just assume that the clavinova was broken.
Since the keyboard was produced in 1983 it does not have a MIDI output, the challenge here is building a velocity sensitive master keyboard with the Clavinova YP-40 claviature!
The first point on the agenda was to understand how the claviature works internally.
The claviature possesses 76 weighted keys. All key contacts are wired as a diode key matrix. If you want to know how a diode matrix works, I advise you to check out this great post: http://pcbheaven.com/wikipages/How_Key_Matrices_Works/
There is one flat cable coming out of the claviature with a two row connector (not unlike IDE hard drive plugs). I tried to reverse engineer the pinout using only a digital multimeter, but this attempt failed. After unmounting the whole claviature and looking at the PCBs at the bottom side, I finally could draw the following pinout scheme:
I was a little puzzled at first since most velocity sensitive keyboards have two contacts per key, but both get closed when the key is pressed. The YP-40 key has two contacts as well, but the upper contact is closed in the idle position, while the lower contact is closed when the key is pressed. The velocity information comes from the time difference between the opening of the upper contact and the closing of the lower contact. The shorter the time delay between opening and closing, the higher the key velocity. So measuring this time delay for all 76 keys is THE problem to be solved for this project.
I measured the time delays for another (lighter weight) keyboard with 24 keys (upper contact closing) and they were in the order of 1-10 ms, depending on the velocity of the key stroke. Sadly, I have not measured the delay times for the YP-40 claviature, but I expect them to be a little longer (since the keys have more weight).
Here’s a shot from a first “proof of concept” set-up, with the 24 key claviature hooked up to an stm32 devel board from ebay.
The custom hardware
Having the necessary information about the claviature, I moved on and built a small PCB accomodating an STM32 microcontroller. I would have used an ATmega microcontroller but from some rough estimations I came to the conclusion that an 8 bit platform clocked at 16 MHz would not be sufficient for the needs of this project. Anyway I was looking for a reason to get acquainted with a state of the art 32bit microcontroller, so this enterprise became my first STM32 project.
The STM32F103C8T6 has 20KByte of SRAM and runs at 72 MHz. This 48 pin chip has 37 I/O pins of which almost all were used in this application.
The schematic of the board is rather “boring”: just a microcontroller, a quartz, a voltage regulator and a big pin header that connects the 26 key matrix lines to 26 individual general purpose I/O pins of the microcontroller.
I made the PCB layout with KICAD, except for the routing. Therefore I exported the PCB project in the spectra DSN format and imported it in the http://freerouting.net autorouter. Consequently I produced the board with the toner transfer method.
Since I am a Linux enthusiast, I had to get an STM32 development toolchain to work under Linux (Mint). This Howto by Peter Seng helped me a lot : http://www.downloads.seng.de/HowTo_ToolChain_STM32_Ubuntu.pdf
I programmed the chip via its SWD interface with my hacked-to-versaloon STM8S discovery board that I got for free at an embedded systems fair.
The readout procedure
The GPIO pins of the microcontroller that are connected to the “k” lines of the key matrix are internally configured as inputs with pulldown resistors, whereas the GPIOs connected to the “u” and “d” lines are configured as outputs.
The scanning procedure is executed in a timer interrupt routine 1500 times per second. For each interrupt the following happens:
- loop over all “u” and “d” pins in the following matter “d1<=Hi, others<=Lo”, “u1<=Hi, others<=Lo”, “d2<=Hi, others<=Lo”, etc …
- capture the bit pattern of the twelve “k” pins
- loop over the bits in the captured pattern which correspond to individual key contacts and evaluate:
- if upper contact of key n is open, then increase (+1) the 16 bit integer in the array slice delay_mem[n] (acting as a counter for the delay time between upper contact opening/lower contact closing)
- if lower contact of key n is closed, then enqueue an entry to a ring buffer, consisting of the key number and delay_mem[n] (which is later to become a NOTE_ON MIDI event)
- if upper contact of key n is closed, then reset delay_mem[n] to zero and enqueue an entry to the ring buffer consisting of the key number and delay=0 (which is later to become a NOTE_OFF MIDI event)
In the normal execution thread (outside the interrupt), the entries from the ring buffer are dequeued and a MIDI message is generated and sent via the UART.
A MIDI message for a note on event of key #20 (example) and velocity 64 (example) consists of three bytes:
byte 1: 0×90 (shown as hex): “I announce you the beginning of a note on event on channel 0″
byte 2: 20 (shown as integer): “It’s about key #20″
byte 3: 64 (shown as integer): “The key was struck down with a velocity value of 64″ (possible values are 0-127)
These bytes have to be sent with 8N1 (standard) encoding and a data rate of 31250 baud.
Before sending the packet, the velocity has to be calculated from the key contact delay time. I assumed that, because velocity (speed) is a distance divided by the time to travel the distance, also the key velocity has to be proportional to 1/(key contact delay time). To adjust the velocity I introduced an arbitrary scaling factor c (which was set to 1440 by trial and error):
When a ring buffer entry with delay==0 gets dequeued, the µC sends a NOTE_OFF MIDI message.
Yep, the keyboard works (as you can see in the video at the top of the page). It works until the present day and was played at live events at least three times in combination with VST instruments on a notebook. The touch and feel is very satisfactory, though I doubt that my built delivers the full 127 steps of velocity resolution. But who can really judge that anyway 😀
Here you find everything (includes KICAD PCB project and C source code+hex file)