This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

ADC touchscreen readings

Hi,

I'm working on a touchscreen driver for a LM3S9B95 Stellaris.

I have an ADC sample sequence set up and I am able to read data out of the adc channels.

But the data does not seem to be of high enough resolution to convert to XY screen coordinates.

The touchscreen is a TTW4028001 on a 320X240 pixels LCD.

The data I'm getting from the ADC channel 11 (ADC_CTL_CH11), when I press the screen, is ranging from 0 to 30 for the Y. Which correlates to the 240 pixels width, on the bottom of the screen. And from 0 to 20 on the top of the screen. Which are quite promising results although quite low resolution of course.

The real problem lies in the reading of the ADC channel 10 (ADC_CTL_CH10), for the X values. That is constant around 43, when I press the screen it goes down by 5 to 38 at maximum.

This is with the reference voltage set to 1 volt external, connected at pin B6. With internal or external 3V, the resolution of the readings go down significantly.

Right now I'm using processor triggered interrupts, fired from a loop with interval. Eventually when I can make sensible readings this will be changed to timer based interrupts.

Another issue for me is getting valid conversion parameters to get valid XY coordinates. Like g_plParmSet in the touchscreen example driver for dk-lm3s9d96, touch.c. I don't know how to produce these values for my touchscreen, no clues in the datasheet.

If anyone can shed some light here, it would be greatly appreciated

Here is my code:

#define TS_ADC_PERIPH_B        SYSCTL_PERIPH_GPIOB // the adc pins
#define TS_ADC_PERIPH_E        SYSCTL_PERIPH_GPIOE // the drive pins

#define TS_ADC_BASE            GPIO_PORTB_BASE
#define TS_DRIVE_BASE        GPIO_PORTE_BASE

#define TS_X_ADC_PIN        GPIO_PIN_4
#define TS_X_DRIVE_PIN        GPIO_PIN_0

#define TS_Y_DRIVE_PIN        GPIO_PIN_1
#define TS_Y_ADC_PIN        GPIO_PIN_5

#define ADC_CTL_CH_XP         ADC_CTL_CH10
#define ADC_CTL_CH_YP         ADC_CTL_CH11

// from touch.c in the dk-lm3s9d96 example
const long g_plParmSet[7] =
{ -864,           // M0
        -79200,         // M1
        70274016,       // M2
        -85088,         // M3
        1056,           // M4
        80992576,       // M5
        199452,         // M6
        };

void ts_Init()
{
    SysCtlPeripheralEnable (SYSCTL_PERIPH_ADC0);
    SysCtlPeripheralEnable (TS_ADC_PERIPH_B);
    SysCtlPeripheralEnable (TS_ADC_PERIPH_E);

    GPIOPinTypeADC(TS_ADC_BASE, TS_X_ADC_PIN | TS_Y_ADC_PIN);
    GPIOPinTypeADC(GPIO_PORTB_BASE, GPIO_PIN_6);

    ADCSequenceDisable(ADC0_BASE, 1);
    ADCReferenceSet(ADC0_BASE, ADC_REF_EXT_1V); // yields best results
    //ADCReferenceSet(ADC0_BASE, ADC_REF_INT);
    //ADCReferenceSet(ADC0_BASE, ADC_REF_EXT_3V);
    //ADCHardwareOversampleConfigure(ADC0_BASE, 4);
    ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_PROCESSOR, 0);
    ADCSequenceStepConfigure(ADC0_BASE, 1, 0, ADC_CTL_CH_XP);
    ADCSequenceStepConfigure(ADC0_BASE, 1, 1, ADC_CTL_CH_YP | ADC_CTL_IE | ADC_CTL_END);

    ADCIntRegister(ADC0_BASE, 1, ts_IntHandler);
    ADCIntEnable(ADC0_BASE, 1);
    ADCSequenceEnable(ADC0_BASE, 1);

    GPIOPinWrite (TS_DRIVE_BASE, TS_X_DRIVE_PIN, TS_X_DRIVE_PIN);
    GPIOPinWrite (TS_DRIVE_BASE, TS_Y_DRIVE_PIN, 0);

    // Changing high low on the pins seems to have no effect

    /*GPIOPinWrite (TS_ADC_BASE, TS_Y_ADC_PIN, 0);
    GPIOPinWrite (TS_DRIVE_BASE, TS_X_DRIVE_PIN, TS_X_DRIVE_PIN);

    GPIOPinWrite (TS_ADC_BASE, TS_X_ADC_PIN, 0);
    GPIOPinWrite (TS_DRIVE_BASE, TS_Y_DRIVE_PIN, 0);*/
}

void ts_IntHandler(void)
{
    unsigned long adc_buffer[4]; // Sequence 1 has a FIFO of 4 entries

    ADCIntClear(ADC0_BASE, 1);
    ADCSequenceDataGet(ADC0_BASE, 1, adc_buffer);

    tRectangle sRect;
    sRect.sXMin = ((GrContextDpyWidthGet(&g_sContext)) / 2) - 75;
    sRect.sYMin = ((GrContextDpyHeightGet(&g_sContext)) / 2) - 40;
    sRect.sXMax = ((GrContextDpyWidthGet(&g_sContext)) / 2) + 75;
    sRect.sYMax = ((GrContextDpyHeightGet(&g_sContext)) / 2) + 40;
    GrContextForegroundSet(&g_sContext, ClrBlack);
    GrRectFill(&g_sContext, &sRect);

    char buf[16];
    GrContextForegroundSet(&g_sContext, ClrWhite);

    // adc_buffer[0] (x) is constant at 43, when screen press about 5 down to 38 max
    // adc_buffer[1] (y) has a range of 0 to 30

    usprintf(buf, "%d", adc_buffer[0]);
    GrStringDrawCentered(&g_sContext, buf, -1, GrContextDpyWidthGet(&g_sContext) / 2 - 40, ((GrContextDpyHeightGet(&g_sContext)) / 2) - 25, 0);

    usprintf(buf, "%d", adc_buffer[1]);
    GrStringDrawCentered(&g_sContext, buf, -1, GrContextDpyWidthGet(&g_sContext) / 2 + 40, ((GrContextDpyHeightGet(&g_sContext)) / 2) - 25, 0);
}

// main.cpp
int main(void)
{
    SysCtlClockSet (SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_8MHZ);
    IntMasterEnable ();

    ...

    // Here the touchscreen init and interrupt loop
    ts_Init();

    while (1)
    {
        ADCProcessorTrigger(ADC0_BASE, 1);
        DelayMs(500);
    }
}

  • See your post languishing - here is quick, plan of attack...

    1) Forum protocol - I've been in Display/Touch biz for past 30 years - doubtful anyone here (myself included) knows anything of, "TTW4028001."  Perhaps unwise to "expect" that would-be helpers will launch google-search in your behalf...  Your attachment of data sheet - and your summary of key Touch-Screen specs - eases effort on such helpers.

    2) Description suggests that you may have Touch-Screen issue - suggest that you deal with and gain confidence with the screen alone - first.

    3) Management of the MCU's ADC is next on the agenda - that will be best/proper focus for this forum - imho.

    Will assume that yours is 4-wire, analog resistive Touch Panel.  Thus 4 electrodes emerge - 2 for X, 2 for Y.  Theory of operation: Drive one "electrode pair" and read one of the orthogonal electrodes to determine touch presence and location.  Let's detail:

    a) remove all connections between the Touch Screen and your Eval or custom Board

    b) connect X+ electrode to 3V3, X- to ground.  (X+ is usually defined as right-side of Touch Panel)

    c) connect hi-Z DVM + to either of the Y electrodes, also connect a 100K resistor from this same Y electrode to ground - along with DVM -. 

    d) Touch the panel - closer you come to the X+ electrode the higher should be DVM reading.  I would expect you to achieve at least 80-90% of 3V3 as you near the panel's right edge.  Likewise - as you touch closer to X- electrode - DVM should approach 0V.  When you remove your touch - DVM should approach 0V.  Should this not result - switch your connection to the other Y electrode and repeat.

    e) Should Touch panel "read" as described - remove the drive completely from X electrodes and excite Y electrodes.  Make connections just as described above - substituting Y for X.  Repeat touch and reading procedure.  Always keep in mind - you are only to "drive" one electrode pair at a time - you read the orthogonal plane while driving the other.

    Method above should confirm the correctness of your Touch Panel.  After that you have far better chance of "massaging" MCU's ADC into operation.

    As regards your MCU test/verify - staff/I always employ known, stable, in-range voltages as initial ADC "sanity-check."  If you feed 1/2 scale ADC Max Voltage to an ADC input -your ADC should yield 1/2 of its 10 or 12 bit output.  I would not connect the Touch Panel until you've confirmed correct ADC readings.

    Once Touch-Screen and ADC have, "test/verified" - only then are you ready to reconnect Touch to your Eval/Custom Board.  Now the MCU must properly drive - in sequence - the Touch Electrodes while reading the orthogonal electrode for output.  This happens often enough that you will need a scope to confirm - but sense that you now may have necessary/sufficient info to proceed.  Bon chance mon ami...

     

  • That's great stuff.

    Thanks!

    With that information I will surely get a lot further.

    It is indeed a 4-wire, analog resistive Touch Panel, datasheet attached.

    On this custom board:

    http://www.mikroe.com/mikromedia/stellaris-m3/

    The touch screen examples for their own compiler mikroc, work fine.

    So I know the hardware setup is good.

    But their libraries are closed source, so I can't see the relevant code.

    So far I have written several drivers with StellarisWare, USB, Serial Flash, LCD etc.

    All functioning fine, now only for the touch panel.

  • Looks like I've guessed/anticipated well then - good for us. 

    Always hardest when "everything is new!"  (we call this, "Maximum Randomness")  Now you have some frame of reference and should be able to proceed far more systematically.

    Again - after you confirm that Touch Screen performs - urge that you drive several ADC inputs from simple pot (staying w/in voltage bounds).  Once you have both the Touch Screen and MCU's ADC mastered - should be straightforward to "marry" the two.  (but for the necessity to properly time/mate the voltage excitation provided sequentially - to each Touch plane.)   Bon chance...

  • Maarten,

    If you want to use the touch.c driver as a debugging reference or as a implementation, you can derive the g_plParmSet values with the dk-lm3s9d96 "calibrate" StellarisWare example. It implements the calibration algorithm found in this article: http://www.embedded.com/design/configurable-systems/4023968/How-To-Calibrate-Touch-Screens.

  • As John's jumped in shall leave you in his experienced/good hands.  (i.e. too many cooks...)

    Belief persists that, "proceeding methodically" - as outlined herein -  trumps new entities & complications...

  • I found a solution.

    The drive pins need to be configured as GPIO_PIN_TYPE_OD. With that configuration I get readings of around 900 resolution for both directions. So with the algorithm suggested by John, I should be able to extrapolate correct coordinates. Only one direction can be driven and read at a time. So in the interrupt I do a switch between these configurations to get X and Y readings alternatingly:

    void SetPins_ADC_X()
    {
        // Turn off the Y drive pin by switching to input
        GPIODirModeSet(TS_DRIVE_BASE, TS_Y_DRIVE_PIN, GPIO_DIR_MODE_IN);
        GPIOPadConfigSet(TS_DRIVE_BASE, TS_Y_DRIVE_PIN, GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_ANALOG);

        // Set X drive pin as output and GPIO_PIN_TYPE_OD
        GPIODirModeSet(TS_DRIVE_BASE, TS_X_DRIVE_PIN, GPIO_DIR_MODE_OUT);
        GPIOPadConfigSet(TS_DRIVE_BASE, TS_X_DRIVE_PIN, GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_OD);
    }

    void SetPins_ADC_Y()
    {
        // Turn off the X drive pin by switching to input
        GPIODirModeSet(TS_DRIVE_BASE, TS_X_DRIVE_PIN, GPIO_DIR_MODE_IN);
        GPIOPadConfigSet(TS_DRIVE_BASE, TS_X_DRIVE_PIN, GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_ANALOG);

        // Set Y drive pin as output and GPIO_PIN_TYPE_OD
        GPIODirModeSet(TS_DRIVE_BASE, TS_Y_DRIVE_PIN, GPIO_DIR_MODE_OUT);
        GPIOPadConfigSet(TS_DRIVE_BASE, TS_Y_DRIVE_PIN, GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_OD);
    }

    Thanks for the knowledgable input.

  • If yours is a 10 bit MCU ADC - then your 900 reading meets my earlier described 80-90% of full-scale - for "touch" near the + electrode.  Looks like you're on your way...

    Might my (lone) Sunday response warrant a, Verified Answer "tick" - to serve as a beacon for future readers?