Get Adobe Flash player
FacebookTwitterGoogle+
English Arabic French German Italian Portuguese Russian Spanish

Did you know?

Recently graduated engineers have higher average starting salaries than their counterparts with other degrees. These salaries vary by branch of engineering and level of education.
 

Help us stay online:

small donate

PS2 mouse logoIn this example we give you complete project for AVR readout of PS2 mouse position. Complete code is well commented and available for download on link below. To understand command format and register meaning, see details in The PS/2 Mouse Interface article .
The code is written for ATmega8535, but can be very easily adjusted for any microcontroller. All interaction with mouse is managed in while(1) loop. Very interesting application of this project would be attempt to make robot navigation using simple and very cheap PS2 or USB optical mouse for encoder purposes. Of course, you can allways use mouse in conventional way for some kind of menu or position navigation and control of your device. Maybe for your implementation of robot arm.. Use your creativity.

In this example we read all mouse registers using function loop1(). These registers are:

mstat register

x buf register and

y buf register

To use this example, place pullup resistors on pins used. These pins are:

CLK  PORTD.2
DATA PORTD.3

Connect these microcontroller pins as well as Vcc (5V) and GND pin to mouse PS2 connector as stated in The PS/2 Mouse Interface article

The standard PS/2 mouse sends movement/button information to the host using the following 3-byte packet:

Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0
Byte 1 Y overflow X overflow Y sign bit X sign bit Always 1 Middle Btn Right Btn Left Btn
Byte 2 X movement
Byte 3 Y movement

The movement values are 9-bit 2's complement integers, where the most significant bit appears as a "sign" bit in byte 1 of the movement data packet. Their value represents the mouse's offset relative to its position when the previous packet was sent, in units determined by the current resolution. The range of values that can be expressed is -127 to +127. If this range is exceeded, the appropriate overflow bit is set.

Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0 Byte 1 Y overflow X overflow Y sign bit X sign bit Always 1 Middle Btn Right Btn Left Btn Byte 2 X movement Byte 3 Y movement

Complete code is provided below with key lines highlighted and complete project with source code written in C language compiler codevision for AVR can be downloaded at the bottom of this page from direct link.

/*****************************************************
This program was produced by the
CodeWizardAVR V2.04.4a Advanced
Automatic Program Generator
© Copyright 1998-2009 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
 
Project : 
Version : 
Date    : 12/26/2011
Author  : 
Company : 
Comments:
Chip type               : ATmega8535
Program type            : Application
AVR Core Clock frequency: 8.000000 MHz
Memory model            : Small
External RAM size       : 0
Data Stack size         : 128
*****************************************************/
#include <mega8535.h>
#include <delay.h>
// Standard Input/Output functions
#include <stdio.h>
#include <stdlib.h>
#define KBD_CLK  PORTD.2
#define KBD_DATA PORTD.3
#define KBD_CLK_DIR  DDRD.2
#define KBD_DATA_DIR DDRD.3
#define MDATA 3
#define MCLK 2
#define DATA_READ PIND.3
#define CLK_READ PIND.2
// External Interrupt 0 service routine
 char mstat;
 signed char mx;
 signed char my;
 signed int acu_x, acu_y;
 char buf[5];
 char maska,i;
interrupt [EXT_INT0] void ext_int0_isr(void)
{
// Place your code here
}
#ifndef RXB8
#define RXB8 1
#endif
#ifndef TXB8
#define TXB8 0
#endif
#ifndef UPE
#define UPE 2
#endif
#ifndef DOR
#define DOR 3
#endif
#ifndef FE
#define FE 4
#endif
#ifndef UDRE
#define UDRE 5
#endif
#ifndef RXC
#define RXC 7
#endif
#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<UPE)
#define DATA_OVERRUN (1<<DOR)
#define DATA_REGISTER_EMPTY (1<<UDRE)
#define RX_COMPLETE (1<<RXC)
// USART Receiver buffer
#define RX_BUFFER_SIZE 8
char rx_buffer[RX_BUFFER_SIZE];
#if RX_BUFFER_SIZE<256
unsigned char rx_wr_index,rx_rd_index,rx_counter;
#else
unsigned int rx_wr_index,rx_rd_index,rx_counter;
#endif
// This flag is set on USART Receiver buffer overflow
bit rx_buffer_overflow;
// USART Receiver interrupt service routine
interrupt [USART_RXC] void usart_rx_isr(void)
{
char status,data;
status=UCSRA;
data=UDR;
if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
   {
   rx_buffer[rx_wr_index]=data;
   if (++rx_wr_index == RX_BUFFER_SIZE) rx_wr_index=0;
   if (++rx_counter == RX_BUFFER_SIZE)
      {
      rx_counter=0;
      rx_buffer_overflow=1;
      };
   };
}
#ifndef _DEBUG_TERMINAL_IO_
// Get a character from the USART Receiver buffer
#define _ALTERNATE_GETCHAR_
#pragma used+
char getchar(void)
{
char data;
while (rx_counter==0);
data=rx_buffer[rx_rd_index];
if (++rx_rd_index == RX_BUFFER_SIZE) rx_rd_index=0;
#asm("cli")
--rx_counter;
#asm("sei")
return data;
}
#pragma used-
#endif
void gohi(int pin)
{
   switch (pin) {
    case MDATA:  
         KBD_DATA=1;
         KBD_DATA_DIR=0;
    break;
    case MCLK: 
         KBD_CLK=1;
         KBD_CLK_DIR=0; 
    break;    
    };
}
void golo(int pin)
{
   switch (pin) {
    case MDATA:  
         KBD_DATA=0;
         KBD_DATA_DIR=1;
    break;
    case MCLK: 
         KBD_CLK=0;
         KBD_CLK_DIR=1; 
    
    break;    
    };
}
void mouse_write(char data)
{
  char i;
  char parity = 1;
 /* put pins in output mode */
  gohi(MDATA);
  gohi(MCLK);
  delay_us(300);
  golo(MCLK);
  delay_us(300);
  golo(MDATA);
  delay_us(10);
  /* start bit */
  gohi(MCLK);
  /* wait for mouse to take control of clock); */
  while (CLK_READ == 1);
  /* clock is low, and we are clear to send data */
  for (i=0; i < 8; i++) {
    if (data & 0x01) {
      gohi(MDATA);
    } 
    else {
      golo(MDATA);
    }
    /* wait for clock cycle */
      while (CLK_READ == 0);
     while (CLK_READ == 1);
    parity = parity ^ (data & 0x01);
    data = data >> 1;
  }  
  /* parity */
  if (parity) {
    gohi(MDATA);
  } 
  else {
    golo(MDATA);
  }
    while (CLK_READ == 0);
   while (CLK_READ == 1);
  /* stop bit */
  gohi(MDATA);
  delay_us(50);
   while (CLK_READ == 1);
  /* wait for mouse to switch modes */
 while ((CLK_READ ==0) || (DATA_READ == 0));
  /* put a hold on the incoming data. */
  golo(MCLK);
}
/*
 * Get a byte of data from the mouse
 */
signed char mouse_read(void)
{
signed  char data = 0x00;
  int i;
  char mask = 0x01;
  /* start the clock */
  gohi(MCLK);
  gohi(MDATA);
  delay_us(50);        
  while (CLK_READ ==1);
  delay_us(5);  /* not sure why */
  while(CLK_READ ==0);
  for (i=0; i < 8; i++) {
    while(CLK_READ ==1);
    if (DATA_READ == 1) {
      data = data | mask;
    }
       while (CLK_READ ==0);
    mask = mask << 1;
  }
  /* eat parity bit, which we ignore */
        while (CLK_READ ==1);
        while (CLK_READ ==0);
  /* eat stop bit */
        while (CLK_READ ==1);
        while (CLK_READ ==0);
  /* put a hold on the incoming data. */
  golo(MCLK);
  return data;
}
void mouse_init()
{
  gohi(MCLK);
  gohi(MDATA);
  mouse_write(0xff);    
  mouse_read();  /* ack byte */
  mouse_read();  /* blank */ 
  mouse_read();  /* blank */
  mouse_write(0xf0);  /* remote mode */
  mouse_read();  /* ack */
  delay_us(100);
}
void loop1(void)
{
  /* get a reading from the mouse */
  mouse_write(0xeb);  /* give me data! */
  mouse_read();      /* ignore ack */
  mstat = mouse_read();
  mx = mouse_read();
  my = mouse_read();      
  acu_x+=mx;
  acu_y+=my;
  delay_ms(20);  /* twiddle */
}
/////////////-------------------------------------------------------------------------------------------------
void main(void)
{
// Declare your local variables here
// Input/Output Ports initialization
// Port A initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
PORTA=0x00;
DDRA=0x00;
// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
PORTB=0x00;
DDRB=0x00;
// Port C initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
PORTC=0x00;
DDRC=0x00;
// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
PORTD=0x00;
DDRD=0x00;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0 output: Disconnected
TCCR0=0x00;
TCNT0=0x00;
OCR0=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT0 Mode: Off
// INT1: Off
// INT2: Off
MCUCR=0x00;
MCUCSR=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x00;
// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: On
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud Rate: 1200
UCSRA=0x00;
UCSRB=0x98;
UCSRC=0x86;
UBRRH=0x01;
UBRRL=0xA0;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;
// Global enable interrupts
#asm("sei")
mouse_init();  
while (1)
      {      
        loop1();
        
        puts(" mstat reg: ");
        maska=0b10000000; 
        for (i=0;i<8;i++)  //we will write one by one bit of this register
            {
            if ((maska&mstat))
                putchar('1');
            else
                putchar('0'); 
            maska>>=1;//shift mask for one position  to right to get one by one bit for uart write of individual bits in mstat register                  
            }
        
        puts(" x reg: ");
        itoa(mx, buf);
        puts(buf);
        puts(" x position: ");
        itoa(acu_x, buf);
        puts(buf);
        puts(" y reg: ");
        itoa(my, buf);
        puts(buf);
        puts(" y position: ");
        itoa(acu_y, buf);
        puts(buf);
        
        delay_ms(200);   
        putchar(13);
      };
}

As it is displayed above, mouse initialization is processed in mouse_init() function. Other loop1() function reads all relevant registers from mouse. After that in while(1) everything is written to UART and loops around again. Mouse's current position is calculated by simple summing every readout from mouse relative movement registers.

First, we will explain what mouse detects. We have used optical mouse. Each of them has its resolution. This resolution represents number of points mouse will measure for movement of one inch. For example, 1200dpi mouse will send approximately buffer value 1200 when we move it for one inch.

I have tested three different mice and surprisingly all three reacted in a different way. :)

First one gives only information of mouse movement with resolution of 1!!! Which means that depending on direction of movement, it only gives +127 or -127 per one readout (for every axis x,y). Mouse1 acts as every time overflow occurs. Of course, this is very inconvenient because we would have to make readout extremely frequent, which would make heavy load to microcontroller. This is shown on first video.

Second mouse used (Mouse 2) works as expected, meaning, it gives exact number of dots mouse is moved. This means we have to make one readout before mouse is moved for 127 dots. After that, overflow occurs and we lose absolute position of mouse. This is possible to be implemented by microcontroller. Working of Mouse2 is shown on video 2.

Third mouse (Mouse 3) is best for absolute positioning by microcontroller. This one actually has internal buffer that remembers exact number of dots mouse is moved, even if it is much more than 127 dots. On PS2 readout by AVR, mouse reports exact movement if it is smaller than 127. If movement between two readouts was bigger than 127 mouse reports 127 and subtracts 127 from remembered movement. This means if movement was 264, in next two readouts Mouse3 will send you 127 in first two readouts and 10 in third readout. 127+127+10=264. This is shown on video 3 below.

Complete Codevision project with source code written in C for AVR microcontroller ATmega8535 can be downloaded from this link.