Charles.Forsyth20060303644188019 years agoHistory
497 lines · 9.4 KB · C
RawDownload
#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "../port/error.h" #define	DPRINT	if(0)print#define	THREEBUT	0	/* !=0, enable 3-button emulation (see below) */ /* * DynaPro touch panel and Maxim MAX192 A/D converter on * York Electronics Centre's BRD/98/024 (Version A) interface, * accessed via mpc8xx SPI interface (see spi.c). * * The highest level of the driver is derived from the ARM/UCB touch panel driver, * simplified because of the differences between the panels. */ /* * Values determined by interface board */enum {	/* MAX192 control words */	MeasureX =	(1<<7)|(0<<6)|(1<<3)|(1<<2)|3,	/* start, channel 0, unipolar, single-ended, external clock */	MeasureY =	(1<<7)|(1<<6)|(1<<3)|(1<<2)|3,	/* start, channel 1, unipolar, single-ended, external clock */ 	/* port B bits */	ADselect = IBIT(16),	/* chip select to MAX192, active low */ 	/* port C bits */	Xenable =	1<<2,	/* PC13: TOUCH_XEN, active low */	Yenable =	1<<3,	/* PC12: TOUCH_YEN, active low */	Touched = 1<<10,	/* PC5: contact detect, active low */ 	/* interrupt control via port C */	TouchIRQ=	2,	/* parallel i/o - PC5 */	TouchEnable=	1<<TouchIRQ,	/* mask for cimr */ 	/* other parameters */	Nconverge =	10,	/* maximum iterations for convergence */	MaxDelta =	2,	/* acceptable change in X/Y between iterations */}; /* * ADC interface via SPI (see MAX192 data sheet) *	select the ADC *	send 8-bit control word and two zero bytes to clock the conversion *	receive three data bytes *	deselect the ADC and return the result */static intgetcoord(int cw){	uchar tbuf[3], rbuf[3];	IMM *io;	int nr; 	tbuf[0] = cw;	tbuf[1] = 0;	tbuf[2] = 0;	io = ioplock();	io->pbdat &= ~ADselect;	iopunlock();	nr = spioutin(tbuf, sizeof(tbuf), rbuf);	io = ioplock();	io->pbdat |= ADselect;	iopunlock();	if(nr != 3)		return -1;	return ((rbuf[1]<<8)|rbuf[2])>>5;} /* * keep reading the a/d until the value stabilises */static intdejitter(int enable, int cw){	int i, diff, prev, v;	IMM *io; 	io = ioplock();	io->pcdat &= ~enable;	/* active low */	iopunlock(); 	i = 0;	v = getcoord(cw);	do{		prev = v;		v = getcoord(cw);		diff = v - prev;		if(diff < 0)			diff = -diff;	}while(diff >= MaxDelta && ++i <= Nconverge); 	io = ioplock();	io->pcdat |= enable;	iopunlock();	return v;} static voidadcreset(void){	IMM *io; 	/* select port pins */	io = ioplock();	io->pcdir &= ~(Xenable|Yenable);	/* ensure set to input before changing state */	io->pcpar &= ~(Xenable|Yenable);	io->pcdat |= Xenable|Yenable;	io->pcdir |= Xenable;	/* change enable bits to output one at a time to avoid both being low at once (could damage panel) */	io->pcdat |= Xenable;	/* ensure it's high after making it an output */	io->pcdir |= Yenable;	io->pcdat |= Yenable;	/* ensure it's high after making it an output */	io->pcso &= ~(Xenable|Yenable);	io->pbdat |= ADselect;	io->pbpar &= ~ADselect;	io->pbdir |= ADselect;	iopunlock();} /* * high-level touch panel interface */ /* to and from fixed point */#define	FX(n)	((n)<<16)#define	XF(v)		((v)>>16) typedef struct Touch Touch; struct Touch {	Lock;	Rendez	r;	int	m[2][3];	/* transformation matrix */	int	rate;	int	down;	int	raw_count;	int	valid_count;	int	wake_time;	int	sleep_time;}; static Touch touch = {	{0},	.r {0},	.m {{FX(1), 0, 0},{0, FX(1), 0}},	/* default is 1:1 */	.rate 20,	/* milliseconds */}; /* * panel-touched state and interrupt */ static inttouching(void){	eieio();	return (m->iomem->pcdat & Touched) == 0;} static intispendown(void*){	return touch.down || touching();} static voidtouchintr(Ureg*, void*){	if((m->iomem->pcdat & Touched) == 0){		m->iomem->cimr &= ~TouchEnable;	/* mask interrupts when reading pen */		touch.down = 1;		wakeup(&touch.r);	}} static voidtouchenable(void){	IMM *io; 	io = ioplock();	io->cimr |= TouchEnable;	iopunlock();} /* * touchctl commands: *	X a b c	- set X transformation *	Y d e f	- set Y transformation *	s<delay>		- set sample delay in millisec per sample *	r<delay>		- set read delay in microsec *	R<l2nr>			- set log2 of number of readings to average */ enum{	Qdir,	Qtouchctl,	Qtouchstat,	Qtouch,}; Dirtab touchdir[]={	".",	{Qdir, 0, QTDIR}, 0, 0555,	"touchctl",	{Qtouchctl, 0}, 	0,	0666,	"touchstat",	{Qtouchstat, 0}, 	0,	0444,	"touch",	{Qtouch, 0},	0,	0444,}; static intptmap(int *m, int x, int y){	return XF(m[0]*x + m[1]*y + m[2]);} /* * read a point from the touch panel; * returns true iff the point is valid, otherwise x, y aren't changed */static inttouchreadxy(int *fx, int *fy){	int rx, ry; 	if(touching()){		rx = dejitter(Xenable, MeasureX);		ry = dejitter(Yenable, MeasureY);		microdelay(40);		if(rx >=0 && ry >= 0){			if(0)				print("touch %d %d\n", rx, ry);			*fx = ptmap(touch.m[0], rx, ry);			*fy = ptmap(touch.m[1], rx, ry);			touch.raw_count++;			return 1;		}	}	return 0;} #define	timer_start()	0	/* could use TBL if necessary */#define	tmr2us(n)	0 static voidtouchproc(void*){	int b, i, x, y;	ulong t1, t2; 	t1 = timer_start();	b = 1;	for(;;) {		//setpri(PriHi);		do{			touch.down = 0;			touch.wake_time += (t2 = timer_start())-t1;			touchenable();			sleep(&touch.r, ispendown, nil);			touch.sleep_time += (t1 = timer_start())-t2;		}while(!touchreadxy(&x, &y)); 		/* 640x480-specific 3-button emulation hack: */		if(THREEBUT){			if(y > 481) { 				b = ((639-x) >> 7);				continue;			} else if(y < -2) {				b = (x >> 7)+3;				continue;			}		} 		DPRINT("#%d %d", x, y);		mousetrack(b, x, y, 0);		setpri(PriNormal);		while(touching()) {			for(i=0; i<3; i++)				if(touchreadxy(&x, &y)) {					DPRINT("*%d %d", x, y);					mousetrack(b, x, y, 0);					break;				}			touch.wake_time += (t2 = timer_start())-t1;			tsleep(&touch.r, return0, nil, touch.rate);			touch.sleep_time += (t1 = timer_start())-t2;		}		mousetrack(0, x, y, 0);		b = 1;	/* go back to just button one for next press */	}} static voidtouchreset(void){	IMM *io; 	spireset();	adcreset();	intrenable(VectorCPIC+TouchIRQ, touchintr, &touch, BUSUNKNOWN, "touch"); 	/* set i/o pin to interrupt when panel touched */	io = ioplock();	io->pcdat &= ~Touched;	io->pcpar &= ~Touched;	io->pcdir &= ~Touched;	io->pcso &= ~Touched;	io->pcint |= Touched;	/* high-to-low trigger */	io->cimr &= ~TouchEnable;	/* touchproc will enable when ready */	iopunlock();} static voidtouchinit(void){	static int done; 	if(!done){		done = 1;		kproc( "touchscreen", touchproc, nil, 0);	}} static Chan*touchattach(char* spec){	return devattach('T', spec);} static Walkqid*touchwalk(Chan* c, Chan *nc, char **name, int nname){	return devwalk(c, nc, name, nname, touchdir, nelem(touchdir), devgen);} static inttouchstat(Chan* c, uchar* dp, int n){	return devstat(c, dp, n, touchdir, nelem(touchdir), devgen);} static Chan*touchopen(Chan* c, int omode){	omode = openmode(omode);	switch((ulong)c->qid.path){	case Qtouchctl:	case Qtouchstat:		if(!iseve())			error(Eperm);		break;	}	return devopen(c, omode, touchdir, nelem(touchdir), devgen);} static void	 touchclose(Chan*){} static long	 touchread(Chan* c, void* buf, long n, vlong offset){	char *tmp;	int x, y; 	if(c->qid.type & QTDIR)		return devdirread(c, buf, n, touchdir, nelem(touchdir), devgen); 	tmp = malloc(READSTR);	if(waserror()){		free(tmp);		nexterror();	}	switch((ulong)c->qid.path){	case Qtouch:		if(!touchreadxy(&x, &y))			x = y = -1;		snprint(tmp, READSTR, "%d %d", x, y);		break;	case Qtouchctl:		snprint(tmp, READSTR, "s%d\nr%d\nR%d\nX %d %d %d\nY %d %d %d\n",			touch.rate, 0, 1,			touch.m[0][0], touch.m[0][1], touch.m[0][2],			touch.m[1][0], touch.m[1][1], touch.m[1][2]);		break;	case Qtouchstat:		snprint(tmp, READSTR, "%d %d\n%d %d\n",			touch.raw_count, touch.valid_count, tmr2us(touch.sleep_time), tmr2us(touch.wake_time));		touch.raw_count = 0;		touch.valid_count = 0;		touch.sleep_time = 0;		touch.wake_time = 0;		break;	default:		error(Ebadarg);		return 0;	}	n = readstr(offset, buf, n, tmp);	poperror();	free(tmp);	return n;} static voiddotouchwrite(Chan *c, char *buf){	char *field[8];	int nf, cmd, pn, m[3], n; 	nf = getfields(buf, field, nelem(field), 1, " \t\n");	if(nf <= 0)		return;	switch((ulong)c->qid.path){	case Qtouchctl:		cmd = *(field[0])++;		pn = *field[0] == 0;		switch(cmd) {		case 's':			n = strtol(field[pn], 0, 0);			if(n <= 0)				error(Ebadarg);			touch.rate = n;			break;		case 'r':			/* touch read delay */			break;		case 'X':		case 'Y':			if(nf < pn+2)				error(Ebadarg);			m[0] = strtol(field[pn], 0, 0);			m[1] = strtol(field[pn+1], 0, 0);			m[2] = strtol(field[pn+2], 0, 0);			memmove(touch.m[cmd=='Y'], m, sizeof(touch.m[0]));			break;		case 'c':		case 'C':		case 'v':		case 't':		case 'e':			/* not used */			/* break; */		default:			error(Ebadarg);		}		break;	default:		error(Ebadarg);		return;	}} static long	 touchwrite(Chan* c, void* vp, long n, vlong){	char buf[64];	char *cp, *a;	int n0 = n;	int bn; 	a = vp;	while(n) {		bn = (cp = memchr(a, '\n', n))!=nil ? cp-a+1 : n;		n -= bn;		bn = bn > sizeof(buf)-1 ? sizeof(buf)-1 : bn;		memmove(buf, a, bn);		buf[bn] = '\0';		a = cp;		dotouchwrite(c, buf);	}	return n0-n;} Dev touchdevtab = {	'T',	"touch", 	touchreset,	touchinit,	devshutdown,	touchattach,	touchwalk,	touchstat,	touchopen,	devcreate,	touchclose,	touchread,	devbread,	touchwrite,	devbwrite,	devremove,	devwstat,};