summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Jung <flo@windfisch.org>2014-04-04 21:08:38 +0200
committerFlorian Jung <flo@windfisch.org>2014-04-04 21:08:38 +0200
commite399da62dc6e0bdc216efc2590e5e9b649afaa9d (patch)
tree616bc95cf15673ebd4fdebd0bc51e5f467e0349f
parent98a2467be3647894e348ff2f646dd34edbc04d05 (diff)
use gamecube as mouse :)
-rw-r--r--main.c248
1 files 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 <avr/interrupt.h>
#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<len; i++)
+ for (int j=0x80; j!=0; j=j>>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<<i) );
+
+ gc_x=0;
+ for (int i=0;i<8;i++)
+ gc_x|= ( (buffer[23-i]&0x80)?0:(1<<i) );
+
+ //gc_x^=0x80;
+ //gc_y^=0x80;
+
+ gc_x = ((signed char)(((unsigned char) gc_x) - 128)) /4;
+ gc_y = ((signed char)(((unsigned char) gc_y) - 128)) /4;
+
+ char tmp=0;
+ for (int i=1;i<8;i++)
+ tmp|= ( (buffer[i]&0x80)?0:(1<<i) );
+
+// if ( (temp & 0xFF) > tmp2 )
+// tmp|=1;
+
+ //PORTB=~gc_x;
+ }
+ else
+ {
+ temp++;
}
}