From: Philippe Ney (philippe.ney@smartdata.ch)
Date: Wed Dec 13 2000 - 05:25:59 EST
Hi all
I currently work on making a touch screen driver
for the DragonBall-EZ and the Burr-Brown ADS7843.
The first step finished now is the user space driver.
I put it at the end of this mail if someone is
interesting about this.
If anyone of you have some comments or remarks or
is working on the same problem, it would be very
interesting for me to have some feed-back.
I go on now with integrating it in the kernel.
Philippe Ney
SMARTDATA
PSE-A, EPFL
1015 Lausanne, SWITZERLAND
/*-----------------------------------------------------------------------------
*
* trap_pen.c Touch screen test. Get the interrup by polling the
address
* and initiate a read of the X and Y coordinates.
* (Not portable)
*
*
* Hardware: Motorola MC68EZ328 DragonBall-EZ
* Burr-Brown ADS7843 Touch Screen Controller
* Rikei REW-A1 Touch Screen
*
* OS: uClinux version 2.0.38.1pre3
*
* Connectivity: Burr-Brown DragonBall
* PENIRQ ----> PF1 & PD4 (with a 100k resistor)
* BUSY ----> PF5
* CS ----> PB4
* DIN ----> PE0
* DOUT ----> PE1
* DCLK ----> PE2
*
* States graph:
*
* ELAPSED & PEN_UP
* (re-init) -------
* ------------------------>| IDLE |<------
* | ------- |
* | | | ELAPSED
* | ------------------------ | (re-init)
* | | PEN_IRQ |
* | | (disable pen_irq) |
* | | (enable & set timer) |
* | | |
* | \/ |
* ------- |
* | WAIT | |
* ------- |
* /\ | |
* | | ----------------------------------------------
* | | | | | |
* | | | | | |
* | | --------- -------- ---------
---------
* | -------->| ASK X | | ASK Y | | READ X | | READ
Y |
* | ELAPSED & --------- -------- ---------
---------
* | PEN_DOWN | /\ | /\ | /\
|
* | (init SPIM) ------------' ----------' ----------'
|
* | (set timer) XCH_COMPLETE XCH_COMPLETE XCH_COMPLETE
|
* |
|
* |
|
* |
|
*
--------------------------------------------------------------------
* XCH_COMPLETE
* (release SPIM)
* (set timer)
*
*
*
* Author: Philippe Ney <philippe.ney@smartdata.ch>
*
*
*
*/
/*---------------------------------------------------------------------------*/
#include <stdio.h> /* for
printf */
#include <sys/time.h> /* for time
functions */
#include <asm/MC68EZ328.h> /* for bus and port definition of DragonBall
EZ */
/*---------------------------------------------------------------------------*/
/* driver state variable */
static int TS_DRV_STATE;
/* irq services state variables*/
static int pen_irq_enable;
static int xch_complete_enable;
static int timer_enable;
/* timer variables */
static struct timeval first_tick;
static unsigned long wake_time;
/* counter to differentiate a click from a move */
static int counter;
/* pen info variables */
static struct position { int x,y; } previous,current;
static struct pen_info {
int x,y;
int dx,dy;
int STATE;
} pen;
/* test variables */
int MOVE_THRESHOLD = 100;
int print=0;
/*---------------------------------------------------------------------------*/
/* Address used defined in <asm/MC68EZ328.h>
* CARD_IRQ_0 = PEN IRQ: port F bit 1 (PFDIR,PFPUEN,PFSEL,PFDATA)
* CARD_SEL_0: port B bit 4 (PBDIR,PBPUEN,PBSEL,PBDATA)
* SPIMDATA and SPIMCONT
* PORT E bit 0-2 multiplexed with SPIMTXD,SPIMRXD,SPIMCLK have to be
* selectionned as connected (PESEL)
*/
/* SPIMCONT fields for this app (not defined in asm/MC68EZ328.h)
* The others (SPIMCONT_ENABLE, SPIMCONT_XCH, ...) are defined in
* asm/MC68EZ328.h
*/
#define SPIMCONT_DATARATE ( 0<<12) /* SPICLK = CLK / 4 */
#define SPIMCONT_BITCOUNT (15<< 0) /* 16 bits transfert */
/* SPIMCONT used values */
#define SPIMCONT_INIT (SPIMCONT_DATARATE | \
SPIMCONT_ENABLE | \
SPIMCONT_XCH *0 | \
SPIMCONT_IRQ *0 | \
SPIMCONT_IRQEN *0 | \
SPIMCONT_PHA | \
SPIMCONT_POL | \
SPIMCONT_BITCOUNT )
/* ADS7843 fields (BURR-BROWN Touch Screen Controller) */
#define ADS7843_START_BIT (1<<7)
#define ADS7843_A2 (1<<6)
#define ADS7843_A1 (1<<5)
#define ADS7843_A0 (1<<4)
#define ADS7843_MODE (1<<3) /* HIGH = 8, LOW = 12 bits conversion
*/
#define ADS7843_SER_DFR (1<<2) /* LOW = differential mode
*/
#define ADS7843_PD1 (1<<1) /* PD1,PD0 = 11 PENIRQ disable
*/
#define ADS7843_PD0 (1<<0) /* PD1,PD0 = 00 PENIRQ enable
*/
/* SPIMDATA used values */
/* Ask for X conversion. Disable PENIRQ */
#define SPIMDATA_ASKX (ADS7843_START_BIT | \
ADS7843_A2 | \
ADS7843_A1 *0| \
ADS7843_A0 | \
ADS7843_MODE *0| \
ADS7843_SER_DFR *0| \
ADS7843_PD1 | \
ADS7843_PD0 )
/* Ask for Y conversion. Disable PENIRQ */
#define SPIMDATA_ASKY (ADS7843_START_BIT | \
ADS7843_A2 *0| \
ADS7843_A1 *0| \
ADS7843_A0 | \
ADS7843_MODE *0| \
ADS7843_SER_DFR *0| \
ADS7843_PD1 | \
ADS7843_PD0 )
/* Enable PENIRQ */
#define SPIMDATA_NOP (ADS7843_START_BIT | \
ADS7843_A2 *0| \
ADS7843_A1 *0| \
ADS7843_A0 *0| \
ADS7843_MODE *0| \
ADS7843_SER_DFR *0| \
ADS7843_PD1 *0| \
ADS7843_PD0 *0 )
/* Generate clock to fall down BUSY signal.
* No start bit to avoid generating a BUSY at end of the transfert
*/
#define SPIMDATA_NULL 0
/* Useful masks */
#define PEN_IRQ_MASK 0x02 /* pen irq signal from the Burr-Brown
is */
#define PEN_IRQ_P2_MASK 0x10 /* connected to 2 bits of the
DragonBall */
/* (bit 2 port F and bit 4 port
D) */
#define AD_BUSY_MASK 0x20
#define CARD_SEL_MASK 0x10
#define PESEL_MASK 0x07
/* Pen states */
#define PEN_ERROR -2
#define PEN_UP -1
#define PEN_DOWN 0 /* = PENIRQ low */
#define PEN_CLICK 1
#define PEN_MOVE 2
/* Touch screen driver states */
#define TS_DRV_ERROR -1
#define TS_DRV_IDLE 0
#define TS_DRV_WAIT 1
#define TS_DRV_ASKX 2
#define TS_DRV_ASKY 3
#define TS_DRV_READX 4
#define TS_DRV_READY 5
/* Timer delay values (in miliseconds) */
#define TIMER_DELAY 20
/* Interruption state */
#define DISABLE 0
#define ENABLE 1
/* Threshold to define a move from a click */
#define DEFAULT_MOVE_THRESHOLD 100
/* PEN_IRQ is active low.
* PENIRQ signal of the ADS7843 is mapped on bit 2 of F register on the
* DragonBall EZ
*/
#define IS_PEN_DOWN ((PFDATA & PEN_IRQ_MASK)==PEN_DOWN)
/* The end of the exchange is defined with the XCH bit of the SPIM
* (the irq will be used in kernel space)
*/
#define IS_XCH_ENDED ((SPIMCONT & SPIMCONT_XCH)==0)
/* State of the BUSY signal of the SPIM */
#define IS_BUSY_ENDED ((PFDATA & AD_BUSY_MASK)==0)
/* Write the SPIM (data and control) */
#define SET_SPIMDATA(x) { SPIMDATA = x; }
#define SET_SPIMCONT(x) { SPIMCONT = x; }
/*---------------------------------------------------------------------------*/
#define SPIM_ENABLE { SET_SPIMCONT(SPIMCONT_ENABLE); } /* enable SPIM
*/
#define SPIM_INIT { SPIMCONT |= SPIMCONT_INIT; } /* init SPIM
*/
#define SPIM_DISABLE { SET_SPIMCONT(0x0000); } /* disable SPIM
*/
/*---------------------------------------------------------------------------*/
#define CARD_SELECT { PBDATA &= ~CARD_SEL_MASK; } /* PB4 active
(=low) */
#define CARD_DESELECT { PBDATA |= CARD_SEL_MASK; } /* PB4 inactive
(=high) */
/*---------------------------------------------------------------------------*/
/* Return the number of miliseconds since the first tick */
unsigned long getticks(void) {
struct timeval now;
gettimeofday(&now,NULL);
return (now.tv_sec -first_tick.tv_sec )*1000 +
(now.tv_usec-first_tick.tv_usec)/1000;
}
void init_pen_info(int reset) {
current.x = 0;
current.y = 0;
previous.x = 0;
previous.y = 0;
if(reset) {
pen.x = 0;
pen.y = 0;
pen.dx = 0;
pen.dy = 0;
pen.STATE = PEN_UP;
}
}
void init_state(void) {
TS_DRV_STATE = TS_DRV_IDLE;
pen_irq_enable = ENABLE;
xch_complete_enable = DISABLE;
timer_enable = DISABLE;
init_pen_info(0);
counter = 0;
}
void init_clock_tick(void) {
gettimeofday(&first_tick,NULL);
}
/* Set the first_tick as the time ref */
void init_clock(void) {
init_clock_tick();
wake_time = getticks();
}
void init_hardware(void) {
/* Init stuff for port E. The 3 LSB are multiplexed with the
SPIM */
PESEL &= ~PESEL_MASK;
/* Set bit 4 of port B for the CARD_SELECT signal of ADS7843 as
output. */
PBPUEN |= ~0; /* Port B as
pull-up */
PBDIR |= CARD_SEL_MASK; /* PB4 as
output */
PBDATA |= CARD_SEL_MASK; /* PB4 inactive
(=high) */
PBSEL |= CARD_SEL_MASK; /* PB4 as I/O not
dedicated */
/* Set bit 5 of port F for the BUSY signal of ADS7843 as
input */
PFPUEN |= AD_BUSY_MASK; /* Port F as
pull-up */
PFDIR &= ~AD_BUSY_MASK; /* PF5 as
input */
PFSEL |= AD_BUSY_MASK; /* PF5 I/O port function pin is
connected */
/* Set bit 4 of port D for the pen irq pull-up of ADS7843 as
output. */
PDDIR |= PEN_IRQ_P2_MASK; /* PD4 as
output */
PDDATA |= PEN_IRQ_P2_MASK; /* PD4 up
(pull-up) */
PDPUEN |= PEN_IRQ_P2_MASK; /* PD4 as
pull-up */
PDSEL |= PEN_IRQ_P2_MASK; /* PD4 I/O port function pin is
connected */
}
/* Reset bits used in each register to their default value */
void release_hardware(void) {
/* Register default value */
/* PESEL 0xFF */
/* PBDIR 0x00 */
/* PBDATA 0x00 */
/* PBPUEN 0xFF */
/* PBSEL 0xFF */
/* PFDIR 0x00 */
/* PFPUEN 0xFF */
/* PFSEL 0xFF */
/* PDDIR 0xFF */
/* PDDATA 0xF0 */
/* PDPUEN 0xFF */
/* PDSEL 0xF0 */
PESEL |= PESEL_MASK;
PBDIR &= ~CARD_SEL_MASK;
PBDATA &= ~CARD_SEL_MASK;
PBPUEN |= CARD_SEL_MASK;
PBDIR |= CARD_SEL_MASK;
PFDIR &= ~AD_BUSY_MASK;
PFPUEN |= AD_BUSY_MASK;
PFSEL |= AD_BUSY_MASK;
PDDIR |= PEN_IRQ_P2_MASK;
PDDATA |= PEN_IRQ_P2_MASK;
PDPUEN |= PEN_IRQ_P2_MASK;
PDSEL |= PEN_IRQ_P2_MASK;
}
void init_TS_drv(void) {
init_state();
init_clock();
init_pen_info(1);
init_hardware();
}
/*---------------------------------------------------------------------------*/
/* Set timer irq to now + delay miliseconds */
void set_timer_irq(unsigned long delay) {
wake_time = getticks() + delay;
}
/* Set ADS7843 and SPIM parameters for transfer */
void set_SPIM_transfert(void) {
/* DragonBall drive I/O 1 and I/O 2 LOW to reverse bias the PENIRQ
diode
* of the ADS7843
*/
PDDATA &= ~PEN_IRQ_P2_MASK; /* Pull down I/0 2 of PENIRQ */
PFDIR |= PEN_IRQ_MASK; /* I/O 1 of PENIRQ as output */
PFDATA &= ~PEN_IRQ_MASK; /* Pull down I/O 2 of PENIRQ */
/* Initiate selection and parameters for using the Burr-Brown ADS7843
* and the DragonBall SPIM
*/
CARD_SELECT; /* Select ADS7843. */
SPIM_ENABLE; /* Enable SPIM */
SPIM_INIT; /* Set SPIM parameters */
}
/* Clock the Burr-Brown to fall the AD_BUSY.
* With a 'start' bit and PD1,PD0 = 00 to enable PENIRQ.
* Used for the first pen irq after booting. And when error occures
during
* conversion that need an initialisation.
*/
void fall_BUSY_enable_PENIRQ(void) {
SET_SPIMDATA(SPIMDATA_NOP);
SPIMCONT |= SPIMCONT_XCH;
}
/* Release transfer settings */
void release_SPIM_transfert(void) {
SPIM_DISABLE;
CARD_DESELECT;
PDDATA |= PEN_IRQ_P2_MASK; /* Pull up I/0 2 of PENIRQ */
PFDIR &= ~PEN_IRQ_MASK; /* I/O 1 of PENIRQ as input */
PFDATA |= PEN_IRQ_MASK; /* Pull down I/O 2 of PENIRQ */
}
/* Ask for an X conversion */
void ask_x_conv(void) {
SET_SPIMDATA(SPIMDATA_ASKX);
SPIMCONT |= SPIMCONT_XCH;
}
/* Ask for an Y conversion. */
void ask_y_conv(void) {
SET_SPIMDATA(SPIMDATA_ASKY);
SPIMCONT |= SPIMCONT_XCH;
}
/* Get the X conversion. Re-enable the PENIRQ */
void read_x_conv(void) {
current.x = (SPIMDATA & 0x7FF8) >> 3;
/* The first clock is used to fall the BUSY line and the following 15
clks
* to transfert the 12 bits of the conversion, MSB first.
* The result will then be in the SPIM register in the bits 14 to 3.
* And this is for any clock rate I tested (32kHz to 4MHz)
* (= 1/512 -> 1/4 of DragonBall CLK)
*/
SET_SPIMDATA(SPIMDATA_NOP); /* nop with a start bit to re-enable */
SPIMCONT |= SPIMCONT_XCH; /* the pen irq. */
}
/* Get the Y result. Clock the Burr-Brown to fall the BUSY. */
void read_y_conv(void) {
current.y = (SPIMDATA & 0x7FF8) >> 3;
/* Same remark as upper. */
SET_SPIMDATA(SPIMDATA_NULL); /* No start bit to fall the BUSY without
*/
SPIMCONT |= SPIMCONT_XCH; /* initiate another BUSY...
*/
}
int is_moving(void) {
return (abs(current.x-previous.x) > MOVE_THRESHOLD ? 1 :
(abs(current.y-previous.y) > MOVE_THRESHOLD ? 1 : 0));
}
void cause_event(void) {
current.x &= 0x0FFF;current.y &= 0x0FFF;
switch(counter) { /* It could be a move */
case 2: if(is_moving()) {
pen.STATE = PEN_MOVE;
pen.x = current.x;
pen.y = current.y;
pen.dx = pen.x - previous.x;
pen.dy = pen.y - previous.y;
previous.x = current.x;
previous.y = current.y;
}
else {
pen.STATE = PEN_DOWN;
pen.x = previous.x; /* No coord passing to */
pen.y = previous.y; /* avoid Parkinson effects */
pen.dx = 0;
pen.dy = 0;
}
break;
case 1: pen.STATE = PEN_CLICK;
pen.x = current.x;
pen.y = current.y;
pen.dx = 0;
pen.dy = 0;
previous.x = current.x;
previous.y = current.y;
break;
case 0: pen.STATE = PEN_ERROR;
pen.x = current.x;
pen.y = current.y;
pen.dx = 0;
pen.dy = 0;
previous.x = 0;
previous.y = 0;
}
printf("(%d,%d, %d) - %d\n",pen.x,pen.y,pen.STATE,counter);
}
/*---------------------------------------------------------------------------*/
void handle_pen_irq(void) {
if(print) printf("handle_pen_irq -- %i\n",TS_DRV_STATE);
pen_irq_enable = DISABLE;
TS_DRV_STATE++;
set_timer_irq(TIMER_DELAY); /* Set timer */
timer_enable = ENABLE; /* Enable timer */
}
void handle_timeout(void) {
if(print) printf("handle_timeout -- %i\n",TS_DRV_STATE);
switch(TS_DRV_STATE) {
case TS_DRV_ASKX: /* Error in release SPIM */
case TS_DRV_ASKY: /* Error in ask X coord */
case TS_DRV_READX: /* Error in read X coord */
case TS_DRV_READY: /* Error in read Y coord */
fall_BUSY_enable_PENIRQ(); /* Force re-enable
of */
TS_DRV_STATE = TS_DRV_ERROR; /* PENIRQ becaus of an */
timer_enable = DISABLE; /* abnormal termination */
xch_complete_enable = ENABLE;/* of conversion */
break;
case TS_DRV_WAIT: if(counter) cause_event(); /* Only after one
loop */
if(IS_PEN_DOWN) {
xch_complete_enable = ENABLE;
timer_enable = ENABLE;
// set_timer_irq(TIMER_DELAY);
/* For test because of time consumption due to 'printf' functions. */
set_timer_irq(1000);
if(counter < 2) counter++;
set_SPIM_transfert();
fall_BUSY_enable_PENIRQ();
TS_DRV_STATE++;
}
else init_state();
break;
default: init_state();
}
}
void handle_xch_complete(void) {
if(print) printf("handle_xch_complete -- %i\n",TS_DRV_STATE);
switch(TS_DRV_STATE) {
case TS_DRV_ASKX: if(IS_BUSY_ENDED) { /* If first BUSY down,
then */
ask_x_conv(); /* go on with
conversion */
TS_DRV_STATE++;
}
else fall_BUSY_enable_PENIRQ(); /* else, re-loop */
break;
case TS_DRV_ASKY: ask_y_conv();
TS_DRV_STATE++;
break;
case TS_DRV_READX: read_x_conv();
TS_DRV_STATE++;
break;
case TS_DRV_READY: read_y_conv();
TS_DRV_STATE = TS_DRV_WAIT;
break;
case TS_DRV_WAIT: release_SPIM_transfert();
timer_enable = ENABLE;
xch_complete_enable = DISABLE;
set_timer_irq(TIMER_DELAY);
break;
default: release_SPIM_transfert();
init_state();
}
}
/*---------------------------------------------------------------------------*/
/* main */
int main(int argc, char** argv)
{
char input[200];
int i;
int help=0,set=0;
printf("%s " __DATE__ " " __TIME__ "\n", argv[0]);
printf("%04x %04x\n",SPIMDATA_ASKX,SPIMDATA_ASKY);
printf("0x%02x 0x%02x %02x %02x\n",PFDIR,PFDATA,PFPUEN,PFSEL);
/* Parse args */
for(i=1; i<argc; ++i) {
if( argv[i][0]=='-' ) {
const char* arg = &argv[i][1];
if(!strcmp(arg, "-help")) help = 1;
else if(!strcmp(arg, "?")) help = 1;
else if(!strcmp(arg, "s")) set = 1;
else if(!strcmp(arg, "-set")) set = 1;
else {
printf("%s: bad option",arg);
printf("Try `%s --help' for more information.\n", argv[0]);
exit(1);
}
}
}
/* Help */
if(help) {
printf("Usage: %s [OPTION]\n", argv[0]);
printf("Utility to tune the DragonBall SPI controller.\n");
printf(" -?, --help display this help\n");
printf(" -s, --set change settings\n");
exit(0);
}
init_TS_drv();
if(set) while(1){
unsigned long v;
char c;
printf("Syntax: [a-d] value | [q]\n");
printf("q:quit\n");
printf("a(%d): 1:print debug 0:no print\n",print);
printf("b(%d): MOVE_THRESHOLD value\n",MOVE_THRESHOLD);
printf("\n%s > ", argv[0]);
fflush(stdout);
gets(input);
c = -1;
v = -1;
sscanf(input, "%c %x", &c, &v);
c = tolower(c);
if(c=='q')
break;
if(c==-1 || (c>='a' && c<='b' && v==-1))
continue;
switch(c) {
case 'a': print = v; break;
case 'b': MOVE_THRESHOLD = v; break;
default: printf("Error\n");
}
printf("\n");
}
{
const char D[] = {"/-\\|"};
int c = 0;
int d = 0;
for(;;) {
if((++c%1000)==0)
printf(" %c \r", D[d++&3]);
if(pen_irq_enable && IS_PEN_DOWN)
handle_pen_irq(); /* pen irq event */
if(timer_enable && (getticks() > wake_time))
handle_timeout(); /* timeout event */
if(xch_complete_enable && IS_XCH_ENDED)
handle_xch_complete(); /* xch complete event */
}
}
}
/* END */
This message resent by the uclinux-dev@uclinux.org list server http://www.uClinux.org/
This archive was generated by hypermail 2.1.4 : Thu Sep 19 2002 - 13:19:20 EDT