A library with drivers for different peripherals on the LPC4088 QuickStart Board or related add-on boards.

Dependencies:   FATFileSystem

Dependents:   bare

Fork of EALib by EmbeddedArtists AB

Files at this revision

API Documentation at this revision

Comitter:
embeddedartists
Date:
Fri Oct 18 12:48:58 2013 +0200
Parent:
3:9d31a3c5013e
Child:
5:3290d7b766d5
Commit message:
Added AR1021 touch controller
Added MMA7455 accelerometer

Changed in this revision

AR1021.cpp Show annotated file Show diff for this revision Revisions of this file
AR1021.h Show annotated file Show diff for this revision Revisions of this file
EaLcdBoard.cpp Show annotated file Show diff for this revision Revisions of this file
EaLcdBoard.h Show annotated file Show diff for this revision Revisions of this file
GFXFb.cpp Show annotated file Show diff for this revision Revisions of this file
LcdController.cpp Show annotated file Show diff for this revision Revisions of this file
MMA7455.cpp Show annotated file Show diff for this revision Revisions of this file
MMA7455.h Show annotated file Show diff for this revision Revisions of this file
TSC2046.cpp Show annotated file Show diff for this revision Revisions of this file
TSC2046.h Show annotated file Show diff for this revision Revisions of this file
TouchPanel.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AR1021.cpp	Fri Oct 18 12:48:58 2013 +0200
@@ -0,0 +1,526 @@
+
+/******************************************************************************
+ * Includes
+ *****************************************************************************/
+
+#include "mbed.h"
+#include "mbed_debug.h"
+
+#include "AR1021.h"
+
+/******************************************************************************
+ * Defines and typedefs
+ *****************************************************************************/
+
+#define AR1021_REG_TOUCH_THRESHOLD        (0x02)
+#define AR1021_REG_SENS_FILTER            (0x03)
+#define AR1021_REG_SAMPLING_FAST          (0x04)
+#define AR1021_REG_SAMPLING_SLOW          (0x05)
+#define AR1021_REG_ACC_FILTER_FAST        (0x06)
+#define AR1021_REG_ACC_FILTER_SLOW        (0x07)
+#define AR1021_REG_SPEED_THRESHOLD        (0x08)
+#define AR1021_REG_SLEEP_DELAY            (0x0A)
+#define AR1021_REG_PEN_UP_DELAY           (0x0B)
+#define AR1021_REG_TOUCH_MODE             (0x0C)
+#define AR1021_REG_TOUCH_OPTIONS          (0x0D)
+#define AR1021_REG_CALIB_INSETS           (0x0E)
+#define AR1021_REG_PEN_STATE_REPORT_DELAY (0x0F)
+#define AR1021_REG_TOUCH_REPORT_DELAY     (0x11)
+
+
+#define AR1021_CMD_GET_VERSION                 (0x10)
+#define AR1021_CMD_ENABLE_TOUCH                (0x12)
+#define AR1021_CMD_DISABLE_TOUCH               (0x13)
+#define AR1021_CMD_CALIBRATE_MODE              (0x14)
+#define AR1021_CMD_REGISTER_READ               (0x20)
+#define AR1021_CMD_REGISTER_WRITE              (0x21)
+#define AR1021_CMD_REGISTER_START_ADDR_REQUEST (0x22)
+#define AR1021_CMD_REGISTER_WRITE_TO_EEPROM    (0x23)
+#define AR1021_CMD_EEPROM_READ                 (0x28)
+#define AR1021_CMD_EEPROM_WRITE                (0x29)
+#define AR1021_CMD_EEPROM_WRITE_TO_REGISTERS   (0x2B)
+
+#define AR1021_RESP_STAT_OK           (0x00)
+#define AR1021_RESP_STAT_CMD_UNREC    (0x01)
+#define AR1021_RESP_STAT_HDR_UNREC    (0x03)
+#define AR1021_RESP_STAT_TIMEOUT      (0x04)
+#define AR1021_RESP_STAT_CANCEL_CALIB (0xFC)
+
+
+#define AR1021_ERR_NO_HDR      (-1000)
+#define AR1021_ERR_INV_LEN     (-1001)
+#define AR1021_ERR_INV_RESP    (-1002)
+#define AR1021_ERR_INV_RESPLEN (-1003)
+#define AR1021_ERR_TIMEOUT     (-1004)
+
+// bit 7 is always 1 and bit 0 defines pen up or down
+#define AR1021_PEN_MASK (0x81)
+
+#define AR1021_NUM_CALIB_POINTS (4)
+
+AR1021::AR1021(PinName mosi, PinName miso, PinName sck, PinName cs, PinName siq) :
+_spi(mosi, miso, sck), _cs(cs), _siq(siq), _siqIrq(siq)
+{
+    _cs = 1; // active low
+
+    _spi.format(8, 1);
+    _spi.frequency(500000);
+
+    // default calibration inset is 25 -> (25/2 = 12.5%)
+    _inset = 25;
+
+    // make sure _calibPoint has an invalid value to begin with
+    // correct value is set in calibrateStart()
+    _calibPoint = AR1021_NUM_CALIB_POINTS+1;
+
+    _x = 0;
+    _y = 0;
+    _pen = 0;
+
+    _initialized = false;
+}
+
+
+bool AR1021::read(touchCoordinate_t &coord) {
+
+    if (!_initialized) return false;
+
+    coord.x = _x * _width/4095;
+    coord.y = _y * _height/4095;
+    coord.z = _pen;
+
+    return true;
+}
+
+
+bool AR1021::init(uint16_t width, uint16_t height) {
+    int result = 0;
+    bool ok = false;
+    int attempts = 0;
+
+    _width = width;
+    _height = height;
+
+    while (1) {
+
+        do {
+            // disable touch
+            result = cmd(AR1021_CMD_DISABLE_TOUCH, NULL, 0, NULL, 0);
+            if (result != 0) {
+                debug("disable touch failed (%d)\n", result);
+                break;
+            }
+
+            char regOffset = 0;
+            int regOffLen = 1;
+            result = cmd(AR1021_CMD_REGISTER_START_ADDR_REQUEST, NULL, 0,
+                    &regOffset, &regOffLen);
+            if (result != 0) {
+                debug("register offset request failed (%d)\n", result);
+                break;
+            }
+
+            // enable calibrated coordinates
+            //                  high, low address,                        len,  value
+            char toptions[4] = {0x00, AR1021_REG_TOUCH_OPTIONS+regOffset, 0x01, 0x01};
+            result = cmd(AR1021_CMD_REGISTER_WRITE, toptions, 4, NULL, 0);
+            if (result != 0) {
+                debug("register write request failed (%d)\n", result);
+                break;
+            }
+
+            // save registers to eeprom
+            result = cmd(AR1021_CMD_REGISTER_WRITE_TO_EEPROM, NULL, 0, NULL, 0);
+            if (result != 0) {
+                debug("register write to eeprom failed (%d)\n", result);
+                break;
+            }
+
+            // enable touch
+            result = cmd(AR1021_CMD_ENABLE_TOUCH, NULL, 0, NULL, 0);
+            if (result != 0) {
+                debug("enable touch failed (%d)\n", result);
+                break;
+            }
+
+            _siqIrq.rise(this, &AR1021::readTouchIrq);
+
+            _initialized = true;
+            ok = true;
+
+        } while(0);
+
+        if (ok) break;
+
+        // try to run the initialize sequence at most 2 times
+        if(++attempts >= 2) break;
+    }
+
+
+    return ok;
+}
+
+bool AR1021::calibrateStart() {
+    bool ok = false;
+    int result = 0;
+    int attempts = 0;
+
+    if (!_initialized) return false;
+
+    _siqIrq.rise(NULL);
+
+    while(1) {
+
+        do {
+            // disable touch
+            result = cmd(AR1021_CMD_DISABLE_TOUCH, NULL, 0, NULL, 0);
+            if (result != 0) {
+                debug("disable touch failed (%d)\n", result);
+                break;
+            }
+
+            char regOffset = 0;
+            int regOffLen = 1;
+            result = cmd(AR1021_CMD_REGISTER_START_ADDR_REQUEST, NULL, 0,
+                    &regOffset, &regOffLen);
+            if (result != 0) {
+                debug("register offset request failed (%d)\n", result);
+                break;
+            }
+
+            // set insets
+            // enable calibrated coordinates
+            //                high, low address,                       len,  value
+            char insets[4] = {0x00, AR1021_REG_CALIB_INSETS+regOffset, 0x01, _inset};
+            result = cmd(AR1021_CMD_REGISTER_WRITE, insets, 4, NULL, 0);
+            if (result != 0) {
+                debug("register write request failed (%d)\n", result);
+                break;
+            }
+
+            // calibration mode
+            char calibType = 4;
+            result = cmd(AR1021_CMD_CALIBRATE_MODE, &calibType, 1, NULL, 0, false);
+            if (result != 0) {
+                debug("calibration mode failed (%d)\n", result);
+                break;
+            }
+
+            _calibPoint = 0;
+            ok = true;
+
+        } while(0);
+
+        if (ok) break;
+
+        // try to run the calibrate mode sequence at most 2 times
+        if (++attempts >= 2) break;
+    }
+
+    return ok;
+}
+
+bool AR1021::getNextCalibratePoint(uint16_t* x, uint16_t* y) {
+
+    if (!_initialized) return false;
+    if (x == NULL || y == NULL) return false;
+
+    int xInset = (_width * _inset / 100) / 2;
+    int yInset = (_height * _inset / 100) / 2;
+
+    switch(_calibPoint) {
+    case 0:
+        *x = xInset;
+        *y = yInset;
+        break;
+    case 1:
+        *x = _width - xInset;
+        *y = yInset;
+        break;
+    case 2:
+        *x = _width - xInset;
+        *y = _height - yInset;
+        break;
+    case 3:
+        *x = xInset;
+        *y = _height - yInset;
+        break;
+    default:
+        return false;
+    }
+
+    return true;
+}
+
+bool AR1021::waitForCalibratePoint(bool* morePoints, uint32_t timeout) {
+    int result = 0;
+    bool ret = false;
+
+    if (!_initialized) return false;
+
+    do {
+        if (morePoints == NULL || _calibPoint >= AR1021_NUM_CALIB_POINTS) {
+            break;
+        }
+
+        // wait for response
+        result = waitForCalibResponse(timeout);
+        if (result != 0) {
+            debug("wait for calibration response failed (%d)\n", result);
+            break;
+        }
+
+        _calibPoint++;
+        *morePoints = (_calibPoint < AR1021_NUM_CALIB_POINTS);
+
+
+        // no more points -> enable touch
+        if (!(*morePoints)) {
+
+            // wait for calibration data to be written to eeprom
+            // before enabling touch
+            result = waitForCalibResponse(timeout);
+            if (result != 0) {
+                debug("wait for calibration response failed (%d)\n", result);
+                break;
+            }
+
+
+            // clear chip-select since calibration is done;
+            _cs = 1;
+
+            result = cmd(AR1021_CMD_ENABLE_TOUCH, NULL, 0, NULL, 0);
+            if (result != 0) {
+                debug("enable touch failed (%d)\n", result);
+                break;
+            }
+
+            _siqIrq.rise(this, &AR1021::readTouchIrq);
+        }
+
+        ret = true;
+
+    } while (0);
+
+
+
+    if (!ret) {
+        // make sure to set chip-select off in case of an error
+        _cs = 1;
+        // calibration must restart if an error occurred
+        _calibPoint = AR1021_NUM_CALIB_POINTS+1;
+    }
+
+
+
+    return ret;
+}
+
+int AR1021::cmd(char cmd, char* data, int len, char* respBuf, int* respLen,
+        bool setCsOff) {
+
+    int ret = 0;
+
+    // command request
+    // ---------------
+    // 0x55 len cmd data
+    // 0x55 = header
+    // len = data length + cmd (1)
+    // data = data to send
+
+    _cs = 0;
+
+    _spi.write(0x55);
+    wait_us(50); // according to data sheet there must be an inter-byte delay of ~50us
+    _spi.write(len+1);
+    wait_us(50);
+    _spi.write(cmd);
+    wait_us(50);
+
+    for(int i = 0; i < len; i++) {
+        _spi.write(data[i]);
+        wait_us(50);
+    }
+
+
+    // wait for response (siq goes high when response is available)
+    Timer t;
+    t.start();
+    while(_siq.read() != 1 && t.read_ms() < 1000);
+
+
+    // command response
+    // ---------------
+    // 0x55 len status cmd data
+    // 0x55 = header
+    // len = number of bytes following the len byte
+    // status = status
+    // cmd = command ID
+    // data = data to receive
+
+
+    do {
+
+        if (t.read_ms() >= 1000) {
+            ret = AR1021_ERR_TIMEOUT;
+            break;
+        }
+
+        int head = _spi.write(0);
+        if (head != 0x55) {
+            ret = AR1021_ERR_NO_HDR;
+            break;
+        }
+
+        wait_us(50);
+        int len = _spi.write(0);
+        if (len < 2) {
+            ret = AR1021_ERR_INV_LEN;
+            break;
+        }
+
+        wait_us(50);
+        int status = _spi.write(0);
+        if (status != AR1021_RESP_STAT_OK) {
+            ret = -status;
+            break;
+        }
+
+        wait_us(50);
+        int cmdId = _spi.write(0);
+        if (cmdId != cmd) {
+            ret = AR1021_ERR_INV_RESP;
+            break;
+        }
+
+        if ( ((len-2) > 0 && respLen  == NULL)
+                || ((len-2) > 0 && respLen != NULL && *respLen < (len-2))) {
+            ret = AR1021_ERR_INV_RESPLEN;
+            break;
+        }
+
+        for (int i = 0; i < len-2;i++) {
+            wait_us(50);
+            respBuf[i] = _spi.write(0);
+        }
+        if (respLen != NULL) {
+            *respLen = len-2;
+        }
+
+        // make sure we wait 50us before issuing a new cmd
+        wait_us(50);
+
+    } while (0);
+
+
+
+    // disable chip-select if setCsOff is true or if an error occurred
+    if (setCsOff || ret != 0) {
+        _cs = 1;
+    }
+
+
+
+    return ret;
+}
+
+int AR1021::waitForCalibResponse(uint32_t timeout) {
+    Timer t;
+    int ret = 0;
+
+    t.start();
+
+    // wait for siq
+    while (_siq.read() != 1 &&
+            (timeout == 0 || (uint32_t)t.read_ms() < (int)timeout));
+
+
+    do {
+
+        if (timeout >  0 && (uint32_t)t.read_ms() >= timeout) {
+            ret = AR1021_ERR_TIMEOUT;
+            break;
+        }
+
+        int head = _spi.write(0);
+        if (head != 0x55) {
+            ret = AR1021_ERR_NO_HDR;
+            break;
+        }
+
+        wait_us(50);
+        int len = _spi.write(0);
+        if (len != 2) {
+            ret = AR1021_ERR_INV_LEN;
+            break;
+        }
+
+        wait_us(50);
+        int status = _spi.write(0);
+        if (status != AR1021_RESP_STAT_OK) {
+            ret = -status;
+            break;
+        }
+
+        wait_us(50);
+        int cmdId = _spi.write(0);
+        if (cmdId != 0x14) {
+            ret = AR1021_ERR_INV_RESP;
+            break;
+        }
+
+
+        // make sure we wait 50us before issuing a new cmd
+        wait_us(50);
+
+    } while (0);
+
+
+    return ret;
+}
+
+
+void AR1021::readTouchIrq() {
+    while(_siq.read() == 1) {
+
+        _cs = 0;
+
+        // touch coordinates are sent in a 5-byte data packet
+
+        int pen = _spi.write(0);
+        wait_us(50);
+
+        int xlo = _spi.write(0);
+        wait_us(50);
+
+        int xhi = _spi.write(0);
+        wait_us(50);
+
+        int ylo = _spi.write(0);
+        wait_us(50);
+
+        int yhi = _spi.write(0);
+        wait_us(50);
+
+        _cs = 1;
+
+
+        // pen down
+        if ((pen&AR1021_PEN_MASK) == (1<<7|1<<0)) {
+            _pen = 1;
+        }
+        // pen up
+        else if ((pen&AR1021_PEN_MASK) == (1<<7)){
+            _pen = 0;
+        }
+        // invalid value
+        else {
+            continue;
+        }
+
+        _x = ((xhi<<7)|xlo);
+        _y = ((yhi<<7)|ylo);
+
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AR1021.h	Fri Oct 18 12:48:58 2013 +0200
@@ -0,0 +1,65 @@
+
+#ifndef AR1021_H
+#define AR1021_H
+
+#include "TouchPanel.h"
+
+/**
+ * Microchip Touch Screen Controller (AR1021).
+ *
+ * Please note that this touch panel has an on-board storage for
+ * calibration data. Once a successful calibration has been performed
+ * it is not needed to do additional calibrations since the stored
+ * calibration data will be used.
+ */
+class AR1021 : public TouchPanel {
+public:
+
+
+    /**
+     * Constructor
+     *
+     * @param mosi SPI MOSI pin
+     * @param miso SPI MISO pin
+     * @param sck SPI SCK pin
+     * @param cs chip-select pin
+     * @param siq interrupt pin
+     */
+    AR1021(PinName mosi, PinName miso, PinName sck, PinName cs, PinName siq);
+
+
+    bool init(uint16_t width, uint16_t height);
+    bool read(touchCoordinate_t &coord);
+    bool calibrateStart();
+    bool getNextCalibratePoint(uint16_t* x, uint16_t* y);
+    bool waitForCalibratePoint(bool* morePoints, uint32_t timeout);
+
+private:
+
+
+    SPI _spi;
+    DigitalOut _cs;
+    DigitalIn _siq;
+    InterruptIn _siqIrq;
+    bool _initialized;
+
+
+    int32_t _x;
+    int32_t _y;
+    int32_t _pen;
+
+    uint16_t _width;
+    uint16_t _height;
+    uint8_t _inset;
+
+    int _calibPoint;
+
+
+    int cmd(char cmd, char* data, int len, char* respBuf, int* respLen, bool setCsOff=true);
+    int waitForCalibResponse(uint32_t timeout);
+    void readTouchIrq();
+
+
+};
+
+#endif
--- a/EaLcdBoard.cpp	Wed Oct 09 07:51:52 2013 +0000
+++ b/EaLcdBoard.cpp	Fri Oct 18 12:48:58 2013 +0200
@@ -1,7 +1,17 @@
+
+/******************************************************************************
+ * Includes
+ *****************************************************************************/
 
 #include "mbed.h"
 #include "EaLcdBoard.h"
 
+/******************************************************************************
+ * Defines and typedefs
+ *****************************************************************************/
+
+#define PORT_PIN_BUG_IN_MBED_SDK
+
 #define LCDB_MAGIC 0xEA01CDAE
 
 
@@ -38,7 +48,7 @@
 /*
  * Set which sequence string version that is supported
  */
-#define LCDB_SEQ_VER 1
+#define LCDB_SEQ_VER 2
 
 #ifndef MIN
 #define MIN(x, y) (((x)<(y))?(x):(y))
@@ -163,6 +173,10 @@
 
     nxp_lcd_param_t lcdParam;
 
+    if (cfg == NULL) {
+        return InvalidArgument;
+    }
+
     getStore(&h);
 
     if (h.magic != LCDB_MAGIC) {
@@ -196,6 +210,10 @@
 EaLcdBoard::Result EaLcdBoard::getDisplayName(char* buf, int len) {
     store_t h;
 
+    if (buf == NULL) {
+        return InvalidArgument;
+    }
+
     getStore(&h);
 
     if (h.magic != LCDB_MAGIC) {
@@ -214,6 +232,10 @@
 EaLcdBoard::Result EaLcdBoard::getDisplayMfg(char* buf, int len) {
     store_t h;
 
+    if (buf == NULL) {
+        return InvalidArgument;
+    }
+
     getStore(&h);
 
     if (h.magic != LCDB_MAGIC) {
@@ -232,6 +254,10 @@
 EaLcdBoard::Result EaLcdBoard::getInitSeq(char* buf, int len) {
     store_t h;
 
+    if (buf == NULL) {
+        return InvalidArgument;
+    }
+
     getStore(&h);
 
     if (h.magic != LCDB_MAGIC) {
@@ -251,6 +277,10 @@
 EaLcdBoard::Result EaLcdBoard::getPowerDownSeq(char* buf, int len) {
     store_t h;
 
+    if (buf == NULL) {
+        return InvalidArgument;
+    }
+
     getStore(&h);
 
     if (h.magic != LCDB_MAGIC) {
@@ -267,6 +297,93 @@
     return Ok;
 }
 
+EaLcdBoard::Result EaLcdBoard::getTouchParameters(TouchParams_t* params) {
+    store_t h;
+
+    if (params == NULL) {
+        return InvalidArgument;
+    }
+
+    getStore(&h);
+
+    if (h.magic != LCDB_MAGIC) {
+        return InvalidStorage;
+    }
+
+
+    if (eepromRead((uint8_t*)params, h.tsOff,
+            (h.end-h.tsOff)) == -1) {
+        return InvalidStorage;
+    }
+
+
+    if (params->panelId <= TouchPanelInvalidFirst
+            || params->panelId >= TouchPanelInvalidLast) {
+        params->panelId = TouchPanelUnknown;
+    }
+
+
+    return Ok;
+}
+
+EaLcdBoard::Result EaLcdBoard::storeParameters(
+  const char* lcdName,
+  const char* lcdMfg,
+  LcdController::Config* cfg,
+  const char* initSeqStr,
+  const char* pdSeqStr,
+  TouchParams_t* touch,
+  bool controlWp)
+{
+    store_t h;
+    nxp_lcd_param_t lcdParam;
+
+    if (lcdName == NULL || lcdMfg == NULL || cfg == NULL
+            || initSeqStr == NULL || pdSeqStr == NULL) {
+        return InvalidArgument;
+
+    }
+
+
+    lcdParam.h_back_porch         = cfg->horizontalBackPorch;
+    lcdParam.h_front_porch        = cfg->horizontalFrontPorch;
+    lcdParam.h_sync_pulse_width   = cfg->hsync;
+    lcdParam.pixels_per_line      = cfg->width;
+    lcdParam.v_back_porch         = cfg->verticalBackPorch;
+    lcdParam.v_front_porch        = cfg->verticalFrontPorch;
+    lcdParam.v_sync_pulse_width   = cfg->vsync;
+    lcdParam.lines_per_panel      = cfg->height;
+    lcdParam.invert_output_enable = (cfg->invertOutputEnable ? 1 : 0);
+    lcdParam.invert_panel_clock   = (cfg->invertPanelClock ? 1 : 0);
+    lcdParam.invert_hsync         = (cfg->invertHsync ? 1 : 0);
+    lcdParam.invert_vsync         = (cfg->invertVsync ? 1 : 0);
+    lcdParam.ac_bias_frequency    = cfg->acBias;
+    lcdParam.optimal_clock        = cfg->optimalClock;
+    lcdParam.lcd_panel_type       = (nxp_lcd_panel_t)cfg->panelType;
+    lcdParam.dual_panel           = (cfg->dualPanel ? 1 : 0);
+
+
+    h.magic = LCDB_MAGIC;
+    strncpy((char*)h.lcd_name, lcdName, 30);
+    strncpy((char*)h.lcd_mfg, lcdMfg, 30);
+
+    h.lcdParamOff = sizeof(store_t);
+    h.initOff     = h.lcdParamOff + sizeof(nxp_lcd_param_t);
+    h.pdOff       = h.initOff + strlen(initSeqStr)+1;
+    h.tsOff       = h.pdOff + strlen(pdSeqStr)+1;
+    h.end         = h.tsOff + sizeof(TouchParams_t);
+
+    if (controlWp) setWriteProtect(false);
+    eepromWrite((uint8_t*)&h,         0,             h.lcdParamOff);
+    eepromWrite((uint8_t*)&lcdParam,  h.lcdParamOff, (h.initOff-h.lcdParamOff));
+    eepromWrite((uint8_t*)initSeqStr, h.initOff,     (h.pdOff-h.initOff));
+    eepromWrite((uint8_t*)pdSeqStr,   h.pdOff,       (h.tsOff-h.pdOff));
+    eepromWrite((uint8_t*)touch,      h.tsOff,       (h.end-h.tsOff));
+    if (controlWp) setWriteProtect(true);
+
+    return Ok;
+}
+
 EaLcdBoard::Result EaLcdBoard::getStore(store_t* store) {
     int num = 0;
 
@@ -287,19 +404,26 @@
 int EaLcdBoard::eepromRead(uint8_t* buf, uint16_t offset, uint16_t len) {
     int i = 0;
     char off[2];
+    uint16_t retLen = 0;
 
     if (len > LCDB_EEPROM_TOTAL_SIZE || offset+len > LCDB_EEPROM_TOTAL_SIZE) {
         return -1;
     }
 
+    wait_ms(10);
+
     off[0] = ((offset >> 8) & 0xff);
     off[1] = (offset & 0xff);
 
-    _i2c.write(LCDB_EEPROM_I2C_ADDR, (char*)off, 2);
-    for ( i = 0; i < 0x2000; i++);
-    _i2c.read(LCDB_EEPROM_I2C_ADDR, (char*)buf, len);
+    do {
+        if (_i2c.write(LCDB_EEPROM_I2C_ADDR, (char*)off, 2) != 0) break;
+        for ( i = 0; i < 0x2000; i++);
+        if (_i2c.read(LCDB_EEPROM_I2C_ADDR, (char*)buf, len) != 0) break;
 
-    return len;
+        retLen = len;
+    } while(0);
+
+    return retLen;
 }
 
 int EaLcdBoard::eepromWrite(uint8_t* buf, uint16_t offset, uint16_t len) {
@@ -312,6 +436,8 @@
         return -1;
     }
 
+    wait_ms(1);
+
     wLen = LCDB_EEPROM_PAGE_SIZE - (off % LCDB_EEPROM_PAGE_SIZE);
     wLen = MIN(wLen, len);
 
@@ -319,11 +445,11 @@
         tmp[0] = ((off >> 8) & 0xff);
         tmp[1] = (off & 0xff);
         memcpy(&tmp[2], (void*)&buf[written], wLen);
-        _i2c.write(LCDB_EEPROM_I2C_ADDR, (char*)tmp, wLen+2);
+        if (_i2c.write(LCDB_EEPROM_I2C_ADDR, (char*)tmp, wLen+2) != 0) break;
 
         // delay to wait for a write cycle
         //eepromDelay();
-        wait_ms(1);
+        wait_ms(10);
 
         len     -= wLen;
         written += wLen;
@@ -400,6 +526,11 @@
 
             break;
 
+            // exec pin set
+        case 'p':
+            execPinSet(c, len-1);
+            break;
+
         }
 
         if (result != Ok) {
@@ -457,6 +588,54 @@
     return Ok;
 }
 
+#ifdef PORT_PIN_BUG_IN_MBED_SDK
+static PinName port_pin2(PortName port, int pin_n) {
+    return (PinName)(((port << 5) | pin_n));
+}
+#endif
+
+
+EaLcdBoard::Result EaLcdBoard::execPinSet(char* cmd, uint32_t len) {
+
+    PortName port;
+
+    do {
+        // cmd: 0_02=1 means p0.2 = 1
+        if (len < 6) break;
+        if (cmd[1] != '_' || cmd[4] != '=') break;
+
+        // port
+        int portnum  = cmd[0] - '0';
+        if (portnum < 0 || portnum > 5) break;
+        port = (PortName)portnum;
+
+        // pin
+        int pinnum = (10*(cmd[2]-'0'))+ cmd[3]-'0';
+        if (pinnum < 0 || pinnum > 31) break;
+
+        // value
+        int value = cmd[5]-'0';
+        if (!(value == 0 || value == 1)) break;
+
+#ifdef PORT_PIN_BUG_IN_MBED_SDK
+        PinName pin = port_pin2(port, pinnum);
+#else
+        PinName pin = port_pin(port, pinnum);
+#endif
+
+        gpio_t gp;
+        gpio_init(&gp, pin, PIN_OUTPUT);
+        gpio_write(&gp, value);
+
+        return Ok;
+
+    } while (false);
+
+
+    return InvalidCommandString;
+}
+
+
 // ###########################
 // PCA9532 is used as a control inteface to the display.
 // voltages can be turned on/off and backlight can be controlled.
@@ -549,6 +728,15 @@
     setLeds();
 }
 
+void EaLcdBoard::setWriteProtect(bool enable)
+{
+    if (enable) {
+        pca9532_setLeds(0, LCDB_EEPROM_WP);
+    } else {
+        pca9532_setLeds(LCDB_EEPROM_WP, 0);
+    }
+}
+
 void EaLcdBoard::set3V3Signal(bool enabled) {
     if (enabled) {
         pca9532_setLeds(LCDB_CTRL_3V3, 0);
@@ -598,5 +786,3 @@
 }
 
 
-
-
--- a/EaLcdBoard.h	Wed Oct 09 07:51:52 2013 +0000
+++ b/EaLcdBoard.h	Fri Oct 18 12:48:58 2013 +0200
@@ -24,6 +24,30 @@
         NameBufferSize = 30
     };
 
+    enum TouchPanelId {
+        // must be first
+        TouchPanelInvalidFirst = 0x80,
+
+        // Texas Instruments Touch Screen Controller (TSC2046).
+        TouchPanel_TSC2046,
+        // Microchip Touch Screen Controller (AR1021).
+        TouchPanel_AR1021,
+
+        // Must be after last panel ID
+        TouchPanelInvalidLast,
+        // unknown touch panel
+        TouchPanelUnknown = 0xFF
+
+    };
+
+    typedef struct {
+      uint8_t swap;         // set to 1 if x and y should be swapped
+      uint32_t xres;        // x resistance
+      uint32_t yres;        // y resistance
+      TouchPanelId panelId; // identifies touch panel
+
+    } TouchParams_t;
+
     /** Create an interface to an Embedded Artists LCD Board
      *
      * @param sda I2C data line pin
@@ -111,6 +135,36 @@
      */
     Result getPowerDownSeq(char* buf, int len);
 
+    /**
+     * Get the touch panel parameters stored in persistent storage
+     *
+     *  @param params pointer to a configuration object. Parameters are copied to
+     *  this object.
+     *
+     * @returns the result of the operation
+     */
+    Result getTouchParameters(TouchParams_t* params);
+
+    /**
+     * Write display parameters to the EEPROM. Please use this function with
+     * care since original parameters will be overwritten and cannot be restored.
+     *
+     * @param lcdName the name of the display
+     * @param lcdMfg the display manufacturer
+     * @param cfg the display configuration parameters
+     * @param initSeqStr the initialization sequence string
+     * @param pdSeqStr the power down sequence string
+     * @param touch touch panel parameters
+     */
+    Result storeParameters(
+      const char* lcdName,
+      const char* lcdMfg,
+      LcdController::Config* cfg,
+      const char* initSeqStr,
+      const char* pdSeqStr,
+      TouchParams_t* touch,
+      bool controlWp = false);
+
 
 private:
 
@@ -134,6 +188,7 @@
     Result checkVersion(char* v, uint32_t len);
     Result execDelay(char* del, uint32_t len);
     Result execSeqCtrl(char* cmd, uint32_t len);
+    Result execPinSet(char* cmd, uint32_t len);
 
     void setLsStates(uint16_t states, uint8_t* ls, uint8_t mode);
     void setLeds(void);
@@ -142,6 +197,7 @@
     void pca9532_setBlink0Duty(uint8_t duty);
     void pca9532_setBlink0Leds(uint16_t ledMask);
 
+    void setWriteProtect(bool enable);
     void set3V3Signal(bool enabled);
     void set5VSignal(bool enabled);
     void setDisplayEnableSignal(bool enabled);
@@ -157,5 +213,3 @@
 };
 
 #endif
-
-
--- a/GFXFb.cpp	Wed Oct 09 07:51:52 2013 +0000
+++ b/GFXFb.cpp	Fri Oct 18 12:48:58 2013 +0200
@@ -1,3 +1,8 @@
+
+/******************************************************************************
+ * Includes
+ *****************************************************************************/
+
 #include "mbed.h"
 #include "GFXFb.h"
 
--- a/LcdController.cpp	Wed Oct 09 07:51:52 2013 +0000
+++ b/LcdController.cpp	Fri Oct 18 12:48:58 2013 +0200
@@ -1,7 +1,15 @@
+
+/******************************************************************************
+ * Includes
+ *****************************************************************************/
 
 #include "mbed.h"
 #include "LcdController.h"
 
+/******************************************************************************
+ * Defines and typedefs
+ *****************************************************************************/
+
 #undef _SBF
 #define _SBF(p,v) (((uint32_t)(v)) << (p))
 
@@ -386,7 +394,8 @@
     LPC_IOCON->P1_28 |= 7; /* LCD_VD_22 @ P1.28 */
     LPC_IOCON->P1_29 |= 7; /* LCD_VD_23 @ P1.29 */
 
-    LPC_IOCON->P2_0 |= 7; /* LCD_PWR @ P2.0 */
+    // pwr always controlled by gpio
+//    LPC_IOCON->P2_0 |= 7; /* LCD_PWR @ P2.0 */
     LPC_IOCON->P2_1 |= 7; /* LCD_LE  @ P2.1 */
     LPC_IOCON->P2_2 |= 7; /* LCD_DCLK @ P2.2 */
     LPC_IOCON->P2_3 |= 7; /* LCD_FP @ P2.3 */
@@ -433,5 +442,3 @@
 }
 
 
-
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MMA7455.cpp	Fri Oct 18 12:48:58 2013 +0200
@@ -0,0 +1,298 @@
+
+/******************************************************************************
+ * Includes
+ *****************************************************************************/
+
+#include "mbed.h"
+#include "mbed_debug.h"
+
+#include "MMA7455.h"
+
+/******************************************************************************
+ * Defines and typedefs
+ *****************************************************************************/
+
+#define MMA7455_I2C_ADDR (0x1D << 1)
+
+#define MMA7455_ADDR_XOUTL  0x00
+#define MMA7455_ADDR_XOUTH  0x01
+#define MMA7455_ADDR_YOUTL  0x02
+#define MMA7455_ADDR_YOUTH  0x03
+#define MMA7455_ADDR_ZOUTL  0x04
+#define MMA7455_ADDR_ZOUTH  0x05
+#define MMA7455_ADDR_XOUT8  0x06
+#define MMA7455_ADDR_YOUT8  0x07
+#define MMA7455_ADDR_ZOUT8  0x08
+#define MMA7455_ADDR_STATUS 0x09
+#define MMA7455_ADDR_DETSRC 0x0A
+#define MMA7455_ADDR_TOUT   0x0B
+#define MMA7455_ADDR_I2CAD  0x0D
+#define MMA7455_ADDR_USRINF 0x0E
+#define MMA7455_ADDR_WHOAMI 0x0F
+#define MMA7455_ADDR_XOFFL  0x10
+#define MMA7455_ADDR_XOFFH  0x11
+#define MMA7455_ADDR_YOFFL  0x12
+#define MMA7455_ADDR_YOFFH  0x13
+#define MMA7455_ADDR_ZOFFL  0x14
+#define MMA7455_ADDR_ZOFFH  0x15
+#define MMA7455_ADDR_MCTL   0x16
+#define MMA7455_ADDR_INTRST 0x17
+#define MMA7455_ADDR_CTL1   0x18
+#define MMA7455_ADDR_CTL2   0x19
+#define MMA7455_ADDR_LDTH   0x1A
+#define MMA7455_ADDR_PDTH   0x1B
+#define MMA7455_ADDR_PW     0x1C
+#define MMA7455_ADDR_LT     0x1D
+#define MMA7455_ADDR_TW     0x1E
+
+#define MMA7455_MCTL_MODE(m) ((m) << 0)
+#define MMA7455_MCTL_GLVL(g) ((g) << 2)
+
+#define MMA7455_STATUS_DRDY (1 << 0)
+#define MMA7455_STATUS_DOVR (1 << 1)
+#define MMA7455_STATUS_PERR (1 << 2)
+
+
+MMA7455::MMA7455(PinName sda, PinName scl) : _i2c(sda, scl)
+{
+    _mode = ModeStandby;
+    _range = Range_8g;
+
+    _xOff = 0;
+    _yOff = 0;
+    _zOff = 0;
+}
+
+bool MMA7455::setMode(Mode mode) {
+    bool result = false;
+    int mCtrl = 0;
+
+    do {
+        mCtrl = getModeControl();
+        if (mCtrl < 0) break;
+
+        mCtrl &= ~(0x03 << 0);
+        mCtrl |= MMA7455_MCTL_MODE(mode);
+
+        if (setModeControl((uint8_t)mCtrl) < 0) {
+            break;
+        }
+
+        _mode = mode;
+        result = true;
+    } while(0);
+
+
+
+    return result;
+}
+
+bool MMA7455::setRange(Range range) {
+    bool result = false;
+    int mCtrl = 0;
+
+    do {
+        mCtrl = getModeControl();
+        if (mCtrl < 0) break;
+
+        mCtrl &= ~(0x03 << 2);
+        mCtrl |= MMA7455_MCTL_GLVL(range);
+
+        if (setModeControl((uint8_t)mCtrl) < 0) {
+            break;
+        }
+
+        _range = range;
+        result = true;
+    } while(0);
+
+
+
+    return result;
+
+}
+
+bool MMA7455::read(int32_t& x, int32_t& y, int32_t& z) {
+    bool result = false;
+
+
+    // nothing to read in standby mode
+    if (_mode == ModeStandby) return false;
+
+    // wait for ready flag
+    int status = 0;
+    do {
+      status = getStatus();
+    } while (status >= 0 && (status & MMA7455_STATUS_DRDY) == 0);
+
+
+    do {
+        if (status < 0) break;
+
+
+        char buf[6];
+        buf[0] = MMA7455_ADDR_XOUTL;
+        if (_i2c.write(MMA7455_I2C_ADDR, buf, 1) != 0) break;
+        if (_i2c.read(MMA7455_I2C_ADDR, buf, 6) != 0) break;
+
+        // check if second bit is set in high byte -> negative value
+        // expand negative value to full byte
+        if (buf[1] & 0x02) buf[1] |= 0xFC;
+        if (buf[3] & 0x02) buf[3] |= 0xFC;
+        if (buf[5] & 0x02) buf[5] |= 0xFC;
+
+        x = (int16_t)((buf[1] << 8) | buf[0]) + _xOff;
+        y = (int16_t)((buf[3] << 8) | buf[2]) + _yOff;
+        z = (int16_t)((buf[5] << 8) | buf[4]) + _zOff;
+
+
+        result = true;
+
+    } while(0);
+
+
+    return result;
+}
+
+bool MMA7455::calibrate() {
+    bool result = false;
+    bool failed = false;
+
+    int32_t x = 0;
+    int32_t y = 0;
+    int32_t z = 0;
+
+    int32_t xr = 0;
+    int32_t yr = 0;
+    int32_t zr = 0;
+
+    int xOff = 0;
+    int yOff = 0;
+    int zOff = 16;
+    if (_range == Range_2g) {
+        zOff = 64;
+    }
+    if (_range == Range_4g) {
+        zOff = 32;
+    }
+
+    do {
+
+        // get an average of 6 values
+        for (int i = 0; i < 6; i++) {
+            if (!read(xr, yr, zr)) {
+                failed = true;
+                break;
+            }
+            x += xr;
+            y += yr;
+            z += zr;
+
+            wait_ms(100);
+        }
+
+        if (failed) break;
+        x /= 6;
+        y /= 6;
+        z /= 6;
+
+        xOff -= x;
+        yOff -= y;
+        zOff -= z;
+
+        /*
+         * For some reason we have not got correct/reliable calibration
+         * by using the offset drift registers. Instead we are
+         * calculating the offsets and store them in member variables.
+         *
+         * These member variables are then used in the read() method
+         */
+
+        _xOff = xOff;
+        _yOff = yOff;
+        _zOff = zOff;
+
+
+        result = true;
+
+    } while (0);
+
+
+
+    return result;
+}
+
+bool MMA7455::setCalibrationOffsets(int32_t xOff, int32_t yOff, int32_t zOff) {
+    _xOff = xOff;
+    _yOff = yOff;
+    _zOff = zOff;
+
+    return true;
+}
+
+bool MMA7455::getCalibrationOffsets(int32_t& xOff, int32_t& yOff, int32_t& zOff) {
+    xOff = _xOff;
+    yOff = _yOff;
+    zOff = _zOff;
+
+    return true;
+}
+
+int MMA7455::getStatus() {
+    int result = -1;
+    char data[1];
+
+    do {
+        data[0] = MMA7455_ADDR_STATUS;
+        if (_i2c.write(MMA7455_I2C_ADDR, data, 1) != 0) break;
+
+        if (_i2c.read(MMA7455_I2C_ADDR, data, 1) != 0) break;
+
+        result = data[0];
+
+    } while (0);
+
+
+
+    return result;
+}
+
+int MMA7455::getModeControl() {
+
+    int result = -1;
+    char data[1];
+
+    do {
+        data[0] = MMA7455_ADDR_MCTL;
+        if (_i2c.write(MMA7455_I2C_ADDR, data, 1) != 0) break;
+
+        if (_i2c.read(MMA7455_I2C_ADDR, data, 1) != 0) break;
+
+        result = data[0];
+
+    } while (0);
+
+
+
+    return result;
+}
+
+int MMA7455::setModeControl(uint8_t mctl) {
+    int result = -1;
+    char data[2];
+
+    do {
+        data[0] = MMA7455_ADDR_MCTL;
+        data[1] = (char)mctl;
+        if (_i2c.write(MMA7455_I2C_ADDR, data, 2) != 0) break;
+
+        result = 0;
+
+    } while (0);
+
+
+
+    return result;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MMA7455.h	Fri Oct 18 12:48:58 2013 +0200
@@ -0,0 +1,94 @@
+
+#ifndef MMA7455_H
+#define MMA7455_H
+
+
+/**
+ * Freescale Accelerometer MMA7455.
+ */
+class MMA7455 {
+public:
+
+    enum Mode {
+        ModeStandby = 0,
+        ModeMeasurement = 1,
+    };
+
+    /** Acceleration range */
+    enum Range {
+        Range_8g = 0,
+        Range_2g = 1,
+        Range_4g = 2
+    };
+
+    /**
+     * Create an interface to the MMA7455 accelerometer
+     *
+     * @param sda I2C data line pin
+     * @param scl I2C clock line pin
+     */
+    MMA7455(PinName sda, PinName scl);
+
+    bool setMode(Mode mode);
+    bool setRange(Range range);
+
+    bool read(int32_t& x, int32_t& y, int32_t& z);
+
+    /**
+     * Calibrate for 0g, that is, calculate offset to achieve
+     * 0g values when accelerometer is placed on flat surface.
+     *
+     * Please make sure the accelerometer is placed on a flat surface before
+     * calling this function.
+     *
+     * @return true if request was successful; otherwise false
+     */
+    bool calibrate();
+
+    /**
+     * Get calculated offset values. Offsets will be calculated by the
+     * calibrate() method.
+     *
+     * Use these values and put them in persistent storage to avoid
+     * having to calibrate the accelerometer after a reset/power cycle.
+     *
+     * @param xOff x offset is written to this argument
+     * @param yOff y offset is written to this argument
+     * @param zOff z offset is written to this argument
+     *
+     * @return true if request was successful; otherwise false
+     */
+    bool getCalibrationOffsets(int32_t& xOff, int32_t& yOff, int32_t& zOff);
+
+    /**
+     * Set calibration offset values. These values should normally
+     * at one point in time have been retrieved by calling the
+     * getCalibrationOffsets method.
+     *
+     *
+     * @param xOff x offset
+     * @param yOff y offset
+     * @param zOff z offset
+     *
+     * @return true if request was successful; otherwise false
+     */
+    bool setCalibrationOffsets(int32_t xOff, int32_t yOff, int32_t zOff);
+
+
+
+private:
+
+    I2C _i2c;
+    Mode _mode;
+    Range _range;
+    int32_t _xOff;
+    int32_t _yOff;
+    int32_t _zOff;
+
+    int getStatus();
+    int getModeControl();
+    int setModeControl(uint8_t mctl);
+
+};
+
+#endif
--- a/TSC2046.cpp	Wed Oct 09 07:51:52 2013 +0000
+++ b/TSC2046.cpp	Fri Oct 18 12:48:58 2013 +0200
@@ -1,5 +1,6 @@
 
 #include "mbed.h"
+#include "mbed_debug.h"
 #include "TSC2046.h"
 
 #ifndef ABS
@@ -47,6 +48,7 @@
 #define DEBOUNCE_MAX 10
 #define DEBOUNCE_TOL  3
 
+
 TSC2046::TSC2046(PinName mosi, PinName miso, PinName sck, PinName cs) :
 _spi(mosi, miso, sck), _cs(cs)
 {
@@ -56,19 +58,39 @@
     _spi.frequency(1500000);
     _calibrated = false;
     _initialized = false;
+
+    _calibPoint = TSC2046_NUM_CALIB_POINTS+1;
+
+    _insetPx = 15;
+}
+
+bool TSC2046::init(uint16_t width, uint16_t height) {
+    _width = width;
+    _height = height;
+
+    _cs = 0;
+
+    _spi.write(REF_ON);
+    _spi.write((READ_12BIT_SER(ADS_A2A1A0_vaux) | ADS_PD10_ALL_ON));
+    _spi.write(PWRDOWN);
+
+    _cs = 1;
+
+
+    _initialized = true;
+
+    return true;
 }
 
 
-void TSC2046::read(touchCoordinate_t &coord) {
+bool TSC2046::read(touchCoordinate_t &coord) {
 
     touchCoordinate_t tmpCoord;
     calibPoint_t displayPoint;
     calibPoint_t screenSample;
 
-    if (!_initialized) {
-        init();
-        _initialized = true;
-    }
+    if (!_initialized) return false;
+
 
     readAndFilter(tmpCoord);
 
@@ -92,54 +114,122 @@
         coord.y = tmpCoord.y;
     }
 
-}
-
-void TSC2046::calibrate(touchCoordinate_t &ref1,
-        touchCoordinate_t &ref2,
-        touchCoordinate_t &ref3,
-        touchCoordinate_t &scr1,
-        touchCoordinate_t &scr2,
-        touchCoordinate_t &scr3) {
-
-    calibPoint_t disp[3];
-    calibPoint_t scr[3];
-
-    disp[0].x = ref1.x;
-    disp[0].y = ref1.y;
-    disp[1].x = ref2.x;
-    disp[1].y = ref2.y;
-    disp[2].x = ref3.x;
-    disp[2].y = ref3.y;
-
-    scr[0].x = scr1.x;
-    scr[0].y = scr1.y;
-    scr[1].x = scr2.x;
-    scr[1].y = scr2.y;
-    scr[2].x = scr3.x;
-    scr[2].y = scr3.y;
-
-    setCalibrationMatrix(disp, scr, &_calibMatrix);
-
-    _calibrated = true;
+    return true;
 
 }
 
-void TSC2046::uncalibrate() {
-    _calibrated = false;
+bool TSC2046::calibrateStart() {
+    if (!_initialized) return false;
+
+    _calibPoint = 0;
+
+    return true;
+}
+
+bool TSC2046::getNextCalibratePoint(uint16_t* x, uint16_t* y) {
+    touchCoordinate_t coord;
+
+    if (!_initialized) return false;
+
+    if (x == NULL || y == NULL) return false;
+
+    if (_calibPoint >= TSC2046_NUM_CALIB_POINTS) return false;
+
+    getCalibratePoint(_calibPoint, &coord.x, &coord.y);
+
+    *x = (uint16_t)coord.x;
+    *y = (uint16_t)coord.y;
+    _calibrateValues[_calibPoint][0] = coord;
+
+    return true;
+}
+
+bool TSC2046::waitForCalibratePoint(bool* morePoints, uint32_t timeout) {
+    int result = 0;
+    bool ret = false;
+    int32_t x = 0;
+    int32_t y = 0;
+    touchCoordinate_t coord;
+
+    if (!_initialized) return false;
+
+    do {
+        if (morePoints == NULL || _calibPoint >= TSC2046_NUM_CALIB_POINTS) {
+            break;
+        }
+
+        result = waitForTouch(&x, &y, timeout);
+        if (result != 0) {
+            debug("wait for touch response failed (%d)\n", result);
+            break;
+        }
+
+        coord.x = x;
+        coord.y = y;
+        _calibrateValues[_calibPoint][1] = coord;
+
+        _calibPoint++;
+        *morePoints = (_calibPoint < TSC2046_NUM_CALIB_POINTS);
+
+        if (!(*morePoints)) {
+
+            calibrate(
+                    _calibrateValues[0][0],
+                    _calibrateValues[1][0],
+                    _calibrateValues[2][0],
+                    _calibrateValues[0][1],
+                    _calibrateValues[1][1],
+                    _calibrateValues[2][1]);
+        }
+
+
+        ret = true;
+
+    } while (0);
+
+
+
+    if (!ret) {
+        // calibration must restart if an error occurred
+        _calibPoint = TSC2046_NUM_CALIB_POINTS+1;
+    }
+
+
+
+    return ret;
+
 }
 
 
-void TSC2046::init() {
+bool TSC2046::calibrate(touchCoordinate_t* values, int numValues) {
+    if (values == NULL || numValues < TSC2046_NUM_CALIB_POINTS) return false;
+
+    touchCoordinate_t ref[TSC2046_NUM_CALIB_POINTS];
+    touchCoordinate_t scr[TSC2046_NUM_CALIB_POINTS];
 
-    _cs = 0;
+    for (int i = 0; i < TSC2046_NUM_CALIB_POINTS; i++) {
+        getCalibratePoint(i, &(ref[i].x), &(ref[i].y));
+        scr[i] = values[i];
+    }
+
+    calibrate(ref[0], ref[1], ref[2], scr[0], scr[1], scr[2]);
 
-    _spi.write(REF_ON);
-    _spi.write((READ_12BIT_SER(ADS_A2A1A0_vaux) | ADS_PD10_ALL_ON));
-    _spi.write(PWRDOWN);
+    return true;
+}
+
+
+bool TSC2046::getCalibrationValues(touchCoordinate_t* values, int numValues) {
+    if (values == NULL || numValues < TSC2046_NUM_CALIB_POINTS) return false;
+    if (!_calibrated) return false;
 
-    _cs = 1;
+    for (int i = 0; i < TSC2046_NUM_CALIB_POINTS; i++) {
+        values[i] = _calibrateValues[i][1];
+    }
+
+    return true;
 }
 
+
 void TSC2046::readAndFilter(touchCoordinate_t &coord)
 {
     int32_t ix, iy, iz1, iz2 = 0;
@@ -296,6 +386,90 @@
     return ((data[0] << 8) | data[1]);
 }
 
+void TSC2046::calibrate(touchCoordinate_t &ref1,
+        touchCoordinate_t &ref2,
+        touchCoordinate_t &ref3,
+        touchCoordinate_t &scr1,
+        touchCoordinate_t &scr2,
+        touchCoordinate_t &scr3) {
+
+    calibPoint_t disp[3];
+    calibPoint_t scr[3];
+
+    disp[0].x = ref1.x;
+    disp[0].y = ref1.y;
+    disp[1].x = ref2.x;
+    disp[1].y = ref2.y;
+    disp[2].x = ref3.x;
+    disp[2].y = ref3.y;
+
+    scr[0].x = scr1.x;
+    scr[0].y = scr1.y;
+    scr[1].x = scr2.x;
+    scr[1].y = scr2.y;
+    scr[2].x = scr3.x;
+    scr[2].y = scr3.y;
+
+    setCalibrationMatrix(disp, scr, &_calibMatrix);
+
+    _calibrated = true;
+
+}
+
+void TSC2046::getCalibratePoint(int pointNum, int32_t* x, int32_t *y) {
+    switch(pointNum) {
+    case 0:
+        *x = _insetPx;
+        *y = _height - _insetPx;
+        break;
+    case 1:
+        *x = _width/2;
+        *y = _insetPx;
+        break;
+    case 2:
+        *x = _width - _insetPx;
+        *y = _height - _insetPx;
+        break;
+    }
+}
+
+int TSC2046::waitForTouch(int32_t* x, int32_t* y, uint32_t timeout) {
+    Timer t;
+    touchCoordinate_t coord;
+    bool waitForRelease = false;
+    int32_t tx = 0;
+    int32_t ty = 0;
+
+
+    t.start();
+    while (timeout == 0 || ((uint32_t)t.read_ms() < timeout)) {
+
+        read(coord);
+
+        if (coord.z == 0 && waitForRelease) {
+            *x = tx;
+            *y = ty;
+            break;
+        }
+
+        if (coord.z > 0) {
+            tx = coord.x;
+            ty = coord.y;
+            waitForRelease = true;
+        }
+
+        wait_ms(10);
+    }
+
+    if (timeout > 0 && (uint32_t)t.read_ms() > timeout) {
+        return -1;
+    }
+
+    return 0;
+
+}
+
+
 
 // ############################################################################
 // >>>>>>>> Calibrate code >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -609,6 +783,7 @@
 }
 
 
+
 // ############################################################################
 // <<<<<<<< Calibrate code <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 // ############################################################################
--- a/TSC2046.h	Wed Oct 09 07:51:52 2013 +0000
+++ b/TSC2046.h	Fri Oct 18 12:48:58 2013 +0200
@@ -2,18 +2,16 @@
 #ifndef TSC2046_H
 #define TSC2046_H
 
+#include "TouchPanel.h"
+
+#define TSC2046_NUM_CALIB_POINTS (3)
 
 /**
  * Texas Instruments Touch Screen Controller (TSC2046).
  */
-class TSC2046 {
+class TSC2046 : public TouchPanel {
 public:
 
-    typedef struct {
-        int32_t x;
-        int32_t y;
-        int32_t z;
-    } touchCoordinate_t;
 
     /**
      * Constructor
@@ -25,34 +23,41 @@
      */
     TSC2046(PinName mosi, PinName miso, PinName sck, PinName cs);
 
-    /**
-     * Read coordinates from the touch panel.
-     *
-     * Before calibrate() is called this function will return uncalibrated
-     * values. If there is no touch active the coordinate values will be 0.
-     *
-     * @param coord pointer to coordinate object. The read coordinates will be
-     * written to this object.
-     */
-    void read(touchCoordinate_t &coord);
+    bool init(uint16_t width, uint16_t height);
+
+    bool read(touchCoordinate_t &coord);
+    bool calibrateStart();
+    bool getNextCalibratePoint(uint16_t* x, uint16_t* y);
+    bool waitForCalibratePoint(bool* morePoints, uint32_t timeout);
 
     /**
-     * Calibrate touch screen based on three reference points and
-     * three actual readings. This means that the user must be presented
-     * with three points (one at a time) and asked to press on these points
-     * to get an actual reading.
+     * Calibrate the touch panel with already gathered calibration values.
+     * The list with calibration points must at one point have been retrieved
+     * by calling getCalibrationValues;
+     *
+     * @param values list with calibration values
+     * @param numValues the size of the list must match the number of
+     * calibration points needed for this touch panel (TSC2046_NUM_CALIB_POINTS)
+     *
+     * @return true if the request was successful; otherwise false
      */
-    void calibrate(touchCoordinate_t &ref1,
-            touchCoordinate_t &ref2,
-            touchCoordinate_t &ref3,
-            touchCoordinate_t &scr1,
-            touchCoordinate_t &scr2,
-            touchCoordinate_t &scr3);
+    bool calibrate(touchCoordinate_t* values, int numValues);
 
     /**
-     * Reset a previous calibration (in order to do a new calibration)
+     * Get calibration values for the calibration points used by this touch
+     * panel. This method may only be called after a successful calibration
+     * has been performed.
+     *
+     * The list with values can be written to persistent storage and used
+     * to calibrate the display without involving the user.
+     *
+     * @param values calibration values will be written to this list
+     * @param numValues the size of the list must match the number of
+     * calibration points needed for this touch panel (TSC2046_NUM_CALIB_POINTS)
+     *
+     * @return true if the request was successful; otherwise false
      */
-    void uncalibrate();
+    bool getCalibrationValues(touchCoordinate_t* values, int numValues);
 
 
 private:
@@ -78,11 +83,27 @@
     bool _initialized;
     calibMatrix_t _calibMatrix;
 
-    void init();
+    uint16_t _width;
+    uint16_t _height;
+    int _calibPoint;
+    int _insetPx;
+
+    touchCoordinate_t _calibrateValues[TSC2046_NUM_CALIB_POINTS][2];
+
     void readAndFilter(touchCoordinate_t &coord);
     int32_t getFilteredValue(int cmd);
     uint16_t spiTransfer(uint8_t cmd);
 
+    void calibrate(touchCoordinate_t &ref1,
+            touchCoordinate_t &ref2,
+            touchCoordinate_t &ref3,
+            touchCoordinate_t &scr1,
+            touchCoordinate_t &scr2,
+            touchCoordinate_t &scr3);
+
+    void getCalibratePoint(int pointNum, int32_t* x, int32_t *y);
+    int waitForTouch(int32_t* x, int32_t* y, uint32_t timeout);
+
 
 
     int setCalibrationMatrix( calibPoint_t * displayPtr,
@@ -92,8 +113,8 @@
             calibPoint_t * screenPtr,
             calibMatrix_t * matrixPtr );
 
+
+
 };
 
 #endif
-
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TouchPanel.h	Fri Oct 18 12:48:58 2013 +0200
@@ -0,0 +1,77 @@
+
+#ifndef TOUCHPANEL_H
+#define TOUCHPANEL_H
+
+
+/**
+ * An abstract class that represents touch panels.
+ */
+class TouchPanel {
+public:
+
+    typedef struct {
+        int32_t x;
+        int32_t y;
+        int32_t z;
+    } touchCoordinate_t;
+
+
+    /**
+     * Initialize the touch controller. This method must be called before
+     * calibrating or reading data from the controller
+     *
+     * @param width the width of the touch panel. This is usually the same as
+     * the width of the display
+     * @param height the height of the touch panel. This is usually the same
+     * as the height of the display.
+     *
+     * @return true if the request was successful; otherwise false
+     */
+    virtual bool init(uint16_t width, uint16_t height) = 0;
+
+    /**
+     * Read coordinates from the touch panel.
+     *
+     * @param coord pointer to coordinate object. The read coordinates will be
+     * written to this object.
+     */
+    virtual bool read(touchCoordinate_t &coord) = 0;
+
+
+    /**
+     * Start to calibrate the display
+     *
+     * @return true if the request was successful; otherwise false
+     */
+    virtual bool calibrateStart() = 0;
+
+    /**
+     * Get the next calibration point. Draw an indicator on the screen
+     * at the coordinates and ask the user to press/click on the indicator.
+     * Please note that waitForCalibratePoint() must be called after this
+     * method.
+     *
+     * @param x the x coordinate is written to this argument
+     * @param y the y coordinate is written to this argument
+     *
+     * @return true if the request was successful; otherwise false
+     */
+    virtual bool getNextCalibratePoint(uint16_t* x, uint16_t* y) = 0;
+
+    /**
+     * Wait for a calibration point to have been pressed and recored.
+     * This method must be called just after getNextCalibratePoint().
+     *
+     * @param morePoints true is written to this argument if there
+     * are more calibrations points available; otherwise it will be false
+     * @param timeout maximum number of milliseconds to wait for
+     * a calibration point. Set this argument to 0 to wait indefinite.
+     *
+     * @return true if the request was successful; otherwise false
+     */
+    virtual bool waitForCalibratePoint(bool* morePoints, uint32_t timeout) = 0;
+
+
+};
+
+#endif