Decoding Pinball Bus Signals Part 5

In the last episode, I spend time figuring out how to detect and respond to the row and column strobes. Here’s the short list of tasks:

  1. Hook up the shift registers to the arduino and write some code to make sure that I knew how to drive them.
  2. Write some timer code to handle the delay after the row and column strobes.
  3. Test the shift register code while connected to the pinball machine
  4. Integrated in the lamp matrix code that I wrote separately.
  5. Figure out how to get the lamp state out of the arduino through the serial port.

It’s about to get real

Shift Registers

I’ll start by seeing if I can read static values out of the shift registers.

I slightly modified a 34-bit connector so I could plug it into my breadboard, and then plugged in the two shift registers underneath. I pulled out my breadboarding wires from Adafruit, and started wiring things up. The basic setup is like this:

  • The 8 data lines are connected to the data lines on the connector.
  • The RCK line is connected to the strobe line from the pinball. When the pin pulls this line low, the shift register will pull data in and latch it.
  • Vcc and GND are connected.
  • Output enable (OE) is grounded to enable the serial output.
  • SCK is hooked to the arduino. This will be driven by the arduino to shift the data out.
  • SLOAD is hooked to the arduino. We will pulse it low/high to move the data from the latch into the shift register.
  • SER (shift input) is grounded. This is used to gang together more than one shift register, but it’s not needed for what I’m doing.
  • QH  is the serial output and will be hooked to the arduino.

I spent a lot of time studying both the truth table and the timing diagrams in the data sheet, and came up with the following sequence:

  1. Pulse SLOAD low
  2. Get the current bit
  3. Pulse SCK low to get the next bit
  4. repeat steps 2-3 until you have 8 bits

I set up a loop that would do this continuously, wired up each data lines to Vcc or GND, and started it running. Success! I got a value out that looked like the way I had the lines wired. And then, I rewired the data lines, and nothing changed. After an embarrassingly long period of debugging, I realized that since I was running not connected to the pinball machine, there was nothing driving the data strobe (RCK) input. I modified the code to pulse RCK low/high, and it started returning real data.

Now, to check the speed.

I set up a pin to be a debug output, drove it high at the beginning of the routine, and low at the end. I could then hook it up to my oscilloscope and figure out how fast my data rate was (I could have used the logic analyzer but it was in the other room connected to pinball machine and the scope was handy). The period was about 0.2 mS, which makes the frequency about 5000 Hz. I would need to do this twice, which would put me at 2500 Hz. That gives me little time to do the rest of the work that I need to do, and seems pretty slow.

So, I did some investigation, and rediscovered that the arduino digitalWrite functions do a lot of work to protect you, but this work makes it very slow – on the order of 150 clock cycles to do a single call. I found a nice set of replacement macros online, and recoded things, and got the full read down to about 10 uS, which should be more than fast enough. Here’s the code:

byte readFromSerial()
{
  byte result = 0;
 
   // pulse SLOAD LH to move data from latches into shift register
  digitalPulseLowHigh(C_SLOAD);

  for (int i = 0; i < 8; i++)
  {
    result = (result << 1)  + digitalState(C_QH);
   
    // pulse clock LH to move to next bit
    digitalPulseLowHigh(C_SCK);
  } 
 
  return result;
}

Another option would be to use SPI to read the data. I didn’t explore that because the bit-banged approach is fast enough and I might need SPI for other communication later.

That gave me one shift register working, so I started on the second one. My original idea was to drive both the shift registers separately, using 6 wires:

Shift register 1

SLOAD1
SCK1
OUT1 (aka Qh)

Shift register 2

SLOAD2
SCK2
OUT2

I have plenty of free pins right now, and this would work just fine, but the speed of the shift register code depends on a fixed set of pins; I can’t pass in a parameter to use. I could easily duplicate the code, but instead I decided to use the output enable feature and share most of the signals. This allowed me to share signals using only 5 wires.

Shift register 1

SLOAD
SCK
OUT (aka Qh)
OE1

Shift register 2

SLOAD
SCK
OUT
OE2

The first 3 signals are in parallel. I choose between the two shift registers by carefully setting OE1 or OE2, putting one shift register into output mode and the other into high-Z mode. This also requires some care in the setup code; I set both OE pins to high before I turn them into outputs so that the outputs will not fight with each other.

At this point, I have code to read both shift registers working. Yea!

Delay code

The problem that I have to address is the strange behavior of the row and column strobes. Sometimes they show up row, column, sometimes they show up row, column, column, row. Which means that I can’t just base it on the row or column strobe. What I came up with is the following:

void handleDataStrobe()
{
  TCNT2=TIMER2_START_VALUE;
  TCCR2B=0x02;
}

void rowDataHandler()
{
  handleDataStrobe();
}

void columnDataHandler()
{
  handleDataStrobe();
}

When either the row or column strobe come in, we set the value of the timer2 count register and set the timer so it’s counting. If another strobe comes it, it merely resets the timer value, so we keep counting. Eventually, we hit the last strobe, the timer overflows, and our interrupt service routine gets called:

ISR(TIMER2_OVF_vect)          // timer compare interrupt service routine
{
  TCCR2B=0x00;
 
  readShiftRegistersAndDecode();
}

Setting TCCR2B to 0x turns off the timer counting, so we only get the interrupt once.

After a bit of tuning, it’s set up and working:

Channel 0 and 1 are the row and column strobes. Channel 2 and 3 are for debugging; channel 2 goes high when the first interrupt handler is called, and then pulses low when the timer expires. Channel 3 is the SCLK channel, and you can see (well, you could see if I zoomed in) two sets of 8 clock cycles to shift the data out.

Here’s the setup. I think that qualifies as the most complicated breadboard I’ve done; there are two 16-pin shift registers, 12 pins from the connector, and then 7 pins out to the arduino.

Testing the shift register for real

Now that I can read data – theoretically – I want to track what data the machine is sending over the bus. The simplest thing to do would be to just add a call to Serial.println() after the data is read.

But the serial port is *slow*. Too slow to be able to capture sequential values of data.  Instead, I allocate 128 bytes for the row and column, capture that many chunks of data, and turn off the capture while the main loop writes the data out the serial port. And then repeat.

I build the code, download, and I start getting data. Lots of data, since the pinball is in attract mode (lots of flashing lights), and that’s not what I need. I flip the machine into single lamp test mode, capture data, and see the following:

      Row       Column
11111110 11111110
11111111 11111101
11111111 11111011
11111111 11110111
11111111 11101111
11111111 11011111
11111111 10111111
11111111 01111111

That is precisely what I had hoped for. The system is walking sequentially through all of the bits in the column byte, and it is setting the row byte values appropriately. It’s not quite as simple as this, because the lamp is flashing on and off, but it’s close.

I did note that the C# code that I wrote has the bitness wrong for the columns, so I went back and fixed it so that both are enabled with a zero bit.

Integrate the lamp matrix code

Finally, I can integrate the C# code I wrote. I do a quick transform into C++ code, and hook it up. I run into a couple of issues, but it mostly goes clean. But the data has a bunch of junk in it; it doesn’t look like the data I was getting before. Thirty minutes yields no clues until I look up and realize the the weight of the Arduino and analyzer had pulled the connector out of the breadboard. A bit of strategically-deployed blue tape holds it in place, and the data comes out fine.

Okay, except for one thing; it’s in row-major form rather than column-major form, but that’s a quick change.

Communicating the state out

To correctly identify the state of the lights, I need to be able to detect three states:

  • Off
  • On
  • Flashing

The way I do this is to allocate a byte for each lamp, and then decode 128 separate chunks of data. Since it takes 8 chunks to cover the whole matrix, this gives us 16 samples for each lamp. That gives us a zero if the lamp is off, a 16 if the lamp is on, and something in between if it is flashing.

To communicate it out we create a 64-character string by adding the value for each lamp to the character ‘0’, so our range is 0123456789:;<=>?@  (I considered a modified hex with 0-G, but that would be a little harder and slower to create), and then send the values out the serial port.

But, as usual, it’s a wee bit more complicated. I don’t have time in the interrupt handler to send that whole string over the serial port, so instead I copy a snapshot of the string and have the main loop write it across the serial port.

And, at this point, this part of the project is all working; I get consistent strings that give me the state of all the bulbs in the machine.

Next steps

Now, I need to convert it from a mess of spaghetti wiring into something better. I have a few options:

  1. A hand-wired Arduino shield
  2. A custom pc board arduino shield
  3. A “Pinduino” design that takes the Arduino Uno reference design, deletes what I don’t need, and adds in the connector and the shift register

#1 and #2 seems the easiest. I might do #3 at some point.

Oh, and there’s the next section, the whole reason I built this thing. But that’s the subject for another post.


So, what do you think ?