USB to quadrature encoder so you use modern USB mice on the Amiga.

Dependencies:   USBHOST

Introduction

This project is devised so you can attach a mouse (and/or steam controller) to a classic computer such as an Amiga. Computers of that era use bus mice which just send the pulses from the quadrature encoder wheels on the mouse directly to the computer with out interpretation.

Use

Program a build base on this repository to a STM32 nucleo board (I tested this on a NUCLEO STM32F411 board). You will then need connect the Nucleo board to your computers joystick and mouse ports as follows (if you only use a mouse you won't need a joystick lead :) ). If you use a steam controller the thumb stick and left pad are mapped to the Amiga's joystick port and the right pad and front trigger buttons are mapped to the mouse.

mouse_port_forward(D10) - Amiga Joystick pin 1
mouse_port_back(D11) - Amiga Joystick pin 2
mouse_port_left(D12) - Amiga Joystick pin 3
mouse_port_right(D13) - Amiga Joystick pin 4
mouse_port_left_button(D9) - Amiga Joystick pin 6
GND - Amiga joystick pin 8
mouse_port_right_button(D8) - Amiga Joystick pin 9

game_port_forward(PC_8) - Amiga Joystick pin 1
game_port_back(PC_12) - Amiga Joystick pin 2
game_port_left(PC_10) - Amiga Joystick pin 3
game_port_right(PC_11) - Amiga Joystick pin 4
game_port_left_button(PC_6) - Amiga Joystick pin 6
GND - Amiga joystick pin 8
game_port_right_button(PC_5) - Amiga Joystick pin 9

It is best NOT to use the 5V pin from the Amiga's joystick port to power the nucleo board as the power suppled by the Amiga is limited!

You will also need to attach a USB socket to the nuclea board so you can use the Nucleo's usb OTG support.

(1) 5V     - 5v
(2) PA11 - Data-
(3) PA12 - Data+
(4) GND - GND

According to the usb spec it is required to attach a 15k ohm resistor between D-/+ and ground but it appears the USB host works fine without this.

NOTES:

  • The right touch pad can take a little while before it generates mouse events.
  • I found that the STM USBHost library needed some modifications to correctly work with boot protocol (it needs to call set protocol) and hubs (getSize doesn't report the correct size). This project uses a modified USB host library with fixes for this (they will need a little more work before I can create a pull request).
Committer:
geekylou
Date:
Sat Aug 26 10:14:24 2017 +0000
Revision:
4:02e4f0ab23ff
Parent:
3:24067f6310fa
Add joystick support on seperate socket.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
geekylou 0:d0c521cb6c90 1 #include "mbed.h"
geekylou 0:d0c521cb6c90 2 #include "USBHostMSD.h"
geekylou 0:d0c521cb6c90 3 #include "USBHostMouse.h"
geekylou 0:d0c521cb6c90 4 #include "USBHostKeyboard.h"
geekylou 1:ab916b5c1a4d 5 #include "USBHostSteamController.h"
geekylou 0:d0c521cb6c90 6 #include "FATFileSystem.h"
geekylou 0:d0c521cb6c90 7 #include <stdlib.h>
geekylou 0:d0c521cb6c90 8
geekylou 4:02e4f0ab23ff 9 inline int max ( int a, int b ) { return a > b ? a : b; }
geekylou 4:02e4f0ab23ff 10
geekylou 4:02e4f0ab23ff 11 #define MAX_QUADRATURE_FREQ 3500
geekylou 4:02e4f0ab23ff 12
geekylou 4:02e4f0ab23ff 13 volatile int x_cur = 0;
geekylou 4:02e4f0ab23ff 14 volatile int y_cur = 0;
geekylou 4:02e4f0ab23ff 15 volatile int x_pos = 0;
geekylou 4:02e4f0ab23ff 16 volatile int y_pos = 0;
geekylou 3:24067f6310fa 17
geekylou 3:24067f6310fa 18 Ticker ticker;
geekylou 0:d0c521cb6c90 19
geekylou 4:02e4f0ab23ff 20 DigitalOut mouse_port_right_button(D8);
geekylou 4:02e4f0ab23ff 21 DigitalOut mouse_port_left_button(D9);
geekylou 4:02e4f0ab23ff 22 DigitalOut mouse_port_forward(D10);
geekylou 4:02e4f0ab23ff 23 DigitalOut mouse_port_left(D12);
geekylou 4:02e4f0ab23ff 24
geekylou 4:02e4f0ab23ff 25 DigitalOut mouse_port_back(D11);
geekylou 4:02e4f0ab23ff 26 DigitalOut mouse_port_right(D13);
geekylou 4:02e4f0ab23ff 27
geekylou 4:02e4f0ab23ff 28
geekylou 4:02e4f0ab23ff 29
geekylou 4:02e4f0ab23ff 30 DigitalOut game_port_right_button(PC_5);
geekylou 4:02e4f0ab23ff 31 DigitalOut game_port_left_button(PC_6);
geekylou 4:02e4f0ab23ff 32 DigitalOut game_port_forward(PC_8);
geekylou 4:02e4f0ab23ff 33 DigitalOut game_port_left(PC_10);
geekylou 4:02e4f0ab23ff 34
geekylou 4:02e4f0ab23ff 35 DigitalOut game_port_back(PC_12);
geekylou 4:02e4f0ab23ff 36 DigitalOut game_port_right(PC_11);
geekylou 4:02e4f0ab23ff 37
geekylou 4:02e4f0ab23ff 38
geekylou 2:095eb20f0d3b 39
geekylou 3:24067f6310fa 40 //DigitalOut led_blue(LED3);
geekylou 3:24067f6310fa 41
geekylou 3:24067f6310fa 42 void update_quadrature()
geekylou 3:24067f6310fa 43 {
geekylou 3:24067f6310fa 44 //led_blue = !led_blue;
geekylou 0:d0c521cb6c90 45
geekylou 3:24067f6310fa 46 if (x_pos != 0 || y_pos != 0)
geekylou 0:d0c521cb6c90 47 {
geekylou 3:24067f6310fa 48 if (x_pos >0)
geekylou 0:d0c521cb6c90 49 {
geekylou 3:24067f6310fa 50 x_pos--;
geekylou 0:d0c521cb6c90 51 x_cur = (x_cur+1) & 3;
geekylou 0:d0c521cb6c90 52 }
geekylou 3:24067f6310fa 53 else if (x_pos < 0)
geekylou 0:d0c521cb6c90 54 {
geekylou 3:24067f6310fa 55 x_pos++;
geekylou 0:d0c521cb6c90 56 x_cur = (x_cur-1) & 3;
geekylou 0:d0c521cb6c90 57 }
geekylou 0:d0c521cb6c90 58
geekylou 3:24067f6310fa 59 if (y_pos >0)
geekylou 0:d0c521cb6c90 60 {
geekylou 3:24067f6310fa 61 y_pos--;
geekylou 0:d0c521cb6c90 62 y_cur = (y_cur+1) & 3;
geekylou 0:d0c521cb6c90 63 }
geekylou 3:24067f6310fa 64 else if (y_pos < 0)
geekylou 0:d0c521cb6c90 65 {
geekylou 3:24067f6310fa 66 y_pos++;
geekylou 0:d0c521cb6c90 67 y_cur = (y_cur-1) & 3;
geekylou 3:24067f6310fa 68 }
geekylou 0:d0c521cb6c90 69
geekylou 2:095eb20f0d3b 70 mouse_port_back = ((x_cur + 1)/2) & 1;
geekylou 4:02e4f0ab23ff 71 mouse_port_right = (x_cur/2) & 0x1;
geekylou 0:d0c521cb6c90 72
geekylou 4:02e4f0ab23ff 73 mouse_port_forward = ((y_cur + 1)/2) & 1;
geekylou 4:02e4f0ab23ff 74 mouse_port_left = (y_cur/2) & 0x1;
geekylou 0:d0c521cb6c90 75 }
geekylou 0:d0c521cb6c90 76 }
geekylou 1:ab916b5c1a4d 77
geekylou 3:24067f6310fa 78 void onMouseEvent(uint8_t buttons, int16_t x, int16_t y, int8_t z) {
geekylou 3:24067f6310fa 79 //printf("ME: buttons: %d, x: %d, y: %d, z: %d\r\n", buttons, x, y, z);
geekylou 3:24067f6310fa 80
geekylou 4:02e4f0ab23ff 81 // Disabling ticker as a quick method of disabling the ticker intterrupt.
geekylou 4:02e4f0ab23ff 82 // This is to stop the IRQ being called when we have updated x_pos but not
geekylou 4:02e4f0ab23ff 83 // y_pos.
geekylou 4:02e4f0ab23ff 84 ticker.detach();
geekylou 4:02e4f0ab23ff 85
geekylou 3:24067f6310fa 86 x_pos += x;
geekylou 3:24067f6310fa 87 y_pos += y;
geekylou 3:24067f6310fa 88
geekylou 4:02e4f0ab23ff 89 // This should be enough to calculate the pulse width. As we just want to
geekylou 4:02e4f0ab23ff 90 // calculate the speed we need to pulse to not lag.
geekylou 4:02e4f0ab23ff 91
geekylou 4:02e4f0ab23ff 92 int dist = max(abs(x_pos),abs(y_pos)) * 30;
geekylou 4:02e4f0ab23ff 93
geekylou 4:02e4f0ab23ff 94 dist = dist > MAX_QUADRATURE_FREQ ? MAX_QUADRATURE_FREQ : dist; /* Cap frequency to 3khz. */
geekylou 4:02e4f0ab23ff 95
geekylou 4:02e4f0ab23ff 96 mouse_port_left_button = ~buttons & 1;
geekylou 4:02e4f0ab23ff 97 mouse_port_right_button = ~buttons & 0x2;
geekylou 3:24067f6310fa 98
geekylou 4:02e4f0ab23ff 99 if (x_pos != 0 || y_pos != 0)
geekylou 4:02e4f0ab23ff 100 {
geekylou 4:02e4f0ab23ff 101 ticker.attach(update_quadrature, 1.0 / dist);
geekylou 4:02e4f0ab23ff 102
geekylou 4:02e4f0ab23ff 103 //ticker.attach_us(update_quadrature, 1000000 / dist);
geekylou 4:02e4f0ab23ff 104 }
geekylou 4:02e4f0ab23ff 105 Thread::yield();
geekylou 4:02e4f0ab23ff 106 }
geekylou 3:24067f6310fa 107
geekylou 4:02e4f0ab23ff 108 uint32_t prev_buttons = 0;
geekylou 4:02e4f0ab23ff 109 int prev_x, prev_y;
geekylou 3:24067f6310fa 110
geekylou 1:ab916b5c1a4d 111 void onJSEvent(uint32_t buttons, int8_t x, int8_t y, int16_t x_b, int16_t y_b) {
geekylou 4:02e4f0ab23ff 112 //printf("buttons: %x, x: %d, y: %d, x_b: %d y_b:%d :", buttons, x, y, x_b, y_b);
geekylou 4:02e4f0ab23ff 113
geekylou 4:02e4f0ab23ff 114
geekylou 4:02e4f0ab23ff 115 game_port_left_button = ~buttons & 0x40;
geekylou 4:02e4f0ab23ff 116 game_port_right_button = ~buttons & 0x10;
geekylou 4:02e4f0ab23ff 117
geekylou 4:02e4f0ab23ff 118 if (buttons & 0x100000)
geekylou 4:02e4f0ab23ff 119 {
geekylou 4:02e4f0ab23ff 120 if (prev_buttons & 0x100000)
geekylou 4:02e4f0ab23ff 121 {
geekylou 4:02e4f0ab23ff 122 //printf("x:%d y:%d", prev_x - x_b, prev_y - y_b);
geekylou 4:02e4f0ab23ff 123
geekylou 4:02e4f0ab23ff 124 ticker.detach();
geekylou 4:02e4f0ab23ff 125
geekylou 4:02e4f0ab23ff 126 x_pos += -((prev_x - x_b)/128);
geekylou 4:02e4f0ab23ff 127 y_pos += (prev_y - y_b)/128;
geekylou 4:02e4f0ab23ff 128
geekylou 4:02e4f0ab23ff 129 int dist = max(abs(x_pos),abs(y_pos)) * 30;
geekylou 1:ab916b5c1a4d 130
geekylou 4:02e4f0ab23ff 131 dist = dist > MAX_QUADRATURE_FREQ ? MAX_QUADRATURE_FREQ : dist; /* Cap frequency to 3khz. */
geekylou 4:02e4f0ab23ff 132
geekylou 4:02e4f0ab23ff 133 if (x_pos != 0 || y_pos != 0)
geekylou 4:02e4f0ab23ff 134 {
geekylou 4:02e4f0ab23ff 135 ticker.attach(update_quadrature, 1.0 / dist);
geekylou 4:02e4f0ab23ff 136
geekylou 4:02e4f0ab23ff 137 //ticker.attach_us(update_quadrature, 1000000 / dist);
geekylou 4:02e4f0ab23ff 138 }
geekylou 4:02e4f0ab23ff 139 }
geekylou 4:02e4f0ab23ff 140 prev_x = x_b; prev_y = y_b;
geekylou 4:02e4f0ab23ff 141 }
geekylou 4:02e4f0ab23ff 142 //printf("\r\n");
geekylou 4:02e4f0ab23ff 143
geekylou 4:02e4f0ab23ff 144 prev_buttons = buttons;
geekylou 4:02e4f0ab23ff 145
geekylou 4:02e4f0ab23ff 146 mouse_port_left_button = ~buttons & 2;
geekylou 4:02e4f0ab23ff 147 mouse_port_right_button = ~buttons & 0x1;
geekylou 4:02e4f0ab23ff 148
geekylou 4:02e4f0ab23ff 149 game_port_forward = !( y > 40);
geekylou 4:02e4f0ab23ff 150 game_port_back = !( y < -40 );
geekylou 4:02e4f0ab23ff 151 game_port_left = !( x < -40 );
geekylou 4:02e4f0ab23ff 152 game_port_right = !( x > 40 );
geekylou 2:095eb20f0d3b 153 //Thread::wait(1);
geekylou 4:02e4f0ab23ff 154 Thread::yield();
geekylou 1:ab916b5c1a4d 155 }
geekylou 0:d0c521cb6c90 156
geekylou 0:d0c521cb6c90 157 void mouse_task(void const *) {
geekylou 0:d0c521cb6c90 158
geekylou 0:d0c521cb6c90 159 USBHostMouse mouse;
geekylou 0:d0c521cb6c90 160 printf("mouse started\n");
geekylou 0:d0c521cb6c90 161 while(1) {
geekylou 0:d0c521cb6c90 162
geekylou 0:d0c521cb6c90 163 // try to connect a USB mouse
geekylou 0:d0c521cb6c90 164 while(!mouse.connect())
geekylou 0:d0c521cb6c90 165 Thread::wait(500);
geekylou 0:d0c521cb6c90 166
geekylou 0:d0c521cb6c90 167 // when connected, attach handler called on mouse event
geekylou 0:d0c521cb6c90 168 mouse.attachEvent(onMouseEvent);
geekylou 0:d0c521cb6c90 169
geekylou 0:d0c521cb6c90 170 // wait until the mouse is disconnected
geekylou 0:d0c521cb6c90 171 while(mouse.connected())
geekylou 0:d0c521cb6c90 172 Thread::wait(500);
geekylou 0:d0c521cb6c90 173 printf("mouse seen disconnected\n");
geekylou 0:d0c521cb6c90 174 }
geekylou 0:d0c521cb6c90 175 }
geekylou 0:d0c521cb6c90 176 void onKey(uint8_t key) {
geekylou 0:d0c521cb6c90 177 printf("Key: %c\r\n", key);
geekylou 0:d0c521cb6c90 178 }
geekylou 0:d0c521cb6c90 179 void keyboard_task(void const *) {
geekylou 0:d0c521cb6c90 180
geekylou 0:d0c521cb6c90 181 USBHostKeyboard keyboard;
geekylou 0:d0c521cb6c90 182
geekylou 0:d0c521cb6c90 183 while(1) {
geekylou 0:d0c521cb6c90 184 // try to connect a USB keyboard
geekylou 0:d0c521cb6c90 185 while(!keyboard.connect())
geekylou 0:d0c521cb6c90 186 Thread::wait(500);
geekylou 0:d0c521cb6c90 187
geekylou 0:d0c521cb6c90 188 // when connected, attach handler called on keyboard event
geekylou 0:d0c521cb6c90 189 keyboard.attach(onKey);
geekylou 0:d0c521cb6c90 190
geekylou 0:d0c521cb6c90 191 // wait until the keyboard is disconnected
geekylou 0:d0c521cb6c90 192 while(keyboard.connected())
geekylou 0:d0c521cb6c90 193 Thread::wait(500);
geekylou 0:d0c521cb6c90 194 }
geekylou 0:d0c521cb6c90 195 }
geekylou 0:d0c521cb6c90 196
geekylou 1:ab916b5c1a4d 197 void steamctrl_task(void const *) {
geekylou 1:ab916b5c1a4d 198
geekylou 1:ab916b5c1a4d 199 USBHostSteamController steam_controller;
geekylou 1:ab916b5c1a4d 200
geekylou 1:ab916b5c1a4d 201 while(1) {
geekylou 1:ab916b5c1a4d 202 // try to connect a USB keyboard
geekylou 1:ab916b5c1a4d 203 while(!steam_controller.connect())
geekylou 1:ab916b5c1a4d 204 Thread::wait(500);
geekylou 1:ab916b5c1a4d 205
geekylou 1:ab916b5c1a4d 206 // when connected, attach handler called on keyboard event
geekylou 1:ab916b5c1a4d 207 steam_controller.attachEvent(onJSEvent);
geekylou 1:ab916b5c1a4d 208
geekylou 1:ab916b5c1a4d 209 // wait until the keyboard is disconnected
geekylou 1:ab916b5c1a4d 210 while(steam_controller.connected())
geekylou 1:ab916b5c1a4d 211 Thread::wait(500);
geekylou 1:ab916b5c1a4d 212 }
geekylou 1:ab916b5c1a4d 213 }
geekylou 1:ab916b5c1a4d 214
geekylou 0:d0c521cb6c90 215
geekylou 0:d0c521cb6c90 216
geekylou 0:d0c521cb6c90 217 void msd_task(void const *) {
geekylou 0:d0c521cb6c90 218
geekylou 0:d0c521cb6c90 219 USBHostMSD msd;
geekylou 0:d0c521cb6c90 220 int i = 0;
geekylou 0:d0c521cb6c90 221 FATFileSystem fs("usb");
geekylou 0:d0c521cb6c90 222 int err;
geekylou 0:d0c521cb6c90 223 printf("wait for usb memory stick insertion\n");
geekylou 0:d0c521cb6c90 224 while(1) {
geekylou 0:d0c521cb6c90 225
geekylou 0:d0c521cb6c90 226 // try to connect a MSD device
geekylou 0:d0c521cb6c90 227 while(!msd.connect()) {
geekylou 0:d0c521cb6c90 228 Thread::wait(500);
geekylou 0:d0c521cb6c90 229 }
geekylou 0:d0c521cb6c90 230 if (fs.mount(&msd) != 0) continue;
geekylou 0:d0c521cb6c90 231 else
geekylou 0:d0c521cb6c90 232 printf("file system mounted\n");
geekylou 0:d0c521cb6c90 233
geekylou 0:d0c521cb6c90 234 if (!msd.connect()) {
geekylou 0:d0c521cb6c90 235 continue;
geekylou 0:d0c521cb6c90 236 }
geekylou 0:d0c521cb6c90 237
geekylou 0:d0c521cb6c90 238 // in a loop, append a file
geekylou 0:d0c521cb6c90 239 // if the device is disconnected, we try to connect it again
geekylou 0:d0c521cb6c90 240
geekylou 0:d0c521cb6c90 241 // append a file
geekylou 0:d0c521cb6c90 242 File file;
geekylou 0:d0c521cb6c90 243 err = file.open(&fs, "test1.txt", O_WRONLY | O_CREAT |O_APPEND);
geekylou 0:d0c521cb6c90 244
geekylou 0:d0c521cb6c90 245 if (err == 0) {
geekylou 0:d0c521cb6c90 246 char tmp[100];
geekylou 0:d0c521cb6c90 247 sprintf(tmp,"Hello fun USB stick World: %d!\r\n", i++);
geekylou 0:d0c521cb6c90 248 file.write(tmp,strlen(tmp));
geekylou 0:d0c521cb6c90 249 sprintf(tmp,"Goodbye World!\r\n");
geekylou 0:d0c521cb6c90 250 file.write(tmp,strlen(tmp));
geekylou 0:d0c521cb6c90 251 file.close();
geekylou 0:d0c521cb6c90 252 } else {
geekylou 0:d0c521cb6c90 253 printf("FILE == NULL\r\n");
geekylou 0:d0c521cb6c90 254 }
geekylou 0:d0c521cb6c90 255 Thread::wait(500);
geekylou 0:d0c521cb6c90 256 printf("again\n");
geekylou 0:d0c521cb6c90 257 // if device disconnected, try to connect again
geekylou 0:d0c521cb6c90 258 while (msd.connected()) {
geekylou 0:d0c521cb6c90 259 Thread::wait(500);
geekylou 0:d0c521cb6c90 260 }
geekylou 0:d0c521cb6c90 261 while (fs.unmount() < 0) {
geekylou 0:d0c521cb6c90 262 Thread::wait(500);
geekylou 0:d0c521cb6c90 263 printf("unmount\n");
geekylou 0:d0c521cb6c90 264 }
geekylou 0:d0c521cb6c90 265 }
geekylou 0:d0c521cb6c90 266 }
geekylou 0:d0c521cb6c90 267
geekylou 3:24067f6310fa 268 int main()
geekylou 4:02e4f0ab23ff 269 {
geekylou 4:02e4f0ab23ff 270 mouse_port_left_button = 1;
geekylou 4:02e4f0ab23ff 271 mouse_port_right_button = 1;
geekylou 3:24067f6310fa 272
geekylou 4:02e4f0ab23ff 273 mouse_port_back = 1;
geekylou 4:02e4f0ab23ff 274 mouse_port_right = 1;
geekylou 4:02e4f0ab23ff 275 mouse_port_forward = 1;
geekylou 4:02e4f0ab23ff 276 mouse_port_left = 1;
geekylou 4:02e4f0ab23ff 277
geekylou 4:02e4f0ab23ff 278 game_port_left_button = 1;
geekylou 4:02e4f0ab23ff 279 game_port_right_button = 1;
geekylou 4:02e4f0ab23ff 280
geekylou 4:02e4f0ab23ff 281 game_port_forward = 1;
geekylou 4:02e4f0ab23ff 282 game_port_back = 1;
geekylou 4:02e4f0ab23ff 283 game_port_left = 1;
geekylou 4:02e4f0ab23ff 284 game_port_right = 1;
geekylou 4:02e4f0ab23ff 285
geekylou 2:095eb20f0d3b 286 //Thread msdTask(msd_task, NULL, osPriorityNormal, 1024 * 4);
geekylou 2:095eb20f0d3b 287 Thread mouseTask(mouse_task, NULL, osPriorityNormal, 2048* 4);
geekylou 2:095eb20f0d3b 288 Thread steamctrlTask(steamctrl_task, NULL, osPriorityNormal, 2048* 4);
geekylou 1:ab916b5c1a4d 289 //Thread keyboardTask(keyboard_task, NULL, osPriorityNormal, 1024 * 4);
geekylou 0:d0c521cb6c90 290
geekylou 0:d0c521cb6c90 291 int x,y = 0;
geekylou 0:d0c521cb6c90 292 for(y=-10; y<10; y++)
geekylou 0:d0c521cb6c90 293 {
geekylou 0:d0c521cb6c90 294 x = y & 3;
geekylou 0:d0c521cb6c90 295 printf("phase %d[%d] %d\r\n",((x + 1)/2) & 1, x, (x/2) & 0x1 );
geekylou 0:d0c521cb6c90 296 }
geekylou 0:d0c521cb6c90 297 while(1) {
geekylou 0:d0c521cb6c90 298 //led=!led;
geekylou 0:d0c521cb6c90 299 Thread::wait(500);
geekylou 0:d0c521cb6c90 300 }
geekylou 0:d0c521cb6c90 301 }