Add parallel port comms to arduino
This commit is contained in:
193
arduino/kbd/kbd.cc
Normal file
193
arduino/kbd/kbd.cc
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
// keyboard wiring diagram:
|
||||||
|
// https://www.minuszerodegrees.net/5150/misc/5150_keyboard_reset.jpg
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
constexpr int kClockPin = PC6;
|
||||||
|
constexpr int kDataPin = PC4;
|
||||||
|
|
||||||
|
constexpr int kBitDelayMicros = 100;
|
||||||
|
constexpr int kCodeDelayMicros = 200;
|
||||||
|
constexpr int kKeyDelayMicros = 100;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void sendCode(int code) {
|
||||||
|
// preconditions: clock and data pins are INPUT
|
||||||
|
|
||||||
|
while (digitalRead(kDataPin) == LOW || digitalRead(kClockPin) == LOW) {
|
||||||
|
// wait. we're not allowed to send
|
||||||
|
}
|
||||||
|
|
||||||
|
digitalWrite(kDataPin, LOW);
|
||||||
|
digitalWrite(kClockPin, LOW);
|
||||||
|
|
||||||
|
pinMode(kClockPin, OUTPUT);
|
||||||
|
delayMicroseconds(kBitDelayMicros);
|
||||||
|
pinMode(kClockPin, INPUT);
|
||||||
|
// delayMicroseconds(kDelayMicros);
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
if ((code & (1 << i)) == 0) {
|
||||||
|
// send a 0
|
||||||
|
pinMode(kDataPin, OUTPUT);
|
||||||
|
} // else do nothing, it's already a 1
|
||||||
|
delayMicroseconds(kBitDelayMicros);
|
||||||
|
|
||||||
|
pinMode(kClockPin, OUTPUT);
|
||||||
|
delayMicroseconds(kBitDelayMicros);
|
||||||
|
pinMode(kDataPin, INPUT);
|
||||||
|
pinMode(kClockPin, INPUT);
|
||||||
|
}
|
||||||
|
delayMicroseconds(kCodeDelayMicros);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
kReady,
|
||||||
|
kMaybeReset,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr int kResetDelay = 19;
|
||||||
|
|
||||||
|
constexpr int kLetterCodes[] = {
|
||||||
|
0x1E, // 'a'
|
||||||
|
0x30, 0x2E, 0x20, 0x12, 0x21, 0x22, 0x23, 0x17, 0x24,
|
||||||
|
0x25, 0x26, 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1F,
|
||||||
|
0x14, 0x16, 0x2F, 0x11, 0x2D, 0x15, 0x2c,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr int kNumberCodes[] = {
|
||||||
|
0x0B, // '0'
|
||||||
|
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
|
||||||
|
};
|
||||||
|
|
||||||
|
void sendNormalCode(int code) {
|
||||||
|
sendCode(code);
|
||||||
|
sendCode(code | 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendShiftCode(int code) {
|
||||||
|
sendCode(42); // left shift
|
||||||
|
sendNormalCode(code);
|
||||||
|
sendCode(42 | 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendCtrlAltDel() {
|
||||||
|
sendCode(0x1d); // ctrl
|
||||||
|
sendCode(0x38); // alt
|
||||||
|
sendCode(0x53); // del
|
||||||
|
sendCode(0x53 | 0x80);
|
||||||
|
sendCode(0x38 | 0x80);
|
||||||
|
sendCode(0x1d | 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendCtrlBreak() {
|
||||||
|
sendCode(0x1d); // ctrl
|
||||||
|
sendCode(0x46); // break
|
||||||
|
sendCode(0x46 | 0x80);
|
||||||
|
sendCode(0x1d | 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void sendAsciiChar(int c) {
|
||||||
|
if (c >= 'a' && c <= 'z') {
|
||||||
|
return sendNormalCode(kLetterCodes[c - 'a']);
|
||||||
|
} else if (c >= '0' && c <= '9') {
|
||||||
|
return sendNormalCode(kNumberCodes[c - '0']);
|
||||||
|
} else if (c >= 'A' && c <= 'Z') {
|
||||||
|
return sendShiftCode(kLetterCodes[c - 'A']);
|
||||||
|
}
|
||||||
|
switch (c) {
|
||||||
|
case '-':
|
||||||
|
return sendNormalCode(12);
|
||||||
|
case '=':
|
||||||
|
return sendNormalCode(13);
|
||||||
|
case '[':
|
||||||
|
return sendNormalCode(26);
|
||||||
|
case ']':
|
||||||
|
return sendNormalCode(27);
|
||||||
|
case ';':
|
||||||
|
return sendNormalCode(39);
|
||||||
|
case '\'':
|
||||||
|
return sendNormalCode(40);
|
||||||
|
case ',':
|
||||||
|
return sendNormalCode(51);
|
||||||
|
case '.':
|
||||||
|
return sendNormalCode(52);
|
||||||
|
case '/':
|
||||||
|
return sendNormalCode(53);
|
||||||
|
case ' ':
|
||||||
|
return sendNormalCode(57);
|
||||||
|
case '\n':
|
||||||
|
return sendNormalCode(28);
|
||||||
|
|
||||||
|
case '!':
|
||||||
|
return sendShiftCode(2);
|
||||||
|
case '@':
|
||||||
|
return sendShiftCode(3);
|
||||||
|
case '#':
|
||||||
|
return sendShiftCode(4);
|
||||||
|
case '$':
|
||||||
|
return sendShiftCode(5);
|
||||||
|
case '%':
|
||||||
|
return sendShiftCode(6);
|
||||||
|
case '^':
|
||||||
|
return sendShiftCode(7);
|
||||||
|
case '&':
|
||||||
|
return sendShiftCode(8);
|
||||||
|
case '*':
|
||||||
|
return sendShiftCode(9);
|
||||||
|
case '(':
|
||||||
|
return sendShiftCode(10);
|
||||||
|
case ')':
|
||||||
|
return sendShiftCode(11);
|
||||||
|
case '_':
|
||||||
|
return sendShiftCode(12);
|
||||||
|
case '+':
|
||||||
|
return sendShiftCode(13);
|
||||||
|
|
||||||
|
case '{':
|
||||||
|
return sendShiftCode(26);
|
||||||
|
case '}':
|
||||||
|
return sendShiftCode(27);
|
||||||
|
case ':':
|
||||||
|
return sendShiftCode(39);
|
||||||
|
case '"':
|
||||||
|
return sendShiftCode(40);
|
||||||
|
case '<':
|
||||||
|
return sendShiftCode(51);
|
||||||
|
case '>':
|
||||||
|
return sendShiftCode(52);
|
||||||
|
case '?':
|
||||||
|
return sendShiftCode(53);
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
return sendCtrlAltDel();
|
||||||
|
case 27:
|
||||||
|
return sendCtrlBreak(); // escape
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupKbd() {
|
||||||
|
pinMode(kClockPin, INPUT);
|
||||||
|
pinMode(kDataPin, INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkKbdReset() {
|
||||||
|
static State state = kReady;
|
||||||
|
static int lastclocklow = 0;
|
||||||
|
|
||||||
|
int clockp = digitalRead(kClockPin);
|
||||||
|
if (state == State::kReady && clockp == LOW) {
|
||||||
|
state = State::kMaybeReset;
|
||||||
|
lastclocklow = millis();
|
||||||
|
} else if (state == State::kMaybeReset) {
|
||||||
|
if (clockp == HIGH && millis() - lastclocklow > kResetDelay) {
|
||||||
|
delay(1);
|
||||||
|
state = State::kReady;
|
||||||
|
sendCode(0xaa);
|
||||||
|
Serial.println("Reset!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
arduino/kbd/kbd.h
Normal file
5
arduino/kbd/kbd.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
void sendAsciiChar(int c);
|
||||||
|
void setupKbd();
|
||||||
|
void checkKbdReset();
|
@@ -1,213 +1,67 @@
|
|||||||
// keyboard wiring diagram: https://www.minuszerodegrees.net/5150/misc/5150_keyboard_reset.jpg
|
#include "kbd.h"
|
||||||
|
#include "paracomm.h"
|
||||||
|
#include "parallel.h"
|
||||||
|
|
||||||
constexpr int kClockPin = PC6;
|
ParaComms pc;
|
||||||
constexpr int kDataPin = PC4;
|
|
||||||
|
|
||||||
constexpr int kBitDelayMicros = 100;
|
|
||||||
constexpr int kCodeDelayMicros = 200;
|
|
||||||
constexpr int kKeyDelayMicros = 100;
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
// put your setup code here, to run once:
|
// put your setup code here, to run once:
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
pinMode(LED_BUILTIN, OUTPUT);
|
pinMode(LED_BUILTIN, OUTPUT);
|
||||||
|
|
||||||
pinMode(kClockPin, INPUT);
|
setupKbd();
|
||||||
pinMode(kDataPin, INPUT);
|
setupParallel();
|
||||||
|
|
||||||
Serial.println("kbd 0.1");
|
pc = ParaComms{
|
||||||
}
|
.mosi_cb =
|
||||||
|
[](uint8_t *data, uint8_t len) {
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
Serial.printf("%c", data[i]);
|
||||||
|
pc.SendByte(data[i]); // echo
|
||||||
|
if (data[i] == '\r') {
|
||||||
|
pc.SendByte('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// 0. reset
|
Serial.println("kbd 0.1");
|
||||||
// 0.0. clock line will be held low externally for 20 ms
|
|
||||||
// when back up, keyboard needs to clock 0xAA as a response
|
|
||||||
//
|
|
||||||
// 1. operation
|
|
||||||
// 1.0. just directly clock out scan codes of pressed keys
|
|
||||||
|
|
||||||
void sendCode(int code) {
|
|
||||||
Serial.printf("sending code 0x%02x\n", code);
|
|
||||||
// preconditions: clock and data pins are INPUT
|
|
||||||
|
|
||||||
while (digitalRead(kDataPin) == LOW || digitalRead(kClockPin) == LOW) {
|
|
||||||
// wait. we're not allowed to send
|
|
||||||
}
|
|
||||||
|
|
||||||
digitalWrite(kDataPin, LOW);
|
|
||||||
digitalWrite(kClockPin, LOW);
|
|
||||||
|
|
||||||
pinMode(kClockPin, OUTPUT);
|
|
||||||
delayMicroseconds(kBitDelayMicros);
|
|
||||||
pinMode(kClockPin, INPUT);
|
|
||||||
// delayMicroseconds(kDelayMicros);
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
if ((code & (1 << i)) == 0) {
|
|
||||||
// send a 0
|
|
||||||
pinMode(kDataPin, OUTPUT);
|
|
||||||
} // else do nothing, it's already a 1
|
|
||||||
delayMicroseconds(kBitDelayMicros);
|
|
||||||
|
|
||||||
pinMode(kClockPin, OUTPUT);
|
|
||||||
delayMicroseconds(kBitDelayMicros);
|
|
||||||
pinMode(kDataPin, INPUT);
|
|
||||||
pinMode(kClockPin, INPUT);
|
|
||||||
}
|
|
||||||
delayMicroseconds(kCodeDelayMicros);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum State {
|
|
||||||
kReady,
|
|
||||||
kMaybeReset,
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr int kResetDelay = 19;
|
|
||||||
|
|
||||||
State state = State::kReady;
|
|
||||||
int lastclocklow = 0;
|
|
||||||
|
|
||||||
constexpr int kLetterCodes[] = {
|
|
||||||
0x1E, // 'a'
|
|
||||||
0x30,
|
|
||||||
0x2E,
|
|
||||||
0x20,
|
|
||||||
0x12,
|
|
||||||
0x21,
|
|
||||||
0x22,
|
|
||||||
0x23,
|
|
||||||
0x17,
|
|
||||||
0x24,
|
|
||||||
0x25,
|
|
||||||
0x26,
|
|
||||||
0x32,
|
|
||||||
0x31,
|
|
||||||
0x18,
|
|
||||||
0x19,
|
|
||||||
0x10,
|
|
||||||
0x13,
|
|
||||||
0x1F,
|
|
||||||
0x14,
|
|
||||||
0x16,
|
|
||||||
0x2F,
|
|
||||||
0x11,
|
|
||||||
0x2D,
|
|
||||||
0x15,
|
|
||||||
0x2c,
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr int kNumberCodes[] = {
|
|
||||||
0x0B, // '0'
|
|
||||||
0x02,
|
|
||||||
0x03,
|
|
||||||
0x04,
|
|
||||||
0x05,
|
|
||||||
0x06,
|
|
||||||
0x07,
|
|
||||||
0x08,
|
|
||||||
0x09,
|
|
||||||
0x0A,
|
|
||||||
};
|
|
||||||
|
|
||||||
void sendNormalCode(int code) {
|
|
||||||
sendCode(code);
|
|
||||||
sendCode(code | 0x80);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendShiftCode(int code) {
|
|
||||||
sendCode(42); // left shift
|
|
||||||
sendNormalCode(code);
|
|
||||||
sendCode(42 | 0x80);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendCtrlAltDel() {
|
|
||||||
sendCode(0x1d); // ctrl
|
|
||||||
sendCode(0x38); // alt
|
|
||||||
sendCode(0x53); // del
|
|
||||||
sendCode(0x53 | 0x80);
|
|
||||||
sendCode(0x38 | 0x80);
|
|
||||||
sendCode(0x1d | 0x80);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendCtrlBreak() {
|
|
||||||
sendCode(0x1d); // ctrl
|
|
||||||
sendCode(0x46); // break
|
|
||||||
sendCode(0x46 | 0x80);
|
|
||||||
sendCode(0x1d | 0x80);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendAsciiChar(int c) {
|
|
||||||
if (c >= 'a' && c <= 'z') {
|
|
||||||
return sendNormalCode(kLetterCodes[c - 'a']);
|
|
||||||
} else if (c >= '0' && c <= '9') {
|
|
||||||
return sendNormalCode(kNumberCodes[c - '0']);
|
|
||||||
} else if (c >= 'A' && c <= 'Z') {
|
|
||||||
return sendShiftCode(kLetterCodes[c - 'A']);
|
|
||||||
}
|
|
||||||
switch (c) {
|
|
||||||
case '-': return sendNormalCode(12);
|
|
||||||
case '=': return sendNormalCode(13);
|
|
||||||
case '[': return sendNormalCode(26);
|
|
||||||
case ']': return sendNormalCode(27);
|
|
||||||
case ';': return sendNormalCode(39);
|
|
||||||
case '\'': return sendNormalCode(40);
|
|
||||||
case ',': return sendNormalCode(51);
|
|
||||||
case '.': return sendNormalCode(52);
|
|
||||||
case '/': return sendNormalCode(53);
|
|
||||||
case ' ': return sendNormalCode(57);
|
|
||||||
case '\n': return sendNormalCode(28);
|
|
||||||
|
|
||||||
case '!': return sendShiftCode(2);
|
|
||||||
case '@': return sendShiftCode(3);
|
|
||||||
case '#': return sendShiftCode(4);
|
|
||||||
case '$': return sendShiftCode(5);
|
|
||||||
case '%': return sendShiftCode(6);
|
|
||||||
case '^': return sendShiftCode(7);
|
|
||||||
case '&': return sendShiftCode(8);
|
|
||||||
case '*': return sendShiftCode(9);
|
|
||||||
case '(': return sendShiftCode(10);
|
|
||||||
case ')': return sendShiftCode(11);
|
|
||||||
case '_': return sendShiftCode(12);
|
|
||||||
case '+': return sendShiftCode(13);
|
|
||||||
|
|
||||||
case '{': return sendShiftCode(26);
|
|
||||||
case '}': return sendShiftCode(27);
|
|
||||||
case ':': return sendShiftCode(39);
|
|
||||||
case '"': return sendShiftCode(40);
|
|
||||||
case '<': return sendShiftCode(51);
|
|
||||||
case '>': return sendShiftCode(52);
|
|
||||||
case '?': return sendShiftCode(53);
|
|
||||||
|
|
||||||
case 0: return sendCtrlAltDel();
|
|
||||||
case 27: return sendCtrlBreak(); // escape
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
static int led_counter = 0;
|
static int led_counter = 0;
|
||||||
static int led = HIGH;
|
static int led = HIGH;
|
||||||
if (led_counter > 400000) {
|
|
||||||
led_counter = 0;
|
|
||||||
led = (led == HIGH) ? LOW : HIGH;
|
|
||||||
digitalWrite(LED_BUILTIN, led);
|
|
||||||
}
|
|
||||||
led_counter += 1;
|
|
||||||
|
|
||||||
int clockp = digitalRead(kClockPin);
|
static int mode = 0; // 0 = keyboard, 1 = parallel
|
||||||
if (state == State::kReady && clockp == LOW) {
|
|
||||||
state = State::kMaybeReset;
|
if (led_counter > 400000) {
|
||||||
lastclocklow = millis();
|
led_counter = 0;
|
||||||
} else if (state == State::kMaybeReset) {
|
led = (led == HIGH) ? LOW : HIGH;
|
||||||
if (clockp == HIGH && millis() - lastclocklow > kResetDelay) {
|
digitalWrite(LED_BUILTIN, led);
|
||||||
delay(1);
|
|
||||||
state = State::kReady;
|
|
||||||
sendCode(0xaa);
|
|
||||||
Serial.println("Reset!");
|
|
||||||
}
|
}
|
||||||
}
|
led_counter += 1;
|
||||||
|
|
||||||
if (Serial.available() > 0) {
|
checkKbdReset();
|
||||||
int c = Serial.read();
|
|
||||||
sendAsciiChar(c);
|
uint8_t mosib;
|
||||||
//delayMicroseconds(kKeyDelayMicros);
|
if (strobeOccurred(mosib)) {
|
||||||
}
|
pc.FeedMosiData(mosib);
|
||||||
|
uint8_t misob = pc.NextMisoNibble();
|
||||||
|
writeParallel(misob);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Serial.available() > 0) {
|
||||||
|
int c = Serial.read();
|
||||||
|
if (c == 2) {
|
||||||
|
mode = 0;
|
||||||
|
} else if (c == 3) {
|
||||||
|
mode = 1;
|
||||||
|
} else {
|
||||||
|
if (mode == 0) {
|
||||||
|
sendAsciiChar(c);
|
||||||
|
} else {
|
||||||
|
pc.SendByte(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
124
arduino/kbd/paracomm.h
Normal file
124
arduino/kbd/paracomm.h
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct ParaComms {
|
||||||
|
using MessageCallback = void (*)(uint8_t *, uint8_t);
|
||||||
|
enum State {
|
||||||
|
kIdle,
|
||||||
|
kLength,
|
||||||
|
kLength2, // for miso
|
||||||
|
kData,
|
||||||
|
// kCrc, // ?
|
||||||
|
};
|
||||||
|
static constexpr uint8_t kMosiStartByte = 0x42;
|
||||||
|
static constexpr uint8_t kIdleByte = 0x00;
|
||||||
|
static constexpr uint8_t kMisoStartNibble = 0xa;
|
||||||
|
static constexpr uint8_t kMisoIdleNibble = 0x0;
|
||||||
|
|
||||||
|
MessageCallback mosi_cb = nullptr;
|
||||||
|
uint8_t *miso_buffer = nullptr;
|
||||||
|
uint8_t miso_size = 0;
|
||||||
|
uint8_t miso_nibbles_sent = 0;
|
||||||
|
State miso_state = kIdle;
|
||||||
|
|
||||||
|
// double work buffer. one being sent, one being written to
|
||||||
|
uint8_t miso_workbuf[2][256];
|
||||||
|
uint8_t miso_workbuf_idx = 0;
|
||||||
|
uint8_t miso_workbuf_size = 0;
|
||||||
|
|
||||||
|
uint8_t mosi_buffer[256];
|
||||||
|
uint8_t mosi_size = 0;
|
||||||
|
uint8_t mosi_received = 0;
|
||||||
|
State mosi_state = kIdle;
|
||||||
|
|
||||||
|
void SwapBuffers() {
|
||||||
|
miso_buffer = miso_workbuf[miso_workbuf_idx];
|
||||||
|
miso_size = miso_workbuf_size;
|
||||||
|
|
||||||
|
miso_workbuf_idx = (miso_workbuf_idx + 1) % 2;
|
||||||
|
miso_workbuf_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SendByte(uint8_t b) {
|
||||||
|
if (miso_workbuf_size == 256) {
|
||||||
|
return -1; // sorry we're full
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *buff = miso_workbuf[miso_workbuf_idx];
|
||||||
|
buff[miso_workbuf_size] = b;
|
||||||
|
miso_workbuf_size += 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FeedMosiData(uint8_t b) {
|
||||||
|
switch (mosi_state) {
|
||||||
|
case kIdle:
|
||||||
|
// check start byte
|
||||||
|
if (b == kMosiStartByte) {
|
||||||
|
mosi_state = kLength;
|
||||||
|
} else if (b == kIdleByte) {
|
||||||
|
// just twiddling our thumb drives
|
||||||
|
} else {
|
||||||
|
// Serial.printf("sp: %02x\n", b);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kLength:
|
||||||
|
// assert(b > 0)
|
||||||
|
mosi_size = b;
|
||||||
|
mosi_received = 0;
|
||||||
|
mosi_state = kData;
|
||||||
|
break;
|
||||||
|
case kData:
|
||||||
|
mosi_buffer[mosi_received] = b;
|
||||||
|
mosi_received += 1;
|
||||||
|
|
||||||
|
if (mosi_received == mosi_size) {
|
||||||
|
if (mosi_cb != nullptr) {
|
||||||
|
mosi_cb(mosi_buffer, mosi_received);
|
||||||
|
}
|
||||||
|
mosi_state = kIdle;
|
||||||
|
}
|
||||||
|
case kLength2: // error
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t NextMisoNibble() {
|
||||||
|
switch (miso_state) {
|
||||||
|
case kIdle:
|
||||||
|
if (miso_size == 0) {
|
||||||
|
SwapBuffers();
|
||||||
|
if (miso_size == 0) {
|
||||||
|
return kMisoIdleNibble;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
miso_state = kLength;
|
||||||
|
return kMisoStartNibble;
|
||||||
|
case kLength:
|
||||||
|
// assert(miso_size > 0);
|
||||||
|
miso_state = kLength2;
|
||||||
|
return miso_size & 0xf;
|
||||||
|
case kLength2:
|
||||||
|
miso_nibbles_sent = 0;
|
||||||
|
miso_state = kData;
|
||||||
|
return miso_size >> 4;
|
||||||
|
case kData: {
|
||||||
|
uint8_t b = miso_buffer[miso_nibbles_sent / 2];
|
||||||
|
if (miso_nibbles_sent % 2 == 0) {
|
||||||
|
b &= 0xf;
|
||||||
|
} else {
|
||||||
|
b >>= 4;
|
||||||
|
}
|
||||||
|
miso_nibbles_sent += 1;
|
||||||
|
if (miso_nibbles_sent == miso_size * 2) {
|
||||||
|
SwapBuffers();
|
||||||
|
miso_state = kIdle;
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
};
|
95
arduino/kbd/parallel.cc
Normal file
95
arduino/kbd/parallel.cc
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
#include "parallel.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr int kParallelDataPins[] = {
|
||||||
|
D9, D8, D7, D6, D5, D4, D3, D2,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr int kParallelBusyPin = D14;
|
||||||
|
constexpr int kParallelAckPin = D15;
|
||||||
|
constexpr int kParallelPaperoutPin = D12;
|
||||||
|
constexpr int kParallelSelectPin = D11;
|
||||||
|
|
||||||
|
constexpr int kParallelNibblePins[] = {
|
||||||
|
kParallelSelectPin,
|
||||||
|
kParallelPaperoutPin,
|
||||||
|
kParallelAckPin,
|
||||||
|
kParallelBusyPin,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr bool kParallelNibbleInverted[] = {
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr int kParallelStrobePin = D10;
|
||||||
|
|
||||||
|
volatile bool strobe_pending = false;
|
||||||
|
volatile uint8_t strobe_byte = 0;
|
||||||
|
|
||||||
|
void strobeIsr() {
|
||||||
|
strobe_pending = true;
|
||||||
|
strobe_byte = readParallel();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
uint8_t readParallel() {
|
||||||
|
uint8_t out = 0;
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
if (digitalReadFast(digitalPinToPinName(kParallelDataPins[i])) ==
|
||||||
|
HIGH) {
|
||||||
|
out |= (1 << i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (digitalReadFast(digitalPinToPinName(kParallelStrobePin)) == HIGH) {
|
||||||
|
out |= (1 << 8);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BIT(x, n) ((x & (1 << n)) ? HIGH : LOW)
|
||||||
|
|
||||||
|
void writeParallel(uint8_t nibble) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
int lvl = BIT(nibble, i);
|
||||||
|
if (kParallelNibbleInverted[i]) {
|
||||||
|
lvl = lvl == HIGH ? LOW : HIGH;
|
||||||
|
}
|
||||||
|
digitalWriteFast(digitalPinToPinName(kParallelNibblePins[i]), lvl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool strobeOccurred(uint8_t &byte) {
|
||||||
|
bool pending;
|
||||||
|
{
|
||||||
|
noInterrupts();
|
||||||
|
pending = strobe_pending;
|
||||||
|
byte = strobe_byte;
|
||||||
|
strobe_pending = false;
|
||||||
|
interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
return pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupParallel() {
|
||||||
|
for (int i = 0; i < sizeof(kParallelDataPins) / sizeof(int); i++) {
|
||||||
|
pinMode(kParallelDataPins[i], INPUT);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < sizeof(kParallelNibblePins) / sizeof(int); i++) {
|
||||||
|
pinMode(kParallelNibblePins[i], OUTPUT);
|
||||||
|
digitalWrite(kParallelNibblePins[i], HIGH);
|
||||||
|
}
|
||||||
|
pinMode(kParallelStrobePin, INPUT);
|
||||||
|
|
||||||
|
attachInterrupt(digitalPinToInterrupt(kParallelStrobePin), strobeIsr,
|
||||||
|
FALLING);
|
||||||
|
}
|
14
arduino/kbd/parallel.h
Normal file
14
arduino/kbd/parallel.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/** Returns the byte present on D0~D7 */
|
||||||
|
uint8_t readParallel();
|
||||||
|
|
||||||
|
/** Writes a nibble on status pins */
|
||||||
|
void writeParallel(uint8_t nibble);
|
||||||
|
|
||||||
|
void setupParallel();
|
||||||
|
|
||||||
|
/** Returns true and sets the byte if a strobe was received */
|
||||||
|
bool strobeOccurred(uint8_t &byte);
|
Reference in New Issue
Block a user