ps2.c
/*******************************************************************************
*PS2.C Robert Buels, rmb32@cornell.edu
*Polls the AT90S1200 chip and translates controller bits to ps/2 packets
*April, 2002
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
******************************************************************************/
#include <90s8515.h>
#include
//#include
#asm
.equ PORTA=0x1b
.equ PORTB=0x18
.equ PINA=0x19
.equ DDRA=0x1a
.def temp=r17
.def delay=r18
.def output=r19
.def input=r19
.def temp2=r20
.def parity=r21
.def ptemp=r22
.equ CLOCK=PORTA,1
.MACRO __SETDATA
cbi DDRA,0 ;DDRA.0 = 0
sbi PORTA,0 ;PORTA.0 = 1
.ENDM
.MACRO __CLEARDATA
sbi DDRA,0 ;DDRA.0 = 1
cbi PORTA,0 ;PORTA.0 = 0
.ENDM
.MACRO __SETCLOCK
cbi DDRA,1
sbi PORTA,1
.ENDM
.MACRO __CLEARCLOCK
sbi DDRA,1
cbi PORTA,1
.ENDM
#endasm
#pragma regalloc-
#define TIMER0RELOAD 62
#define DPAD_SPEED 3
#define ANALOG_THRESHOLD 9
unsigned char buttonState,lastbuttonState;
char deltaX, deltaY, deltaZ,extendedByte;
char lastdeltaX, lastdeltaY, lastdeltaZ,lastextendedByte;
char packet1,packet2,packet3,packet4,extended;
char transmitted;
unsigned char hostCommand,mouseResponse;
char error;
char transmitMode;
char settingSampleRateFlag;
char setResolutionFlag;
char sampleRate,lastSampleRate,lastlastSampleRate;
char resolution;
int timeTillSample;
unsigned char deviceID;
#pragma regalloc+
void receiveByte();
void sendData();
void sendByte();
void processCommand();
void readN64();
char notEqual(char,char);
void init() {
PORTA = 0x03;
DDRA = 0x00; //port A: PS2 in-out
DDRB = 0x03; //port B: control to 1200
PORTB = 0x00; //portb pullups not activated
DDRC = 0x00; //portc hi-z
PORTC = 0x00;
DDRD = 0x00; //port D: input from 1200
PORTD = 0x00; //make sure the pullup resistors aren't activated: hi-z
transmitted = 1;
mouseResponse = 0xFA;
setResolutionFlag = 0;
error = 0;
resolution = 4;
sampleRate = lastSampleRate = lastlastSampleRate = 100;
settingSampleRateFlag = 0;
deltaZ = deltaY = deltaX = 0;
packet1 = packet2 = packet3 = packet4 = extended = 0;
timeTillSample = 1000; //initial sample rate 100Hz
deviceID = 0x00; //standard PS/2 mouse
TCNT0 = TIMER0RELOAD;
TIMSK = 2; //turn on timer 0 overflow ISR
TCCR0 = 3; //prescalar to 64
//For 8515:
/* UCR = 0x18;
UBRR = 25; //using a 4 MHz crystal
putsf("reset\r\n"); */
transmitMode = 0; //initially, not in data streaming mode
delay_ms(400);
mouseResponse = 0xAA;
sendByte();
mouseResponse = 0x00;
sendByte();
#asm("sei")
}
//timer 0 overflow ISR
interrupt [TIM0_OVF] void timer0_overflow() {
//reload for 1 mSec overflow
TCNT0=TIMER0RELOAD;
//Decrement time if not already 0
if (timeTillSample>0) timeTillSample--;
}
void main() {
init();
readN64();
readN64();
while(1) {
/*
//readN64();
mouseResponse = ~PIND;
sendByte();
// sendData();
delay_us(800);
PORTB++;
*/
if(!PINA.0) {
receiveByte();
if(!error) processCommand();
else { //handle error
//printf("Error: %d\r\n",error);
switch (error) {
case 0x01: break; //aborted, do nothing
case 0x02: break; //data early release, do nothing
case 0x03: //parity error, ask for a resend
mouseResponse = 0xFE;
sendByte();
break;
case 0x04: //data not released, send an error
mouseResponse = 0xFC;
sendByte();
break;
}
}
}
if(!timeTillSample) readN64();
if(transmitMode && ((lastbuttonState != buttonState)
|| (deltaX)
|| (deltaY)
|| (deltaZ))) {
transmitted = 1;
sendData();
}
}
}
void processCommand() {
//printf("h:0x%x(%d)\r\n ",hostCommand,hostCommand);
delay_us(100);
//acknowledge
#asm
ldi output,0xFA
rcall __ByteOut
#endasm
if(setResolutionFlag) {
//putsf("\r\n");
setResolutionFlag = 0;
if(hostCommand >= 0 && hostCommand <= 3)
resolution = hostCommand;
if(hostCommand < 0 || hostCommand > 3) { //send an error
// putsf("\r\nBadrez\r\n");
#asm
ldi output,0xFC
rcall __ByteOut
#endasm
return;
} else {
resolution = hostCommand;
// printf("R=%d\r\n",resolution);
return;
}
return;
}
if(settingSampleRateFlag) {
settingSampleRateFlag = 0;
if(hostCommand >= 10 && hostCommand <= 200) {
lastlastSampleRate = lastSampleRate;
lastSampleRate = sampleRate;
sampleRate = hostCommand;
}
if(hostCommand >= 10 && hostCommand <= 200) {
// printf("sr=>%d\r\n",hostCommand,hostCommand);
lastlastSampleRate = lastSampleRate;
lastSampleRate = sampleRate;
sampleRate = hostCommand;
if((lastlastSampleRate == 200) && (lastSampleRate == 100) && (sampleRate ==
80)) {
// putsf("MS3\r\n");
deviceID = 0x03; // enter MS intellimouse mode
}
if(deviceID == 0x03 && lastlastSampleRate == 200 && lastSampleRate == 200 &&
sampleRate == 80) {
// putsf("MS5\r\n");
deviceID = 0x04; // enter MS 5-button mouse mode
}
} else { // bad sample rate, output an error
// putsf("\r\nBadrate\r\n");
#asm
ldi output,0xFC
rcall __ByteOut
#endasm
}
return;
}
switch (hostCommand) {
case 0xFF: //reset 0xAA, 0x00
//putsf(" Rset\r\n");
delay_ms(400);
#asm
ldi output,0xAA
rcall __ByteOut
lds output,_deviceID
rcall __ByteOut
#endasm
transmitMode = 0;
sampleRate = 100;
deviceID = 0x00;
break;
case 0xF4: //enable transmit
//putsf("rp +\r\n");
transmitMode = 1;
break;
// case 0xE6: //set scaling 1:1
// case 0xE7: //set scaling 2:
case 0xF2: //get device ID
//printf("ID=0x%x\r\n",deviceID);
delay_ms(1);
#asm
lds output,_deviceID
rcall __ByteOut
#endasm
break;
/* case 0xEB: //read data, for remote mode
if(remoteMode) sendData(); break;*/
case 0xE8: //set resolution
//putsf(" Setres\r\n");
setResolutionFlag = 1;
break;
case 0xE9: //status request respond 0xFA, 0x00, 0x02, 0x64
//putsf(" Sreq\r\n");
#asm
lds output, _transmitMode
swap output
lsr output
andi output,0x20 ; set the data reporting bit of the first status
byte
rcall __ByteOut
lds output, _resolution
rcall __ByteOut
lds output, _sampleRate
rcall __ByteOut
#endasm
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 0xF3: //set sample rate
//putsf(" set srate\r\n");
settingSampleRateFlag = 1;
break;
case 0xF5: //disable data reporting
//putsf(" rep-\r\n");
transmitMode = 0;
break;
case 0xF6: //set defaults
//putsf(" dflts\r\n");
transmitMode = 0;
sampleRate = 100;
resolution = 4;
break;
default: //all other commands
//putsf(" ?\r\n");
}
}
// uses analog stick to move the mouse
void readN64() {
char temp;
timeTillSample = 1000/sampleRate;
transmitted = 0;
PORTB = 0;
delay_us(200);
packet1 = PIND;
PORTB = 1;
delay_us(200); //we're not using these
packet2 = PIND;
// PORTB = 2;
// delay_us(200);
// packet3 = PIND;
PORTB = 3;
delay_us(200);
packet4 = PIND;
lastbuttonState = buttonState;
buttonState = 0x08;
lastdeltaY = deltaY;
deltaY = 0;
lastdeltaX = deltaX;
deltaX = 0;
if(packet2 > 127) { //it's negative
if(packet2 < (0xFF-ANALOG_THRESHOLD)) {
deltaX = ((packet2+ANALOG_THRESHOLD)>>2) | 0b11000000;
buttonState |= 0x10;
}
} else if(packet2 > ANALOG_THRESHOLD) { //it's positive
deltaX = (packet2-ANALOG_THRESHOLD)>>2;
}
if(packet4 > 127) { //it's negative
if(packet4 < (0xFF-ANALOG_THRESHOLD)) {
deltaY = ((packet4+ANALOG_THRESHOLD)>>2) | 0b11000000;
buttonState |= 0x20;
}
} else if(packet4 > ANALOG_THRESHOLD) { //it's positive
deltaY = (packet4-ANALOG_THRESHOLD)>>2;
}
//translate the stuff into PS2 packets
buttonState |= packet1 >> 5; //now is 00YX1ABZ
}
/*// WORKS: uses DPAD to move mouse
void readN64() {
timeTillSample = 1000/sampleRate;
transmitted = 0;
PORTB = 0;
delay_us(200);
packet1 = PIND;
lastbuttonState = buttonState;
buttonState = 0x08;
lastdeltaY = deltaY;
deltaY = 0;
lastdeltaX = deltaX;
deltaX = 0;
if(packet1 & 0x08) { //dpad up
deltaY = DPAD_SPEED;
}else if(packet1 & 0x04) { //dpad down
deltaY = -DPAD_SPEED;
buttonState |= 0x20;
}
if(packet1 & 0x02) { //dpad left
deltaX = -DPAD_SPEED;
buttonState |= 0x10;
} else if(packet1 & 0x01) { //dpad right
deltaX = DPAD_SPEED;
}
//translate the stuff into PS2 packets
buttonState |= packet1 >> 5; //now is 00YX1ABZ
} */
char notEqual(char new, char old) {
char s;
s = new - old;
if(s > -4 && s < 4) return 0;
return 1;
}
void sendByte() {
#asm
lds output,_mouseResponse
rcall __ByteOut
#endasm
}
void receiveByte() {
#asm
__SETDATA
__SETCLOCK ; make sure i am not holding down data or clock
ByteIn:
sbis PINA,1 ; wait for clock high
rjmp ByteIn
__DELAY_USB 100
sbic PINA,0 ; is data still low? if not, host aborted
rjmp DataEarlyRelease
__DELAY_USB 80
ldi temp2,8
ByteInLoop:
rcall __BitIn
sbis PINA,1 ; check inhibit
rjmp Aborted
dec temp2
brne ByteInLoop
;;read parity bit
mov input,temp
clr temp
rcall __BitIn
mov parity,temp
rol parity
rol parity
sbis PINA,1 ; check inhibit
rjmp Aborted
;;read stop bit
rcall __BitIn
sbis PINA,1 ; check inhibit
rjmp Aborted
__DELAY_USB 100
;;wait for the data line to come high
sbis PINA,0 ; if not high by now, generate an error
rjmp WaitForDataHigh
;;output the acknowledge bit
__DELAY_USB 20 ; delay about 15 us
__CLEARDATA
__DELAY_USB 9 ; delay about 5 ms
__CLEARCLOCK
__DELAY_USB 54 ; 40 ms
__SETCLOCK
__DELAY_USB 9
__SETDATA
mov temp,input
mov temp2,input
swap temp2
eor temp,temp2
mov temp2,temp
lsr temp
lsr temp
eor temp,temp2
mov temp2,temp
lsr temp
eor temp2,temp
com temp2 ;now lsb of temp2 is odd parity
;; now lsb of temp2 is the parity of what the host sent
andi temp2,0x01
cp temp2,parity
brne ParityWrong
__DELAY_USB 55 ;delay about 45 us
rjmp NormalExit
WaitForDataHigh:
ldi delay,0x04 ; remember to send an error code in the main loop
rcall __BitIn
sbrs temp,0
rjmp WaitForDataHigh
rjmp Aborted
ParityWrong:
ldi delay,0x03 ; remember to ask for a resend in main loop
rjmp ByteInEnd
DataEarlyRelease:
ldi delay,0x02
rjmp ByteInEnd
Aborted:
ldi delay,0x01
rjmp ByteInEnd
__BitIn:
__DELAY_USB 27
__CLEARCLOCK
__DELAY_USB 54 ; delay 40 us
__SETCLOCK
__DELAY_USB 27 ; 20 us
lsr temp ;read a bit from the port
sbic PINA,0
ori temp,0x80
ret
NormalExit:
ldi delay,0x00
;; out PORTB,delay
ByteInEnd:
sts _error,delay
sts _hostCommand,input
#endasm
}
void sendData() {
#asm
lds output,_buttonState
rcall __ByteOut
lds output,_deltaX
rcall __ByteOut
lds output,_deltaY
rcall __ByteOut
#endasm
if(deviceID == 0x03) {
#asm
lds output,_deltaZ
rcall __ByteOut
#endasm
} else if(deviceID == 0x04) {
#asm
lds output,_extendedByte
rcall __ByteOut
#endasm
}
#asm
__SETDATA ;let the data and clock lines float
__SETCLOCK
#endasm
}
//ByteOut: an assembly procedure for outputting the byte in register output to
the PS2 port
#asm
.MACRO __CHECKINHIBIT
sbis PINA,1
rjmp ByteOutDone
.ENDM
__ByteOut:
clr temp
sts _error,temp
Inhibited:
sbis PINA,1 ; check if clock is being held low, wait here
till its high
rjmp Inhibited
__DELAY_USB 60 ; delay about 50 us, taking parity comp into
account
;; while we are waiting, why not just calculate parity?
mov temp,output
mov parity,output
swap parity
eor temp,parity
mov parity,temp
lsr temp
lsr temp
eor temp,parity
mov parity,temp
lsr temp
eor parity,temp ; now lsb of parity is even parity
com parity ; invert it to get odd parity
sbis PINA,1 ; check again for inhibited
rjmp Inhibited
;; by now, we can definitely send
sbis PINA,0 ; if data is low, host wants to send. Abort
rjmp ByteOutAborted
__DELAY_USB 27 ; delay 20 us
ldi temp,0
rcall __OutBit ; output a 0 (the start bit)
__CHECKINHIBIT
mov temp,output
rcall __OutBit
ldi temp2,7
ByteOutLoop:
asr temp
rcall __OutBit
__CHECKINHIBIT
dec temp2
brne ByteOutLoop
__DELAY_USB 3
mov temp,parity
rcall __OutBit ; output the parity bit
__DELAY_USB 8
ser temp
rcall __OutBit ; output the stop bit (1)
__DELAY_USB 33
ldi delay,0x00
sts _error,delay
rjmp ByteOutDone
ByteOutAborted:
ldi delay,0x01
sts _error,delay
ByteOutDone:
__SETCLOCK
__SETDATA
ret
;;outputs a single bit to the ps2 line
__OutBit: ; function to output the bit in temp to the ps2
data line
sbrs temp,0
rjmp OutBitClear
rjmp OutBitSet
OutBitClear:
__CLEARDATA
rjmp OutBit2
OutBitSet:
__SETDATA
OutBit2:
__DELAY_USB 30 ; delay a little less than 20 us
__CLEARCLOCK
__DELAY_USB 54 ; 40 us
__SETCLOCK
__DELAY_USB 25
ret
#endasm
|