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:
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
Das Programm ist für das RNControl 1.4 mit 16MHz Quarz geschrieben.
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
Lesezeichen