This repository has been archived by the owner on May 24, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
GRIPv1.0.1.ino
141 lines (116 loc) · 5.39 KB
/
GRIPv1.0.1.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#pragma GCC optimize ("-O3")
/*
GRiP to HID joystick converter for the Gravis Gamepad Pro
This code is built and tested on a Leonardo. Also works with a Chinese 32u4 'Pro Micro' clone.
Devices built with an ATmega32u4 should work, but may need modification.
Would probably also work on an STM32duino board, (not tested)
The gamepad pro has a switch on the back, and has 3 modes.
in mode 1, only two buttons per gamepad are used
in mode 2, all four buttons are used by gamepad 1, and gamepad 2 is disabled
in mode 3 (GRiP), up to four gamepads can be used at once, and all 10 buttons are available each
This code only supports two devices, mostly because I only HAVE two to test with. I measured
this taking about 220usec per cycle through loop(), which means we're reporting at 4500Hz with
two gamepads connected, for a refresh rate of 2250Hz for each gamepad. This seems sufficient :)
so I'm not planning on implementing any further optimizations. This is about 3500ops per poll,
which seems reasonable.
I have no idea what will happen with gamepads in non-grip mode. Probably nothing.
GRiP mode uses button 0 as a 20-25kHz clock signal, and button 1 as data
Data packet consists of 24 bytes, as follows:
0 1 1 1 1 1 0 Select Start R2 Blue 0 L2 Green Yellow Red 0 L1 R1 Up Down 0 Right Left
*/
//Includes. Need Keyboard for JS->KB emulation, and/or a lib for JS HID
#include <Keyboard.h>
#include "grip.h" // pin definitions, grip packet definition and keymaps
/* Global definitions for the interrupt handler */
volatile uint32_t JSbuff[2] = {0,0}; //the buffer where the handler shifts the bits
void setup() {
//enable all the inputs I'll need, and activate the internal pullup resistor.
pinMode(JS1CLKPin, INPUT_PULLUP);
pinMode(JS1DATPin, INPUT_PULLUP);
pinMode(JS2CLKPin, INPUT_PULLUP);
pinMode(JS2DATPin, INPUT_PULLUP);
pinMode(TXLED, OUTPUT);
pinMode(RXLED, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT);
//initialize USB-serial
// Serial.begin(230400); //This is the USB virtual UART.
// Serial.println(F("Initialize Serial Hardware UART Pins"));
attachInterrupt(digitalPinToInterrupt(JS1CLKPin), GETJS1BIT, FALLING);
attachInterrupt(digitalPinToInterrupt(JS2CLKPin), GETJS2BIT, FALLING);
delay(500); //wait for things to stabilize and the gamepads themselves to bootup and fill the buffers
Keyboard.begin();
}
void loop() {
static uint16_t JSpacket[2]; // static to preserve the variable for the next loop
static bool JSnum;
uint16_t JSprevious = JSpacket[JSnum];
uint32_t JSsync;
noInterrupts(); //copying the buffer takes multiple cycles and we can't have the interrupts writing data
JSsync = JSbuff[JSnum];
interrupts();
JSpacket[JSnum] = SyncPacket(JSsync, JSprevious);
if (JSpacket[JSnum] != JSprevious) //if no joypad keypresses have *changed*, we don't need to send anything
SendKeys(JSpacket[JSnum], JSprevious, JSnum);
JSnum=!JSnum;
}
/*
This sub rotates though our captured data to find the synchronization bits.
once the bits are in the right position, exits. If we can't find sync,
then we just return the previous packet. Rotating bits like this means we'll be getting some button
presses from the 'last' packet mixed into the 'current' packet, but this only means about 1ms of
error.
*/
uint16_t SyncPacket(uint32_t buff, uint16_t previous) {
byte i = 1;
if (!buff) //if the buffer is 'zero,' then assume no JS is connected, no further processing is needed.
return 0;
while ((buff & 0x00FE1084) != 0x007C0000) { //FE1084 masks the bits we care about, 7C is our 'sync' pattern
if (i > GRIPSZ) { // if we've rotated through the entire buffer and not found sync
return previous;
}
buff = rotl24(buff); //rotate the entire buffer by 1 bit, and re-test
i++;
}
//Compress the packet down to fit into an int, by
//stripping out the sync bits and the framing zeros.
//hopefully, this is more efficient than passing around 32 bits
return ( (buff & 0b0000000000000011) |
( (buff>>1) & 0b0000000000111100) |
( (buff>>2) & 0b0000001111000000) |
( (buff>>3) & 0b0011110000000000) );
}
/*Ok, so, at this point we have a nicely aligned GRiP packet.
* SendKeys() will parse and send it out as keypresses.
*/
void SendKeys(uint16_t packet, uint16_t previous, byte JSnum) { //JSnum = 0 for the first JS, and 1 for the 2nd
uint16_t mask=packet^previous; // this will set any bits that have *changed* to a '1'.
for (byte i = 0; i < 14; i++) {
if (mask & 0x01){
if (packet & 0x01)
Keyboard.press(JS_keys[JSnum][i]);
else
Keyboard.release(JS_keys[JSnum][i]);
}
mask >>= 1;
packet >>= 1;
}
}
//rotates LSB24 bits left 1 position, from a 32-bit unsigned long
inline uint32_t rotl24(uint32_t n) {
return (n << 1) | (n >> (24-1));
}
/* Interrupt handlers.
* We're shifting the buffer each time, so that we don't have to worry about preventing overruns
* If parsing is taking too long, we just keep on capturing bits and shifting the oldest bits off
* the end of the buffer and into oblivion.
*/
void GETJS1BIT () {
JSbuff[0] <<= 1;
JSbuff[0] |= ((PIND >> 4) & 1); //PIND4 is the register for JS1 data
//JSbuff[0]|=digitalRead(JS1DATPin); //slower, but works.
}
void GETJS2BIT () {
JSbuff[1] <<= 1;
JSbuff[1] |= ((PIND >> 7) & 1); //PIND7 is the register for JS2 data
//JSbuff[1]|=!digitalRead(JS2DATPin); //slower, but works.
}