Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Looking for a Touchscreen controller driver written in C18 for PIC

Status
Not open for further replies.

crocu

Member
Hello

I looking a resistive touchscreen controller (4 pins) driver written in C18 for PIC.

I would like to add touchscreen facility in a GLCD 128x64

There are many touchscreen controllers in the market : Microchip AR1020, TCS2046, ADS7843 but i did not found any driver or test code for one of them. :(

If someone has one working with a PIC, would you please share it ?
Many thanks for your help,
 
Hi,

I have attached the code I wrote for using an ADS7843 touch controller. It was written for a PIC32 but with a few minor modifications you should have it working on your PIC18 pretty easily.

This was just a quick one I wrote up to test an LCD module I had so there are probably better ways of doing things but it should get you started anyway.
 

Attachments

  • ADS7843.c
    7.3 KB · Views: 549
  • ADS7843.h
    1.6 KB · Views: 391
A bit of an explanation, now I have more time:

As you can see everytime the ReadTouchXY() routine is called it checks if there is a touch detected (using the touch IRQ pin) and if there is it takes one reading, stores it into a buffer 10 readings long and averages the result. I used a timer to call the routine at (I forget now) but 100Hz-500Hz should be good. If you are using a PIC that doesn't have prioritised interrupts, then a timer interrupt may not be your best option due to the delays and length of the routine. If you are using a PIC that does have prioritised interrupts, then make it low priority. Otherwise try to call it in your main code regularly. Remember that when a touch is detected it will take 10 reading to get a result. You can remove the averaging and buffers if you want but it works well and accurately as is.

If no touch is detected then it clears the X and Y buffers, and returns. Therefore the coordinates of 0,0 means no touch is detected. In practice you are unlikely to read a 0,0 from your screen. The final result is placed into TouchX and TouchY and is in pixels. You will need to change the code (scaling of the result) if your screen isn't 320 x 240.

There are measured calibration values in the code too. You will need to change these to suit your screen, as every screen is different. Basically all I did was touch each edge of the screen and see what the corresponding X or Y value is for that edge by inserting a break point in the program or pausing it while you are touching the screen and then reading the last A/D result.

Keep in mind though it is not finished code, it is the start. I know it is poorly commented so if you have any questions feel free to ask :)
 
Last edited:
Hello Gobbledok,

Would you please explain me what do the following lines, they come from your reading function.
I guess it is for calibration purpose, but i do not understand how you proceed.

( My project use a 128x64 GLCD, Vref pin of the ADS7843 is VCC= 3.3V )

Why do you shift 3 bits right ?
What is the offset , how should i calculate mine for my project ?
How did you find 0.0936768 ?

Code:
Touch_Buffer[0][Touch_Counter]>>=3;
Touch_Buffer[0][Touch_Counter] = 4096-Touch_Buffer[0][Touch_Counter];
Touch_Buffer[0][Touch_Counter] -= 280;		// subtracts theoffset					THESE ARE MEASURED CALIBRATION VALUES
Touch_Buffer[0][Touch_Counter] *= 0.0936768;	// Multiplied by the range (320/(3696-280))

Many thanks for your help,
 
Hi Crocu,

The offset is the minimum reading when touching the corner of the screen.

The calibration values are the measured minimum and maximum values read from the ADS7843. EG if you place your finger on the top-left corner of the screen and it is supposed to read 0,0 but it reads 280, 90, then you need to scale the result so it directly relates to the pixels on your screen. The same for the maximum values. If you place your finger/pen on the bottom-right corner of the screen and it is supposed to read 320,240 but it reads 3696, 3800 then you will have to scale it for your screen.

The range is simply 320 (the number of horizontal pixels) divided by (largest value) - (smallest value). What that does is scales the 10-bit result from the ADS7843 into a result directly relating to the pixels on your screen.

I don't remember why I shifted the pixels right, I'll have a look this arvo when I get home from work. Remember that these calibration values are obtained from the RAW data from the ADS7843. Do not scale or otherwise do any maths on the result until you have obtained the raw values. I hope this made sense, I sometimes find my thingy* lacking.

In the mean time, have a look here. It is a later version of the same code. It is better commented and I was better at C when I wrote it :)

*vocabulary ;)
 
I've had a look at your newer code, it looks , i will try to understand the calibration process once i'm confortable with the ReadTouchXY function :confused:

In your new code, did you use the 8 bit mode instead of 12 bit mode ?

into the void Touch_Init(void) you've set :
Code:
SpiChnPutC(SpiChn, 0b10011100);

S = 1 ( mandatory )
A2-A0 = Points to X+ converter
Mode = 1 (8 bits mode)
SER = 1
Power down enabled


Regarding, void ReadTouchXY(void)
-How do you fill-in the 30 convertion into it , i do not see a 'for' loop ( for i = 0 i<30 i++ ) in there ?
- I'm sure to understand well the steps you have made :

First, it seems you read Y+ channel in 8 bits mode, don't you ?
Code:
SpiChnPutC(SpiChn, 0b11011100);			// Sends the control byte
Here A2-A1 address is 101 ( bit 6 to bit 4 )

But i don't see when you X+ channel afterward .

Many thanks for your help,
 
Last edited:
In your new code, did you use the 8 bit mode instead of 12 bit mode ?

Hi Crocu,

You are correct and that's a good question. I have no idea why I used 8 bit mode but feel free to change it :)



Regarding, void ReadTouchXY(void)
-How do you fill-in the 30 convertion into it , i do not see a 'for' loop ( for i = 0 i<30 i++ ) in there ?

There is no for loop because only 1 register is filled in the buffer at a time. The register is decided by:
Code:
void ReadTouchXY(void)
{
	Touch_Counter++;
	if(Touch_Counter==30)   Touch_Counter = 0;
	if(Touch_Counter>30)   Touch_Counter = 0;	// The pointer for the FIFO buffer
...
...

Touch_Buffer[1][Touch_Counter] = xxx;

The touch counter which is incremented each time the routine is called. When the touch counter = 30 it is reset to 0. Can actually get rid of the 'if(Touch_Counter>30)', it is double-handling and is an artifact from testing (like the 8-bit mode ;) ).
Just as an example, if you wanted to have a 50 word buffer instead, then you would make your touch buffer bigger:

Code:
unsigned short Touch_Buffer[2][50];

and then only reset the touch counter once it reaches 50.



First, it seems you read Y+ channel in 8 bits mode, don't you ?
Code:
SpiChnPutC(SpiChn, 0b11011100);			// Sends the control byte
Here A2-A1 address is 101 ( bit 6 to bit 4 )

But i don't see when you X+ channel afterward .

Many thanks for your help,

Code:
	else
	{
		Touch_PenIRQ_TRIS = 0;					// Sets the PenIRQ to an output
		Touch_PenIRQ_LAT = 0;					// Drives the PenIRQ low so the diode is not forward biased

		Touch_CS = 0;		TDelay();			// Asserts the CS line and gives a required delay
*	SpiChnPutC(SpiChn, 0b11011100);			// Sends the control byte
		SpiChnGetC(SpiChn);						// Reads the dummy data to clear the receive buffer
		while(Touch_Busy == 1);					// Makes sure the controller isn't busy
		SpiChnPutC(SpiChn, 0x00);				// Sends a dummy byte in order to clock the data out
		unsigned char rxd1 = SpiChnGetC(SpiChn);// Puts the received data into rxd1
*	SpiChnPutC(SpiChn, 0b10011100);			// Sends the next control byte to read the other axis
		unsigned char rxd2 = SpiChnGetC(SpiChn);// Reads the lower byte of the first received data

;)

If you have any more questions feel free to ask.
 
Last edited:
Thanks,

Regarding the calibration step : did you write a dedicated function for to be called once only ?

If i take as reference the bottom left hand side of the screen, will it be efficient ?
Once i get the coordinates of that point, how should i perform the calculations then ?
 
I didn't actually calibrate it in the program, I calibrated it on the desk with a PicKit and MPLAB.

You could comment out the lines which do the math on the raw value. Then the touch buffers will contain your raw value. e.g:

Code:
		// The following figure is the result (rxd1 & rxd2) inverted (4096-) - the offset (measured) * the range
		Touch_Buffer[1][Touch_Counter] = 4096-(rxd1<<5) + (rxd2>>3);
//		if(Touch_Buffer[1][Touch_Counter]<Ymin){Touch_Buffer[1][Touch_Counter] = 0;}
//		else{Touch_Buffer[1][Touch_Counter] -= Ymin;	Touch_Buffer[1][Touch_Counter] *= Yrange;}
//		if(Touch_Buffer[1][Touch_Counter]>239) Touch_Buffer[1][Touch_Counter] = 239;
...
and
...

		// The following figure is the result (rxd1 & rxd2) - the offset (measured) * the range 
		Touch_Buffer[0][Touch_Counter] = ((rxd1<<5) + (rxd2>>3));
//		if(Touch_Buffer[0][Touch_Counter]<Xmin){Touch_Buffer[0][Touch_Counter] = 0;}
//		else{Touch_Buffer[0][Touch_Counter] -= Xmin;	Touch_Buffer[0][Touch_Counter] *= Xrange;}
//		if(Touch_Buffer[0][Touch_Counter]>319) Touch_Buffer[0][Touch_Counter] = 319;

Once I did that, I just ran that program on my hardware with my PicKit, then touched the corner and then paused the program. Once the program is paused I can check the Touch Buffer in the Watch Window and see the raw values. If you don't use a PicKit (or other hardware capable of debugging within MPLAB) then you will have to write a program to calibrate it within your hardware.

If your screen resolution is different to 320 * 240, then change it on the last line on each block of the above code. e.g:

Code:
		if(Touch_Buffer[1][Touch_Counter]>239) Touch_Buffer[1][Touch_Counter] = 63;
...
and
...
		if(Touch_Buffer[0][Touch_Counter]>319) Touch_Buffer[0][Touch_Counter] = 127;

You only need the calibration values of each extreme of the screen. e.g. you need 0,0 and 127,63 but not 0,63 and not 127,0.

Once you have found out the raw coordinates of each corner of your screen, you can substitute those values with the values I have used in the header file.

You only need to change these:
Code:
// These are measured calibration values
// Place your own measured calibration values here
#define Xmin 320
#define Xmax 3744
#define Ymin 352
#define Ymax 3792
#define Xrange 0.093458	// =(num of x pixels/(Xmax-Xmin))
#define Yrange 0.069768	// =(num of y pixels/(Ymax-Ymin))
//

For Xrange and Yrange, you can calculate them based on your measured values and your screen resolution.

For example, to calculate mine I used:

Xrange = num of x pixels on screen/(Xmax-Xmin)
Xrange = 320/(3744-320)
Xrange = 0.093458

I hope that answers your questions, and I hope you use MPLAB and a PicKit :)
 
Last edited:
Thanks for your help Gobbledok,
I also use MPLAB environnement and i have the Microchip ICD3 programmer / debugger.

I'm currently having troubles with ADS7843 :

i try to read the X & Y coordinates when PEN IRQ pin goes low. ( simply with a 'if' condition tested from the infinite loop )

At the moment this pin is always down, even there is or there is not a touch on the screen.
( I've pulled up PENIRQ pin to Vcc (3.3V)

Here is attached the drawing i use, would you please tell me if it is correct ?
 
Hi Crocu yes that should be right as you have it in the schematic. Most app notes use a 100k, however that shouldn't be the source of your problems.

From memory, the times for me that the PenIRQ didn't work is because I didn't initialise the ADS7843 first/properly. The PenIRQ is not enabled from start-up and must be initialised.

Are you initialising the ADS7843 first? Which Power-Down mode are you using? The PenIRQ will only work when PD0 = 0 and PD1 = 0.
 
Well, in order to get the X and Y pos, i run once Init_ADS7843 and from the main loop, i call ADS7843_read function for X and Y positions, however PENIRQ is not going down ... :confused:

Tell me if i'm wrong : PENIRQ will go low only after a proper reading command.

Actually I've though PENIRQ should flag the PIC that a position has been pointed, and then let the user sending a reading function afterward but it looks not working like this way, isn't it ?


Code:
unsigned short int PEN_X_pos = 0;
unsigned short int PEN_Y_pos = 0;

...

PEN_X_pos = ADS7843_read(0x90);
PEN_Y_pos = ADS7843_read(0xD0);

Code:
void Init_ADS7843(void)
{
	// Configure PENIRQ pin
	Touch_PenIRQ_TRIS = 1;						// Touch_PenIRQ is input
	Touch_PenIRQ_IO = 0;

	// Set up the SPI module on the PIC for communications with the ADS7843 :
	ADS7843_CS_IO = 1;							// Disable ADS7843 CS pin
	ADS7843_CS_TRIS = 0; 						// Pic CS pin is output 
					
	ADS7843_SCK_TRIS = 0;  						// Set SCK pin as an output
	ADS7843_SDI_TRIS = 1;  						// Make sure SDI pin is an input
	ADS7843_SDO_TRIS = 0;						// Set SDO pin as an output

    ADS7843_SPI_IF = 0;							// Clear SPI Flag

    ADS7843_SPISTATbits.CKE = 1;     			// Transmit data on rising edge of clock
    ADS7843_SPISTATbits.SMP = 0;     			// Input sampled at middle of data output time
}


Code:
unsigned short int ADS7843_read(unsigned char address)
{
	volatile BYTE Dummy;	
	unsigned char msb, lsb; 
	      
	ADS7843_CS_IO = 0;        						// Enable CS 
	ADS7843_SPI_IF = 0;       						// Clear SPI Flag

	_asm        NOP        _endasm					// Generate some µS delay after CS pin goes low
		
	ADS7843_SSPBUF = address;    					// Transmit Start bit ( SSP1BUF )
	while(!ADS7843_SPI_IF);ADS7843_SPI_IF = 0;  	// Wait until data is shifted out
	Dummy = ADS7843_SSPBUF;							// Reading SSPBUF clears SPI Buffer
	   
	Delay10us(1);       							// Add 10us for Tacq delay time
	
	ADS7843_SSPBUF = 0x00;							// Send dummy value (0x00) in order to get the FIRST 8 bit byte
	while(!ADS7843_SPI_IF);ADS7843_SPI_IF = 0;  	// Wait until data is shifted out
	msb = ADS7843_SSPBUF;      						// Get the FIRST 8 bit byte ( 8 clock edges )
	   
	ADS7843_SSPBUF = 0x00;							// Send dummy value (0x00) in order to get the SECOND 8 bit byte
	while(!ADS7843_SPI_IF);ADS7843_SPI_IF = 0;  		// Wait until data is shifted out
	lsb = ADS7843_SSPBUF;      						// Get the SECOND 8 bit byte ( 8 clock edges )
	   
	ADS7843_CS_IO = 1;        						// Disable CS
	return (msb*0x100) | lsb;
}
}
 
Adding the logic screen capture while reading X position ( MOSI sends 0x90 to ADS7843 )
MISO does not receive anything back ( response is 0x00 ) , PEN_IRQ stays Up.
 
Last edited:
Hi Crocu,

The PenIRQ will only go low after PD0 & PD1 = 0.

After power up, you need to send 1 control byte to set PD0 and PD1 and then the Pen IRQ should work. From then on the PenIRQ should go low whenever a touch is detected, allowing you to know the status of the touch screen without constantly polling it.

I would have expected the PenIRQ to work after your first read (which sets the PD0 & PD1 bits).

Hopefully this helps but otherwise I will have a good look at your code this arvo once I get home from work.
 
Thanks,

I've tried to send 0x90 (0b10010000) at the end of my Init_function in order to set PD0 and PD1 = 0 .

Once the init function has been sent, i send a reading command with 0x90 again.
( 0x90 should X pos in power down mode enabled )

For now, i can see PENIRQ changing its state :
- PENIRQ goes Low when a touch has happened.
- PENIRQ stays Up if there is no touch.

However, X pos reading is not getting any result in all cases ( returned result is always 0x00 ) either there was a touch or not.

Should it be a problem in my reading function ? :(

Here is my new Init function :

Code:
void Init_ADS7843(void)
{
// Configure PENIRQ pin
Touch_PenIRQ_TRIS = 1;					// Touch_PenIRQ is input
Touch_PenIRQ_IO = 0;

// Set up the SPI module on the PIC for communications with the ADS7843 :
ADS7843_CS_IO = 1;					// Disable ADS7843 CS pin
ADS7843_CS_TRIS = 0; 					// Pic CS pin is output 
					
ADS7843_SCK_TRIS = 0;  					// Set SCK pin as an output
ADS7843_SDI_TRIS = 1;  					// Make sure SDI pin is an input
ADS7843_SDO_TRIS = 0;					// Set SDO pin as an output

ADS7843_SPI_IF = 0;					// Clear SPI Flag

ADS7843_SPISTATbits.CKE = 1;     			// Transmit data on rising edge of clock
ADS7843_SPISTATbits.SMP = 0;     			// Input sampled at middle of data output time

Delay10us(1);
	
ADS7843_read(0x90);					// Send a read sequence in order to activate PENIRQ (PD1 and PD0 = 0)
}
 
Would you please tell me what SPI modes are compatibles with ADS7843 ?

What is the maximum speed ( SCK clock frequency ) accepted by this chip ?
I did not find this information in the datasheet.

I currently using CKE = 0 ( SPI mode 0,0 ) and my SPI speed is 2.5 MHz
 
Low[/COLOR] when a touch has happened.
- PENIRQ stays Up if there is no touch.

Yep that's right.

Is it possible you were getting 0 back on the x axis because there was no touch?


Would you please tell me what SPI modes are compatibles with ADS7843 ?

What is the maximum speed ( SCK clock frequency ) accepted by this chip ?
I did not find this information in the datasheet.

I currently using CKE = 0 ( SPI mode 0,0 ) and my SPI speed is 2.5 MHz

If you have the same datasheet as me, figure 5 (or figure 6) shows the bus timing. It shows that the clock is active high (CKE = 1) with a low rest state (CKP = 0).

In Table VI: timing specs it shows a minimum DCLK HIGH and DCLK LOW of 200ns each = a period of 400ns = 2.5MHz.

If your PenIRQ is still working then your ADS7843 is receiving al least something. Are you getting anything back yet?
 
I'm finally able to read the X and Y positions from ADS7843 when PENIRQ gets low. ( I've got some issues with my SPI configuration )

Would you please explain the next steps, i can see perform this calculation once you acquire the X and Y positions, i do not understand why you do this calculation :
[ 4096 - ( msb + lsb ) ] - ( offset * range )

What is the purpose of inverting the result ?
I'm still not confortable with offset and range, can you explain me further please ?


I've have 128x64 GLCD when i touch turn by turn the 4 angles of the screen with a sharp pen, here are the results i get : (X,Y)

left, top corner : (31924,29056) right top corner : (30736,28720)

left bottom corner(29124,35286) right bottom corner : (31368,39520)


if i redo that test many times i always get differents values, they vary a lot at each attempt, it looks ADS7843 does not be very accurate, does it ?


I've tried to calculate X range for X axis :

with top of the screen values i get :
X range = 128 / (31924-30736) = 128 / 1188 = 0.1078

If do the same calculation with the bottom screen values :
X range = 128 / (31368-29124) = 128 / 2244 = 0.0058

i get a X range quite different !
 
Last edited:
Hi Crocu sorry for the delay.

I'm at work at the moment and will answer the rest later.

I invert the result simply because I wanted that particular axis to start from the other side. e.g. I wanted it to read 0 - 320 instead of 320 - 0. If you don't need this then you can remove that part.

One thing though, you must still have a problem with your receiving data because your returned numbers are far larger than a 12-bit number. The largest number you should get is 2^12 = 4096. I have found the ADS7843 to be very accurate.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top