4 #include <avr/interrupt.h>
5 #include <util/delay.h>
6 #include <avr/pgmspace.h>
13 PROGMEM char usbHidReportDescriptor[] = {
14 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
15 0x09, 0x04, // USAGE (Joystick)
16 0xa1, 0x01, // COLLECTION (Application)
17 0x09, 0x01, // USAGE (Pointer)
18 0xa1, 0x00, // COLLECTION (Physical)
19 // 0x85, 0x01, // REPORT_ID (1)
20 0x09, 0x30, // USAGE (X)
21 0x09, 0x31, // USAGE (Y)
22 0x15, 0x81, // LOGICAL_MINIMUM (-127)
23 0x25, 0x7f, // LOGICAL_MAXIMUM (127)
24 0x75, 0x08, // REPORT_SIZE (8)
25 0x95, 0x02, // REPORT_COUNT (2)
26 0x81, 0x02, // INPUT (Data,Var,Abs)
28 0x05, 0x09, // USAGE_PAGE (Button)
29 0x19, 1, // USAGE_MINIMUM (Button 1)
30 0x29, 8, // USAGE_MAXIMUM (Button 8)
31 0x15, 0x00, // LOGICAL_MINIMUM (0)
32 0x25, 0x01, // LOGICAL_MAXIMUM (1)
33 0x75, 1, // REPORT_SIZE (1)
34 0x95, 8, // REPORT_COUNT (8)
35 0x81, 0x02, // INPUT (Data,Var,Abs)
36 0xc0, // END_COLLECTION
37 0xc0, // END_COLLECTION
44 uchar buttons; // Note: bitstream goes [8...1]
47 static report_t buttonState;
48 static report_t reportBuffer;
49 static uint8_t pollInProgress = 0;
50 static unsigned char idleRate;
51 static uint8_t interrupt_count = 0;
52 static uint8_t readyToPollSnes = 0;
54 usbMsgLen_t usbFunctionSetup(uchar data[8]) {
56 usbRequest_t *rq = (void *)data;
58 /* The following requests are never used. But since they are required by
59 * the specification, we implement them in this example.
61 if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){ /* class request type */
62 //DBG1(0x50, &rq->bRequest, 1); /* debug output: print our request */
63 if(rq->bRequest == USBRQ_HID_GET_REPORT){ /* wValue: ReportType (highbyte), ReportID (lowbyte) */
64 /* we only have one report type, so don't look at wValue */
65 usbMsgPtr = (void *)&reportBuffer;
66 return sizeof(reportBuffer);
67 } else if (rq->bRequest == USBRQ_HID_GET_IDLE){
68 usbMsgPtr = &idleRate;
70 } else if (rq->bRequest == USBRQ_HID_SET_IDLE){
71 idleRate = rq->wValue.bytes[1];
74 /* no vendor specific requests implemented */
76 return 0; /* default for not implemented requests: return no data back to host */
80 ISR(TIMER1_COMPA_vect) {
81 readyToPollSnes = 1; // Set flag for mainloop to poll SNES (better not to do this in an ISR)
84 static void getButtons() { // Poll the SNES for the button states, arrange bytes appropriately for the descriptor
85 uint16_t temp= 0x0000;
93 for(counter = 0; counter != 16; counter++) {
96 temp |= (PINC & _BV(PINC2)) ? 0 : 1;
103 buttonState.y = -127;
104 //buttonState.x = 127;
105 else if (temp & 0x0400)
107 //buttonState.x = -127;
108 else buttonState.y = 0;
111 buttonState.x = -127;
112 else if (temp & 0x0100)
114 else buttonState.x = 0;
115 buttonState.buttons = ((temp & 0xf000) >> 8) | ((temp & 0x00f0) >> 4) ;
119 static void initPins() {
120 // Enable PD3 as output high (pull-up to D-)
121 //DDRD |= _BV(PIND3);
122 //PORTD |= _BV(PIND3);
124 // Enable PC1:0 as output, and set data clock high
125 // Serial data is PC2 (input, hi-z)
126 // Data latch is PC1 (output, default LOW)
127 // Data clock is PC0 (output, default HIGH)
128 DDRC |= _BV(PINC1) | _BV(PINC0);
132 static void initTimer() {
133 // See section 15.11 for reference
134 // Using CTC mode (non-PWM)
136 // TCCR1A = 0b00000000 (No timer output, no PWM)
137 // TCCR1B = 0b00001010 (CTC mode, ckli_o/8)
138 // TCCR1C = 0b00000000 (No output compare)
142 // {OCR1AH, OCR1AL} = 37500 = 10010010 01111100
143 // 37500 * 8 * 60 = 18MHz
146 // TIMSK1 = 0b00000010 (Enable interrupt on Output Compare A Match)
153 wdt_enable(WDTO_1S); // Enable watchdog with 1s reset time
155 /* Even if you don't use the watchdog, turn it off here. On newer devices,
156 * the status of the watchdog (on/off, period) is PRESERVED OVER RESET!
159 cli(); // Disable interrupts (technically, they're disabled on reset, but...)
161 //DBG1(0x00, 0, 0); /* debug output: main starts */
163 /* RESET status: all port bits are inputs without pull-up.
164 * That's the way we need D+ and D-. Therefore we don't need any
165 * additional hardware initialization on the USB side.
168 // We need to set up the pin configuration for D3 and everything on the SNES side
171 // Set up the clock prescaler to CLKI_O/8, which gives us 2250000 ticks per second
172 // This makes 37500 such ticks give us a 60Hz signal, which is exactly how we set up
173 // the timer interrupt to poll the SNES controller
176 // Start with no buttons pressed
177 buttonState.buttons = 0;
178 reportBuffer.buttons = 0;
184 usbDeviceDisconnect(); /* enforce re-enumeration, do this while interrupts are disabled! */
186 while(--counter){ /* fake USB disconnect for > 250 ms */
192 sei(); // Enable interrupts (after device reconnect)
193 //DBG1(0x01, 0, 0); /* debug output: main loop starts */
196 reportBuffer.buttons = 0;
198 for(;;){ /* main event loop */
199 //DBG1(0x02, 0, 0); /* debug output: main loop iterates */
202 if(readyToPollSnes) {
205 if(usbInterruptIsReady()){
206 /* called after every poll of the interrupt endpoint */
207 // Actual code to be executed - updates the HID report with the status of the SNES controller
208 reportBuffer.x = buttonState.x;
209 reportBuffer.y = buttonState.y;
210 reportBuffer.buttons = buttonState.buttons;
212 // Testing USB reports - change button state every second, incrementing the report buffer by 1.
213 //if(counter++ == 10) {
214 // reportBuffer.buttons = 'a';
215 // reportBuffer.buttons2 = 'a';
216 // reportBuffer.buttons3 = 'a';
219 //DBG1(0x03, 0, 0); /* debug output: interrupt report prepared */
220 usbSetInterrupt((void *)&reportBuffer, sizeof(reportBuffer));