A library for ADS7843 touch-screens which is interrupt driven, allowing you to register callback handlers for touchStart, touchMove and touchEnd events.

Dependents:   TouchScreenCalibrate TouchScreenGUIDemo

TouchScreen.cpp

Committer:
duncanFrance
Date:
2016-05-08
Revision:
2:e5ea47fb1ede
Parent:
0:3f0160100cc9
Child:
3:8b5fcf3857ac

File content as of revision 2:e5ea47fb1ede:

#include "TouchScreen.h"

TouchScreen::TouchScreen(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName irq) :
    _spi(mosi, miso, sclk), _cs(cs), _intPin(irq), _int(irq), _precision(TOUCHSCREEN_PRECISION_12)
{
    setLCDGeometry(320,240,TOUCHSCREEN_ORIENTATION_LANDSCAPE);
    setCalibration(540, 3700, 340, 3656);
    
    _movementThresholdSquared = TOUCHSCREEN_MOVEMENT_THRESHOLD * TOUCHSCREEN_MOVEMENT_THRESHOLD;
    _cs = 1;
    _spi.frequency(TOUCHSCREEN_SPI_FREQUENCY) ;
    _spi.format(8,0) ;
    _state = TOUCHSCREEN_STATE_IDLE;
    _int.fall(this, &TouchScreen::_fallInterruptHandler);
    _handlerThread = new Thread(&TouchScreen::_monitor, this);
}

void TouchScreen::setLCDGeometry(int width, int height, uint8_t orientation) {
    _lcdWidth = width;
    _lcdHeight = height;
    _lcdOrientation = orientation;
}

void TouchScreen::setCalibration(int xmin, int xmax, int ymin, int ymax) {
    _xmin = xmin; _xmax = xmax;
    _ymin = ymin; _ymax = ymax;   
}

void TouchScreen::setTouchStartHandler(TouchCallbackHandler handler)
{
    _touchStartHandler = handler;
}

void TouchScreen::setTouchMoveHandler(TouchCallbackHandler handler)
{
    _touchMoveHandler = handler;
}

void TouchScreen::setTouchEndHandler(TouchCallbackHandler handler)
{
    _touchEndHandler = handler;
}

void TouchScreen::setMovementTheshold(int thresholdInPixels) {
    _movementThresholdSquared = thresholdInPixels * thresholdInPixels;
}

TouchPosition TouchScreen::_getPosition()
{
    int xf, yf, xt, yt;
    int xmin = 65535, ymin = 65535;
    int xmax = 0, ymax = 0;

    unsigned int samples;

    TouchPosition position;

    xf=0;
    yf=0;
    samples=0;

    for(int i=0; i<32; i++) {
        xt = _read(TOUCHSCREEN_CMD_GETX + _precision);
        yt = _read(TOUCHSCREEN_CMD_GETY + _precision);
        if(xt>0 && yt>0) {
            xf+=xt;
            yf+=yt;
            samples++;
            if(xt < xmin) xmin = xt;
            if(xmax < xt) xmax = xt;
            if(yt < ymin) ymin = yt;
            if(ymax < yt) ymax = yt;
        }
    }
    // remove the outlier samples
    xf = xf - xmin - xmax;
    yf = yf - ymin - ymax;
    
    
    if(samples > 2) {
        samples -=2;
        xf = xf / samples;
        yf = yf / samples;
        
        // Now adjust and scale to fit screen
        
        if(_lcdOrientation & TOUCHSCREEN_ORIENTATION_PORTRAIT) {
            // swap x and y
            xt = xf;
            xf = yf;
            yf = xt;
        }
        
        position.x = xf;
        position.y = yf;
        position.screenX = ((xf - _xmin) * _lcdWidth)  / (_xmax - _xmin);
        position.screenY = ((yf - _ymin) * _lcdHeight) / (_ymax - _ymin);

        if(_lcdOrientation & TOUCHSCREEN_ORIENTATION_ROTATED) {
            position.screenX = _lcdWidth  - position.screenX;
            position.screenY = _lcdHeight - position.screenY;
        }

        position.valid = true;

    } else {
        position.valid = false;
    }

    return position;
}

void TouchScreen::_handleTouchStart()
{
    _lastPosition.valid = false;
    _currentPosition = _getPosition();
    // Raise an event if we got a valid position
    if(_currentPosition.valid && _touchStartHandler) {
        _touchStartHandler(_currentPosition);
    }
}

void TouchScreen::_handleTouchEnd()
{
    // Raise an event if we got a valid position
    if(_currentPosition.valid && _touchEndHandler) {
        _touchEndHandler(_currentPosition);
    }
}

void TouchScreen::_handleTouchMoved()
{
    // Update the position
    TouchPosition newPosition = _getPosition();
    if(!_lastPosition.valid) {
        _lastPosition = newPosition;
    }
    
    if(_touchMoveHandler && _moved(newPosition, _lastPosition)) {
        _lastPosition = _currentPosition;
        _currentPosition = newPosition;
        _touchMoveHandler(_currentPosition);
    }
}

bool TouchScreen::_moved(TouchPosition a, TouchPosition b)
{
    bool moved = false;

    if(a.valid && b.valid) {
        int dx = a.screenX - b.screenX;
        int dy = a.screenY - b.screenY;
        if((dx*dx + dy*dy) > _movementThresholdSquared) {
            moved = true;
        }
    }

    return moved;
}

void TouchScreen::_fallInterruptHandler()
{
    __disable_irq(); // is this needed in an ISR?

    _int.fall(0);
    _int.rise(this, &TouchScreen::_riseInterruptHandler);
    _ticker.attach_us(this, &TouchScreen::_tickerInterruptHandler, TOUCHSCREEN_DEBOUNCE_MICROS);
    _state = TOUCHSCREEN_STATE_DEBOUNCE;

    __enable_irq();

}

void TouchScreen::_riseInterruptHandler()
{
    __disable_irq();

    _int.rise(0);
    _int.fall(this, &TouchScreen::_fallInterruptHandler);
    _ticker.attach_us(this, &TouchScreen::_tickerInterruptHandler, TOUCHSCREEN_DEBOUNCE_MICROS);
    _state = TOUCHSCREEN_STATE_DEBOUNCE;

    __enable_irq();
}

void TouchScreen::_tickerInterruptHandler()
{
    __disable_irq();
    switch(_state) {
            /**
            * This state should not be reachable in this ISR
            **/
        case TOUCHSCREEN_STATE_IDLE:
            // fallthrough to set the pin interrupt handler

            /**
            * The debounce timer timed-out, so we have a valid level.
            **/
        case TOUCHSCREEN_STATE_DEBOUNCE:

            _ticker.detach();

            if(_intPin == 1) {
                _int.rise(0);
                _int.fall(this, &TouchScreen::_fallInterruptHandler);
                _state = TOUCHSCREEN_STATE_IDLE;

                if(_handlerThread != 0) {
                    _handlerThread->signal_set(TOUCHSCREEN_SIGNAL_END);
                }
            } else {
                _int.fall(0);
                _int.rise(this, &TouchScreen::_riseInterruptHandler);
                _state = TOUCHSCREEN_STATE_POLL;
                _ticker.attach_us(this, &TouchScreen::_tickerInterruptHandler, TOUCHSCREEN_POLL_MICROS);
                
                if(_handlerThread != 0) {
                    _handlerThread->signal_set(TOUCHSCREEN_SIGNAL_START);
                }
            }

            break;

        case TOUCHSCREEN_STATE_POLL:
            if(_handlerThread != 0) {
                _handlerThread->signal_set(TOUCHSCREEN_SIGNAL_POLL);
            }

    }
    __enable_irq();
}

unsigned int TouchScreen::_read(uint8_t cmd)
{
    unsigned int tmp;

    __disable_irq();
    _cs = 0;
    wait_us(1);
    _spi.write(cmd);
    wait_us(1);
    // First clock start conversion, so we only get 7-bits on this read
    tmp = _spi.write(0x00) << 5;
    // Remaining 5 bits on this read
    tmp |= _spi.write(0x00) >> 3;
    _cs = 1;

    if (_precision == TOUCHSCREEN_PRECISION_8) {
        tmp = tmp & 0x0ff0; // mask off lowest 4 bits since they are meaningless
    }
    tmp &= 0xfff;
    
    __enable_irq();
    return tmp;
}