Friday, August 1, 2014

Understanding Rotary Encoders

While attempting to improve the physical interface to the Tapuino I thought about Rotary Encoders which I've wanted to experiment with for a while. I managed to obtain some locally for a fairly reasonable price and set about looking for a solid code base to use. There seem to be a huge number of libraries and as many complaints about the quality of these libraries. Eventually I stumbled across this blog post which promised: Rotary encoders, done properly

The premise looked really promising: a simple state machine for matching encoder state transitions and some lightweight code. Unfortunately the library behaved rather oddly. The first step was always missed and transitioning from one direction to another would also miss a step. Looking at the code it seemed that the state transition table was incorrect in some way, but for the life of me I couldn't reverse engineer the logic as the transitions seemed counter intuitive.

I decided to recreate the table from first principles so that I would completely understand the logic behind the idea and ended up with a solution that seems to work without error which I will explain below.

Firstly some basic theory. Rotary encoders generally have two output pins and a common pin.



Mechanical Rotary Encoder, center pin is common.


As the shaft is rotated the pins are switched on and off in a specific sequence. For example, rotating the shaft clockwise produces the following output:

Output Stage  Pin A  Pin B 
000
110
211
301
400

The first and last entries are when the encoder is at rest. Each set of outputs is one 'click' in an encoder with 'dedents'. When rotated in the counter clockwise direction the code is reversed:

Output Stage  Pin A  Pin B 
000
101
211
310
400


You can get more detail on this Wikipedia article under the section "Incremental rotary encoder". The theory from the post mentioned above was to build a state machine to map these states. In the post the author describes a state machine with seven possible states and gives each a value. Each state name maps to an output state in the tables above. The states and values in the original code are:

R_START0x0
R_CW_FINAL0x1
R_CW_BEGIN0x2
R_CW_NEXT0x3
R_CCW_BEGIN0x4
R_CCW_FINAL0x5
R_CCW_NEXT0x6

Where: R_START is the common 0-0 value and used as a beginning and ending state, R_CW_BEGIN would map to the 1-0 output state, R_CW_NEXT to the 1-1 output state, R_CW_FINAL to the 0-1 output stage and so on. While the value of the states is arbitrary, I chose to change the values so that they followed a 'logical' order:

R_START0x0
R_CW_BEGIN0x1
R_CW_NEXT0x2
R_CW_FINAL0x3
R_CCW_BEGIN0x4
R_CCW_NEXT0x5
R_CCW_FINAL0x6
 
 
The code then goes on to read the state of pins A and B and computes a combined pin state as:

value = A << 1 | B


In other words the combined pin state can have the binary values 00, 01, 10, 11 or decimal 0, 1, 2, 3. This pin state is then used to perform a transition in a state table. The original state table looks like this:

const unsigned char ttable[7][4] = {
  // R_START
  {R_START,    R_CW_BEGIN,  R_CCW_BEGIN, R_START},
  // R_CW_FINAL
  {R_CW_NEXT,  R_START,     R_CW_FINAL,  R_START | DIR_CW},
  // R_CW_BEGIN
  {R_CW_NEXT,  R_CW_BEGIN,  R_START,     R_START},
  // R_CW_NEXT
  {R_CW_NEXT,  R_CW_BEGIN,  R_CW_FINAL,  R_START},
  // R_CCW_BEGIN
  {R_CCW_NEXT, R_START,     R_CCW_BEGIN, R_START},
  // R_CCW_FINAL
  {R_CCW_NEXT, R_CCW_FINAL, R_START,     R_START | DIR_CCW},
  // R_CCW_NEXT
  {R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START},
};
The current state is the row in the table and the combined pin state is used to find a column in the table indicating the next state to transition to. The code that does this looks like this:

state = ttable[state & 0xf][pinstate];

The DIR_CW and DIR_CCW values are OR'd into the state to signal that a transition has occurred. So at starting state R_START with pin values 1-0 (the first set of values in a clockwise transition) the table above would transition to R_CCW_BEGIN....

And I'm not sure how that is supposed to be correct. Lets find out what the correct values should be by mapping this out. Here is a basic table describing the state machine. States as rows and transitions as columns:

Basic Layout

The next step is to realise that if we are in a state and we read the same pin values that got us into this state, we should stay in the state:

Transitioning back to the same state
So, if we are in R_START and read 0-0 we should transition back to R-START, similarly if we are in R_CW_BEGIN and read 1-0 we should transition back to R_CW_BEGIN. With this out of the way, we can map out the transitions for clockwise:


Clockwise direction state map added
Here in sequence:
  • State = R_START, pins read 1-0, transition to R_CW_BEGIN (row 1, column 3)
  • State = R_CW_BEGIN, pins read 1-1, transition to R_CW_NEXT (row 2, column 4)
  • State = R_CW_NEXT, pins read 0-1, transition to R_CW_FINAL (row 3, column 2)
  • State = R_CW_FINAL, pins read 0-0, transition to R_START (row 4, column 1)
At the point of the final transition, the pattern has been matched and we should emit a Clockwise Event.

Similarly we can map out the transitions for counter clockwise:

Direction 2 state map added
Here in sequence:
  • State = R_START, pins read 0-1, transition to R_CCW_BEGIN (row 1, column 1)
  • State = R_CCW_BEGIN, pins read 1-1, transition to R_CCW_NEXT (row 5, column 4)
  • State = R_CCW_NEXT, pins read 1-0, transition to R_CW_FINAL (row 6, column 3)
  • State = R_CCW_FINAL, pins read 0-0, transition to R_START (row 7, column 1)
At the point of the final transition, the pattern has been matched and we should emit a Counter Clockwise Event.

So now that we've mapped out the two complete transitions there is only one thing left to do. All of the blank entries in the table map out illegal states. If we find an illegal state all we need to do is transition back to R_START so that the next legal set of pin inputs can be followed:


Error state map added
In code this would look like this:

unsigned char ttable[7][4] = {

  {R_START, R_CCW_BEGIN, R_CW_BEGIN,  R_START},

  {R_START, R_START,     R_CW_BEGIN,  R_CW_NEXT},

  {R_START, R_CW_FINAL,  R_START,     R_CW_NEXT},

  {R_START, R_CW_FINAL,  R_START,     R_START},

  {R_START, R_CCW_BEGIN, R_START,     R_CCW_NEXT},

  {R_START, R_START,     R_CCW_FINAL, R_CCW_NEXT},

  {R_START, R_START,     R_CCW_FINAL, R_START},

};

Adding in the flags to indicate the final state transitions then becomes extremely logical:

ttable[R_CW_FINAL][R_START] |= DIR_CW;
ttable[R_CCW_FINAL][R_START] |= DIR_CCW;

"When in the final state and transitioning to the start state, emit the direction"

Well this was really interesting to figure out and I hope it helps some folk out there. Now I need to implement this code into the Tapuino...

Till next time, hack on!
-(e)



Monday, July 28, 2014

Tapuino Part 3: Write support and some circuit changes

Well the Tapuino is out in the wild and has been successfully built by another user!

(please note, most recent build instructions are now here: http://sweetlilmre.blogspot.com/2015/03/building-tapuino-r2.html)

Whilst that has been going down I've been hacking away on the code base extending the firmware to handle:

  • Long filename support: I've moved over to the FatFs library to enable this
  • Write support! This was a killer but finally works... if you have a FAST SD Card.
  • Many bug fixes (I2C display now works across more backpacks)
  • Restructuring and code cleanup
There has been a minor HW change to facilitate write support:

The C64 Datasette write line now must be connected to pin D8 of the Nano. If you built the Tapuino as per my previous post all that should be involved is moving it across to the correct pin, possibly soldering up a new header. The breadboard diagram and Fritzing file in GitHub have been updated accordingly:

New wiring
Also, I put in the 5V and GND lines that were missing before.

Write support has been tested with some simple BASIC save files and also via the Elite save game mechanism and all appears well. Files are written to TAP format in a folder on the root of the SD Card called "Recorded" (this folder will be auto-created for you) and have the filename "rec-XXXX.tap" where XXXX is a 4 digit decimal number.

At this point the Tapuino is functionally complete. I'd like to get a few more folk to build and use this to help track down the inevitable bugs and also work on a few more features. Features I have been thinking about include:

  • Button repeat functionality for scrolling through long lists of files.
  • A name input mechanism for naming recorded files.
Go forth and build!
-(e)

Wednesday, July 16, 2014

Tapuino Part 2: Building the $20 C64 Tape Emulator

Hi,

(please note, most recent build instructions are now here: http://sweetlilmre.blogspot.com/2015/03/building-tapuino-r2.html)

Having detailed the Tapuino in my last post, its now time to get down to the specifics of how to build one. In keeping with the low-cost requirement I developed the initial prototype on a breadboard and then moved that design onto strip or vero board. The vero board based construction will eventually live in a nice neat project box (when I finally get there).

DISCLAIMER: If you build this and it blows up your C64, sets your house on fire, kidnaps your dog or any other negative occurrence, I take no responsibility or liability whatsoever. That said I will do my best to help troubleshoot any builds.

So lets get started. Firstly a bill of materials:

Major components:
  • Arduino Nano V3
  • 16x2 LCD Display with I2C backpack
  • SD Card module with built-in level conversion
  • 40 wire Dupont female-female 'ribbon'
I got all of this from TxHang Electronics on eBay. They are cheap and ship relatively quickly, so are worth it in my book. Here is an example shopping list:


Addition components:
  • Piece of vero board. The one I got was 100mm wide x 200mm long. This is also known as strip board and must be the kind with strips of copper (as opposed to individual 'cells')
  • 32 pin (16x2) WIDE dip socket (this is what the Nano will plug into)
  • 6 pin (3x2) dip socket (for the opto-coupler)
  • A strip of male pin headers
  • A strip of male right-angle pin headers
  • A strip of female pin headers (sockets)
  • 5x 10K Ohm resistors
  • 1x 430 Ohm resistor
  • 4x Tactile switches (6x6)
  • 1x 4N25 Opto-coupler
  • Some jumper wire (I use single core wire from a piece of telephone cable)
Here are examples of the headers:


So lets build the main board first. You'll want to put it together like this:

Top view of the main board

Note the minimum dimensions of the board: 26x15. You may wish to make the board larger to allow for mounting holes.

R1 = 10K Ohm
R2 = 430 Ohm
Green blocks = straight header pins.

Break the header pins into appropriate size groups:
3x3 pins
1x1 pin
1x6 pins
and cut the female pin headers to size (mine were 40 pin, so cut down to 19 pins)

Bottom view of the main board
The lighter yellow bits are where the vero board has been cut. It is essential that you check that these tracks are cut properly. Use a multimeter to test for continuity between the tracks once cut. To cut the tracks on my prototype I used a small sharp drill bit that was luckily a perfect fit.
Please take note of the cut track between the two pins of R2.

I would suggest that you solder in this order:
  1. 32 pin wide dip
  2. 6 pin dip
  3. 2x3 pin header group next to the 6 pin dip
  4. Remaining pin headers
  5. Jumper wires

Feel free to extend the pin headers to access additional pins on the Nano if you like, I just wanted to keep it simple for wire-up later and expose only the pins necessary.

Next up is the button board:

Top view of the button board

Note the minimum dimensions of the board: 22 x 21. You may wish to make the board larger to allow for mounting holes.

R1, R2, R3, R4 are all 10K Ohm
Green pin headers are right angle.
The pinouts of the green pin headers, left to right are as follows: GND, PWR, BTN1, BTN2, BTN3, BTN4

Bottom view of the button board

Take careful note of the tracks to cut:
  • There is a cut between each end of each resistor
  • Between the poles of the switches
  • On the 6th track (from the left) to isolate button 3 from button 1
Also note that the jumper wires for the power and ground lines are soldered through to multiple points. I achieved this by cutting the wires into individual sections:
  • power line (top most line) there are 4 jumper wires of 3, 6, 6, 6 tracks in length.
  • ground line (under the switches) there are 4 jumper wires of 4, 6, 6, 6 tracks in length.

Now lets install the components and connect it all up.
Firstly install the 4N25 opto-coupler into the 6 pin socket:

Installing the 4N25 opto-coupler

Note the small dot on the chip, this indicates pin 1. The chip should be oriented such that pin 1 connects through to the 440 Ohm resistor (R2).

Next the Nano:

Installing the Nano

As the Nano has 2x15 pins and the socket 2x16 pins, care must be taken to place the Nano correctly. The Nano must be aligned so that the empty socket pins are on the right most pins of the socket as per the image above i.e. the Nano is mounted as close as possible to the opto-coupler.

If you trace out the circuit you will observe that the 2x3 pin headers between the opto and the Nano expose a ground and power rail (left is GND, right is PWR). You will use these rails to provide power to the LCD, SD Card and Button breakout boards.

Connect in the following manner:

LCD:
  • Power and Ground go to the rails described above
  • Nano A5 goes to SCL
  • Nano A4 goes to SDA
SD Card:
  • Power and Ground go to the rails described above
  • Nano D13 goes to SCK
  • Nano D12 goes to MISO
  • Nano D11 goes to MOSI
  • Nano D10 goes to SS
Button Board:
  • Power and Ground go to the rails described above
  • Nano A3 goes to BTN1
  • Nano A2 goes to BTN2
  • Nano A1 goes to BTN3
  • Nano A0 goes to BTN4
Finally the pinout for the C2N connector to the board:


You will need to break out 15 pins from a pin header i.e. have 15 continous pins.
Solder the C2N connector to the pins in the follow manner:

  • MOTOR to PIN 1
  • GND to PIN 4
  • PWR to PIN 5
  • WRITE to PIN 12
  • READ to PIN 13
  • SENSE to PIN 15

Remove all other non-connected pins: e,g, pins 2, 3 6-11 and 14. I recommend you heat-shrink the pins as above.

Your connector should look like the one above, the colour to pin map in my case is:
  • Red (MOTOR) to PIN 1
  • Black (GND) to PIN 4
  • Green (PWR) to PIN 5
  • Brown (WRITE) to PIN 12
  • White (READ) to PIN 13
  • Blue (SENSE) to PIN 15
Final assembly should look something like this:
Final Assembly! YES! :)

NOTE:

PIN 1 (RED/MOTOR) is connected to the left-most pin of the female pin header.

Now all that is left to do is flash the sketch to the Nano, disconnect it from USB, insert an SD Card with TAP files, connect the Tapuino to the C64 and enjoy!

The UI is controlled as follows:

BTN 1 is SELECT
BTN 2 is ABORT (during a load) or BACK one directory if browsing
BTN 3 is PREVIOUS
BTN 4 is NEXT

If you have directories on your SD card they will be indicated by an arrow in the right-most column of the LCD where the filename is displayed. Currently only 8.3 format names are supported. I will look into LFN names at a later date.


Caution: Do not connect the Nano to both the C64 and PC. Also check all soldering very carefully for shorts before wiring up to your beloved machine!

Hope you enjoyed this, it was a helluva post to write!

-(e)

Friday, July 11, 2014

Tapuino, the $20 C64 Tape Emulator

I’ve been into retro computing for a while now. It all started when I discovered AmiBay and realised that I could finally afford to buy a Commodore Amiga 1200, a machine that I had seen in my youth and lusted after but had no chance of getting hold of. This passion has grown over the years to the point where I own several A500 and A1200 machines in various states (including a mint condition new / old stock A1200 found in a warehouse in India!)

One machine that I haven’t gotten hold of until recently is the venerable C64, the machine that I cut my coding teeth on. I first discovered binary when I needed to learn how to design sprites for C64 games and I can probably attribute some of my success in my career to this incredible device and learning platform. At any rate, a good mate of mine recently emigrated and bequeathed his childhood C64 to me (big up to Mr +Craig Bettridge for that). Along with the C64 came his Datasette, 1541 drive and several boxes of disks. Unfortunately no tapes, and the 1541 drive seems to require significant restoration.

This left me with a C64 and nothing to load! Looking around the ‘net I found various solutions to this problem: The 1541 Ulitimate, SD2IEC etc. for disk emulation and the DC2N and devices similar to the Cassadapt for tape emulation. The 1541 Ultimate is an incredible piece of hardware but very expensive. Implementations of the SD2IEC also seem to be on the expensive side. The same goes for the tape emulators unless you look at the Cassadapt variety and those did not appeal. At this point I had decided to try and build something myself. Inspired by the Uno2Tap firmware, I wondered if I could move the PC part of that solution onto an MCU and create something standalone. My grand goal was to try to build my own fully functional (at least in terms of playback) tape emulator and so the Tapuino was born.

What I wanted to build was a full playback emulator, including motor signal handling, which would allow the user to choose a TAP file from an SD Card via an LCD and then interact with the C64 in a manner indistinguishable from a real Datasette. As I started this project I had the stated aim that I would try to build the device for under $20 and use common components that were easy for anyone to put together. With this in mind I went shopping on eBay and came up with this component list:

  • Arduino Nano Clone $5.87
  • MicroSD breakout board $1.96
  • 16x2 LCD with I2C backpack $5.16

For a total of $12.99, not too shabby! Additionally I obtained the following bits and pieces:
  • Opto-coupler: 4N25
  • 5x 10K resistors
  • 1x 330 Ohm resistor
  • 4x tactile buttons
  • Some jumper wire

I’ve written firmware for various MCUs including the ATMega328 and so had a basis to start from which included having a good idea of the capabilities of the chip and how to interface it with an SD Card and LCD display. For SD Card access I used the amazing PetitFatFs library from Chan, which I modified to support the current directory code from FatFS (PetitFS’ bigger brother).

As the LCD I chose came with an I2C backpack, interfacing to it was relatively easy and reduced the required pin count down to only 4 pins (which came in handy later on). An alternative would be to use a shift register circuit, but at the price, an I2C solution was a no-brainer.

The most expensive part to source was the Datasette connector, which cost over $7 in single quantities from the usual parts suppliers. As I had an existing Datasette at hand I chose to remove the cable and use this. I am still looking for an alternative and inexpensive solution.

The Datasette Victim / Donor

With the connector removed I proceeded to slap together some code from a previous bit of firmware I had written for an ATMega128 based MP3 player and got some filenames displaying on the LCD via the SD Card:

First base firmware actually doing something

Now that I had the basics in place I wired up the sense, read and ground lines from the Datasette connector to the Nano and got the sense line working. From here on out there was a huge amount of firmware hacking and slog to get a game loading. The first success was one of my favourites: Uridium, but while the game loaded flawlessly I couldn’t get anything else to load, specifically any Novaload games.

First bitter / sweet success!
My test games were Monty On The Run and Monty Mole. This failure puzzled me for several days until after walking the code base repeatedly, I realised that I had left the SPI bus at half speed during initialisation! Basically what was happening was that the SD Card read was slow enough to cause a delay in the signal generation for fast loaders, resulting in all kinds of havoc. 

With that problem solved (which incidentally had caused me to rewrite the interrupt handler part of the code three times whilst trying to find the bug!) I was onto a stable base. The firmware still required a serial port to issue commands to the Nano as I hadn’t wired in any buttons yet, but was good enough to demo to a few mates at work. The response was gratifying :)

YEAH! Its finally working!

The next port of call was to get the motor signalling working. I wanted to provide full emulation for fastloaders that stopped the motor during decrunching and for the usual pause when the first part of the tape loaded. This proved (and continues to prove) somewhat tricky. Firstly in terms of wiring details:

I hooked the motor line up to the Nano via a transistor switch. While this worked, current leaked via the transistor into the Nano causing the Nano to become powered via the input pin when the motor was on. This didn’t seem like a great idea so I switched to an opto-coupler which cleanly separated the signal from the Nano. Then came the headaches… The previously mentioned interrupt routine was altered to check the motor signal and exit if the motor was off. This worked… except for Rambo First Blood Part II. This game for some reason seems to switch the motor off before the TAP file is complete which in turn caused the main loading code to loop endlessly waiting for the TAP to complete (you get the picture). The current code has some rather “interesting” ways to mitigate this behaviour and I am not yet satisfied with the result. Unfortunately testing these outcomes is a rather laborious process when you take into consideration the length of a tape load.

Finally I threw together some button reading code, polished up the interface a bit and the standalone unit was finally ready!

The next steps are to build a nice housing using a project box and find an alternative connector for the C64 Datasette port. However, that said, the code is functional if not clean and I’d be interested to see if anyone else would like to give this a go.

Code can be found at https://github.com/sweetlilmre/tapuino on GitHub. Please feel free to send me a pull request if you have some suggestions on how to improve it. I would like to get to the stage where I can provide full instructions and a veroboard schematic so those less comfortable with the technical details can build one. Here is a Fritzing diagram of how I wired things up, I think its correct, but there may be errors :).



Stay tuned for further updates.