FEZTouch - using interrupt to read touch point

I’ve been playing with the FEZTouch driver and got to wondering why the touch IRQ pin was being polled instead of taking advantage of the fact that it is wired to an interrupt capable pin. My guess was that polling was faster but I thought I would try it using an interrupt as well.

First step, comment out the TouchThread configuration, and create the interrupt handler:


            /// <summary>
            /// Initialize the touch screen
            /// </summary>
            /// <param name="touchConfig">Touch screen configuration settings</param>
			private void InitTouch(TouchConfiguration touchConfig)
			{
				this.SPIBus = new SPI(new SPI.Configuration(touchConfig.ChipSelect, false, 1, 1, false, true, 2000, touchConfig.Channel));
                
                // Use this for polling IRQ
                //this.TouchIRQ = new InputPort(touchConfig.TouchIRQ, false, Port.ResistorMode.Disabled);
                //this.TerminateTouchThread = false;
                //this.TouchThread = new Thread(this.TouchThreadMethod);
                //this.TouchThread.Priority = ThreadPriority.Highest;
                //this.TouchThread.Start();

                // Use this for using inturrpt on IRQ
                TouchIRQ = new InterruptPort(touchConfig.TouchIRQ, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);
                TouchIRQ.OnInterrupt += new NativeEventHandler(TouchInt);
			}

Here is the original polling based code. Ignore the ‘Point’ object, it is just a simple struct to hold (x, y). The commented out variables were moved out and made globals so I could easily switch between the polling and interrupt versions.


/// <summary>
            /// Thread to poll for touch events
            /// </summary>
            /// todo: call touch events on seperate thread
            private void TouchThreadMethod()
            {
                //Point CurrentPoint = new Point(0, 0);
                //Point LastPoint = new Point(0, 0);

                //bool TouchStatus;
                //bool LastTouchStatus = false; // true means there are touches

                //byte[] WriteBuffer = new byte[] { 0, 0, 0, 0 };
                //byte[] ReadBuffer = new byte[2];

                while (TerminateTouchThread == false)
                {
                    Thread.Sleep(TOUCH_SAMPLING_TIME);

                    TouchStatus = !TouchIRQ.Read();

                    if (TouchStatus == true)
                    {
                        TouchWriteBuffer[0] = 0x90;
                        SPIBus.WriteRead(TouchWriteBuffer, TouchReadBuffer, 1);
                        CurrentPoint.Y = TouchReadBuffer[0]; CurrentPoint.Y <<= 8;
                        CurrentPoint.Y |= TouchReadBuffer[1]; CurrentPoint.Y >>= 3;

                        TouchWriteBuffer[0] = 0xD0;
                        SPIBus.WriteRead(TouchWriteBuffer, TouchReadBuffer, 1);
                        CurrentPoint.X = TouchReadBuffer[0]; CurrentPoint.X <<= 8;
                        CurrentPoint.X |= TouchReadBuffer[1]; CurrentPoint.X >>= 3;

                        CalibratePoint(ref CurrentPoint);

                        // First touch
                        if (LastTouchStatus == false)
                        {
                            FireTouchDownEvent(CurrentPoint);
                            LastTouchStatus = true;
                            LastPoint = CurrentPoint;
                        }
                        else // drag across screen
                        {
                            // filter small changes
                            if (global::System.Math.Abs(CurrentPoint.X - LastPoint.X) > 5
                                || global::System.Math.Abs(CurrentPoint.Y - LastPoint.Y) > 5)
                            {
                                FireTouchMoveEvent(CurrentPoint);
                                LastPoint = CurrentPoint;
                            }
                        }
                    }
                    else if (LastTouchStatus == true) // finger pulled up
                    {
                        FireTouchUpEvent(LastPoint);
                        LastTouchStatus = false;
                    }
                }
            }

No here is the interrupt based method.


 private void TouchInt(uint port, uint state, DateTime time)
            {
                //Point CurrentPoint = new Point(0, 0);
                //Point LastPoint = new Point(0, 0);

                //bool TouchStatus;
                //bool LastTouchStatus = false; // true means there are touches

                //byte[] TouchWriteBuffer = new byte[] { 0, 0, 0, 0 };
                //byte[] TouchReadBuffer = new byte[2];

                TouchStatus = (state == 0);

                if (TouchStatus == true)
                {
                    TouchWriteBuffer[0] = 0x90;
                    SPIBus.WriteRead(TouchWriteBuffer, TouchReadBuffer, 1);
                    CurrentPoint.Y = TouchReadBuffer[0]; CurrentPoint.Y <<= 8;
                    CurrentPoint.Y |= TouchReadBuffer[1]; CurrentPoint.Y >>= 3;

                    TouchWriteBuffer[0] = 0xD0;
                    SPIBus.WriteRead(TouchWriteBuffer, TouchReadBuffer, 1);
                    CurrentPoint.X = TouchReadBuffer[0]; CurrentPoint.X <<= 8;
                    CurrentPoint.X |= TouchReadBuffer[1]; CurrentPoint.X >>= 3;

                    CalibratePoint(ref this.CurrentPoint);

                    // First touch
                    if (LastTouchStatus == false)
                    {
                        FireTouchDownEvent(CurrentPoint);
                        LastTouchStatus = true;
                        LastPoint = CurrentPoint;
                    }
                    else // drag across screen
                    {
                        // filter small changes
                        if (global::System.Math.Abs(CurrentPoint.X - LastPoint.X) > 5
                            || global::System.Math.Abs(CurrentPoint.Y - LastPoint.Y) > 5)
                        {
                            FireTouchMoveEvent(CurrentPoint);
                            LastPoint = CurrentPoint;
                        }
                    }
                }
                else if (LastTouchStatus == true) // finger pulled up
                {
                    FireTouchUpEvent(LastPoint);
                    LastTouchStatus = false;
                }

                
            }

The interesting thing is that when the screen is touched the TouchInt handler is called (TouchDown), the point read back is something goofy like (0, 4095). Then it is called on the other edge (TouchUp), the point read is the same goody point. And, just like the Engergizer bunny it just keeps going and going, the TouchInt handler is called over and over again even though the screen is not being touched. I’m too lazey to grab the O’scope to see if the pin is actually toggling repeatedly (I doubt it) but I’m at a loss to explain the why the handler keeps getting called and why it is always reading in the same (and wrong) point.

Any thoughts?

Here is a quick idea from the datasheet:

Is there a pull-up resistor on that pin?

I’m not sure if it has an external pull-up or not, I did try setting the pin more to ResistorPullup though. At any rate the polling method that GHI used in the provided driver works just fine which leads me to believe it is not pull up related.

Thanks for the idea. Where sis you find the datasheet for the touch controller?

Here is the datasheet:
[url]http://www.ti.com/lit/ds/symlink/ads7843.pdf[/url]

Also, some information about he interrupt pin and the way it should be handled. I haven’t found this before. I think you’re not handling the interrupt as described:
[url]http://www.ti.com/lit/an/sbaa028/sbaa028.pdf[/url]

After reading through the data sheet and app note I understand a bit more but I’m still confused. The GHI touch driver does set the touch controller into power save mode, so the IRQ bit should indicate when the screen is touched. The interesting thing is that the IRQ bit is connected to the X input section, so it is only telling you that the screen is now being touched. Also, touching the screen does not start the A2D conversion process asking for the data does. Anytime your finger is on the screen the IRQ bit will always be low so you must then poll the touch controller anyhow to be able to pick up drag type movements!

The above tells me that you can use the IRQ to know when to start polling and that when you don’t see activity for a while you can stop polling. This could save power and processor resources by not polling endlessly. One things i don’t quite understand though is that when I added a read to my interrupt handler it prevents the endless loop of interrupts I was seeing before. This may be due to some hardware difference when a pin is set up for IRQ as opposed to a simple input (and how the PENIRQ is wired up). I’m not certain.

Anyhow I guess I’ll play with starting/stopping the polling thread by using the interrupt handler. That will at least make the system a little more efficient.

I pulled a new FEZTOuch out of the package and found it has a XPT2046 touch controller. The datasheet is here: http://aitendo2.sakura.ne.jp/aitendo_data/product_img2/product_img/touch/XPT2046_english.pdf . It has some more goodies like temp and battery sensing.

I now have the touch polling thread controlled by the IRQ. Works pretty slick, a ManualResetEvent is used to pause the polling thread based on the state of the IRQ pin. Reading the pin i the interrupt looks silly but prevents the oscillation I saw previously.

I would like to add support for reading touch pressure as well but I need to know the resistance across the X plate, Y plate and across from XP to YN (and YP to XN). I’ll measure this on a spare touch screen I have but was hoping one of the guys from GHI might pull up the datasheet for the touch screen and let us know the rated values are. (In case the impedance of the touch controller chip throws off my measurements.)


            /// <summary>
            /// Called when screen is touched, starts polling thread
            /// </summary>
            /// <param name="port"></param>
            /// <param name="state"></param>
            /// <param name="time"></param>
            private void TouchInt(uint port, uint state, DateTime time)
            {
                if (TerminateTouchThread == false)
                {
                    if (!TouchIRQ.Read())
                    {
                        TouchManualResetEvent.Set();
                    }
                    else
                    {
                        TouchManualResetEvent.Reset();
                    }
                }
            }

        /// <summary>
        /// Thread to poll for touch events
        /// </summary>
        private void TouchThreadMethod()
        {
            while (TerminateTouchThread == false)
            {
                Thread.Sleep(TOUCH_SAMPLING_TIME);

                TouchStatus = !TouchIRQ.Read();

                if (TouchStatus == true)
                {
                    TouchWriteBuffer[0] = 0x90;
                    SPIBus.WriteRead(TouchWriteBuffer, TouchReadBuffer, 1);
                    CurrentPoint.Y = TouchReadBuffer[0]; CurrentPoint.Y <<= 8;
                    CurrentPoint.Y |= TouchReadBuffer[1]; CurrentPoint.Y >>= 3;

                    TouchWriteBuffer[0] = 0xD0;
                    SPIBus.WriteRead(TouchWriteBuffer, TouchReadBuffer, 1);
                    CurrentPoint.X = TouchReadBuffer[0]; CurrentPoint.X <<= 8;
                    CurrentPoint.X |= TouchReadBuffer[1]; CurrentPoint.X >>= 3;

                    CalibratePoint(ref CurrentPoint);

                    // First touch
                    if (LastTouchStatus == false)
                    {
                        FireTouchDownEvent(CurrentPoint);
                        LastTouchStatus = true;
                        LastPoint = CurrentPoint;
                    }
                    else // drag across screen
                    {
                        // filter small changes
                        if (global::System.Math.Abs(CurrentPoint.X - LastPoint.X) + 
                            global::System.Math.Abs(CurrentPoint.Y - LastPoint.Y) > 5)
                        {
                            FireTouchMoveEvent(CurrentPoint);
                            LastPoint = CurrentPoint;
                        }
                    }
                }
                else if (LastTouchStatus == true) // finger pulled up
                {
                    FireTouchUpEvent(LastPoint);
                    LastTouchStatus = false;
                    TouchManualResetEvent.WaitOne(); // we can pause thread here
                }
            }
        }

I tried to meausre the XPLate and YPlate resistance and got about 300ohms and 500ohms respectively (which does not make much sense.)

Can one of you nice guys at GHI look up these values form the touch screen datasheet please?