Tja, ich hab's immer noch nicht geschafft, mal die PS/2 Maus zu dokumentieren und einen Wiki-Artikel zu schreiben. Wenigstens hat sich der Code weiter entwickelt, ich poste ihn hier einfach mal (wegen einer Anfrage in einem anderen Thread).

In der main() wird zunächst ein Reset ausgelöst und die Rückgabewerte werden geprüft. Danach wird die Auflösung hoch gesetzt und der Continous Reporting Mode eingeschaltet.

Code:
// ************************************************************************************************************************

#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <string.h>
#include <stdlib.h>

#define u8  unsigned char
#define s8  signed char
#define u16 unsigned int
#define s16 signed int

// ************************************************************************************************************************

typedef struct {
  u8 head;
  s8 rwheel;
  s8 lwheel;
} ps2_movement_type;

// ************************************************************************************************************************

#define TIMER0_25US             TCNT0 = 231; TCCR0 = (1<<CS01)
#define TIMER0_50US             TCNT0 = 206; TCCR0 = (1<<CS01)
#define TIMER0_75US             TCNT0 = 181; TCCR0 = (1<<CS01)
#define TIMER0_100US            TCNT0 = 156; TCCR0 = (1<<CS01)
#define TIMER0_500US            TCNT0 = 193; TCCR0 = (1<<CS01) | (1<<CS00)

#define TIMER0_1MS              TCNT0 = 131; TCCR0 = (1<<CS01) | (1<<CS00)
#define TIMER0_2MS              TCNT0 =   6; TCCR0 = (1<<CS01) | (1<<CS00)
#define TIMER0_5MS              TCNT0 = 217; TCCR0 = (1<<CS02) | (1<<CS00)
#define TIMER0_10MS             TCNT0 = 178; TCCR0 = (1<<CS02) | (1<<CS00)
#define TIMER0_20MS             TCNT0 = 100; TCCR0 = (1<<CS02) | (1<<CS00)

#define TIMER0_RUNNING          (TCCR0)
#define NOOP                    asm volatile (";")

#define SLEEP_10MS              TIMER0_10MS; while (TIMER0_RUNNING) NOOP
#define SLEEP_20MS              TIMER0_20MS; while (TIMER0_RUNNING) NOOP


#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)


// ************************************************************************************************************************

SIGNAL (SIG_OVERFLOW0)                                              // * TIMER0 ÜBERLAUF *
{
  TCCR0 = 0x00;                                                     // Timer 0 ausschalten
}

// ************************************************************************************************************************

u8 ps2_wait_long_for_clck_lo ()                                     // PS/2: Bis zu 10ms auf CLCK=LO warten
{
  TIMER0_10MS;                                                      // Timer0 Preload 10ms
  while (TIMER0_RUNNING)
  {                                                                 // wenn der Timer läuft
    if (!PS2_CLCK_IS_HI) return 0x00;                               // und CLCK geht auf LO -> Return OK
  }
  return (0xFF);                                                    // Timer abgelaufen -> Return Timeout
}

// ************************************************************************************************************************

u8 ps2_wait_for_clck_lo ()                                          // PS/2: Bis zu 50µs auf CLCK=LO warten
{
  TIMER0_50US;                                                      // Timer 0 Preload  50µs
  while (TIMER0_RUNNING)
  {                                                                 // wenn der Timer läuft
    if (!PS2_CLCK_IS_HI) return 0x00;                               // und CLCK geht auf LO -> Return OK
  }
  return (0xFF);                                                    // Timer abgelaufen -> Return Timeout
}

// ************************************************************************************************************************

u8 ps2_wait_for_clck_hi ()                                          // PS/2: Bis zu 50µs auf CLCK=HI warten
{
  TIMER0_50US;                                                      // Timer 0 Preload  50µs
  while (TIMER0_RUNNING)
  {                                                                 // wenn der Timer läuft
    if (PS2_CLCK_IS_HI) return 0x00;                                // und CLCK geht auf LO -> Return OK
  }
  return (0xFF);                                                    // Timer abgelaufen -> Return Timeout
}

// ************************************************************************************************************************

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
}

// ************************************************************************************************************************

void fatal_error (u8 errcode, u16 addinfo)
{
    // Fehlerbehandlung hier rein....
    while (1) {}
}

// ************************************************************************************************************************

int main (void)
{
                                                                    // Variablen
  u8  c,i;                                                          // Hilfsvariablen
  ps2_movement_type moved;                                          // Bewegungsmeldung

  TIMSK |= (1<<TOIE0);                                              // Overflow Interrupt für Timer0 einschalten
  sei();                                                            // Interrupts einschalten

  for (i=0;i<50;i++) { SLEEP_20MS; }                                // 1s warten (50*20ms)
  if (ps2_send(0xFF))   fatal_error(0x01,0x00);                     // PS/2 Cmd: Reset
  SLEEP_20MS;                                                       // 20ms warten
  if (ps2_read(&c,1,i)) fatal_error(0x02,0x00);                     // auf Lesefehler von PS/2 prüfen
  if (c != 0xFA)        fatal_error(0x03,c);                        // auf Quittung (ACK/$FA) prüfen
  for (i=0;i<50;i++) { SLEEP_10MS; }                                // 1s warten (50*20ms)
  if (ps2_read(&c,1,i)) fatal_error(0x04,0x00);                     // auf Lesefehler von PS/2 prüfen
  if (c != 0xAA)        fatal_error(0x05,c);                        // auf BAT-Quittung ($AA) prüfen
  if (ps2_read(&c,1,i)) fatal_error(0x06,0x00);                     // auf Lesefehler von PS/2 prüfen (Device ID überspringen)

  for (i=0;i<25;i++) { SLEEP_10MS; }                                // 1s warten (50*20ms)
  if (ps2_send(0xE8))   fatal_error(0x07,0x00);                     // PS/2 Cmd: Set Resolution
  SLEEP_20MS;                                                       // 20ms warten
  if (ps2_read(&c,1,i)) fatal_error(0x08,0x00);                     // auf Lesefehler von PS/2 prüfen
  if (c != 0xFA)        fatal_error(0x09,c);                        // auf Quittung (ACK/$FA) prüfen

  for (i=0;i<25;i++) { SLEEP_10MS; }                                // 1s warten (50*20ms)
  if (ps2_send(0x02))   fatal_error(0x0A,0x00);                     // Resolution 2 = 4 counts/mm = 2 Impulse pro Zahn
  SLEEP_20MS;                                                       // 20ms warten
  if (ps2_read(&c,1,i)) fatal_error(0x0B,0x00);                     // auf Lesefehler von PS/2 prüfen
  if (c != 0xFA)        fatal_error(0x0C,c);                        // auf Quittung (ACK/$FA) prüfen

  for (i=0;i<25;i++) { SLEEP_10MS; }                                // 1s warten (50*20ms)
  if (ps2_send(0xF4))   fatal_error(0x0D,0x00);                     // PS/2 Continuous Reporting Mode einschalten
  SLEEP_20MS;                                                       // 20ms warten
  if (ps2_read(&c,1,i)) fatal_error(0x0E,0x00);                     // auf Lesefehler von PS/2 prüfen
  if (c != 0xFA)        fatal_error(0x0F,c);                        // auf Quittung (ACK/$FA) prüfen

  // ----------------------------------------------------------------------------------------------------------------------

  mainloop:                                                         // Sprungmarke für Hauptschleife

  // ----------------------------------------------------------------------------------------------------------------------

  if (!ps2_read((char*)&moved,3,c))                                 // Maus lesen
  {
    // hier kann mit den Inkrementalgeberwerten und den Keys gearbeitet werden
  }

  // ----------------------------------------------------------------------------------------------------------------------

  goto mainloop;

}
Er ist für einen Atmega8 mit 8 MHz. Bei anderer Taktung müssen die Timer-Makros natürlich angepasst werden.

PS2 CLCK ist an PC0, DATA ist an PC1. Das kann in den Makrodefinitionen geändert werden.

Bei mir sind Encoder für das linke und das rechte Hinterrad angeschlossen. Durch die gewählte Auflösung erhalte ich Zähleveränderungen mit jeder Flanke, d.h. bei jedem "Farbwechsel" wird der Zähler erhöht bzw. verringert je nach Drehrichtung.

Durch den Continous Reporting Mode muss man in der Hauptschleife nur immer wieder drei Bytes vom PS/2 Bus abholen und erhält damit die gezählten Impulse seit dem letzten Auslesen.

So long,
Chris