]> git.zarvox.org Git - usbsnes.git/blob - main.c
Functional code for SNES port 2.
[usbsnes.git] / main.c
1
2 #include <avr/io.h>
3 #include <avr/wdt.h>
4 #include <avr/interrupt.h>
5 #include <util/delay.h>
6 #include <avr/pgmspace.h>
7
8 #include "usbconfig.h"
9 #include "usbdrv.h"
10
11 uint16_t pollSnes();
12
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)
27
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
38 };
39
40 typedef struct {
41         //uchar id;
42         char x;
43         char y;
44         uchar buttons; // Note: bitstream goes [8...1]
45 } report_t;
46
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;
53
54 usbMsgLen_t usbFunctionSetup(uchar data[8]) {
55
56         usbRequest_t    *rq = (void *)data;
57
58         /* The following requests are never used. But since they are required by
59          * the specification, we implement them in this example.
60          */
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;
69                         return 1;
70                 } else if (rq->bRequest == USBRQ_HID_SET_IDLE){
71                         idleRate = rq->wValue.bytes[1];
72                 }
73         } else {
74                 /* no vendor specific requests implemented */
75         }
76         return 0;   /* default for not implemented requests: return no data back to host */
77 }
78
79
80 ISR(TIMER1_COMPA_vect) {
81         readyToPollSnes = 1; // Set flag for mainloop to poll SNES (better not to do this in an ISR)
82 }
83
84 static void getButtons() { // Poll the SNES for the button states, arrange bytes appropriately for the descriptor
85         uint16_t temp= 0x0000;
86         uint8_t counter = 16;
87
88         PORTC |= _BV(PINC1);
89         _delay_us(12);
90
91         PORTC &= ~_BV(PINC1);
92
93         for(counter = 0; counter != 16; counter++) {
94                 _delay_us(6);
95                 temp = temp << 1 ;
96                 temp |= (PINC & _BV(PINC2)) ? 0 : 1;
97                 PORTC &= ~_BV(PINC0);
98                 _delay_us(6);
99                 PORTC |= _BV(PINC0);
100         }
101
102         if (temp & 0x0800)
103                 buttonState.y = -127;
104                 //buttonState.x = 127;
105         else if (temp & 0x0400)
106                 buttonState.y = 127;
107                 //buttonState.x = -127;
108         else buttonState.y = 0;
109
110         if (temp & 0x0200)
111                 buttonState.x = -127;
112         else if (temp & 0x0100)
113                 buttonState.x = 127;
114         else buttonState.x = 0;
115         buttonState.buttons = ((temp & 0xf000) >> 8) | ((temp & 0x00f0) >> 4) ;
116         readyToPollSnes = 0;
117 }
118
119 static void initPins() {
120         // Enable PD3 as output high (pull-up to D-)
121         //DDRD |= _BV(PIND3);
122         //PORTD |= _BV(PIND3);
123
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);
129         PORTC |= _BV(PINC0);
130 }
131
132 static void initTimer() {
133         // See section 15.11 for reference
134         // Using CTC mode (non-PWM)
135
136         // TCCR1A = 0b00000000 (No timer output, no PWM)
137         // TCCR1B = 0b00001010 (CTC mode, ckli_o/8)
138         // TCCR1C = 0b00000000 (No output compare)
139         TCCR1A = 0b00000000;
140         TCCR1B = 0b00001010;
141         TCCR1C = 0b00000000;
142         // {OCR1AH, OCR1AL} = 37500 = 10010010 01111100
143         // 37500 * 8 * 60 = 18MHz
144         OCR1AH = 0b10010010;
145         OCR1AL = 0b01111000;
146         // TIMSK1 = 0b00000010 (Enable interrupt on Output Compare A Match)
147         TIMSK1 = 0b00000010;
148 }
149
150 int main(void)
151 {
152         uchar counter = 0;
153         wdt_enable(WDTO_1S); // Enable watchdog with 1s reset time
154
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!
157          */
158
159         cli(); // Disable interrupts (technically, they're disabled on reset, but...)
160         
161         //DBG1(0x00, 0, 0);       /* debug output: main starts */
162         
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.
166          */
167         
168         // We need to set up the pin configuration for D3 and everything on the SNES side
169         initPins();
170
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
174         initTimer();
175
176         // Start with no buttons pressed
177         buttonState.buttons = 0;
178         reportBuffer.buttons = 0;
179
180         //odDebugInit();
181         
182         usbInit();
183         
184         usbDeviceDisconnect();  /* enforce re-enumeration, do this while interrupts are disabled! */
185         counter = 0;
186         while(--counter){             /* fake USB disconnect for > 250 ms */
187                 wdt_reset();
188                 _delay_ms(1);
189         }
190         usbDeviceConnect();
191
192         sei(); // Enable interrupts (after device reconnect)
193         //DBG1(0x01, 0, 0);       /* debug output: main loop starts */
194         reportBuffer.x = 0;
195         reportBuffer.y = 0;
196         reportBuffer.buttons = 0;
197
198         for(;;){                /* main event loop */
199                 //DBG1(0x02, 0, 0);   /* debug output: main loop iterates */
200                 wdt_reset();
201                 usbPoll();
202                 if(readyToPollSnes) {
203                         getButtons();
204                 }
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;
211
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';
217                         //      counter = 0;
218                         //}
219                         //DBG1(0x03, 0, 0);   /* debug output: interrupt report prepared */
220                         usbSetInterrupt((void *)&reportBuffer, sizeof(reportBuffer));
221                 }
222         }
223         return 0;
224 }
225