From e399da62dc6e0bdc216efc2590e5e9b649afaa9d Mon Sep 17 00:00:00 2001 From: Florian Jung Date: Fri, 4 Apr 2014 21:08:38 +0200 Subject: use gamecube as mouse :) --- main.c | 248 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 245 insertions(+), 3 deletions(-) diff --git a/main.c b/main.c index f47d043..d7b7761 100644 --- a/main.c +++ b/main.c @@ -27,6 +27,16 @@ connect usb.DATA{+,-} over 3v6-Z-Diode ( ----|<---- ) to GND connect usb.DATA- over 1.5kOhm to usb.Vcc connect usb.GND to avr.GND. + + supply 3.3V to the 3.3V line of the gamecube controller. + supply 5V to the 5V line of the gamecube controller. + connect the GND and SHIELD lines of the controller to the common GND. + connect the DATA line of the gamecube controller to the C5 pin. + + !!! ensure that the PORTC has bit 0 cleared at any time. !!! + !!! otherwise, +5V is supplied to the pin; the gamecube !!! + !!! controller, however, may shortcut the line down to !!! + !!! GND at any time, leading to HARDWARE DAMAGE. !!! */ #define F_CPU 12000000L @@ -37,6 +47,9 @@ #include #include "usbdrv/usbdrv.h" +static char buffer[300]; // contains the exploded gamecube bits, i.e. each bit sent/received occupies one byte here. + + PROGMEM const char usbHidReportDescriptor[52] = { /* USB report descriptor, size must match usbconfig.h */ 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x02, // USAGE (Mouse) @@ -111,9 +124,171 @@ USB_PUBLIC uchar usbFunctionSetup(uchar data[8]) } +#define OUT0 "sbi %2, 5 \n" /* pull line to GND */ +#define OUT1 "cbi %2, 5 \n" /* tristate the line */ + +#define WAIT10 "ldi r16, 1 \n rcall delay_loop \n" +#define WAIT34 "ldi r16, 9 \n rcall delay_loop \n" +#define WAIT10MINUS10 "" +#define WAIT34MINUS10 "ldi r16,5 \n rcall delay_loop \n nop \n nop \n" + +/* send the first "len" bytes stored in "bytes" to the controller, + * then receive the reply and store it into the global "buffer". */ +int send_recv_gc(char* bytes, int len) +{ + /* Phase 1: Send the data. + * Phase 1.1: Explode the bits into buffer. buffer[0..7] will contain the bits of bytes[0], + * buffer[8..15] contains the bits of bytes[1], and so on. MSB first. + * Phase 1.2: Actually send the data. + * + * intermediate Phase: wait for data line to become high again. + * + * Phase 2: Receive the reply. + * Phase 2.1: Busy-loop until the line is pulled down for the first time. + * Phase 2.2: Actually receive now: + * A counter is set to 0x80. + * Busy-loop until the line becomes high. Increment the counter in each iteration. + * (The line is now high.) + * Busy-loop until the line becomes low again. Decrement the counter each iteration. + * (Done receiving the bit) + * Write out the counter to buffer[], and proceed with the next bit. + * + * If the counter over- or underflows, stop receiving, because the line + * seems to be idle again (i.e. data transfer is finished). That's a timeout + * of ca. 53 us. (when running at 12 MHz) + * + * buffer[] now contains the counter values of the bits. + * if (buffer[42] > 0x80), i.e. (buffer[42] & 0x80), then the line was longer LOW than HIGH -> bit42 = 0 + * otherwise, the line was longer HIGH than LOW -> bit42 = 1. + */ + + // The NOPs are there because of symmetry reasons. + // the "// 2" comments after the assembly directives are the number of cycles this + // instruction will take to execute. + + char* buf_ptr = buffer; + + /****** SEND PART ******/ + + int k=0; + for (int i=0; i>1, k++) + buffer[k] = ((bytes[i] & j)!=0)?1:0 ; + + len=len*8+1; + + asm volatile( + "push r31 ; save Z\n" + "push r30 \n\n" + + ";;;;;;;; SEND PART ;;;;;;;;\n\n" + + "send_loop: \n" + "sbiw %3, 1 \n" // 2 + "breq send_done \n" // 1 if not done, 2 if done + "ld r16, z+ \n" // 2 + "tst r16 \n" // 1 + "brne send_one \n\n" // 1 if zero, 2 if nonzero + + "; otherwise, send zero \n" + "nop \n" // 1 + OUT0 + WAIT34 + OUT1 + WAIT10MINUS10 + "rjmp send_loop \n\n" // 2 + + "send_one: \n" + OUT0 + WAIT10 + OUT1 + WAIT34MINUS10 + "rjmp send_loop \n\n" + + "delay_loop:\n" + "; this costs 7 + 3*(r16) cycles \n" + "dec r16\n" + "brne delay_loop\n" + "ret\n\n" + + "send_done: \n" + "; now send the stop bit and release the line \n" + "nop \n nop \n nop \n" + OUT0 + "; instead of WAIT10, do sensible work \n" + "pop r30 ; restore Z \n" // 2 + "pop r31 \n" // 2 + "clr r16 \n" // 1 + "nop \n nop \n nop \n nop \n nop \n" // 5 + OUT1 + "; done :) \n\n\n" + + + "; now the final thing is to wait for DATA become high again (should be immediately anyway) \n" + "send_final_loop: \n" + "inc r16 \n" + "breq timeout \n" + "sbis %1, 5 \n" + "rjmp send_final_loop \n\n\n" + + + + ";;;;;;;; RECEIVE PART ;;;;;;;;\n\n" + + + "clr r16 \n" + "recv_wait_low_initial_loop: \n" + "inc r16 \n" + "breq timeout \n" + "sbic %1, 5 \n" + "rjmp recv_wait_low_initial_loop \n" // from low to the start of counting: 6 cycles = 0.5us + "nop \n" + "nop \n" + "nop \n" + "nop \n" + + + "recv_loop: \n" + "recv_low_begin: \n" + "ldi r16, 128 \n" // 1 + "recv_low_loop: \n" + "inc r16 \n" // 1 + "breq timeout \n" // 1 if no timeout + "sbis %1, 5 \n" // 1 // von high auf dec: 6 + "rjmp recv_low_loop \n\n" // 2 if executed, 1 if skipped. + "nop \n" // to account for the rjmp recv_loop below. + "nop \n" + "nop \n" + "nop \n" + + "recv_high_begin: \n" + "nop \n" // to account for the ldi in recv_low_begin. + "recv_high_loop: \n" + "dec r16 \n" // 1 + "breq timeout \n" // 1 if no timeout + "sbic %1, 5 \n" // 1 // von low auf inc: 6 + "rjmp recv_high_loop \n\n" // 2 if executed, 1 if skipped + + "st z+, r16 \n" // 2 + "rjmp recv_loop \n\n" // 2 + + "timeout: \n" + : "+z" ((unsigned char volatile*) buf_ptr) + : "I" (_SFR_IO_ADDR(PINC)), + "I" (_SFR_IO_ADDR(DDRC)), + "w" (len) + : "r16", "memory" + ); + + return buf_ptr-buffer; + + // a value of >=128 means "0", <127 means "1" bit. +} + int main (void) { char rand=123; + char gc_x=3, gc_y=3; DDRC=0x00; PORTC=0x00; @@ -137,6 +312,9 @@ int main (void) sei(); debug(4); + int temp=5; + int n_received; + while(1) { wdt_reset(); @@ -148,12 +326,76 @@ debug(4); rand=(rand*109+89)%251; // move to a random direction - reportBuffer.dx = (rand&0xf)-8; - reportBuffer.dy = ((rand&0xf0)>>4)-8; + reportBuffer.dx = gc_x; + reportBuffer.dy = -gc_y; usbSetInterrupt((void *)&reportBuffer, sizeof(reportBuffer)); - debug(2); +// debug(2); + } + + + +/* temp++; + if (!(PIND & 0x08)) // check if uC is hung up + { + //PORTB=~(1 << ((temp>>9)%6)); + PORTB=~temp; + } + else if (!(PIND & 0x20)) // debug num_received + { + PORTB=~n_received>>3; + } + else if (!(PIND & 0x40)) // clear debug output + { + PORTB=~0x00; + } + else*/ + { + // decode "buffer" and write button states to PORTB + + //PORTB=~buffer[4]; + } + + + debug(temp % 64); + _delay_ms(0.3); + + char foo[] = { 0x40, 0x03, 0x02 }; + + if (!(PIND & 0x10)) + foo[2]=0x03; + + n_received=send_recv_gc(foo, 3); + if (n_received == 64) + { + + gc_y=0; + for (int i=0;i<8;i++) + gc_y|= ( (buffer[23+8-i]&0x80)?0:(1< tmp2 ) +// tmp|=1; + + //PORTB=~gc_x; + } + else + { + temp++; } } -- cgit v1.2.1