Tuesday, November 03, 2015

Go for C64 - Part II - The Arduino Leonardo solution

After trying to turn my C64  into a USB keyboard the whole project ended in a cupboard for a year at 90% completion. In the end the whole PICAXE - Arduino Pro Micro combination felt a bit clumsy.
Then I recently stumbled on a blogpost where someone connects a ZX81 keyboard to the Arduino Leonardo, and I found a cheap (€12,-) Leonardo-clone so I decided this was the way to go.

The board came with front headers, which makes connecting the keyboard super easy.
This is the C64 Keyboard matrix: ( diagram from the 'WaitingForFriday' blog)



Corrected C64 Keyboard matrix and keyboard connector diagrams
It is connected to the Arduino as follows:

A - D0
B - D1
C - D2
D - D3
E - D4
F - D5
G - D6
H - D7
0 - D8
1 - D9
2 - D10
3 - D11
4 - A0
5 - A1
6 - A2
7 - A3

The code can be a mix of the code from 'Biosrythm' and TechTonic . :

#include <Keyboard.h>

// ZX81 USB Keyboard for Leonardo
// (c) Dave Curran
// 2013-04-27

// Modified with Function keys by Tony Smith
// 2014-02-15
// Adapted for use with Commodore 64 Keyboard by Cees Meijer
// 2015-11-04
// Enable the debug mode (serial output) by keeping F7 pressed when 
// starting the program
// Special Commodore graphic characters are not implemented

#define NUM_ROWS 8
#define NUM_COLS 8

#define SHIFT_ROW 3
#define SHIFT_COL 1

#define RSHIFT_ROW 4
#define RSHIFT_COL 6

#define F7_ROW 7
#define F7_COL 7

#define DEBOUNCE_VALUE 100
#define REPEAT_DELAY 500

// Keymap for normal use

byte keyMap[NUM_ROWS][NUM_COLS] =
{
  {'1', '3', '5', '7', '9', '+', '$', KEY_BACKSPACE},
  {KEY_LEFT_ARROW, 'w', 'r', 'y', 'i', 'p', '*', KEY_RETURN},
  {'~', 'a', 'd', 'g', 'j', 'l', ';', KEY_LEFT_ARROW},
  {'~', 0  , 'x', 'v', 'n', ',', '/', KEY_UP_ARROW},
  {' ', 'z', 'c', 'b', 'm', '.', 0  , KEY_F1},
  {'~', 's', 'f', 'h', 'k',':', '=' , KEY_F3},
  {'q', 'e', 't', 'u', 'o', '@', KEY_UP_ARROW, KEY_F5},
  {'2', '4', '6', '8', '0', '-', '~', KEY_F7}
};

// Keymap if Shift is pressed

byte keyMapShifted[NUM_ROWS][NUM_COLS] =
{
  {'!', '#', '%', '\'', ')', '+', '$', KEY_BACKSPACE},
  {KEY_LEFT_ARROW, 'W', 'R', 'Y', 'I', 'P', '*', KEY_RETURN},
  {'~', 'A', 'D', 'G', 'J', 'L', ']', KEY_RIGHT_ARROW},
  {'~', 0  , 'X', 'V', 'N', '<', '?', KEY_DOWN_ARROW},
  {' ', 'Z', 'C', 'B', 'M', '>', 0  ,KEY_F2},
  {'~', 'S', 'F', 'H', 'K','[', '=', KEY_F4},
  {'Q', 'E', 'T', 'U', 'O', '@', KEY_UP_ARROW, KEY_F6},
  {'"', '$', '&', '(', '0', '-', '~', KEY_F8}
};
// Global Variables

int debounceCount[NUM_ROWS][NUM_COLS];
int altKeyFlag;
bool serial_output;

// Define the row and column pins

byte colPins[NUM_COLS] = {0,1,2 ,3 ,4 ,5 ,6 ,7}; // A,B,C,D,E,F,G,H
byte rowPins[NUM_ROWS] = {8,9,10,11,A0,A1,A2,A3};

// SETUP

void setup()
{
  // Set all pins as inputs and activate pull-ups
  serial_output = false;
  for (byte c = 0 ; c < NUM_COLS ; c++)
  {
    pinMode(colPins[c], INPUT);
    digitalWrite(colPins[c], HIGH);
    
    // Clear debounce counts
    
    for (byte r = 0 ; r < NUM_ROWS ; r++)
    {
      debounceCount[r][c] = 0;
    }
  }
  
  // Set all pins as inputs
  
  for (byte r = 0 ; r < NUM_ROWS ; r++)
  {
    pinMode(rowPins[r], INPUT);
  }
  
  // Function key is NOT pressed
  
  altKeyFlag = ALT_KEY_OFF;
  pinMode(rowPins[F7_ROW], OUTPUT);
  if (digitalRead(colPins[F7_COL]) == LOW) serial_output = true;
  // Initialise the keyboard
  if (serial_output )
   {
    Serial.begin(9600);
   }
   else
   {
    Keyboard.begin();  
   }
}

// LOOP

void loop()
{
  bool shifted = false;
  bool r_shifted = false;
  bool keyPressed = false;
  
  // Check for the Shift key being pressed
  
  pinMode(rowPins[SHIFT_ROW], OUTPUT);
  if (digitalRead(colPins[SHIFT_COL]) == LOW) shifted = true;
  
  pinMode(rowPins[RSHIFT_ROW], OUTPUT);
  if (digitalRead(colPins[RSHIFT_COL]) == LOW) shifted = true;
  
    pinMode(rowPins[SHIFT_ROW], INPUT);
    pinMode(rowPins[RSHIFT_ROW], INPUT);
    
    for (byte r = 0 ; r < NUM_ROWS ; r++)
    {
      // Run through the rows, turn them on
      
      pinMode(rowPins[r], OUTPUT);
      digitalWrite(rowPins[r], LOW);
      
      for (byte c = 0 ; c < NUM_COLS ; c++)
      { 
        if (digitalRead(colPins[c]) == LOW)
        {
          // Increase the debounce count
          
          debounceCount[r][c]++;
          
          // Has the switch been pressed continually for long enough?
          
          int count = debounceCount[r][c];
          if (count == DEBOUNCE_VALUE)
          {
            // First press
            
            keyPressed = true;
            pressKey(r, c, shifted);
          }
          else if (count > DEBOUNCE_VALUE)
          {
            // Check for repeats
            
            count -= DEBOUNCE_VALUE;
            if (count % REPEAT_DELAY == 0)
            {
              // Send repeat
              
              keyPressed = true;
              pressKey(r, c, shifted);
            }
          }
        }
        else
        {
          // Not pressed; reset debounce count
          
          debounceCount[r][c] = 0;
        }
      }
     
    // Turn the row back off
     
    pinMode(rowPins[r], INPUT);
    }
    digitalWrite(rowPins[RSHIFT_ROW], LOW);
    digitalWrite(rowPins[SHIFT_ROW], LOW);
  
}

void pressKey(byte r, byte c, bool shifted)
{  
  // Send the keypress
  if (serial_output) 
    { 
    Serial.print("|");Serial.print("\r\n");Serial.print("|"); 
    Serial.print(r);Serial.print(",");Serial.print(c);Serial.print(":");
    }
  byte key = shifted ? keyMapShifted[r][c] : keyMap[r][c];

  if (serial_output)
   {
   if (key > 0){ Serial.write(key);}
   }
   else
   {
   if (key > 0 ) Keyboard.write(key);
   }
  
}


1 comment:

pdrift said...

I found this page while searching for info for my project. I'm working on something similar where I'm using a keyboard from an HP Jornada 680 hooked up to a teensy++ 2.0.

The keyboard has a matrix with 9 rows and 14 columns. I've mapped the buttons out and i have all the pin combinations i need.

I think i can use your code as a start to get mine working. I also have an analog joystick and 2 pushbuttons connected for use as a mouse which is already working.

So hopefully with a little of your code and a little of the other code I'm using i can end up with something that works for both keyboard and mouse.

Thanks for the info I will study it and use what i can.