Attached is my program and the electronic scheme. It's based on a example from this forum, but it was made with Teensy 3.2.
Thanks
Code: Select all
/*
* Wiring for Teensy4.0:
* Teensy 1(TX1) -- RJ 1
* Teensy 2 -- RJ 2 (optional)
* Teensy GND -- RJ 3,4,5
* Teensy Vin -- RJ 6
*
* Note that a real Coleco keyboard cable is reversed.
* It is best to check that 3,4,5 are connected to one another on the Teensy end.
*/
// See https://console5.com/techwiki/images/b/b5/Coleco_ADAM_Technical_Reference_Manual.pdf, Chapter 3.3.
// The high nibble is the message type and the low nibble is the address.
// Since we only send to device 1, the keyboard, we bake that in.
const uint8_t MN_RESET = 0x01; // command.control (reset)
const uint8_t MN_STATUS = 0x11; // command.control (status)
const uint8_t MN_ACK = 0x21; // command.control (ack)
const uint8_t MN_CLR = 0x31; // command.control (clr)
const uint8_t MN_RECEIVE = 0x41; // command.control (receive)
const uint8_t MN_CANCEL = 0x51; // command.control (cancel)
const uint8_t MN_SEND = 0x61; // command.data (send)
const uint8_t MN_NACK = 0x71; // command.control (nack)
const uint8_t MN_READY = 0xD1; // command.control (ready)
const uint8_t NM_STATUS = 0x81; // response.control (status)
const uint8_t NM_ACK = 0x91; // response.control (ack)
const uint8_t NM_CANCEL = 0xA1; // response.control (cancel)
const uint8_t NM_SEND = 0xB1; // response.data (send)
const uint8_t NM_NACK = 0xC1; // response.control (nack)
#define DEBUG_ADAMNET 1
#define SERIAL_TX_PIN 1
#define RESET_PIN 2
IMXRT_LPUART_t *s_pkuart = &IMXRT_LPUART6; // underlying hardware UART for Serial1
void send_command(uint8_t command) {
#if DEBUG_ADAMNET
Serial.print("S: ");
Serial.println(command, HEX);
#endif
s_pkuart->CTRL |= LPUART_CTRL_TXDIR; // Set in to TX Mode...
Serial1.write(command);
Serial1.flush(); // Wait for transmit complete.
s_pkuart->CTRL &= ~LPUART_CTRL_TXDIR; // Set in to RX Mode...
}
bool receive_response(uint8_t *buffer, uint8_t expected_length) {
uint8_t len = Serial1.readBytes(buffer, expected_length);
#if DEBUG_ADAMNET
Serial.print("R:");
for (uint8_t i = 0; i < len; i++) {
Serial.print(" ");
Serial.print(buffer[i], HEX);
}
Serial.println();
#endif
return len == expected_length;
}
void kbsetup() {
// Setup Serial1 for Half duplex:
Serial1.begin(62500, SERIAL_8N1_RXINV_TXINV);
// Lets setup that IO pin to be in half duplex
s_pkuart->CTRL |= LPUART_CTRL_LOOPS | LPUART_CTRL_RSRC;
// Lets try to enable PU resistor on that pin...
//*(portControlRegister(SERIAL_TX_PIN)) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3) | IOMUXC_PAD_HYS;
//IOMUXC_LPUART6_TX_SELECT_INPUT = 1; // need to get to right one...
Serial1.setTimeout(100);
// Hard RESET.
pinMode(RESET_PIN, OUTPUT);
digitalWrite(RESET_PIN, HIGH);
delay(100);
// Soft RESET. Mostly turns off LOCK.
send_command(MN_RESET);
}
const uint32_t RECEIVE_INTERVAL = 10;
void kbrun() {
static uint32_t last_receive_time = 0;
if (millis() - last_receive_time > RECEIVE_INTERVAL) {
uint8_t buf[8];
send_command(MN_RECEIVE);
if (receive_response(buf, 1)) {
if (buf[0] == NM_ACK) {
send_command(MN_CLR); // Clear to Send
if (receive_response(buf, 5) && buf[0] == NM_SEND) {
if (buf[1] == 0 && buf[2] == 1 && buf[3] == buf[4]) {
// Length 1, checksum okay.
send_command(MN_ACK);
#if DEBUG_ADAMNET
char ch = buf[3];
Serial.print("A: ");
Serial.print(ch, HEX);
if (ch > 0x20 && ch < 0x7F) {
Serial.print(" ");
Serial.write(ch);
}
Serial.println();
#else
Serial.write(buf[3]);
#endif
last_receive_time = millis();
} else {
send_command(MN_NACK);
// Try again right away.
}
}
} else if (buf[0] == NM_NACK) {
// No input available, wait a bit.
last_receive_time = millis();
}
}
}
}