Hi,
ist nicht schwer, ist eigentlich super simpel, wenn man mal verstanden hat, wie's läuft.
Anbei mein Code für den WINAVR in C:
Das Programm ist für das RNControl 1.4 mit 16MHz Quarz geschrieben.Code:// ************************************************************************************************************************ // // PS/2 Mouse Communication // // (c) 2005 Chris Hendricks // // ************************************************************************************************************************ #include <avr/io.h> #include <avr/interrupt.h> #include <avr/signal.h> #include <string.h> #define u8 unsigned char #define u16 unsigned int // ************************************************************************************************************************ // 16 MHz Clock #define TIMER0_25US TCNT0 = 206; TCCR0 = (1<<CS01) #define TIMER0_50US TCNT0 = 156; TCCR0 = (1<<CS01) #define TIMER0_75US TCNT0 = 106; TCCR0 = (1<<CS01) #define TIMER0_100US TCNT0 = 56; TCCR0 = (1<<CS01) #define TIMER0_500US TCNT0 = 131; TCCR0 = (1<<CS01) | (1<<CS00) #define TIMER0_1MS TCNT0 = 6; TCCR0 = (1<<CS01) | (1<<CS00) #define TIMER0_5MS TCNT0 = 178; TCCR0 = (1<<CS02) | (1<<CS00) #define TIMER0_10MS TCNT0 = 100; TCCR0 = (1<<CS02) | (1<<CS00) #define TIMER0_RUNNING (TCCR0) #define NOOP asm volatile (";") // ************************************************************************************************************************ #define URXBUFSIZE 255 // Größe des UART Empfangspuffers #define UTXBUFSIZE 255 // Größe des UART Sendepuffers // ************************************************************************************************************************ u8 urxbuf[URXBUFSIZE]; // UART Empfangspuffer u8 utxbuf[UTXBUFSIZE]; // UART Sendepuffer u8 urxl = 0; // Anzahl Zeichen im UART Empfangspuffer u8 utxl = 0; // Anzahl Zeichen im UART Sendepuffer volatile u8 timcntdwn; // ************************************************************************************************************************ void uart_prstr (char * string) // * UART-TX-PUFFER: STRING ABLEGEN * { memcpy (&utxbuf[utxl],string,strlen(string)); // String an den Puffer anhängen utxl += strlen(string); // Länge des Pufferinhalts erhöhen } // ************************************************************************************************************************ void uart_prchr (char chr) // * UART-TX-PUFFER: CHARACTER ABLEGEN * { utxbuf[utxl++] = chr; // Character an den Puffer anhängen } // ************************************************************************************************************************ void uart_prhex (u8 val) // * UART-TX-PUFFER: HEX-WERT ABLEGEN * { u8 ln, hn; // Variablen hn = (val & 0xF0)>>4; // HiNibble Wert ln = (val & 0x0F); // LoNibble Wert utxbuf[utxl++] = '$'; // Dollarzeichen anhängen utxbuf[utxl++] = hn>9 ? hn+0x37 : hn+0x30; // HiNibble an den Puffer anhängen utxbuf[utxl++] = ln>9 ? ln+0x37 : ln+0x30; // LoNibble an den Puffer anhängen } // ************************************************************************************************************************ u8 uart_havebyte (void) // * UART-RX-PUFFER: ZEICHEN VORHANDEN? * { return (urxl); // Anzahl Zeichen im Empfangspuffer zurückgeben } // ************************************************************************************************************************ u8 uart_getbyte (void) // * UART-RX-PUFFER: ZEICHEN LESEN * { u8 c; // Hilfsvariable c = urxbuf[0]; // Zeichen aus dem FIFO lesen urxl--; // Anzahl Zeichen verringern memmove (&urxbuf[0],&urxbuf[1],urxl); // Puffer shiften return (c); // Zeichen zurückgeben } // ************************************************************************************************************************ SIGNAL (SIG_UART_RECV) // * UART EMPFANGSINTERRUPT * { if (urxl < URXBUFSIZE) // noch Kapazität im Empfangsbuffer { urxbuf[urxl] = UDR; // neues Zeichen anhängen if (urxbuf[urxl] == 0x0D) { // Empfangenes Zeichen ist ein CR uart_prstr ("\r\n"); // CR+LF als Echo zurücksenden } else { // Empfangenes Zeichen ist kein CR uart_prchr(urxbuf[urxl]); // Echo ans Terminal zurücksenden } urxl++; // Anzahl vorhandener Zeichen im FIFO erhöhen } } // ************************************************************************************************************************ SIGNAL (SIG_INTERRUPT0) { if (PIND & (1<<PD3)) uart_prchr ('1'); else uart_prchr ('0'); } // ************************************************************************************************************************ SIGNAL (SIG_INTERRUPT1) { if (PIND & (1<<PD3)) uart_prchr ('1'); else uart_prchr ('0'); } // ************************************************************************************************************************ SIGNAL (SIG_OUTPUT_COMPARE0) { if (timcntdwn) timcntdwn--; // timcntdwn runterzählen wenn > 0 } SIGNAL (SIG_OVERFLOW0) { TCCR0 = 0x00; } // ************************************************************************************************************************ #define PS2_CLCK_IS_HI (PINC & (1<<PC0)) #define PS2_DATA_IS_HI (PINC & (1<<PC1)) #define PS2_CLCK_LO DDRC |= (1<<PC0) #define PS2_CLCK_HI DDRC &= ~(1<<PC0) #define PS2_DATA_LO DDRC |= (1<<PC1) #define PS2_DATA_HI DDRC &= ~(1<<PC1) // ************************************************************************************************************************ u8 ps2_wait_long_for_clck_lo () { TIMER0_10MS; while (TIMER0_RUNNING) { if (!PS2_CLCK_IS_HI) return 0x00; } return (0xFF); } // ************************************************************************************************************************ u8 ps2_wait_for_clck_lo () { TIMER0_50US; while (TIMER0_RUNNING) { if (!PS2_CLCK_IS_HI) return 0x00; } return (0xFF); } // ************************************************************************************************************************ u8 ps2_wait_for_clck_hi () { TIMER0_50US; while (TIMER0_RUNNING) { if (PS2_CLCK_IS_HI) return 0x00; } return (0xFF); } // ************************************************************************************************************************ u8 ps2_send (u8 val) { u8 i = -1; u8 pb = 0; PS2_CLCK_LO; // setze CLCK LO TIMER0_100US; while (TIMER0_RUNNING) NOOP; // warte 100µs PS2_DATA_LO; // setze CLCK LO, DATA LO TIMER0_25US; while (TIMER0_RUNNING) NOOP; // warte 25µs PS2_CLCK_HI; // setze CLCK HI, DATA LO if (ps2_wait_long_for_clck_lo()) goto ps2_send_error; // 10ms auf fallende Flanke (CLCK) warten for (i=0; i<8; i++) { // Datenbits LSB->MSB if (val & 0x01) { // Bit ist 1 pb++; // Parityzähler erhöhen PS2_DATA_HI; // Datenleitung HI sezen } else { // Bit ist 0 PS2_DATA_LO; // Datenleitung LO setzen } if (ps2_wait_for_clck_hi()) goto ps2_send_error; // 50µs auf steigende Flanke (CLCK) warten if (ps2_wait_for_clck_lo()) goto ps2_send_error; // 50µs auf fallende Flanke (CLCK) warten val = val >> 1; } if (pb & 0x01) // PB ungerade? PS2_DATA_LO; // -> kein Parity Bit else // PB gerade? PS2_DATA_HI; // -> Parity Bit if (ps2_wait_for_clck_hi()) goto ps2_send_error; // 50µs auf steigende Flanke (CLCK) warten if (ps2_wait_for_clck_lo()) goto ps2_send_error; // 50µs auf fallende Flanke (CLCK) warten i++; PS2_DATA_HI; // CLCK und DATA freigeben PS2_CLCK_HI; // CLCK und DATA freigeben if (ps2_wait_for_clck_hi()) goto ps2_send_error; // 50µs auf steigende Flanke (CLCK) warten if (ps2_wait_for_clck_lo()) goto ps2_send_error; // 50µs auf fallende Flanke (CLCK) warten PS2_CLCK_LO; // CLCK LO setzen (Bus blockieren) return (0); // Fehlerfrei ps2_send_error: // Fehlerhandling PS2_CLCK_LO; // CLCK LO setzen (Bus blockieren) return (i); // Fehlernummer zurückgeben } // ************************************************************************************************************************ u8 ps2_read (u8 * buffer, u8 len, u8 bytes_read) { u8 i; bytes_read = 0; // Anzahl gelesener Zeichen PS2_CLCK_HI; // CLCK freigeben while (bytes_read < len) { buffer[bytes_read] = 0; for (i=1; i<=11; i++) { if (i==1) // beim Startbit { if (ps2_wait_long_for_clck_lo()) goto ps2_read_error; } // 10ms auf fallende Flanke (CLCK) warten else // sonst { if (ps2_wait_for_clck_lo()) goto ps2_read_error; } // 50µs auf fallende Flanke (CLCK) warten if (i>=2 && i<=9) { // wenn Datenbit if (PS2_DATA_IS_HI) // HI buffer[bytes_read] = (buffer[bytes_read]>>1) | 0x80; else // LO buffer[bytes_read] = (buffer[bytes_read]>>1) | 0x00; } if (ps2_wait_for_clck_hi()) goto ps2_read_error; // 50µs auf steigende Flanke (CLCK) warten } bytes_read++; // Bytezähler erhöhen } PS2_CLCK_LO; // CLCK LO setzen (Bus blockieren) return (0); // Fehlerfrei ps2_read_error: // Fehlerhandling PS2_CLCK_LO; // CLCK LO setzen (Bus blockieren) return (i); // Fehlernummer zurückgeben } // ************************************************************************************************************************ int main (void) // * HAUPTPROGRAMM * { // Variablen u8 c,i; // Hilfsvariablen u8 rbuf[10]; // Empfangspuffer // RS232 Bus initialisieren UBRRL=16; // 57k6 @ 16MHz UCSRB=_BV(TXEN) | _BV(RXEN) | _BV(RXCIE); // Sender, Empfänger und EmpfangsIRQ einschalt TIMSK |= (1<<TOIE0); // Overflow Interrupt für Timer0 einschalten sei(); // Interrupts starten for (i=0; i<255; i++) asm volatile (";"); // ein kurzes Nickerchen uart_prstr ("\r\n"); // Welcome Text ausgeben uart_prstr ("*******************\r\n"); // Welcome Text ausgeben uart_prstr ("* PS/2 started... *\r\n"); // Welcome Text ausgeben uart_prstr ("*******************\r\n"); // Welcome Text ausgeben PORTC = 0x00; DDRC |= 0x00; while (1) // Hauptschleife { // -------------------------------------------------------------------------------------------------------------------- if (utxl && (UCSRA & (1<<UDRE))) { // Bereit zum Versand eines Zeichens über UART cli(); // Interrupts abschalten UDR = utxbuf[0]; // erstes Zeichen aus dem FIFO ins Senderegister utxl--; // Länge der Warteschlange verringern memmove (&utxbuf[0],&utxbuf[1],utxl); // FIFO shiften sei(); // Interrupts einschalten } // -------------------------------------------------------------------------------------------------------------------- if (uart_havebyte()) { i = 0; switch (uart_getbyte()) { case 'a': { i=ps2_send(0xF6); break; } case 'b': { i=ps2_send(0xF4); break; } case 'z': { i=ps2_send(0xFF); break; } case 'r': { i=ps2_send(0xE8); break; } case '1': { i=ps2_send(0x00); break; } case '2': { i=ps2_send(0x01); break; } case '4': { i=ps2_send(0x02); break; } case '8': { i=ps2_send(0x03); break; } case '+': { DDRC = 0x00; break; } case '-': { DDRC = 0x01; break; } case '#': { i=ps2_read(rbuf,1,c); if (!i) uart_prhex (rbuf[0]); break; } } if (i) { uart_prstr ("RES="); uart_prhex(i); uart_prstr ("\r\n"); } } } // Ende Hauptschleife } // Ende main
Es wird ein Serielles Terminal (HyperTerm) erwartet mit 57600 8N1.
Die CLCK-Leitung der Maus muss an PC0, die DATA Leitung an PC1.
RNControl meldet sich mit einer Hallo-Meldung und erwartet dann einen Tastendruck, der dann ein entsprechendes Kommando an die Maus schickt.
Die Projektdoku ist noch nicht fertig. Wenn Interesse besteht, erzähle ich gerne mehr...
Gruß,
Chris







Zitieren

Lesezeichen