home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Virtual Reality Zone
/
VRZONE.ISO
/
mac
/
PC
/
PCGLOVE
/
GLOVE
/
GLOVE.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-11-07
|
14KB
|
491 lines
/**********************************************************************
Originally "power.c" (c) manfredo 9/91 (manfredo@opal.cs.tu-berlin.de)
Developed on an ATARI 1040ST with TC 1.1 using a logic analyzer to get
the correct timings.
**********************************************************************/
/*********************************************************************
ported to PC compatibles by
Greg Alt 10/91
galt@peruvian.utah.edu
or galt@es.dsd.com
**********************************************************************/
/*********************************************************************
Substantially rewritten by Dave Stampe (c) 1991: PWRFILT.C
No cash, no warranty, no flames.
This stuff works great, so gimme credit.
Goals <achieved> were:
Higher speed, smaller code.
Polled operation is now possible.
Graphics test (VGA)
Noise reduction added, gets rid of 99.5% of noise with NO DELAY!
This runs on a 486/25 with an i/o card.
Someone should adapt it for the usual printer port adapter.
It was compiled with Turbo C++ 2.0 but will probably
work on any Turbo C directly. MSC will need library calls checked.
dstamp@watserv1.uwaterloo.ca 17/10/91
**********************************************************************/
/*********************************************************************
Re-converted to use printer port by Dave Stampe and Bernie Roehl.
October 18, 1991.
I also split off Dave's graphics code into a separate file, and put some
of the stuff that was shared between the two into glove.h
I also added a little judicious whitespace here and there to enhance
readability, and made a whole bunch of globals static.
I also added init_glove(mode) and glove_read(glov), the latter of which
calls getglove(glov), deglitch(glov) and dehyst(glov).
--Bernie, October 18-19 1991
********************************************************************/
/* More changes:
init_glove() now auto-calibrates. A new function (available outside
of this module) called "udelay" delays for a certain number of micro-
seconds. Calls to fdelay have been replaced by udelay(),
and the D2BITS and D2BYTES values are now in microseconds.
init_glove() now takes an additional parameter, a pointer to a
function (currently unused).
--Bernie Roehl, October 21 1991
*/
/* Interrupt-driven operation is now implemented! Simply specify IHIRES
(for interrupt-driven high-resolution mode).
Also, based on suggestions by Dave Stampe, I've:
- put the calibration stuff into a separate routine
- gone back to using counts instead of microseconds in most
places (for performance reasons; it saves a MULT instruction);
- udelay() is now gone, and a uconvert() macro does the conversion
I have also:
- renamed the functions so they all begin with "glove_"; they
are now named glove_init(), glove_read() and glove_quit()
Many thanks to Dave for all his help, especially in finding some
annonying timing problems.
-- Bernie Roehl, November 8 1991
*/
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <bios.h>
#include <signal.h>
#include "glove.h"
#define PC_PRINTER 1 /* use the PC Printer port for i/o */
#define D2BITS 4 /* microseconds */
#define D2BYTES 96 /* microseconds */
#define SAMPLE_TIME 20 /* milliseconds */
#ifdef PC_PRINTER
#define INPORT 0x379 /* i/o port addresses */
#define OUTPORT 0x378
/* bits from parallel port */
#define GDATA 0x10 /* PG data in */
#define GLATCH 0x02 /* PG latch out */
#define GCLOCK 0x01 /* PG clock out */
#define GCLOLAT 0x03 /* clock + latch */
#define SHIFTVAL 4 /* shift data right 4 bits */
#endif
#ifdef DSTAMPE /* stuff from here down to #else is i/o card-specific */
#define INPORT 0x2A0 /* i/o port addresses */
#define OUTPORT 0x2A0
/* bits for i/o ports */
#define GDATA 0x01 /* PG data in */
#define GLATCH 0x02 /* PG latch out */
#define GCLOCK 0x01 /* PG clock out */
#define GCLOLAT 0x03 /* clock + latch */
#define SHIFTVAL 0 /* don't shift */
#endif
static unsigned long bitdelay, bytedelay, longdelay;
static void fdelay(unsigned long val)
{
while (val--);
}
static unsigned long microfactor = 0L; /* usec/iteration times 91 */
#define uconvert(microseconds) (((microseconds) * microfactor)/91)
void glove_delay()
{
fdelay(longdelay);
}
/* defines for output line pair control */
#define C0L0() outportb(OUTPORT, 0) /* clock 0 latch 0 */
#define C0L1() outportb(OUTPORT, GLATCH) /* clock 0 latch 1 */
#define C1L0() outportb(OUTPORT, GCLOCK) /* clock 1 latch 0 */
#define C1L1() outportb(OUTPORT, GCLOLAT) /* clock 1 latch 1 */
static unsigned char getbyte() /* read a byte from glove <rolled code> */
{
register int i;
register unsigned char x = 0;
C1L0(); /* generate a reset (latch) pulse */
C1L1();
fdelay(bitdelay); /* hold for 3 us */
C1L0();
fdelay(bitdelay); /* hold for 3 us */
for(i = 0; i < 8; i++)
{
x = (x << 1) + ((inportb(INPORT) & GDATA) >> SHIFTVAL);
C0L0();
fdelay(bitdelay);
C1L0(); /* pulse */
fdelay(bitdelay);
}
return x; /* return the byte */
}
void getglove(glove_data *buf) /* read 6 byte data packet */
{
register unsigned char *bp = (char *) buf;
register int i;
for (i = 0; i < 6; ++i) {
*bp++ = getbyte(); /* read data */
fdelay(bytedelay);
}
/* throwaways (speeds up polling later) */
getbyte();
fdelay(bytedelay);
getbyte();
}
/* HIRES ENTRY CODES
byte:
1- any value between $05 and $31
2- only $C1 and $81 work OK
3- no effect
4- no effect
5- no effect
6- only $FF works
7- seems to affect read rate slightly, 1 fastest
*/
static int hires_code[7] = { 0x06, 0xC1, 0x08, 0x00, 0x02, 0xFF, 0x01 };
void Hires() /* enter HIRES mode <rolled code- speed unimportant> */
{
int i,j,k;
/* dummy read 4 bits from glove: */
C1L0(); C1L1(); /* generate a reset (latch) pulse */
fdelay(bitdelay); /* delay for 6 us */
fdelay(bitdelay);
C1L0();
fdelay(bitdelay); /* delay for 6 us */
fdelay(bitdelay);
C0L0(); C1L0(); /* pulse clock */
fdelay(bitdelay);
C0L0(); C1L0(); /* pulse clock */
fdelay(bitdelay);
C0L0(); C1L0(); /* pulse clock */
fdelay(bitdelay);
C0L0(); C1L0(); /* pulse clock */
/* handshake for command code? */
C1L0();
fdelay(uconvert(7212)); /* 7212 us delay */
C1L1();
fdelay(uconvert(2260)); /* 2260 us delay */
for (i = 0; i < 7; i++) /* send 7 bytes */
{
k = hires_code[i];
for (j = 0; j < 8; j++) /* 8 bits per byte, MSB first */
{
if (k & 0x80)
{
C1L1();
C0L1();
C1L1();
}
else
{
C1L0();
C0L0();
C1L0();
}
k <<= 1;
fdelay(bitdelay);
}
fdelay(bytedelay);
}
fdelay(uconvert(892)); /* 892 us delay (end of 7. byte) */
C1L0(); /* drop the reset line */
fdelay(uconvert(40000)); /* some time for the glove controller to relax */
}
#define XHYST 2 /* hysterisis for X, Y low noise reduction */
#define YHYST 2 /* 2 eliminates +/-3 quanta of noise */
static int ox = -1000; /* last x,y for hysterisis */
static int oy = -1000;
static void dehyst(glove_data *g) /* hysterisis deglitch (low noise removal) */
{
int x = g->x;
int y = g->y;
if(g->keys==0) ox = oy = 0; /* handle recentering ("0"key or "Center") */
if(x-ox>XHYST) ox = x-XHYST; /* X hysterisis */
if(ox-x>XHYST) ox = x+XHYST;
if(y-oy>YHYST) oy = y-YHYST; /* Y hysterisis */
if(oy-y>YHYST) oy = y+YHYST;
g->x = ox; /* replace present X,Y data */
g->y = oy;
}
#define XACC 8 /* X, Y maximum accel/decel level. Should */
#define YACC 8 /* be 6-10, but too high limits gesturing */
#define XXTEND 1 /* stretches deglitching time */
#define YXTEND 1
static int x1 = 0; /* delayed 1 sample (for smoothed velocity test) */
static int y1 = 0;
static int x2 = 0; /* delayed 2 samples */
static int y2 = 0;
static int lx = 0; /* last good X,Y speed */
static int ly = 0;
static int lax = 0; /* bad data "stretch" counter */
static int lay = 0;
static int lsx = 0; /* X,Y "hold" values to replace bad data */
static int lsy = 0;
static int lcx = 0; /* last X,Y speed for accel. calc. */
static int lcy = 0;
static void deglitch(glove_data *g)
{
int vx, vy;
int x = g->x;
int y = g->y;
if(g->keys == 0) /* reset on recentering ("0" or "Center" key) */
{
x1 = x2 = y1 = y2 = 0;
lx = ly = lax = lay = 0;
lsx = lsy = lcx = lcy = 0;
}
vx = x-((x1+x2)>>1); /* smoothed velocity */
vy = y-((y1+y2)>>1);
x2 = x1; /* update last values */
x1 = g->x;
y2 = y1;
y1 = g->y;
if (abs(lcx-vx) > XACC) lax = XXTEND; /* check for extreme acceleration */
if (lax == 0) lx = vx; /* save only good velocity */
lcx = vx; /* save velocity for next accel. */
if (abs(lcy-vy) > YACC) lay = YXTEND; /* same deal for Y accel. */
if (lay == 0) ly = vy;
lcy = vy;
if (lax != 0) /* hold X pos'n if glitch */
{
g->x = lsx;
lax--;
}
if (lay != 0) /* hold Y pos'n if glitch */
{
g->y = lsy;
lay--;
}
lsx = g->x; /* save position for X,Y hold */
lsy = g->y;
/* g->y = x;*/
}
static void calibrate()
{
unsigned long n;
/* calibrate timing loop; note that instead of dividing by 18.2,
we multiply by 5 now and divide by 91 later */
n = biostime(0, 0L);
fdelay(1000000); /* divide by a milllion to get microfactor */
microfactor = (biostime(0, 0L) - n) * 5;
bitdelay = uconvert(D2BITS);
bytedelay = uconvert(D2BYTES);
longdelay = uconvert(4000);
}
static int glovemode = HIRES; /* operating mode of glove */
static void (*upcall)() = NULL; /* user's function to call up to */
static unsigned char uses_ints = 0; /* non-zero if we're interrupt-driven */
static glove_data glove_int_data; /* our copy of the most recent data */
/* Following is the number of our ticks per real tick, rounded up */
#define DIVISOR ((55+SAMPLE_TIME/2)/SAMPLE_TIME)
#define UNUSED_VECT 128 /* unused interrupt vector (we hope!) */
static void interrupt (*oldint8)() = NULL; /* previous interrupt handler */
static void interrupt (*oldunused)() = NULL; /* previous unused vector */
static unsigned n_ints = 0; /* number of interrupts since oldint8 called */
static unsigned unready = 0; /* number of times glove has been not ready */
static char samples[7500]; /* samples of X values */
static char dsamples[7500]; /* samples of X values after deglitching */
static int nsamples = 0;
static void interrupt int8()
{
disable();
if (getbyte() != 0xA0) {
if (++unready > 500) { /* glove not responding... reset it */
unready = 0;
if (glovemode == IHIRES)
Hires();
}
}
else { /* data ready! */
fdelay(bytedelay);
unready = 0;
getglove(&glove_int_data);
deglitch(&glove_int_data); /* remove spikes and jumps */
dehyst(&glove_int_data); /* add hysteresis to remove LL noise */
++glove_int_data.nmissed; /* flag data as new */
if (upcall) (*upcall)(); /* upcall to application */
}
if (++n_ints >= DIVISOR) { /* call previous int 8 handler periodically */
n_ints = 0;
geninterrupt(UNUSED_VECT);
}
else
outportb(0x20, 0x20); /* signal EOI */
}
#define TIMER_CONTROL 0x43 /* timer control register */
#define TIMER_0 0x40 /* timer zero data register */
#define TIMER_MODE 0x36 /* byte to write to control register */
int glove_init(int mode, void (*function)())
{
calibrate();
if (mode == LORES) mode = HIRES;
if (mode == ILORES) mode = IHIRES;
if (mode == HIRES || mode == IHIRES) Hires();
glove_int_data.nmissed = 0; /* go this one, start counter again! */
if (mode == ILORES || mode == IHIRES) { /* an interrupt mode */
void glove_quit();
unsigned rate;
uses_ints = 1;
oldint8 = getvect(8);
oldunused = getvect(UNUSED_VECT);
setvect(UNUSED_VECT, oldint8);
setvect(8, int8);
rate = 65535/DIVISOR;
/* reprogram timer */
disable();
outportb(TIMER_CONTROL, TIMER_MODE); /* timer control port */
outportb(TIMER_0, rate & 0xFF); /* low byte to timer zero */
outportb(TIMER_0, (rate >> 8) & 0xFF); /* high byte to timer zero */
/* make sure we clean up at exit or on errors */
atexit(glove_quit);
signal(SIGABRT, glove_quit);
signal(SIGFPE, glove_quit);
signal(SIGINT, glove_quit);
enable();
}
else
uses_ints = 0;
glovemode = mode;
upcall = function;
return glovemode; /* returns mode actually set */
}
int glove_read(glove_data *glov)
{
if (uses_ints) { /* interrupt-driven */
disable();
*glov = glove_int_data;
glove_int_data.nmissed = 0;
enable();
return glov->nmissed;
}
/* polled operation */
getglove(glov); /* retrieve the data */
deglitch(glov); /* remove spikes and jumps */
dehyst(glov); /* add hysteresis to remove LL noise */
glove_int_data = *glov;
return 1; /* always new data */
}
int glove_ready() /* returns 1 if glove ready, 0 otherwise */
{
if (uses_ints) /* interrupt handler makes sure there's always data */
return 1;
if (getbyte() == 0xA0) { /* ready */
unready = 0;
if (upcall) (*upcall)();
return 1;
}
/* not ready */
if (++unready > 500) { /* glove not responding... reset it */
unready = 0;
if (glovemode == HIRES || glovemode == IHIRES)
Hires();
}
return 0;
}
void glove_quit()
{
if (uses_ints) {
disable();
/* reprogram timer */
outportb(TIMER_CONTROL, TIMER_MODE);
outportb(TIMER_0, 0); /* low byte */
outportb(TIMER_0, 0); /* high byte */
setvect(8, oldint8);
setvect(UNUSED_VECT, oldunused);
signal(SIGABRT, SIG_DFL);
signal(SIGFPE, SIG_DFL);
signal(SIGINT, SIG_DFL);
enable();
}
upcall = NULL;
}