Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

How to debug PS/2 protocol?

Status
Not open for further replies.

o_0

Member level 3
Joined
Sep 30, 2009
Messages
58
Helped
2
Reputation
4
Reaction score
2
Trophy points
1,288
Activity points
1,873
The attached Codevision code is for an Atmega32a-based accelerometer mouse that interfaces to the computer via PS/2 protocol. (Notice this is not PlayStation2, it's this: **broken link removed**)
I have been able to show that the ADC portion works, which is dataFromMouse(), however I'm still not getting any mouse movement. All the outputs of the uC remain at +5V (idle state) all the time.
I honestly don't have a clue where to start debugging this huge program, other than putting LED lighting commands in strategic places. If anyone has any prior experience with this, I would greatly appreciate any suggestions on how to approach this.
Thanks in advance.

I've attached the file with the code, as well as the schematic.
Some differences in my setup:
* Instead of a crystal, I'm using int.RC 8 MHz.
* Not using the buffers.
* AREF has a 100nF capacitor to GND

Also I should mention I don't have an LCD, though there's code for it.

Code:
#include <Mega32.h> 
#include <stdio.h> // sprintf
#include <delay.h>

//used LCD when debugging
/*#asm
.equ __lcd_port=0x15
#endasm

#include <lcd.h> // LCD driver routines
*/
#define CLK_TIME 250
#define RESPOND_WAIT 700

//define PS/2 data line states
#define IDLE 0
#define INHIBIT 1
#define BUSY 2
#define REQUEST 3   
#define POWEROFF 4

/******************************************
*
*Ports:
*   D - Input/Output to Computer
*      D.1 - Output Clock
*      D.2 - Input Clock
*      D.6 - Output Data
*      D.7 - Input Data      
*
*   B - Status LEDs
*
*   C - Buttons
********************************************/

//define some useful maps to bits
#define CLK_OUT PORTD.1
#define DATA_OUT PORTD.6
#define CLK_IN PIND.2
#define DATA_IN PIND.7    

#define VCC_IN PIND.4


//state of the data line
char transmitting, receiving, waiting;

//conuters in tim1
int count, count1, count2;

unsigned char lastClkIn, ClkIn, dataIn;     
unsigned char myState;
unsigned char rcvBit, rcvByte;


//variables for writing to PS/2 port

unsigned char PS2byte, lastPS2byte;
unsigned char position, PS2parity;

char clockState, nextClockState, nextCLK_OUT;



unsigned char packetCount;

void initChip(void);



//define clock states

#define HIGH 0
#define FALLING 1
#define LOW 2
#define RISING 3     






/////////////////////////////MOUSE

//Reaction timer states
#define NoPush 11 
#define MaybePush 12
#define Pushed 13
#define MaybeNoPush 14

#define LCDwidth 16


void initialize(void);    //all the usual mcu stuff 
void displayAxis(unsigned char,unsigned char, unsigned char); //display first the accel for each axis
//void displayAxis(float, float, float);                              

unsigned char led;    //light states  
unsigned char lcddelay;  //timing variable for lcd
unsigned char btndelay;  //timing variable for button push
unsigned char btn1holddelay, btn2holddelay;  //timing variable for button hold
//unsigned char ReactState; 
float tempthresh, temperature; //the threshold tSemperture to turn the fan on.   
//the button push states and flags.
unsigned char PushState1, PushState2, PushFlag1, PushFlag2, lastPushFlag1, lastPushFlag2;

//record the voltage of x,y,z coordinate
unsigned char xADC, yADC, zADC;
//calibration variables 
unsigned char xCal, yCal, zCal;

bit scaling;
unsigned char lcdPosCount0,lcdPosCount1;


unsigned char PushState[5];
unsigned char PushFlag[5];

enum{BTN_LEFT=0, BTN_RIGHT, BTN_MIDDLE, BTN_FREEZE, BTN_DOUBLE};

enum{POSS_SCROLL=0, POSS_BEGIN_SCROLL, BEGIN_SCROLL, POSS_UP_SCROLL, POSS_DOWN_SCROLL, REAL_UP_SCROLL, REAL_DOWN_SCROLL};

enum{BTN_PACKET=0, X_PACKET, Y_PACKET, Z_PACKET};

enum{X_AXIS=0, Y_AXIS, Z_AXIS};

void debounce(unsigned char* PushFlag, unsigned char* PushState, unsigned char button);

void dataFromMouse(void);
void posConvert(void);
void getMovement(void);
void packetGen(void);

//define queue values
#define QUEUELEN 20
#define TRUE 1
#define FALSE 0

unsigned char queue[QUEUELEN]; //queue of data sent by keyboard. (first in, first out)
unsigned char queueFull;    //indicates if queue is full
unsigned char queueEmpty;//indicates if queue is empty 
unsigned char queueIn;       //indicates where to put data into queue
unsigned char queueOut;    //indicates where to take data out of queue

unsigned char queuePut(unsigned char d);       //put data into queue
unsigned char queueGet(void);          //get data from    
unsigned char packetGenFlag, resendFlag;   
unsigned char inhibited;    


//////////////////////////////TESTUING

bit outInhibit;
bit forceDelay;

unsigned char powerOffSend;


//////////Record positions

//the current position of x y z
char curPosition[3];
char lastPosition[3];
char movement[3];

////////////Processes Command variables
unsigned char lastlastSampleRate, lastSampleRate, sampleRate, deviceID;
bit transmitMode, setResolutionFlag, settingSampleRateFlag;

//set when the mouse have significent movemenet
bit realMoveFlag, realBtnFlag, realScrollFlag;
bit transmitFlag;

////////////Process scroll motions
unsigned char scrollMotion[5];
char scrollCount, falseStateCount;
unsigned char scrollIndex;
unsigned char scrollState;



char resolution;


//****************************
//PS/2 Host Command set.
#define ERROR            0xFC
#define RESET             0xFF
#define RESEND             0xFE
#define SET_DEFAULT     0xF6
#define DIS_DATA_REPORT 0xF5
#define EN_DATA_REPORT     0xF4
#define SET_SAMPLE_RATE 0xF3
#define GET_DEVICE_ID     0xF2
#define SET_REMOTE_MODE 0xF0
#define SET_WRAP_MODE     0xEE
#define RESET_WRAP_MODE 0xEC
#define READ_DATA         OxEB
#define SET_STREAM_MODE 0xEA
#define STATUS_REQ         0xE9
#define SET_RES         0xE8
#define SET_SCALING21    0xE7
#define SET_SCALING11    0xE6

#define ACK             0xFA
#define INIT_SUCC        0xAA
#define INIT_SUCC_1        0x00
#define SCALING21        0x02



//float voltage;
unsigned char Ain;
int clock;
int ticked = 0;  

//LCD buffer used for debugging
//unsigned char ledbuf[17];


//*****************************
//Push button state machine variables
unsigned char  mousekill, but_state, butinfo; 
bit leftBtn, rightBtn, middleBtn, lastLeftBtn, lastRightBtn, lastMiddleBtn;
unsigned char test0; 
unsigned int debounceTimer;
#define STATE_READY 0        
#define STATE_ALMOST_READY 1
#define STATE_BTN_PRESS 2
#define STATE_BTN_RELEASE1 3
#define STATE_BTN_RELEASE2 4

//#define but_press 2
//#define but_release1 3
//#define but_release2 4



void debounceBtn(void)
{         
    unsigned char PINCval;
    debounceTimer = 0;

    PINCval = PINC&0x0f;          

    //Debounce State machine for the button pushes
    switch(but_state)
    {   
        //No button push state
    case(STATE_READY):     
        if(~(PINCval) != 0)
        {                 
            butinfo = PINCval;              
            but_state = STATE_ALMOST_READY;
        }     
        else
        {           
            test0 = 0;
            but_state = STATE_READY;
        }                      
        break;

        //Maybe-button push detected
    case(STATE_ALMOST_READY):
        if(PINCval == butinfo)
            but_state = STATE_BTN_PRESS;
        else
            but_state = STATE_READY;
        break;

        //Button push definately detected
    case STATE_BTN_PRESS:             
        if(PINCval == butinfo)
        {    
            if(~PINC.1 == 0x01)  //Left mouse click
                leftBtn = 1;           
            else if (~PINC.2 == 0x01)  //Middle mouse click
                middleBtn = 1; 
            else if (~PINC.3 == 0x01)  //Right mouse click
                rightBtn = 1; 

            but_state = STATE_BTN_PRESS;
        }  
        else 
            but_state = STATE_BTN_RELEASE1;                       
        break;

        //Maybe Button is released                                      
    case STATE_BTN_RELEASE1:
        if(PINCval== butinfo)
            but_state = STATE_BTN_PRESS;
        else
            but_state = STATE_BTN_RELEASE2;
        break;

        //Last check if button is released        
    case STATE_BTN_RELEASE2:      
        if(PINCval == butinfo)
            but_state = STATE_BTN_RELEASE1;
        else
        {
            //reaset all the button state
            but_state = STATE_READY;
            leftBtn = 0;
            rightBtn = 0;
            middleBtn =0;
            butinfo = 0;          
            test0 = 0;
        }  
        break;
    }   
}   



/////Debounce an array of buttons
void debounce(unsigned char* PushFlag, unsigned char* PushState, unsigned char button) 
{
    unsigned char btn_mask;

    switch(button)
    {
    case BTN_LEFT:
        btn_mask = 0x01;
        break;
    case BTN_RIGHT:
        btn_mask = 0x02;
        break;
    case BTN_FREEZE:
        btn_mask = 0x04;
        break;
    case BTN_DOUBLE:
        btn_mask = 0x08;
        break;
    }

    switch (PushState[button])
    {
    case NoPush: 
        if (~PIND == btn_mask) PushState[button]=MaybePush;
        else PushState[button]=NoPush;
        break;

    case MaybePush:        
        if (~PIND == btn_mask) 
        {
            PushState[button]=Pushed;   
            PushFlag[button]=1;
        }
        else PushState[button]=NoPush;
        break;

    case Pushed:  
        if (~PIND == btn_mask) PushState[button]=Pushed; 
        else PushState[button]=MaybeNoPush;    
        break;

    case MaybeNoPush:
        if (~PIND == btn_mask) PushState[button]=Pushed; 
        else 
        {
            PushState[button]=NoPush;
            PushFlag[button]=0;
        }    
        break;
    }
}





//insert data into queue.  Return 1 if queue full, or 0 if inserted data sucessfully
char queuePut(char d)
{

    if (queueFull==TRUE)        //check if queue is full
        return(TRUE);

    queue[queueIn]=d;            //insert d into queue
    queueIn++;                    //increment where to stick in the next d value
    queueEmpty=FALSE;            //indicate queue isnt' empty anymore

    if (queueIn==QUEUELEN)        //if reached the end of the queue
        queueIn=0;                //wrap around to the beginning

    if (queueIn==queueOut)        //if queueIn caught up to queueOut
        queueFull=TRUE;            //indicate queue is full

    return(0);
}

//get data out of queue.  Return 0 if queue empty or the actual data if not empty
char queueGet(void)
{

    char d;

    if(resendFlag == TRUE)
    {               
        resendFlag=FALSE;
        return lastPS2byte;
    }


    if (queueEmpty==TRUE) //check if queue is empty    
        return(0);



    d=queue[queueOut];        //get data out of queue    
    queueOut++;                //increment location where to get next d value
    queueFull=FALSE;        //indicate queue isn't full anymore

    if (queueOut==QUEUELEN)    //if reached the end of the queue
        queueOut=0;            //wrap around to the beginning


    if (queueOut==queueIn)    //if queueOut caught up to queueIn
        queueEmpty=TRUE;    //indicate queue is empty


    if(d==0xAA)
        delay_ms(375);    


    lastPS2byte = d;
    return(d); //return the data from queue
}


///Generate a standard 4 byte Microsoft IntelliMouse: left, middle, right, x, y, z packet.
void packetGen(void)
{       

    unsigned char btnPacket=0x08;
    //the first byte is at least 0x08, because the third bit is always 1
    char scroll;

    //check if the y-axis is a negative move | check if the x-axis is a negative move | middle button click | right button click | left button click

    //when there is only button click
    if(realBtnFlag && realMoveFlag==FALSE)
    { 
        btnPacket = btnPacket | ((middleBtn&0x1) <<2) | ((rightBtn&0x1)<<1) | (leftBtn&0x1);                             
        queuePut(btnPacket);
        queuePut(0x00);    
        queuePut(0x00);
        queuePut(0x00);
    }       
    else if(!realBtnFlag && realScrollFlag)//&& !realMoveFlag
    {
        btnPacket = btnPacket | ((middleBtn&0x1) <<2) | ((rightBtn&0x1)<<1) | (leftBtn&0x1);
        queuePut(btnPacket);
        queuePut(0x00);    
        queuePut(0x00);                                                              

        scroll = movement[Z_AXIS] - zCal;

        if(scrollState == REAL_UP_SCROLL)
        {
            queuePut(0x03);  
        }
        else if(scrollState == REAL_DOWN_SCROLL)
        {
            queuePut(0xFD);
        }
        scrollState = POSS_SCROLL;

    }

    else if(realMoveFlag == TRUE)
    {
        btnPacket = btnPacket | (((movement[Y_AXIS]>>7)&0x1)<<5) | (((movement[X_AXIS]>>7)&0x1)<<4) | ((middleBtn&0x1) <<2) | ((rightBtn&0x1)<<1) | (leftBtn&0x1);                             
        queuePut(btnPacket);
        queuePut(movement[X_AXIS] << resolution);    
        queuePut(movement[Y_AXIS] << resolution);    
        queuePut(0x00);
        //queuePut((movement[Z_AXIS]<2 && movement[Z_AXIS]>-2)?0:movement[Z_AXIS]&0x0f);

    }


    /*
    if(packetCount < 20)
    {
    queuePut(0x8);
    queuePut(0x8);
    queuePut(0x8);
    queuePut(0x0);
    }
    else
    {
    queuePut(0x8 | 0x30);
    queuePut(0xF3);
    queuePut(0xF3);
    queuePut(0x0);

    }                  
    packetCount++;

    */
}



//Obtain x, y, z movements.
void getMovement(void)
{    
    unsigned char i=0;    
    //position conversion formula.
    // xxxxxxxOLD (int)(voltage - first voltage(when not moving) * 100)
    // ADC reading - init adc reading(calibrating data) 

    curPosition[X_AXIS] = (signed char)(xADC - xCal);
    curPosition[Y_AXIS] = (signed char)(yADC - yCal);
    curPosition[Z_AXIS] = (signed char)(zADC - zCal);

    //only when the mouse has significent movement we generate a packet

    //get the difference between the previous position and current position
    for(i=0;i<3;i++)
    {
        movement[i] = curPosition[i];// - lastPosition[i] ;

        //only check x and y movements        
        if(i<2)
        {                              
            if(!(movement[i] < 3 || movement[i] > -3))
                realMoveFlag = TRUE;               
            /*
            if(movement[i] > 3 || movement[i] < -3)
            realMoveFlag = TRUE;
            */
        }

    }                    
}

/*
//////////FAILED ATTEMPT: TRYING THE CALCULATE THE SLOPE.
void detectScroll(void)
{         
signed char slope, slopePrev;
signed char scrollPos;
scrollPos = (signed char)movement[Z_AXIS];    
switch (scrollState)
{
case POSS_SCROLL:
if(scrollPos < 10 && scrollPos > -10)
{                               
PORTB = ~0x80;                     
scrollState = BEGIN_SCROLL;
}
else 
{
PORTB = ~0x40;
scrollState = POSS_SCROLL;
}    
//PORTB = ~0x00;                          
scrollMotion[1]=scrollPos;
break;

case POSS_BEGIN_SCROLL:
if(scrollPos < 10 || scrollPos > -10)
{                               
//PORTB = ~0xa0;                     
scrollState = BEGIN_SCROLL;
scrollMotion[1]=scrollPos;
}
else 
{
//PORTB = ~0x40;
scrollState = POSS_SCROLL;
}    
PORTB = ~0x00;                          
break;

case BEGIN_SCROLL:
//PORTB = ~0x80;
if(scrollPos < 12 && scrollPos > -12)
{    

scrollMotion[1] = scrollPos;                         
scrollState = POSS_SCROLL;
}    
else    
{
PORTB = ~0x0f;       
scrollMotion[2] = scrollPos;                     

slopePrev = (scrollMotion[1]-scrollMotion[0]);                         
slope = (scrollMotion[2]-scrollMotion[1]);
if(slope > 0 )//&& slopePrev > 0)              
{
PORTB = ~0xFF;
scrollState = POSS_UP_SCROLL;
}
else
scrollState = POSS_SCROLL;
}
break;

case POSS_UP_SCROLL:
scrollMotion[3] = scrollPos;
slope = (scrollMotion[3]-scrollMotion[2]);
if(slope > 0)
scrollState = REAL_UP_SCROLL;
else
scrollState = POSS_SCROLL;

break;                         

case REAL_UP_SCROLL:
scrollMotion[4] = scrollPos;
slope = (char)(scrollMotion[4]-scrollMotion[3]);
if(slope > 0)
scrollState = REAL_UP_SCROLL;
else
scrollState = POSS_UP_SCROLL;
break;

case POSS_DOWN_SCROLL:
scrollState = POSS_SCROLL;
break;
case REAL_DOWN_SCROLL:
scrollState = POSS_SCROLL;
break;
}               

}
*/


void detectScroll(void)
{         
     signed char slope, slopePrev;
    signed char scrollPos;
    scrollPos = (signed char)movement[Z_AXIS];    
    switch (scrollState)
    {
    case POSS_SCROLL:
        if(scrollPos < 10 && scrollPos > -10)
        {                               
            PORTB = ~0x80;                     
            scrollState = POSS_SCROLL;
        }
        else 
        {
            PORTB = ~0x40;
            scrollState = BEGIN_SCROLL;
        }    
        //PORTB = ~0x00;                          
        scrollMotion[1]=scrollPos;
        break;                 

    case BEGIN_SCROLL:
        //PORTB = ~0x80;
        if(scrollPos <= 15 && scrollPos >= -15)
        {    

            //scrollMotion[1] = scrollPos;                         
            scrollState = POSS_SCROLL;
        }    
        else    
        {
            PORTB = ~0x0f;       
            scrollMotion[2] = scrollPos;                     

            //slopePrev = (scrollMotion[1]-scrollMotion[0]);                         
            //slope = (scrollMotion[2]-scrollMotion[1]);
            if(scrollPos > 15 )//&& slopePrev > 0)              
            {
                PORTB = ~0xFF;
                scrollState = POSS_UP_SCROLL;
            }
            else if(scrollPos < -15)
            {
                scrollState = POSS_DOWN_SCROLL;
                PORTB = ~0xF0;
            }
        }
        break;

    case POSS_UP_SCROLL:
        scrollMotion[3] = scrollPos;
        //slope = (scrollMotion[3]-scrollMotion[2]);

        if(scrollPos <= 10 && scrollPos >= -10)
            scrollState = POSS_SCROLL;

        PORTB = ~0x00;

        if(scrollPos < -20)
            scrollState = REAL_UP_SCROLL;
        else
            scrollState = POSS_UP_SCROLL;
        break;                         

    case REAL_UP_SCROLL:
        scrollMotion[4] = scrollPos;
        //slope = (char)(scrollMotion[4]-scrollMotion[3]);
        //if(slope > 0)
        scrollState = REAL_UP_SCROLL;
        //else
        //    scrollState = POSS_UP_SCROLL;
        break;

    case POSS_DOWN_SCROLL:      

        scrollMotion[3] = scrollPos;
        //slope = (scrollMotion[3]-scrollMotion[2]);

        if(scrollPos <= 10 && scrollPos >= -10)
            scrollState = POSS_SCROLL;

        if(scrollPos > 20) 
        {
            PORTB = ~0x01;
            scrollState = REAL_DOWN_SCROLL;
        }
        else
            scrollState = POSS_DOWN_SCROLL;
        break;
    case REAL_DOWN_SCROLL:
        scrollState = REAL_DOWN_SCROLL;
        break;
    }               

}


void dataFromMouse(void)
{

    /////////////////// X /////////////////////      

    ADMUX =  0b01100000; //read voltage on A.0 for x axis
    ADCSRA.6 = 1;

    //ADCSRA.6 bit will be clear after the conversion is done.
    while(ADCSRA.6 == 1){}

    xADC = ADCH;    //read the ADC value

    /////////////////// Y /////////////////////            
    ADMUX =  0b01100001; //read voltage on A.1 for y axis
    ADCSRA.6 = 1;

    //ADCSRA.6 bit will be clear after the conversion is done.
    while(ADCSRA.6 == 1){}

    yADC = ADCH;    //read the ADC value

    /////////////////// Z /////////////////////            
    ADMUX =  0b01100010; //read voltage on A.2 for z axis
    ADCSRA.6 = 1;

    //ADCSRA.6 bit will be clear after the conversion is done.
    while(ADCSRA.6 == 1){}

    zADC = ADCH;    //read the ADC value

    //We need to record the init reading of ADC for computing relative position
    //the initial position of the mouse should be positioned on a flat surface.
    if(xCal == 0)
    {
        xCal = xADC;
        yCal = yADC;
        zCal = zADC;
    }    

    //displayAxis(xADC, yADC, zADC);  

}



//Process a command received from the host.  Reply appropriate feedback to 
//the computer.
void processCommand(unsigned char hostCommand) {

    delay_us(50);

    if (hostCommand == RESET)
    {
        queueIn = 0;
        queueOut=0;
        queueEmpty = TRUE;
        queueFull = FALSE;
        delay_us(500);

    }                                        

    //always acknowledge the incoming request
    queuePut(ACK);

    if(setResolutionFlag) {
        setResolutionFlag = 0;
        if(hostCommand >= 0 && hostCommand <= 3)
            resolution = hostCommand;
        if(hostCommand < 0 || hostCommand > 3) { //send an error
            queuePut(ERROR);

            return;
        }    else {                                           
            resolution = hostCommand;
            return;
        }
        return;
    }

    if(settingSampleRateFlag) {
        settingSampleRateFlag = 0;


        if(hostCommand >= 10 && hostCommand <= 200) {
            lastlastSampleRate = lastSampleRate;
            lastSampleRate = sampleRate;
            sampleRate = hostCommand;
            if((lastlastSampleRate == 200) && (lastSampleRate == 100) && (sampleRate == 80)) {
                deviceID = 0x03; // enter MS intellimouse mode
            }
            if(deviceID == 0x03 && lastlastSampleRate == 200 && lastSampleRate == 200 && sampleRate == 80) {
                deviceID = 0x03; // enter MS 5-button mouse mode
            } 
        } else { // bad sample rate, output an error
            queuePut(ERROR);

        }
        return;
    }

    switch (hostCommand) {  
        case RESEND:
            resendFlag = TRUE;
            break;


        case RESET:  //reset  RESP: 0xAA, 0x00


            //putsf(" Rset\r\n");

            /*            forceDelay = TRUE;
            delay_ms(1000);   
            forceDelay = FALSE;
            */
            deviceID = 0x00;
            queuePut(INIT_SUCC);
            queuePut(deviceID);
            //queuePut(INIT_SUCC_1);
            transmitMode = 0;
            sampleRate = 100;


            break;

        case EN_DATA_REPORT: //enable transmit  
            //putsf("rp +\r\n");
            transmitMode = 1;
            break;

        case 0xE6: //set scaling 1:1
            scaling = 0;
            break;
        case 0xE7:  //set scaling 2:
            scaling = 1;
            break;
        case GET_DEVICE_ID: //get device ID      
            //printf("ID=0x%x\r\n",deviceID);
            //delay_ms(1);
            queuePut(deviceID);
            //transmitMode = 1;

            break;


        case SET_RES:  //E8: set resolution
            //putsf(" Setres\r\n");
            setResolutionFlag = 1;
            break;

        case STATUS_REQ: //status request respond 0xFA, 0x00, 0x02, 0x64
            //putsf(" Sreq\r\n");

            //    queuePut(ACK);
            queuePut(0x00);
            queuePut(resolution);
            queuePut(sampleRate);


            break;

            //        case 0xEA: //set stream mode    

            //       case 0xEB: //read data (for Remote Mode)

            //        case 0xEC: //reset wrap mode               

            //        case 0xEE: //set wrap mode

            //        case 0xF0: //set remote mode

        case SET_SAMPLE_RATE: //F3: set sample rate
            //putsf(" set srate\r\n");
            settingSampleRateFlag = 1;
            break;

        case DIS_DATA_REPORT: //disable data reporting
            //putsf(" rep-\r\n");
            transmitMode = 0;
            break;

        case SET_DEFAULT: //set defaults       
            //putsf(" dflts\r\n");
            transmitMode = 0;
            sampleRate = 100;
            resolution = 4;
            break;


        default: //all other commands
            break;
    }
}

/*//Display the current axis.
void displayInput(unsigned char from, unsigned char inFromHost)
{               
//1 = host and 0 = device
if(from && lcdPosCount0 == 0 || from && lcdPosCount0 > 14)
{
lcdPosCount0 = 2;

//lcd_clear();
lcd_gotoxy(0,0);            
lcd_putsf("H:");

lcd_gotoxy(2,0);
sprintf(ledbuf, "%2x", inFromHost);
lcd_puts(ledbuf);

}
else if(from)
{

lcd_gotoxy(lcdPosCount0,0);                
sprintf(ledbuf, "%2x", inFromHost);
lcd_puts(ledbuf);
lcdPosCount0 = lcdPosCount0 + 2;
}
////DEVICE print out
if(!from && lcdPosCount1 == 0 || !from && lcdPosCount1 > 14)
{
lcdPosCount1 = 2;

//lcd_clear();
lcd_gotoxy(0,1);            
lcd_putsf("D:");

lcd_gotoxy(2,1);
sprintf(ledbuf, "%2x", inFromHost);
lcd_puts(ledbuf);

}
else if(!from)
{

lcd_gotoxy(lcdPosCount1,1);                
sprintf(ledbuf, "%2x", inFromHost);
lcd_puts(ledbuf);
lcdPosCount1 = lcdPosCount1 + 2;
}
} 


void displayAxis(unsigned char xAxis, unsigned char yAxis, unsigned char zAxis)
{       
lcd_clear();
lcd_gotoxy(0,0);            
lcd_putsf("x=");

lcd_gotoxy(2,0);
sprintf(ledbuf, "%d", xAxis);
lcd_puts(ledbuf);


lcd_gotoxy(7,0);            
lcd_putsf("y=");

lcd_gotoxy(10,0);
sprintf(ledbuf, "%d", yAxis);
lcd_puts(ledbuf);

lcd_gotoxy(0,1);            
lcd_putsf("z=");

lcd_gotoxy(2,1);
sprintf(ledbuf, "%d", zAxis);
lcd_puts(ledbuf);

} 
*/

//***********************************************************************
//Compute the odd parity of a character.  If the character has even numbers
//of 1, the parity bit will be 1, otherwise it will be 0.
unsigned char parity(unsigned char x)//calculate the parity of a character
{
    unsigned char temp, i; 
    temp=1;
    for(i=0;i<8;i++)
    {
        temp=temp^(x&1);
        x>>=1;
    }
    return temp;
}


//***********************************************************************
void clockStateNow(void)
{
    switch (myState)
    {  
    case BUSY:
        //if the clock being hold low when the clock should've been high.
        //Transition to inhibit state
        if ((clockState == HIGH || clockState == RISING) && CLK_IN == 0)
        {         
            inhibited = TRUE;
            myState = INHIBIT; 
            clockState = LOW;
            transmitting = 0;
            receiving = 0;   
            count2 = 0;
        }
        else if ( !(transmitting || receiving) ) //if not doing anything, then we go back to idle
            myState = IDLE;
        else myState = BUSY;
        break;
    
    case IDLE:
        //if the clock being hold low when the clock should've been high.
        //Transition to inhibit state
        if ((clockState == HIGH || clockState == RISING) && CLK_IN == 0)
        {   
            inhibited = TRUE;
            myState = INHIBIT;
            clockState = LOW;
            transmitting = 0;
            receiving = 0;  
            count2 = 0;
        }   
        //if there is data in the queue waiting to be sent
        //transition to BUSY state
        else if(queueEmpty == FALSE) 
        {
            myState = BUSY;
            transmitting=1;
            nextClockState=RISING;
            position = 0;
        }
        else 
        {
            myState = IDLE;
        }
        break;

    case INHIBIT:    
        //if the computer inhibit before we finishing sending the byte
        //we need to resend the byte
        if(position != 0 && position < 10)
        {
            resendFlag = TRUE;
        }
        position = 0;

        //right after the clock get out of inhibit state
        //we need to check if the computer have hold down the data.
        //If the computer pulls low the data line, then the mouse goes into
        //REQUEST state.
        if (outInhibit == TRUE)
        {
            if(DATA_IN == 0)
            {
                //The computer is request to send some data to the mouse
                myState = REQUEST;
                clockState = RISING;
            }
            else
            {
                //If the data line wasn't low.
                //then the computer must have put the mouse into inhibit
                //for processing data.
                myState = IDLE;
                clockState = RISING;                
            } 
            outInhibit = FALSE;
        }
        //if the clock high, meaning the computer has released clock line
        if(CLK_IN == 1)
        {
            outInhibit = TRUE;
        }
        else
        {      
            inhibited = TRUE;
            myState = INHIBIT;
            transmitting = 0;
            clockState = LOW;
            receiving = 0; 
            outInhibit = FALSE;
        }
        break;

    case REQUEST:  
        position = 0;
        receiving = 1; 
        nextClockState = FALLING;

        //if the clock being hold low when the clock should've been high.
        //Transition to inhibit state
        if ((clockState == HIGH || clockState == RISING) && CLK_IN == 0)           
        {       
            inhibited = TRUE;
            myState = INHIBIT;
            clockState = LOW;
            transmitting = 0;
            receiving = 0; 
            count2 = 0;
        }
        else if ( transmitting || receiving)
            myState = BUSY;
        else 
            myState = REQUEST;
        break;  

    }//end switch

}




//interrupt for clock generation, byte receive and transmition
//This interrupted get triggered every 20us
interrupt [TIM1_COMPA] void clockGenNrecvNtrans(void)
{

    lcddelay++;  

    if (queueEmpty == TRUE)
        count++;    

    count2++;

    if(debounceTimer <= 480)
        debounceTimer++;
    else
        debounceBtn();

    if (count >= 500)
    {
        packetGenFlag = TRUE;
        count = 0;
    }        

    lastClkIn = ClkIn;
    ClkIn = CLK_IN; 
    dataIn = DATA_IN;     

    //transmitting flag is set.
    if(transmitting)              
    {                             
        clockState = nextClockState;
        if(clockState==HIGH)//write data out here
        {            
            if(position==0)//start bit
            {
                DATA_OUT=0;
            }
            else if(position<9)//data bits
            {
                DATA_OUT=PS2byte&0x01;
                PS2byte = PS2byte>>1;
            }
            else if(position==9)//parity bit
            {
                DATA_OUT=PS2parity;
            }        
            else if (position == 10)
            {
                DATA_OUT =1;
            }
            //increment the position after trasmitting one bit
            position++;  

            nextClockState = FALLING;

        }//end clockState == HIGH
        else if(clockState==FALLING)
        {
            CLK_OUT=0;
            nextClockState = LOW;
        }
        else if(clockState==LOW)
        {
            nextClockState=RISING;
        }
        else //clockstate == rising
        {         
            if (position == 0)//read input from queue
            { 
                //check if there is a packet needed to be sent in the queue
                if (queueEmpty == FALSE)
                {
                    //get the packet from the queue
                    PS2byte = queueGet();
                    PS2parity = parity(PS2byte);
                    if(queueEmpty == TRUE)
                        transmitFlag = TRUE;
                    //delay 150us before transmitting.
                    delay_us(150);

                } 
            } 

            if (position == 11) //stop the transmittion
            {

                transmitting=0;
                position = 0;  
            }      
            CLK_OUT=1;
            nextClockState=HIGH;
        }

    }//end if transmitting


    //RECEIVING from the computer host
    if(receiving)
    {              
        clockState = nextClockState;    
        if(clockState==HIGH)//write data out here
        {                    

            if(position==11)
            {            
                DATA_OUT=1; //ACK bit 
                
                //display is used for debugging.
                //displayInput(1, rcvByte);  

                receiving = 0; 
                position = 0;

                //process the received byte from the computer and provide proper feedback.
                processCommand(rcvByte);

                rcvByte = 0;                  
                rcvBit = 0;
            }

            if (position == 10)
            {
                DATA_OUT = 0;
            }                

            nextClockState = FALLING; 

        }
        else if (clockState ==FALLING)
        {           
            CLK_OUT = 0;
            nextClockState = LOW;    
        }
        else if (clockState ==LOW)
        {
            nextClockState = RISING;    
        }
        else //clockState == RISING
        {     

            CLK_OUT = 1; 
            if (position >=0 && position < 8)
            {
                //fatch a bit from the computer
                rcvBit = DATA_IN;
                //if pos < 11 then receive bits from host
                rcvByte = ((rcvBit & 0x01) << position) | rcvByte;
            }    

            //if we are still in recveiving mode then increament the position counter.
            if (position < 11)
            {          
                nextClockState = HIGH;                         
                position++;    
            } 
        }
    }

    //transition to the correct state after finishing receiving or trasmitting.
    clockStateNow();
}


//The entry point of the software.
void main(void)
{    
    unsigned char lastSpos, currSpos;
    unsigned char upS, downS, lastUpS, lastDownS;
    char scroll;              


    //initialize the trasmission part of the code
    initChip();

    //initialize the part of code handles the mouse movement
    initialize();   

    //tell the computer that the mouse is ready and alive.
    queuePut(0xAA);
    queuePut(0x00);


    while(1)
    {              

        //BLINK corresponding status LED.
        if(myState==IDLE)
        {        
            //PORTB = ~0x20;
        }
        else if ( myState == INHIBIT)
        {          
            //PORTB = ~0x80;
            CLK_OUT = 1;
            DATA_OUT = 1;
        }    
        else if (myState == REQUEST)
        {             
            //PORTB = ~0x40;
        }   
        else if (myState == BUSY)
        {    

            /*    if (transmitting == 1)
            PORTB = ~0x10;
            if (receiving == 1)
            PORTB = ~0x08;
            */          
        }
        else //error state
        {                
            //PORTB = ~0x01;
        }  

        //timer1 sets this flag every 500*20us
        if(packetGenFlag == TRUE)
        {                        
            scrollCount++;

            if(scrollCount >0) 
            {
                //scroll state machine, detect if there is a scroll.
                detectScroll();
                scrollCount =0;
            }

            //if the scroll state never changes back to POSS_SCROLL state
            //we need to reset its state to POSS_SCROLL after 20*20us
            if(scrollState != POSS_SCROLL)
            {
                falseStateCount ++;
            }
            else if(scrollState == POSS_SCROLL ||scrollState == BEGIN_SCROLL )
                falseStateCount = 0;

            if(falseStateCount > 20)
            {
                falseStateCount = 0;
                scrollState = POSS_SCROLL;
            }
                
            if(scrollState == REAL_UP_SCROLL || scrollState == REAL_DOWN_SCROLL)
            {                              
                realScrollFlag = TRUE;
                //there should be a pause between scroll
                //we pause 20*20us
                scrollCount = -20;
            }

            //get the position data from accelerometers
            dataFromMouse();

            //convert position into relative position movement
            getMovement();

            //if any of the button is pushed then we need to generate a packet to represent it
            if(leftBtn ==1 || middleBtn ==1 || rightBtn ==1)
                realBtnFlag = TRUE;
            //if any of the button has changed to unpushed state, we also need to send a packet to indicate the change
            if(lastLeftBtn != leftBtn || lastMiddleBtn != middleBtn || lastRightBtn != rightBtn  )
                realBtnFlag = TRUE;

            //detetect if there is a scroll            
            /*    scroll = movement[Z_AXIS];
            if(!(scroll > -10 && scroll < 10))
            realScrollFlag = TRUE;
            */    

            //keep the previous button state
            lastLeftBtn = leftBtn;
            lastMiddleBtn = middleBtn;
            lastRightBtn = rightBtn;                     

            if(transmitMode ==1 && (realMoveFlag==TRUE || realBtnFlag==TRUE || realScrollFlag==TRUE))
                packetGen();    

            packetGenFlag = FALSE;
            realBtnFlag = FALSE;
            realMoveFlag = FALSE;
            realScrollFlag = FALSE;   

        }
    }      
}

void initChip(void)
{         
    count = 0;

    TIMSK = 0x10 ;   //Set the compare A interrupt

    PS2byte = 0xFA; 
    rcvByte = 0x00;
    rcvBit = 0x00;   
    ClkIn = 1; 
    lastClkIn = 1;

    outInhibit = FALSE;
    forceDelay = FALSE;

    position = 0;
    clockState = HIGH;
    nextClockState = HIGH;
    myState = IDLE;

    //timer 1 setup: running at 12.5 kHz
    TCCR1A = 0;
    TCCR1B = 8 + 3;        //CTC mode, prescalar = 1024
    OCR1A = 4; 
    
    DDRD = 0b01000010;        //set data direction (see initial wiriing comments)
    PORTD = 0b01000010;        //initialize outputs to high

    DDRB = 0xFF;        //set LEDs to all output
    PORTB = ~0x00;        //set all LED's to off

    CLK_OUT = 1;
    DATA_OUT = 1;

}


//********************************************************** 
//Set up all variables that's not releated to clock generation
void initialize(void)
{       
    unsigned char i; 
    //make c as input
    DDRC = 0x00;
    
    //init the buttons
    debounceTimer =0;   
    but_state = STATE_READY;
    butinfo =0;
    leftBtn =0;
    rightBtn =0;
    middleBtn =0;
    lastLeftBtn = 0;
    lastRightBtn = 0;
    lastMiddleBtn = 0;    



    //init current position and previous position
    for(i=0;i<3; i++)
    {
        curPosition[i] = 0;
        lastPosition[i] = 0;
        movement[i] = 0;
    }

    //init calibration variables
    xCal = 0;
    yCal = 0;
    zCal = 0;

    scrollCount = 0;
    scrollIndex = 0;
    scrollState = POSS_SCROLL;

    packetGenFlag = FALSE;  
    queueEmpty = TRUE;
    queueFull = FALSE;
    queueIn = 0;
    queueOut =0;
    transmitMode = FALSE;    
    realMoveFlag = FALSE;
    realScrollFlag = FALSE;
    realBtnFlag = FALSE;

    //init the LCD 
    //used for debugging purposes only
    /*    lcd_init(LCDwidth); //initialize the display
    lcd_clear();               //clear the display
    lcdPosCount0 = 0;
    lcdPosCount1 = 0;
    */
    
    //read voltage on A.0
    ADMUX =  0b01100000; 
    //ADC on
    ADCSRA = 0b11000111; 
    //enable sleep mode
    MCUCR = 0b10000000;       

    //carnk up the ISRs
    #asm
        sei
    #endasm 
}
 

Attachments

  • ps2_stuff.zip
    24.8 KB · Views: 91
Last edited:

Here is my understanding of what this code is supposed to do. Rather than regular PS/2, which has 3-byte packets, this uC is emulating a Microsoft Intellimouse, with 4-byte packets. Packet is shown here:
**broken link removed**

A PS/2 byte has 11 bits:
0 - - - - - - - - 1
start,(8 bits of data, LSB first),stop

Rx/Tx: 4 states

(1) Idle: outputs float high (this is where I am, all the time)
*Mouse has something to send? Go to (2)

(2) Busy: -set flags (Rx or Tx)
-Host inhibit: pull clk low for >100 us
(clk period=80us, that is, 12.5 kHz)
* Mouse checks for this condition by seeing if clk. output is high but reading low. If true, go to (3)

(3) Inhibit: -Stop all xmit
-clk,data=1
-(If byte interrupted, resend later)
*Host request to send (pull data line low after inhibit; this tells the mouse to start the clock (high first)). Go to (4)

(4) Request: -will go back to Busy in 1 clock cycle
-Clock low: host send PS/2 byte (11 bits)
-High: mouse send ACK (0xFA) after stop bit
-Low: Host reads ACK

* State changes are caused by timer 1 interrupt.
 

Hi,

Nice to see improvement of your project.
I suggest you to analyze the PS/2 output of you circuit. (ie. signal pattern of Data and Clock lines). Then you can compare it with standard PS/2 mouse-protocol, which you have described above.

In that case you need digital-logic analyzer which can emulate PS/2 protocol. Several commercial Digital-logic analyzer available for the purpose; but you can built PC based analyzer very cheaply (but with high accuracy).

Following link show such a project for Digital logic analyzer (17 channels) with PS/2 protocol-analyzer; **broken link removed**

Thank you,
 

Thanks for your response.

The data and clock lines just stay on +5V all the time (idle state).

The project you posted is cool, but I think I'll just stick with a multimeter. First of all, the project uses a parallel cable, I looked at the adapters they recommend and they look like the kind that you actually have to open up the computer to install. Probably wouldn't fit in my laptop anyway. (They say USB/parallel adapters are not suitable.) Also, opening the zip file it looks like I'd need a C++ compiler, and would need to figure out how to deal with that, etc. In short, trying to make the logic analyzer would be like starting a whole new project.

Thanks for your suggestion, anyway. Definitely saving it.
 

Hi,

Once agian I think you should give some thought on the point number 2 of yours. Remember that PS2 devices are with clk high when you plug-in. The Host or PC should pull this clk to low so that hand shaking starts. Here in your case it is 80uS where as the host can send only if it is 100uS. please try to change this and see if you succeed.

Thankyou
 

Hey prmurthy,

You mentioned that you did a mouse project with PIC and USB. When you were doing that, how did you troubleshoot it?

Thanks
 
Last edited:

Hi,

I don't think I had any kind of problem. There are some compilers which has buit-in USB drivers. I was doing it on 18F series not ATMEL.

Thankyou
 

Regarding what you said about the 100uS thing, that's only if the host (that is, the computer) wants to inhibit communication. Do you think it might be stuck in inhibit instead of idle? (Both outputs would float high in that case too.) Why would the host want to inhibit communication? In other words, in what situations would the host inhibit?
 

Here is oscilloscope output.
(picture and two movies)

In the attachment (scope.zip):
IMG_6103.jpg is output of the data line
MVI_6104.avi is the clock line
MVI_6101.avi is output of one of the blinking LEDs on Port B (these were to test the ADC)

--------
Update to this: I'm now using a better probe, and in all these configurations all 4 output pins stay on +5V all the time. I could attach those pictures, but I think you can imagine how it looks. I previously described the clock line going down once in a while, however I'm not seeing that now and I have a better probe now. It's all just flat +5V. I think what I was seeing before was due to a lousy probe.
 

Attachments

  • scope.zip
    2.1 MB · Views: 95
Last edited:

I'm looking at the function for generating the packet. Right now I'm only considering pointer movement, not scroll or buttons, so those have been omitted from the code sample below. I'm trying to figure out what this code is saying to do. Can anyone please help me interpret it? Just to check it to see if it's doing what it's supposed to. To me, what's confusing is the >> and <<.


Code:
///Generate a standard 4 byte Microsoft IntelliMouse: left, middle, right, x, y, z packet.
void packetGen(void)
{       

    unsigned char btnPacket=0x08;
    //the first byte is at least 0x08, because the third bit is always 1

//scroll and button parts taken out
//for now only looking at movement

 else if(realMoveFlag == TRUE)
    {
        btnPacket = btnPacket | (((movement[Y_AXIS]>>7)&0x1)<<5) | (((movement[X_AXIS]>>7)&0x1)<<4) | ((middleBtn&0x1) <<2) | ((rightBtn&0x1)<<1) | (leftBtn&0x1);                             
        queuePut(btnPacket);
        queuePut(movement[X_AXIS] << resolution);   
        queuePut(movement[Y_AXIS] << resolution);   
        queuePut(0x00);
        //queuePut((movement[Z_AXIS]<2 && movement[Z_AXIS]>-2)?0:movement[Z_AXIS]&0x0f);

    }


X_AXIS, Y_AXIS, and Z_AXIS are the indices, which were declared as follows:
Code:
enum{X_AXIS=0, Y_AXIS, Z_AXIS};

If interested, here's the function that sets the realMoveFlag:

Code:
//Obtain x, y, z movements.
void getMovement(void)
{   
    unsigned char i=0;   
    //position conversion formula.
    // xxxxxxxOLD (int)(voltage - first voltage(when not moving) * 100)
    // ADC reading - init adc reading(calibrating data)

    curPosition[X_AXIS] = (signed char)(xADC - xCal);
    curPosition[Y_AXIS] = (signed char)(yADC - yCal);
    curPosition[Z_AXIS] = (signed char)(zADC - zCal);

    //only when the mouse has significent movement we generate a packet

    //get the difference between the previous position and current position
    for(i=0;i<3;i++)
    {
        movement[i] = curPosition[i];// - lastPosition[i] ;

        //only check x and y movements       
        if(i<2)
        {                             
            if(!(movement[i] < 3 || movement[i] > -3))
                realMoveFlag = TRUE;               
            /*
            if(movement[i] > 3 || movement[i] < -3)
            realMoveFlag = TRUE;
            */
        }

    }                   
}


Here's the packet format:
Microsoft "3-Button" IntelliMouse Z-Mode Report Format
A Microsoft IntelliMouse 3-Button Z-mode motion report consists of a 4-byte packet defined as shown in Table 2.
Table 2. PS/2 4-Byte Mouse Packets

Code:
       D7  D6   D5  D4  D3  D2  D1 D0
Byte 1 Yov Xov Y8   X8   1  MB  RB  LB
Byte 2 X7   X6   X5   X4 X3  X2  X1  X0
Byte 3 Y7   Y6   Y5   Y4 Y3  Y2  Y1  Y0
Byte 4 Zse Zse  Zse Zse  Zs  Z2  Z1  Z0

Symbol definition:
LB: State of the left button. Pressed = 1.
RB: State of the right button. Pressed = 1.
MB: State of the middle button. Pressed = 1.
Xov, Yov: Overflow. Indicates more than 9 bits of movement detected. X8-X0 or Y8-Y0 should be set to maximum magnitude in the appropriate direction.
X8-X0: 9-bit signed twos complement integer that presents the relative displacement of the device in the X direction since the last data transmission. A positive value indicates motion to the right; a negative value indicates motion to the left.
Y8-Y0: Same as X8-X0. A positive value indicates device motion upward; a negative value indicates motion downward.
Z2-Z0: Z-wheel motion. Twos complement with Z3, the sign bit (+7 and -8 counts maximum).
Zs: Z-wheel twos complement sign bit.
Zse: Z-wheel sign extension bits. Always the same as Z3. Used for compatibility with old drivers.
MS Extension: Many Microsoft mice use 8-bit internal counters and sign extend the values into X8 and Y8. Microsoft mice do not use the Yov or Xov bits, and they set them to 0.
 

Hello,

Try this one. This is written in MikroC for PIC you can use the code wherever you want you can insert this one in your code.
 

Attachments

  • PS2_Mouse.zip
    3 KB · Views: 88
  • Like
Reactions: o_0

    o_0

    Points: 2
    Helpful Answer Positive Rating
Thanks for posting this, I wonder if it would work on AVR? Probably not, I'll need to buy PIC and a programmer.
(When I try to compile this on Codevision, it says "too many errors, compilation process stopped." )

Did you use 18F4550 or 18F2550 ? For programmer, PICKit2? Also, what compiler/version did you use?
(I've never used PIC before)
Also, by any chance do you have a schematic?

This seems to have a PS/2 interface? I thought you said yours was USB. Do you know that this code works? (After all, my code is supposed to work, too.)

What frequency would the PIC be running at for this code?

Sorry I'm asking a lot, but I'm trying to determine if it is worth switching to this, or work with my existing code.
Actually I'd prefer to work with my existing code because the uC responds to tilt, shown by the blinking LED outputs that vary with tilt, so at least that's something rather than having to start all over again learning a whole new microcontroller and everything. Thanks anyway though.

I mean, the code I have now at least it's written for AVR (which is what I have) and at least it compiles ok and produces some response (not the full response, but at least something).


---------- Post added at 08:14 ---------- Previous post was at 08:13 ----------

Anyone know how to translate statements like
(((movement[Y_AXIS]>>7)&0x1)<<5)
into words?
If someone just does one of them please, that would help me figure out the rest.
 

Hi,

This written in C which is general launguage it does matter what processor you use. I posted this just to see how the PS2 works. Once you understand then you can as well use for any processor. Only you may have to change some parameters like the register names rest remains the same.

Thankyou.
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top