Keyboard finished and PCM sound output prototyping

The bring-up of the keyboard controller board was a protracted affair.

The first problem was obtaining an RJ10 cable.

When drawing up the schematics for the IO board and the keyboard controller board I had naively assumed that the RJ10 cable I had salvaged from an old telephone was wired straight through with pin 1 connected to pin 1 etc. Of course this wasn’t the case; one end was reversed.

Therefore I ordered, from Ebay, what claimed to be an RJ10, RJ11 and RJ45 crimping tool along with some RJ10 plugs. Cutting off one end of the cable and attaching a crimped plug with straight through wiring was impossible with the tool I bought; the clamp would not engage with the pins in the end of the plug. However it crimped an RJ45 plug just fine. In the end I crimped the plug “by hand” using a screwdriver and a hammer.

Next I modified the monitor code to send commands to the keyboard to flash the RGB LED. This worked as expected.

After that, attaching the keyboard flex to the flex connector and modifying the monitor to read scancode bytes. It took me only a few seconds to realise that when I’d soldered the flex connector to the controller board I’d made a critical error: the connector was attached backwards!

I attempted to remove the connector with a solder sucker. My best tool for removing through-hole parts is an all-in-one iron and sucker, like this one:

This actually did a passable job at removing the solder but I still managed to deform the plastic in the connector and even damaged the PCB. In the end I had to order another connector, as this was my last one. These parts are now so rare that the best price was £6 from none other then AmigaKit. While waiting for it to arrive I built a new PCB.

I managed to recycle most of the parts onto another controller board, now the third one to be made up. I’m getting quite good at SMT construction.

With all this work came success, finally. I hacked up the monitor code to read the keyboard, which was done by translating the MAXI09OS keyboard driver from 6809 to 68000 assembly. More accurately I ported the scancode translator, the portion of the keyboard handling which translates the scancode stream into ASCII, tracking the shift and control keys as it goes.

The final job for the keyboard was to figure out some kind of base for it. I’d already ruled out the idea of 3D printing a proper case for the keyboard, for two reasons:

  • Producing the CAD model would take days and would be relatively uninteresting work
  • A pretty enormous 3D printer would be required

I was originally thinking about attaching the keyboard to a piece of acrylic, in a similar way to how the keyboard for MAXI09 was originally mounted. But in the end I decided it would be more satisfactory to 3D print a kind of wedge for the keyboard to screw into that provided a decent typing angle. The controller PCB would sit underneath the keyboard, on top of the base of the wedge. Here’s the 3D model:

I came up with this design using Fusion 360 in a couple of hours.

The Amiga 600 keyboard attaches to the base using screws. These screws are ordinarily part of the keyboard and attach a metal backing plate to the plastic body of the keyboard which holds the actual keys. Luckily the screws are just long enough to screw through the 2mm thick 3D printed plastic before screwing through the metal backing plate and into the keyboard body.

The controller board itself sits on standoffs. As you can see in the pictures I don’t really have the correct hardware for this task, but it’ll do for now:

The feet at the space bar end of the keyboard are unfortunately a little close together. I rounded the bottom corners on the base so it didn’t get obstructed by other screws holding the metal plate, but the result is if a key at the bottom corner of the keyboard is pressed then there is a very slight tendency for the keyboard to tilt sideways. Nonetheless I’m pretty happy with the 3D printed solution, though I may yet iterate the design if the tilting becomes annoying.

The result of all this is I now have a 68000-based computer with a pretty decent keyboard and screen.

One thing that is currently lacking, software wise, is the ability to transfer files from my Linux development server to the memory of the MINI000’s 68000. This would be useful for at least:

  • Transferring blocks of compiled code including, perhaps, routines written in C
  • Using files to exercise the hardware:
    • Displaying bitmaps, as previously demonstrated
    • Playing VGM files, as also previously demonstrated

Readers who have been following this blog for a long time will remember that MAXI09OS had the ability to transfer files from a remote machine and run them. The same mechanism can be used here, except instead of automatically running the downloaded file it needs to simply be written into memory.

To do this from within the 68000 monitor I’ve written required the command parser to be extended to cope with quoted strings so that the filename to download can be passed in.

This was fairly trivial to do. The types and their type code supported by the command parser are now:

  • 1: byte
  • 2: word
  • 3: long
  • 0x80: string

Types and their values are held in two arrays; types are always a word in length and values are always a long in length, which means that shorter values are padded with zeros. This makes it easy for a command handler to extract values of any type by index.

Strings are handled in a similar way to longs except the value is the spring’s starting address, the strings themselves being added to a third array whose address is passed in via A4 to the parser routine. One other detail is the the type code is 0x80 instead of the more obvious 4 because strings are not subject to the same validation rules, which allow shorter integers to be used in place of wider ones. If bit 7 in the type is set then the match against the command type requirements is exact instead of allowing any shorter type.

So far only one command has been written which uses strings, the file downloader. It takes two parameters: the name of the file to download and the address to write it to.

The protocol is essentially unchanged from the MAXI09OS implementation, but in summary:

  1. Client (the 68000) sends the operation code, 1 for get, followed by the filename, followed by a null.
    1. One other command code has been implemented, 0, which operates as a “are you there” operation.
  2. Server replies with a 1, if the file exists.
    1. Otherwise it can reply with an error code.
  3. The file size is sent, in network byte order (big endian), as a long.
  4. Finally the file bytes are sent.

As before no acknowledgment of sent data occurs. Also, if the server gets out of step with the client, say because the client was restarted mid transfer and then another command was initiated before the server finished with the first one, then things can get very confused and it usually requires the server to be restarted. None the less the system works quite well, the current use of a rather slow 9600 band rate connection notwithstanding.

The main motivator for writing this facility was to test a piece of hardware for the future MAXI000 board: a DAC for outputting PCM sounds and music.

There are a few different approaches to including a DAC in a circuit like the MAXI000 board, but the one currently favoured is to attach one to the video controller FPGA. This would allow the video memory, in addition to being used to hold the screen text and bitamp, to hold PCM sample data. Playback could then occur independently of both the 68000 and its memory bus; playback wouldn’t have to pause the MPU or viva-versa. The complication this brings is that video memory access (read and writes) would be blocked when reading memory for sound playback, but this should not be too much of an issue due to sound playback requiring memory accesses several of orders of magnitude slower then the video display.

Before proceeding further, it seems sensible to prototype a DAC in action when interfaced directly to a 68000.

Because of a shortage of pins on the second FPGA on the proposed MAXI000 board (I’ve started calling the FPGAs Alpha and Beta), and because modern peripheral ICs are almost exclusively serial bused, the logical starting point for a set of requirements is:

  • SPI based
  • 5V operation
  • 8 and/or 16 bit resolution
  • Suitable for audio applications up to at least 20Khz sample rate

After searching around I found the MCP49x2 (PDF). It has the following features:

  • MCP4902: Dual 8-Bit Voltage Output DAC
  • MCP4912: Dual 10-Bit Voltage Output DAC
  • MCP4922: Dual 12-Bit Voltage Output DAC
  • Rail-to-Rail Output
  • SPI Interface with 20 MHz Clock Support
  • Simultaneous Latching of the Dual DACs with LDAC pin
  • Fast Settling Time of 4.5 μs
  • Selectable Unity or 2x Gain Output
  • External Voltage Reference Inputs
  • External Multiplier Mode
  • 2.7V to 5.5V Single-Supply Operation
  • Extended Temperature Range: -40°C to +125°C

A settling time of 4.5 microseconds yields an approximate max sample rate of 222KHz, which is easily sufficient. The IC is dual channel, but instead of using one IC for stereo output a more interesting setup can be had if one channel drives the reference voltage of the other channel. Thus one channel, channel B, can then be used as a volume control for playback of the actual PCM data on channel A. With two ICs (the unit price is less then $1) then stereo output can still be achieved.

The SPI packet format is not completely trivial and consists of a 16 bit word. This word is made up of the following bits:

  • Bit 15: The channel (A (high) or B (low)).
  • Bit 14: If high buffering is used on the Vref pin,
  • Bit 13: If high the gain on the Vref pin is x1, otherwise it is x2.
  • Bit 12: If low, the channel is turned off (shut down).
  • Bit 11 to Bit 4: The 8 bit value
  • Bit 3 to Bit 0: Unused

In the 12 bit precision DAC ICs, bits 3 to bit 0 are used. Likewise bits 3 and 2 are used on the 10 bit precision DAC. I’m currently undecided whether to make use of these parts, or stick to the 8 bit part. It’s a bit of a shame there isn’t a 16 bit version of this IC, though I could still use a 12 bit DAC and simply discard the upper bits when sending 16 bit sample data.

In my current prototyping, Bit 15 sets whether the volume or data channel is used, bit 14 is low, and the remaining bits are high, ie. buffering is disabled, the Vref gain is fixed at x1, and the channel is not turned off.

Here is the proposed schematic for the sound output section of the MAXI000 board:

As can be seen, the B channels themselves have a reference voltage of 5V, which is then used for the A channel reference. The output of A is divided down by approximately 2.2K over 1K+2.2K, or around 68% and then decoupled (removing the DC offset).  Following the maths, the peak to peak amplitude of a waveform at maximum volume should be 3.4V.

Here is the waveform of a saw tooth wave playing at maximum amplitude:

The peak to peak is measured at 3.56V, which is pretty close to the predicted value.

Actually interfacing the DAC IC was accomplished using spare pins on the CPLD on the MINI000 board. Initially SPI was bit-banged using 68000 code, with the CPLD just passing the pin signals across from CPLD register writes, but for fun and interests sake, I quickly switched to doing the bit-shifting in the CPLD instead, in a similar manner to how DISCo acted as an SPI master on the MAXI09 board.

Here is a capture of the SPI waveforms:

As can be seen, the 68000 is able to perform SPI writes at around 48KHz.

Here is a picture of the breadboard setup:

As usual with my breadboards, it’s a bit of a bodge. The RCA jack was hooked up to my PC speakers, and after playing back some saw tooth waveforms, I decided to try playing back some 8 bit sample data.

The result was far better then I expected it to be, and in fact sounded indistinguishable from when the sample was played back on my Linux desktop. A picture (obviously) does not do it justice, but for fun I obtained a capture from the oscilloscope while playing back a sample:

Code wise, this was implemented with a simple timing delay to output PCM samples at the rate required by the file in question, 11127Hz, pushing each byte at the SPI master implemented in the MINI000 CPLD.

Thinking ahead, for the MAXI000 computer the sample data will be sourced either from:

  1. The 68000 – the SPI master will be presented to the 68000 bus in a similar way to the MINI000 CPLD.
  2. DMA from main memory – the 68000 will be able to configure the Alpha FPGA to push data to the SPI master on Beta at a particular rate.
  3. DMA from video memory – the 68000 will configure the Beta FPGA with a video memory start address, for a particular length and data rate. It could also be possible to configure the volume level, which the Beta FPGA would communicate to the DAC before starting playback.

I’m pretty excited about how well this should work.

Hopefully the next post will contain a complete description of the MAXI000 schematic, although there remains a small amount of prototyping to do…


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.