Reading The C16 Keyboard (Part 2 - Overcomplicating vs. Undercomplicating)

If you read my last post on dealing with the delightful Commodore C16 keyboard (You didn't? Shame on you; I don't write these posts for myself you know!... Actually that's not true - I write them almost exclusively for myself and am genuinely amazed at the four people who come to this blog and read this crap.) then you'll probably be thinking that an Arduino with a keyboard connection which uses up almost all of its digital pins is pretty useless.

And right you are.

I did explain that I'd first tried to use shift registers to reduce the pin count but had run into a few problems. Well... in this post, I'm putting the 74HC595 back into the circuit so that "Port A" only takes up three Arduino pins instead of the eight we used last time.

The 74HC595 is a dead simple serial to parallel IC. And here's a perfect example of why I hate schematics... they make everything look so complicated.

C16-s-1

I've left out the data lines for "Port B" since at the moment, they're still directly wired into the Arduino as before. "Port A" is now the eight Q output lines from the chip; we're not using the RESET function so that's tied high; and GND goes to... ground!?

I'm going to be borrowing SPI terminology here. We need to control three lines to pass serial data from the Arduino to the 74HC595. We have a CLOCK pulse line, an enable line (OE - Output Enable) which is active low and tells the 74HC595 to load the data it has received onto the Q output pins; and a MOSI (master-output/slave-input or Arduino-output/595-input if you like).

If you look again at the schematic, in particular at the IC, you'll spot two clock lines. Yep, the 74HC595 uses two clock pulses but I'm tying them together to keep things slightly simpler. To cut a long story short, what this means is that I need to add an extra clock pulse after I've finished sending my data byte, otherwise the final bit is missed off. If you can't figure out why from reading the datasheet, feel free to ask.

Coding this is beautifully simple - declare your pins.

byte _OE = 9;
byte _CLOCK = 2;
byte _MOSI = 3;

Make sure you set them up in setup() and initialise their default values.

pinMode(_OE, OUTPUT);
digitalWrite(_OE, HIGH);
pinMode(_CLOCK, OUTPUT);
digitalWrite(_CLOCK, LOW);
pinMode(_MOSI, OUTPUT);
digitalWrite(_MOSI, LOW);

Now when you're ready to send a byte to the C16 keyboard in order to select a row from the key matrix, you pull OE high so that the 595 doesn't display the output yet; use the shiftOut() function to send the data byte; pulse the clock one extra time; and then bring OE low so that the final byte value is sent to the keyboard connector pins.

void KBD_Write(byte data)
{
  digitalWrite(_OE, HIGH);
  shiftOut(_MOSI, _CLOCK, MSBFIRST, data);
  digitalWrite(_CLOCK, HIGH); //need an extra clock since we've tied the two clock pins on the IC together
  digitalWrite(_CLOCK, LOW);
  digitalWrite(_OE, LOW);
}

That's our new KBD_Write() function - replacing the shit from the last post with a much cleaner way of selecting those key rows.

One import thing to note: although this looks like SPI, it isn't. Or at least, it doesn't work with the SPI library available from the Arduino IDE. I'm staying away from the standard pins because even including the library in your sketch messes all of this up if you try to put your CLOCK and MOSI on the pins the library uses. And I need that library later...

Next... taking another crack at using a 74HC165 for handling the keyboard's input.

Posted
 

Reading The C16 Keyboard (Part 1 – From The Jaws Of Defeat)

I've got to admit, this one almost had me beat. I first constructed a simple pin-saving circuit using a 74HC595 (serial in, parallel out) and a 74HC165 (parallel in, serial out) to read from the Commodore C16 keyboard. And it worked too... some of the time. But all too regularly, the Arduino would claim that keys were pressed which weren't.

Throwing out my circuit, I started again. This time, keeping it to the most basic form that you see described below. The spurious key press issue re-emerged but this time I came to a realisation that provided the solution – the pull-up resistors built into the Arduino might just save the day.

They did!

So this is the quick and dirty way of reading a C16 keyboard.

Commodore_c16
The C16 Keyboard

The C16 and Plus/4 keyboards work much the same as on a Commodore 64. Keys are arranged in a switch matrix and to read the key states, you first have to select which column of the matrix you wish to access. We'll call this output port “Port A”. Then there's the unimaginatively titled “Port B”, which is an input port. Reading this tells you the state of the keys in the selected column.

  $FE $FD $FB $F7 $EF $DF $BF $7F
$FE Ins/del 3 5 7 9 Down Right 1
$FD Return w r y i p * Clr/Home
$FB £ a d g j l ] Control
$F7 Help 4 6 8 0 Up Left 2
$EF F1 z c b m > Escape Space
$DF F2 s f h k [ = Commodore
$BF F3 e t u o - + q
$7F @ Shift x v n < / Run/Stop


However, let's not get too carried away yet. Before all that you need to connect the keyboard to your Arduino. The keyboard has a twenty-pin connector, and you need access to sixteen of those pins (eight bits for Port A, and eight for Port B). Without the shift registers, we're going to need to use the analogue input pins on the Arduino as digital inputs.

The pin outs on the C16 and Plus/4 are the same (I think) but probably differ from other Commodores. You might need to search for the computer's schematics to figure it out if you're not using a C16.

Port A pins – 19, 8, 12, 11, 13, 1, 16, 6
Port B pins – 18, 15, 14, 17, 7, 9, 10, 3

C16 Keyboard
Connector Pin
Arduino Pin
1 Digital 2
N/C  
3 Digital 11
4 Ground
N/C  
6 Digital 9
7 Analogue In 4
8 Digital 8
9 Analogue In 5
10 Digital 10
11 Digital 5
12 Digital 4
13 Digital 6
14 Analogue In 2
15 Analogue In 1
16 Digital 8
17 Analogue In 3
18 Analogue In 0
19 Digital 2
N/C  


On to some code.

In our setup() routine, we want to make sure all of the pins are configured correctly, and initialise the serial port.

We call pinMode(x, OUTPUT) for all of the pins being used as Port A. Then pinMode(x, INPUT) for all the pins in Port B. Then we enable the all important pull-up resistors for Port B, by calling digitalWrite(x, HIGH) on each of the pins.

void setup()
{
  pinMode(PA0, OUTPUT);
  pinMode(PA1, OUTPUT);
  pinMode(PA2, OUTPUT);
  pinMode(PA3, OUTPUT);
  pinMode(PA4, OUTPUT);
  pinMode(PA5, OUTPUT);
  pinMode(PA6, OUTPUT);
  pinMode(PA7, OUTPUT);
  
  pinMode(PB0, INPUT);
  pinMode(PB1, INPUT);
  pinMode(PB2, INPUT);
  pinMode(PB3, INPUT);
  pinMode(PB4, INPUT);
  pinMode(PB5, INPUT);
  pinMode(PB6, INPUT);
  pinMode(PB7, INPUT);
  
  // enable pull-up resistors or weird things happen
  digitalWrite(PB0, HIGH);
  digitalWrite(PB1, HIGH);
  digitalWrite(PB2, HIGH);
  digitalWrite(PB3, HIGH);
  digitalWrite(PB4, HIGH);
  digitalWrite(PB5, HIGH);
  digitalWrite(PB6, HIGH);
  digitalWrite(PB7, HIGH);
  
  Serial.begin(9600);
}

To write a byte to Port A (to select a key column) we treat each pin as a representation of a bit. I won't bother to explain all of this – the code is lazy enough that it should be clear. Feel free to post a comment if this doesn't make sense.

void KBD_Write(byte o)
{
  if ((o & 0x80) == 0x80)
    digitalWrite(PA7, HIGH);
  else
    digitalWrite(PA7, LOW);
  if ((o & 0x40) == 0x40)
    digitalWrite(PA6, HIGH);
  else
    digitalWrite(PA6, LOW);
  if ((o & 0x20) == 0x20)
    digitalWrite(PA5, HIGH);
  else
    digitalWrite(PA5, LOW);
  if ((o & 0x10) == 0x10)
    digitalWrite(PA4, HIGH);
  else
    digitalWrite(PA4, LOW);
  if ((o & 0x08) == 0x08)
    digitalWrite(PA3, HIGH);
  else
    digitalWrite(PA3, LOW);
  if ((o & 0x04) == 0x04)
    digitalWrite(PA2, HIGH);
  else
    digitalWrite(PA2, LOW);
  if ((o & 0x02) == 0x02)
    digitalWrite(PA1, HIGH);
  else
    digitalWrite(PA1, LOW);
  if ((o & 0x01) == 0x01)
    digitalWrite(PA0, HIGH);
  else
    digitalWrite(PA0, LOW);
}

To read from Port B (to get the key press states), we just need to read from the Port B pins and assemble them into a byte value with bits set to match whether the pin was HIGH or LOW.

byte KBD_Read()
{
  byte res = 0;
  res |= digitalRead(PB7) << 7;
  res |= digitalRead(PB6) << 6;
  res |= digitalRead(PB5) << 5;
  res |= digitalRead(PB4) << 4;
  res |= digitalRead(PB3) << 3;
  res |= digitalRead(PB2) << 2;
  res |= digitalRead(PB1) << 1;
  res |= digitalRead(PB0);
  return res;
}

Now, in our loop() lets do something super simple. We write 0xFE to Port A to select the first column of keys that we're interested in. Then we read from Port B. If the return value is 0xFF then no key is down. Otherwise, each bit of the result indicates the state of a different key from that column in the matrix. You can see this in action by printing the result in binary form to the serial port. The delay just slows down the code so you don't get too many responses in the serial monitor.

void loop()
{
  KBD_Write(0xfe);
  byte tmp = KBD_Read();
  if (tmp != 0xff)
  {
    Serial.println(KBD_Read(), BIN);
    delay(500);
  }
  KBD_Write(0x00);
}

To check the keys in a different column, we can just change that 0xFE. Eventually, our loop will test each column in turn and report back to us.

But first I need to put my original circuit back together so that I don't tie up so many pins on the Arduino. Since I now know that I need pull-up resistors of some kind, I'm pretty confident it will all work this time... famous last words!

Posted
 

What's this? What's this?

Photo

A prototype serial RAM shield holding a 23K640? A Gameduino? And an Arduino Duemilanove? All stacked together!?!?! What could it be for?

Posted
 

Using the 23K640 (or 23K256) Serial SRAM Chips With An Arduino

For my current (as yet undisclosed) project, I needed to add some extra memory to my Arduino Duemilanove. Microchip's 23K640 serial SRAM chips looked just the thing, but proved a little more awkward to get working than I had imagined. Searching the web, it seems a few others have had problems with these so now that I have it working, I thought I'd post my results.

To communicate with this chip (and its big brother, the 23K256) you need to use SPI. The Arduino handles this nicely and you would think it would all be pretty easy. Well... it kinda is, but there's a few things to watch out for.

Firstly, the 23K640 is 3.3V logic device – so not only do you need to feed it from the 3V3 line on your Arduino, but you'll need to shift the logic levels from your Arduino's output pins. You can connect MISO directly, since the Arduino will accept a 3.3V logic level just fine, but for communications into the 23K640: drop those levels! We're talking about the CLOCK, MOSI and CS outputs here.

There are couple of ways of doing this and I tried two.

Using resistors to reduce the voltage level did not work. It caused erratic behaviour from the S-SRAM chip (such as being able to write to the status register but not into actual memory). Using the diode method described on the SparkFun page, did work; but only with Schottky diodes – I had more erratic behaviour when using regular diodes.

Here's the schematic – hopefully it's just about readable.

Arduino23k640

The three resistors are 10K and, as I mentioned, the three diodes are 1A Schottky diodes. Other things to note are that I've tied the HOLD line high so that it isn't available for use (and isn't going to give us even more complications), and we're using the standard Arduino SPI pins here (13 – CLOCK, 12 – MISO, 11 – MOSI) with pin 10 as our chip select. Certain shields may use pin 10 for their own SPI chip select, so if need be, you can easily use a different output pin - just remember, at the start of your sketch, call pinMode() on it to set it up as an output pin and then send it high to deselect the ram chip.

Assuming your circuit is right, the code for communicating with the 23K640 is really simple. You don't even need to use any configuration changes with the SPI library, just include it and call SPI.begin().

You'll first want to check that the status register is working. To do that, you can set it to change the addressing mode (to page or sequential) and then read back the result.

E.g.

// write to the status register - 23K640 command 1
digitalWrite(10, LOW);
SPI.transfer(0b00000001);
SPI.transfer(0b11000001);
digitalWrite(10, HIGH);

// read from the status register - 23K640 command 5
digitalWrite(10, LOW);
SPI.transfer(0b00000101);
byte res = SPI.transfer(0xFF);
digitalWrite(10, HIGH);
Serial.print("STATUS: ");
Serial.println(res, HEX);

Note that the result will be 2 higher than the number you sent, since Bit 1 always returns 1.

Once you have that working, you can either delete your status register code or specifically set to byte mode (two most significant bits set to 0). Reading and writing to memory should now be easy. (I'm only using byte mode here in these two functions).

E.g.

//write a byte to the specified address - 23K640 command 2
void sWrite(unsigned int address, char data_byte)
{
  digitalWrite(10, LOW);
  SPI.transfer(0b00000010);
  SPI.transfer((char)(address >> 8));
  SPI.transfer((char)address);
  SPI.transfer(data_byte);
  digitalWrite(10, HIGH);
}

//read a byte from the specified address - 23K640 command 3
byte sRead(unsigned int address)
{
  digitalWrite(10, LOW);
  byte res = 0;
  SPI.transfer(0b00000011);
  SPI.transfer((char)(address >> 8));
  SPI.transfer((char)address);
  res = SPI.transfer(0xFF);
  digitalWrite(10, HIGH);
  return res;
}

Good luck.

Posted
 

Dragon 32 to/from iPhone (Cassette Interface)

I've been meaning to note this down and post it somewhere for ages. Hopefully I've remembered all the details correctly.

Not too long ago I bought myself a Dragon 32 - which I love. But I failed to buy myself a suitable cassette player to load and save programs. I have as many fond memories of loading games off cassettes as the next man, but if we're honest, they are a bit of a pain when you compare them to things like iPods and iPhones.

So, I made a cable that lets me use my iPhone as a tape drive. The difference between this, and what you see others doing, is that this cable lets you RECORD.

It's pretty simple really; you only need a couple of items: a 4-pole 3.5mm jack, a 5 pin DIN socket (MIDI), some cable (I used shielded 2-core signal wire - using the shield as the mic wire), a 1K resistor, and a 20K resistor. I always throw on a bit of heat shrink and some Techflex, but that's just me.

Dragon_iphone

When playing a mono file, both the left and right audio channels from the iPod/iPhone/iPad device seem to work, so you can connect your wire to either of those.

The 1K/20K resistor grouping does two things. Firstly, the iPhone requires a certain amount of resistance on the mic line to even register that there's a microphone present. Secondly, this attentuates the signal so that it's not so loud as might damage your £700 phone. You can actually get away with less attentuation than this, but I was taking no chances.

If you buy yourself a decent metal DIN plug then you can put the resistors inside the plug and there'll be plenty of protection - wasn't the easiest soldering job but it's certainly doable.

Posted
 

Honesty is the best policy!

I'm nothing if not honest... sometimes.

While fiddling around, I fried the NES. I still haven't got around to replacing it so that project is pretty much on indefinite hold. Aside from that, I hadn't really been doing any Arduino stuff until very recently.

I have a new project and will blog about it very soon. But so far, it's coming along nicely.

Posted
 

Arduino-Powered NES Mods (Day Six???)

Wow. Long time, no blog.

Turns out I made a few more mistakes in the NES circuit boards than I thought. This resulted in the [not-unusual] attitude of "oh fuck, can I really be bothered to continue with this?"...

... if you look at how long it's been since my last post then clearly the answer is no.

I wouldn't say I'd quit; just took an extended leave of absence from Arduino (and electronics in general). Maybe that's about to change - I have an idea which I'm mulling over at the minute. No Arduinos or games consoles involved, but definitely a bit of VNC1L work.

Posted
 

Arduino-Powered NES Mods (Day Five)

Finally got my PCBs back... with good news and bad news.

The good news is I ordered three, and received six. :-) The bad news is I made a couple of small mistakes - the boards are still workable but I expected the silkscreen to be on the bottom with the solder pads (because I'm dumb) and secondly, I missed a mounting hole out of the design.

Currently assembling one now and will have to drill the extra hole when my cordless drill's battery is recharged.

Nes-pcb3

And yes, I need to learn to take better photos. Sorry about that.

Posted
 

Logic3 PS3 Freebird Controller Library

Click here to download:
PlayStation3.rar (10 KB)

Here's the Arduino library that uses the VNC1L (VDIP2) to talk to a Logic3 wireless PlayStation3 gamepad.

I'll do a bit more of a write-up later on. But everything more or less works - haven't quite figured out the nitty gritty of the force feedback implementation but there's a function in there that activates the two rumblers ok.

Posted
 

Revised Libraries!

Click here to download:
Thrustmaster.rar (9 KB)

Click here to download:
VNC1L.rar (8 KB)

Posted