Skip to content

Commit

Permalink
new project code
Browse files Browse the repository at this point in the history
  • Loading branch information
jepler committed Apr 1, 2024
1 parent 818abfb commit 4081803
Show file tree
Hide file tree
Showing 3 changed files with 280 additions and 0 deletions.
Empty file.
2 changes: 2 additions & 0 deletions USB_Keyboard_to_Xerox_820_adapter/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build
makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
// SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
//
// SPDX-License-Identifier: MIT

// pio-usb is required for rp2040 host
#include "pio_usb.h"
#include "Adafruit_TinyUSB.h"
#include "pico/stdlib.h"

// Pin D+ for host, D- = D+ + 1
#ifndef PIN_USB_HOST_DP
#define PIN_USB_HOST_DP 16
#endif

// Pin for enabling Host VBUS. comment out if not used
#ifndef PIN_5V_EN
#define PIN_5V_EN 18
#endif

#ifndef PIN_5V_EN_STATE
#define PIN_5V_EN_STATE 1
#endif

// USB Host object
Adafruit_USBH_Host USBHost;

void setup() {
}

void loop() {
}

#define KD0 (5) // n.b. all data lines are inverted
#define KD1 (6)
#define KD2 (7)
#define KD3 (9)
#define KD4 (10)
#define KD5 (11)
#define KD6 (12)
#define KD7 (13) // n.b. masked off by FW
#define KSTRB (4)

void setup1() {

// override tools menu CPU frequency setting
set_sys_clock_khz(120'000, true);

#if 0
while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
#endif

#ifdef PIN_5V_EN
pinMode(PIN_5V_EN, OUTPUT);
digitalWrite(PIN_5V_EN, PIN_5V_EN_STATE);
#endif

pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG;
pio_cfg.pin_dp = PIN_USB_HOST_DP;
USBHost.configure_pio_usb(1, &pio_cfg);

// run host stack on controller (rhport) 1
// Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have most of the
// host bit-banging processing works done in core1 to free up core0 for other works
USBHost.begin(1);

pinMode(KD0, OUTPUT);
digitalWrite(KD0, 1);
pinMode(KD1, OUTPUT);
digitalWrite(KD1, 1);
pinMode(KD2, OUTPUT);
digitalWrite(KD2, 1);
pinMode(KD3, OUTPUT);
digitalWrite(KD3, 1);
pinMode(KD4, OUTPUT);
digitalWrite(KD4, 1);
pinMode(KD5, OUTPUT);
digitalWrite(KD5, 1);
pinMode(KD6, OUTPUT);
digitalWrite(KD6, 1);
pinMode(KD7, OUTPUT);
digitalWrite(KD7, 1);
pinMode(KSTRB, OUTPUT);
digitalWrite(KSTRB, 1);
}

int old_ascii = -1;
uint32_t repeat_timeout;
// this matches Linux default of 500ms to first repeat, 1/20s thereafter
const uint32_t default_repeat_time = 50;
const uint32_t initial_repeat_time = 500;

void send_ascii(uint8_t code, uint32_t repeat_time=default_repeat_time) {
old_ascii = code;
repeat_timeout = millis() + repeat_time;
if (code > 32 && code < 127) {
Serial.printf("'%c'\r\n", code);
} else {
Serial.printf("'\\x%02x'\r\n", code);
}
digitalWrite(KSTRB, 0); // pull line low to signify data will occur soon
digitalWrite(KD0, (~code) & (1 << 0));
digitalWrite(KD1, (~code) & (1 << 1));
digitalWrite(KD2, (~code) & (1 << 2));
digitalWrite(KD3, (~code) & (1 << 3));
digitalWrite(KD4, (~code) & (1 << 4));
digitalWrite(KD5, (~code) & (1 << 5));
digitalWrite(KD6, (~code) & (1 << 6));
digitalWrite(KD7, (~code) & (1 << 0)); // not used by firmware so why bother
delayMicroseconds(100); // allow several Z80 cycles for data to stabilize before the strobe... (1us should even suffice but this is a keyboard sir)
digitalWrite(KSTRB, 1);
delayMicroseconds(100); // allow several Z80 cycles for data to hold after the strobe... (1us should even suffice but this is a keyboard sir)
}

void loop1()
{
uint32_t now = millis();
uint32_t deadline = repeat_timeout - now;
if (old_ascii >= 0 && deadline > INT32_MAX) {
send_ascii(old_ascii);
deadline = repeat_timeout - now;
} else if (old_ascii < 0) {
deadline = UINT32_MAX;
}
tuh_task_ext(deadline, false);
}

// Invoked when device with hid interface is mounted
// Report descriptor is also available for use.
// tuh_hid_parse_report_descriptor() can be used to parse common/simple enough
// descriptor. Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE,
// it will be skipped therefore report_desc = NULL, desc_len = 0
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) {
(void)desc_report;
(void)desc_len;
uint16_t vid, pid;
tuh_vid_pid_get(dev_addr, &vid, &pid);

Serial.printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
Serial.printf("VID = %04x, PID = %04x\r\n", vid, pid);

uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
if (itf_protocol == HID_ITF_PROTOCOL_KEYBOARD) {
Serial.printf("HID Keyboard\r\n");
if (!tuh_hid_receive_report(dev_addr, instance)) {
Serial.printf("Error: cannot request to receive report\r\n");
}
}
}

// Invoked when device with hid interface is un-mounted
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
Serial.printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
}

#define FLAG_ALPHABETIC (1)
#define FLAG_SHIFT (2)
#define FLAG_NUMLOCK (4)
#define FLAG_CTRL (8)
#define FLAG_LUT (16)

const char * const lut[] = {
"!@#$%^&*()", /* 0 - shifted numeric keys */
"\r\x1b\10\t -=[]\\#;'`,./", /* 1 - symbol keys */
"\n\x1b\177\t _+{}|~:\"~<>?", /* 2 - shifted */
"\12\13\10\22", /* 3 - arrow keys RLDU */
"/*-+\n1234567890.", /* 4 - keypad w/numlock */
"/*-+\n\xff\2\xff\4\xff\3\xff\1\xff\xff.", /* 5 - keypad w/o numlock */
};

struct keycode_mapper {
uint8_t first, last, code, flags;
} keycode_to_ascii[] = {
{ HID_KEY_A, HID_KEY_Z, 'a', FLAG_ALPHABETIC, },

{ HID_KEY_1, HID_KEY_9, 0, FLAG_SHIFT | FLAG_LUT, },
{ HID_KEY_1, HID_KEY_9, '1', 0, },
{ HID_KEY_0, HID_KEY_0, ')', FLAG_SHIFT, },
{ HID_KEY_0, HID_KEY_0, '0', 0, },

{ HID_KEY_ENTER, HID_KEY_ENTER, '\n', FLAG_CTRL },
{ HID_KEY_ENTER, HID_KEY_SLASH, 2, FLAG_SHIFT | FLAG_LUT, },
{ HID_KEY_ENTER, HID_KEY_SLASH, 1, FLAG_LUT, },

{ HID_KEY_F1, HID_KEY_F1, 0x1e, 0, }, // help key on xerox 820 kbd

{ HID_KEY_ARROW_RIGHT, HID_KEY_ARROW_UP, 3, FLAG_LUT },

{ HID_KEY_KEYPAD_DIVIDE, HID_KEY_KEYPAD_DECIMAL, 4, FLAG_NUMLOCK | FLAG_LUT },
{ HID_KEY_KEYPAD_DIVIDE, HID_KEY_KEYPAD_DECIMAL, 5, FLAG_LUT },
};


bool report_contains(const hid_keyboard_report_t &report, uint8_t key) {
for (int i = 0; i < 6; i++) {
if (report.keycode[i] == key) return true;
}
return false;
}

hid_keyboard_report_t old_report;

void process_event(uint8_t dev_addr, uint8_t instance, const hid_keyboard_report_t &report) {
bool alt = report.modifier & 0x44;
bool shift = report.modifier & 0x22;
bool ctrl = report.modifier & 0x11;
bool caps = old_report.reserved & 1;
bool num = old_report.reserved & 2;
uint8_t code = 0;

if (report.keycode[0] == 1 && report.keycode[1] == 1) {
// keyboard says it has exceeded max kro
return;
}

// something was pressed or release, so cancel any key repeat
old_ascii = -1;

for (auto keycode : report.keycode) {
if (keycode == 0) continue;
if (report_contains(old_report, keycode)) continue;

/* key is newly pressed */
if (keycode == HID_KEY_NUM_LOCK) {
num = !num;
} else if (keycode == HID_KEY_CAPS_LOCK) {
caps = !caps;
} else {
for (const auto &mapper : keycode_to_ascii) {
if (!(keycode >= mapper.first && keycode <= mapper.last))
continue;
if (mapper.flags & FLAG_SHIFT && !shift)
continue;
if (mapper.flags & FLAG_NUMLOCK && !num)
continue;
if (mapper.flags & FLAG_CTRL && !ctrl)
continue;
if (mapper.flags & FLAG_LUT) {
code = lut[mapper.code][keycode - mapper.first];
} else {
code = keycode - mapper.first + mapper.code;
}
if (mapper.flags & FLAG_ALPHABETIC) {
if (shift ^ caps) {
code ^= ('a' ^ 'A');
}
}
if (ctrl) code &= 0x1f;
if (alt) code ^= 0x80; // n.b. alt not supported in firmware, top bit is always masked off.
send_ascii(code, initial_repeat_time);
break;
}
}
}

uint8_t leds = (caps | (num << 1));
if (leds != old_report.reserved) {
Serial.printf("Send LEDs report %d (dev:instance = %d:%d)\r\n", leds, dev_addr, instance);
// no worky
auto r = tuh_hid_set_report(dev_addr, instance/*idx*/, 0/*report_id*/, HID_REPORT_TYPE_OUTPUT/*report_type*/, &leds, sizeof(leds));
Serial.printf("set_report() -> %d\n", (int)r);
}
old_report = report;
old_report.reserved = leds;
}

// Invoked when received report from device via interrupt endpoint
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) {
if ( len != sizeof(hid_keyboard_report_t) ) {
Serial.printf("report len = %u NOT 8, probably something wrong !!\r\n", len);
} else {
process_event(dev_addr, instance, *(hid_keyboard_report_t*)report);
}
// continue to request to receive report
if (!tuh_hid_receive_report(dev_addr, instance)) {
Serial.printf("Error: cannot request to receive report\r\n");
}
}

0 comments on commit 4081803

Please sign in to comment.