Graphical demo for the LPC4088 Experiment Base Board with one of the Display Expansion Kits. This demo shows a number of bubbles bouncing around.

Dependencies:   EALib mbed

Files at this revision

API Documentation at this revision

Comitter:
embeddedartists
Date:
Fri Oct 03 13:09:11 2014 +0000
Commit message:
First version

Changed in this revision

AR1021I2C.cpp Show annotated file Show diff for this revision Revisions of this file
AR1021I2C.h Show annotated file Show diff for this revision Revisions of this file
BubbleDemo.cpp Show annotated file Show diff for this revision Revisions of this file
BubbleDemo.h Show annotated file Show diff for this revision Revisions of this file
EALib.lib Show annotated file Show diff for this revision Revisions of this file
EaLcdBoardGPIO.cpp Show annotated file Show diff for this revision Revisions of this file
EaLcdBoardGPIO.h Show annotated file Show diff for this revision Revisions of this file
Graphics.cpp Show annotated file Show diff for this revision Revisions of this file
Graphics.h Show annotated file Show diff for this revision Revisions of this file
TestDisplay.cpp Show annotated file Show diff for this revision Revisions of this file
TestDisplay.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 4839ec6c350d AR1021I2C.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AR1021I2C.cpp	Fri Oct 03 13:09:11 2014 +0000
@@ -0,0 +1,560 @@
+/*
+ *  Copyright 2013 Embedded Artists AB
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/******************************************************************************
+ * Includes
+ *****************************************************************************/
+
+#include "mbed.h"
+#include "mbed_debug.h"
+
+#include "AR1021I2C.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)
+
+#define AR1021_ADDR  (0x4D << 1)
+
+#define AR1021_TIMEOUT     1000        //how many ms to wait for responce 
+#define AR1021_RETRY          5        //how many times to retry sending command 
+
+#define AR1021_MIN(__a, __b) (((__a)<(__b))?(__a):(__b))
+
+
+AR1021I2C::AR1021I2C(PinName sda, PinName scl, PinName siq) :
+_i2c(sda, scl), _siq(siq), _siqIrq(siq)
+{
+    _i2c.frequency(200000);
+
+    // 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 AR1021I2C::read(touchCoordinate_t &coord) {
+
+    if (!_initialized) return false;
+
+    coord.x = _x * _width/4095;
+    coord.y = _y * _height/4095;
+    coord.z = _pen;
+
+    return true;
+}
+
+
+bool AR1021I2C::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, &AR1021I2C::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 AR1021I2C::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 AR1021I2C::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 AR1021I2C::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, &AR1021I2C::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 AR1021I2C::cmd(char cmd, char* data, int len, char* respBuf, int* respLen,
+        bool setCsOff) {
+
+    int ret = 0;
+    int readLen = (respLen == NULL) ? 0 : *respLen;
+    for (int attempt = 1; attempt <= AR1021_RETRY; attempt++) {
+        if (attempt > 1) {
+            wait_ms(50);
+        }
+
+        // command request
+        // ---------------
+        // 0x00 0x55 len cmd data
+        // 0x00 = protocol command byte
+        // 0x55 = header
+        // len = data length + cmd (1)
+        // data = data to send
+
+        _i2c.start(); 
+        _i2c.write(AR1021_ADDR);                   //send write address 
+        _i2c.write(0x00); 
+        _i2c.write(0x55);                          //header 
+        _i2c.write(len+1);                         //message length 
+        _i2c.write(cmd);
+        for (int i = 0; i < len; i++) {
+            _i2c.write(data[i]);
+        }
+        wait_us(60);
+        _i2c.stop();
+     
+        // wait for response (siq goes high when response is available)
+        Timer t;
+        t.start();
+        while(_siq.read() != 1 && t.read_ms() < AR1021_TIMEOUT);
+        
+        if (t.read_ms() < AR1021_TIMEOUT) {
+        
+            // command response
+            // ---------------
+            // 0x55 len status cmd data
+            // 0x55 = header
+            // len = number of bytes following the len byte (i.e. including the status&cmd)
+            // status = status
+            // cmd = command ID
+            // data = data to receive
+            _i2c.start();
+            _i2c.write(AR1021_ADDR + 1);        //send read address 
+            char header = _i2c.read(1);         //header should always be 0x55
+            if (header != 0x55) {
+                ret = AR1021_ERR_NO_HDR;
+                continue;
+            }
+            char length = _i2c.read(1);         //data length
+            if (length < 2) {
+                ret = AR1021_ERR_INV_LEN;       //must have at least status and command bytes
+                continue;
+            }
+            length -= 2;
+            if (length > readLen) {
+                ret = AR1021_ERR_INV_LEN;       //supplied buffer is not enough
+                continue;
+            }
+
+            char status = _i2c.read(1);         //command status
+            char usedCmd;
+            if (readLen <= 0) {
+                usedCmd = _i2c.read(0);         //no data => read command byte + NACK
+            } else {
+                usedCmd = _i2c.read(1);         //which command
+
+                //we need to send a NACK on the last read. 
+                int i;
+                for (i = 0; i < (length-1); i++) {
+                    respBuf[i] = _i2c.read(1);
+                }
+                respBuf[i] = _i2c.read(0);      //last returned data byte + NACK
+            }
+            _i2c.stop();
+            
+            
+            if (status != AR1021_RESP_STAT_OK) {
+                ret = -status;
+                continue;
+            }
+            if (usedCmd != cmd) {
+                ret = AR1021_ERR_INV_RESP;
+                continue;
+            }
+            
+            // success
+            ret = 0;
+            break;
+            
+        } else {
+            ret = AR1021_ERR_TIMEOUT;
+            continue;
+        }
+    }
+
+    return ret;
+}
+
+int AR1021I2C::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;
+        }
+
+        // command response
+        // ---------------
+        // 0x55 len status cmd data
+        // 0x55 = header
+        // len = number of bytes following the len byte (should be 2)
+        // status = status
+        // cmd = command ID
+        _i2c.start();
+        _i2c.write(AR1021_ADDR + 1);        //send read address 
+        char header = _i2c.read(1);         //header should always be 0x55  
+        char length = _i2c.read(1);         //data length
+
+        if (header != 0x55) {
+            ret = AR1021_ERR_NO_HDR;
+            break;
+        }
+        if (length < 2) {
+            ret = AR1021_ERR_INV_LEN;
+            break;
+        }
+        char status = _i2c.read(1);         //status
+        char cmd    = _i2c.read(0);         //command, should be NACK'ed
+        _i2c.stop();
+        if (status != AR1021_RESP_STAT_OK) {
+            ret = -status;
+            break;
+        }
+        if (cmd != AR1021_CMD_CALIBRATE_MODE) {
+            ret = AR1021_ERR_INV_RESP;
+            break;
+        }
+        
+        // success
+        ret = 0;
+
+    } while (0);
+
+    return ret;
+}
+
+
+void AR1021I2C::readTouchIrq() {
+    while(_siq.read() == 1) {
+
+        // touch coordinates are sent in a 5-byte data packet
+        _i2c.start();
+        _i2c.write(AR1021_ADDR + 1);        //send read address 
+        int pen = _i2c.read(1);
+        int xlo = _i2c.read(1);
+        int xhi = _i2c.read(1);
+        int ylo = _i2c.read(1);
+        int yhi = _i2c.read(0);
+        _i2c.stop();
+        
+        // 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);
+    }
+}
+
+bool AR1021I2C::info(int* verHigh, int* verLow, int* resBits, int* type)
+{
+    char buff[3] = {0,0,0};
+    int read = 3;
+    int res = cmd(AR1021_CMD_GET_VERSION, NULL, 0, buff, &read);
+    if (res == 0) {
+        *verHigh = buff[0];
+        *verLow = buff[1];
+        switch(buff[2] & 3) {
+            case 0:   
+                *resBits = 8;
+                break;
+            case 1:   
+                *resBits = 10;
+                break;
+            case 2:   
+                *resBits = 12;
+                break;
+            case 3:   
+                *resBits = 12;
+                break;
+            default:
+                res = 25;
+                printf("Invalid resolution %d\n", buff[2]&3);
+                break;
+        }
+        *type = buff[2]>>2;
+    }
+    return (res == 0);
+}
diff -r 000000000000 -r 4839ec6c350d AR1021I2C.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AR1021I2C.h	Fri Oct 03 13:09:11 2014 +0000
@@ -0,0 +1,77 @@
+/*
+ *  Copyright 2013 Embedded Artists AB
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef AR1021I2C_H
+#define AR1021I2C_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 AR1021I2C : public TouchPanel {
+public:
+
+
+    /**
+     * Constructor
+     *
+     * @param mosi I2C SDA pin
+     * @param miso I2C SCL pin
+     * @param siq interrupt pin
+     */
+    AR1021I2C(PinName sda, PinName scl, PinName siq);
+
+    bool info(int* verHigh, int* verLow, int* resBits, int* type);
+
+    virtual bool init(uint16_t width, uint16_t height);
+    virtual bool read(touchCoordinate_t &coord);
+    virtual bool calibrateStart();
+    virtual bool getNextCalibratePoint(uint16_t* x, uint16_t* y);
+    virtual bool waitForCalibratePoint(bool* morePoints, uint32_t timeout);
+
+private:
+
+
+    I2C _i2c;
+    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
+
diff -r 000000000000 -r 4839ec6c350d BubbleDemo.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BubbleDemo.cpp	Fri Oct 03 13:09:11 2014 +0000
@@ -0,0 +1,182 @@
+/******************************************************************************
+ * Includes
+ *****************************************************************************/
+
+#include "mbed.h"
+
+#include "LcdController.h"
+#include "EaLcdBoard.h"
+#include "BubbleDemo.h"
+
+#include "wchar.h"
+
+/******************************************************************************
+ * Typedefs and defines
+ *****************************************************************************/
+
+#define PI 3.1415926535897932384626433832795
+
+/* Red color mask, 565 mode */
+#define REDMASK       0xF800
+/* Red shift value, 565 mode */
+#define REDSHIFT      11
+/* Green color mask, 565 mode */
+#define GREENMASK     0x07E0
+/* Green shift value, 565 mode */
+#define GREENSHIFT    5
+/* Blue color mask, 565 mode */
+#define BLUEMASK      0x001F
+/* Blue shift value, 565 mode */
+#define BLUESHIFT     0
+
+/* Number of colors in 565 mode */
+#define NUM_COLORS    65536
+/* Number of red colors in 565 mode */
+#define RED_COLORS    0x20
+/* Number of green colors in 565 mode */
+#define GREEN_COLORS  0x40
+/* Number of blue colors in 565 mode */
+#define BLUE_COLORS   0x20
+
+/******************************************************************************
+ * Local variables
+ *****************************************************************************/
+
+
+/******************************************************************************
+ * External variables
+ *****************************************************************************/
+
+/******************************************************************************
+ * Local functions
+ *****************************************************************************/
+
+
+void BubbleDemo::initialize() {
+ float radian;
+ const float phase1 = 2 * PI / 3;
+ const float phase2 = 4 * PI / 3;
+
+ for(i = 0; i < NumBalls; i++) {
+  x[i] = this->windowX / 2;
+  y[i] = this->windowY / 2;
+  r[i] = i * 2 + 10;
+
+  oldX[i] = x[i];
+  oldY[i] = y[i];
+
+  velX[i] = 1;
+  velY[i] = 1;
+
+  radian = i * 2 * PI / NumBalls;
+  red[i] = cos(radian) * RED_COLORS / 2 + (RED_COLORS / 2 - 1);
+  green[i] = cos(radian + phase2) * GREEN_COLORS / 2 + (GREEN_COLORS / 2 - 1);
+  blue[i] = cos(radian + phase1) * BLUE_COLORS / 2 + (BLUE_COLORS / 2 - 1);
+ }
+}
+
+void BubbleDemo::collision() {
+ float disX = x[j] - x[i];
+ float disY = y[j] - y[i];
+ float d2 = disX * disX + disY * disY;
+
+ if(d2 != 0) {
+  float rij = r[i] + r[j];
+  float rij2 = rij * rij;
+
+  if(d2 < rij2) {
+   float ii = (disX * velX[i] + disY * velY[i]) / d2;
+   float ji = (disX * velY[i] - disY * velX[i]) / d2;
+   float ij = (disX * velX[j] + disY * velY[j]) / d2;
+   float jj = (disX * velY[j] - disY * velX[j]) / d2;
+   float ratio = rij / sqrt(d2);
+
+   velX[i] = ij * disX - ii * disY;
+   velY[i] = ij * disY + ii * disX;
+   velX[j] = ji * disX - jj * disY;
+   velY[j] = ji * disY + jj * disX;
+
+   disX *= (ratio - 1) / 2;
+   disY *= (ratio - 1) / 2;
+
+   x[j] += disX;
+   y[j] += disY;
+   x[i] -= disX;
+   y[i] -= disY;
+  }
+ }
+}
+
+void BubbleDemo::borders() {
+ if(x[i] >= this->windowX - r[i] - 1) {
+  x[i] = this->windowX - r[i] - 1;
+  velX[i] = -velX[i];
+ } else if(x[i] <= r[i]) {
+  x[i] = r[i];
+  velX[i] = -velX[i];
+ }
+
+ if(y[i] >= this->windowY - r[i] - 1) {
+  y[i] = this->windowY - r[i] - 1;
+  velY[i] = -velY[i];
+ } else if(y[i] <= r[i]) {
+  y[i] = r[i];
+  velY[i] = -velY[i];
+ }
+}
+
+void BubbleDemo::draw() {
+ graphics.put_circle( oldX[i], oldY[i], 0, r[i], 0 );
+ graphics.put_circle( x[i], y[i], (red[i] << REDSHIFT) + (green[i] << GREENSHIFT) + (blue[i] << BLUESHIFT), r[i], 0);
+
+ oldX[i] = x[i];
+ oldY[i] = y[i];
+}
+
+
+/******************************************************************************
+ * Public functions
+ *****************************************************************************/
+ 
+BubbleDemo::BubbleDemo(uint8_t *pFrameBuf, uint16_t dispWidth, uint16_t dispHeight) 
+    : graphics((uint16_t *)pFrameBuf, dispWidth, dispHeight) {
+
+    this->windowX = dispWidth;
+    this->windowY = dispHeight;
+    this->pFrmBuf  = (uint16_t *)pFrameBuf;
+    this->pFrmBuf1 = (uint16_t *)pFrameBuf;
+    this->pFrmBuf2 = (uint16_t *)((uint32_t)pFrameBuf + dispWidth*dispHeight*2);
+    this->pFrmBuf3 = (uint16_t *)((uint32_t)pFrameBuf + dispWidth*dispHeight*4);
+
+    initialize();
+}
+
+void BubbleDemo::run(EaLcdBoardGPIO& lcdBoard, uint32_t loops, uint32_t delayMs) {
+  
+  printf("BubbleDemo, %d loops, %dms delay\n", loops, delayMs);
+
+    //update framebuffer
+    graphics.setFrameBuffer(this->pFrmBuf);
+    lcdBoard.setFrameBuffer((uint32_t)this->pFrmBuf);
+    memset((void*)(pFrmBuf), 0, this->windowX * this->windowY * 2);
+
+    for(int32_t n=0;n<loops;n++) {
+
+        for(i = 0; i < NumBalls; i++) {
+            x[i] += velX[i];
+            y[i] += velY[i];
+            
+            for(j = i + 1; j < NumBalls; j++)
+                collision();
+                
+            borders();
+            
+            if((int)x[i] != (int)oldX[i] || (int)y[i] != (int)oldY[i])
+                draw();
+        }
+
+        wait_ms(delayMs);
+    }
+    memset((void*)(pFrmBuf), 0, this->windowX * this->windowY * 2);
+}
+
diff -r 000000000000 -r 4839ec6c350d BubbleDemo.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BubbleDemo.h	Fri Oct 03 13:09:11 2014 +0000
@@ -0,0 +1,72 @@
+
+#ifndef BUBBLEDEMO_H
+#define BUBBLEDEMO_H
+
+#include "Graphics.h"
+#include "EaLcdBoardGPIO.h"
+
+class BubbleDemo {
+public:
+
+    enum Constants {
+        NumBalls = 17
+    };
+
+    /** Set the address of the frame buffer to use.
+     *
+     *  It is the content of the frame buffer that is shown on the
+     *  display. All the drawing on the frame buffer can be done
+     *  'offline' and whenever it should be shown this function
+     *  can be called with the address of the offline frame buffer.
+     *
+     *  @param pFrameBuf  Pointer to the frame buffer, which must be
+     *                    3 times as big as the frame size (for tripple
+     *                    buffering).
+     *         dispWidth  The width of the display (in pixels).
+     *         dispHeight The height of the display (in pixels).
+     *         loops      Number of loops in the demo code.
+     *         delayMs    Delay in milliseconds between schreen updates.
+     *
+     *  @returns
+     *       none
+     */
+    BubbleDemo(uint8_t *pFrameBuf, uint16_t dispWidth, uint16_t dispHeight);
+    
+    void run(EaLcdBoardGPIO& lcdBoard, uint32_t loops, uint32_t delayMs);
+
+private:
+    int32_t windowX;
+    int32_t windowY;
+    uint16_t *pFrmBuf;
+    uint16_t *pFrmBuf1;
+    uint16_t *pFrmBuf2;
+    uint16_t *pFrmBuf3;
+    
+    Graphics graphics;
+    
+    uint8_t i;
+    uint8_t j;
+    
+    float x[NumBalls];
+    float y[NumBalls];
+    uint8_t r[NumBalls];
+    
+    float oldX[NumBalls];
+    float oldY[NumBalls];
+    
+    float velX[NumBalls];
+    float velY[NumBalls];
+    
+    uint8_t red[NumBalls];
+    uint8_t green[NumBalls];
+    uint8_t blue[NumBalls];
+    
+    
+    void initialize();
+    void collision();
+    void borders();
+    void draw();
+};
+
+#endif /* BUBBLEDEMO_H */
+
diff -r 000000000000 -r 4839ec6c350d EALib.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EALib.lib	Fri Oct 03 13:09:11 2014 +0000
@@ -0,0 +1,1 @@
+https://mbed.org/users/embeddedartists/code/EALib/#b3a179cc3d88
diff -r 000000000000 -r 4839ec6c350d EaLcdBoardGPIO.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EaLcdBoardGPIO.cpp	Fri Oct 03 13:09:11 2014 +0000
@@ -0,0 +1,96 @@
+/*
+ *  Copyright 2013 Embedded Artists AB
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/******************************************************************************
+ * Includes
+ *****************************************************************************/
+
+#include "mbed.h"
+#include "EaLcdBoardGPIO.h"
+
+/******************************************************************************
+ * Defines and typedefs
+ *****************************************************************************/
+
+
+EaLcdBoardGPIO::EaLcdBoardGPIO(PinName sda, PinName scl)
+  : EaLcdBoard(sda, scl), /*pinWP(P4_15),*/ pin3v3(P2_0), pin5v(P2_21), pinDE(P2_11), pinContrast(P2_1)
+{
+  pinContrast.period_ms(10);
+  setWriteProtect(true);
+  set3V3Signal(false);
+  set5VSignal(false);
+  setDisplayEnableSignal(false);
+  setBacklightContrast(0);
+}
+
+
+void EaLcdBoardGPIO::setWriteProtect(bool enable)
+{  
+    // Not Applicable
+}
+
+void EaLcdBoardGPIO::set3V3Signal(bool enabled) { //P2.0 L=3.3V on
+    if (enabled) {
+        pin3v3 = 0;
+    } else {
+        pin3v3 = 1;
+    }
+}
+
+void EaLcdBoardGPIO::set5VSignal(bool enabled) { //P2.21 H=5V on
+    if (enabled) {
+        pin5v = 1;
+    } else {
+        pin5v = 0;
+    }
+}
+
+void EaLcdBoardGPIO::setDisplayEnableSignal(bool enabled) { //P2.11 H=enabled
+    LPC_IOCON->P2_11 &= ~7; /* GPIO2[11]  @ P2.11 */
+    if (enabled) {
+        pinDE = 1;
+    } else {
+        pinDE = 0;
+    }
+}
+
+void EaLcdBoardGPIO::setBacklightContrast(uint32_t value) { //P2.1, set to 4.30 for now
+#if 0    
+    LPC_IOCON->P2_1 &= ~7; /* GPIO2[1]  @ P2.1 */
+  if (value > 50) {
+      pinContrast = 1;
+  } else {
+      pinContrast = 0;
+  }
+#else
+    uint32_t tmp = LPC_IOCON->P2_1;
+    tmp &= ~7;
+    tmp |= 1;
+    LPC_IOCON->P2_1 = tmp; /* PWM2[1]  @ P2.1 */
+    float f = value;
+    pinContrast = f/100.0f;
+#endif
+  
+//    if (value > 100) return;
+
+//    pca9532_setBlink0Duty(100-value);
+//    pca9532_setBlink0Period(0);
+//    pca9532_setBlink0Leds(LCDB_CTRL_BL_C);
+}
+
+
+
diff -r 000000000000 -r 4839ec6c350d EaLcdBoardGPIO.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EaLcdBoardGPIO.h	Fri Oct 03 13:09:11 2014 +0000
@@ -0,0 +1,50 @@
+/*
+ *  Copyright 2013 Embedded Artists AB
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef EALCDBOARDGPIO_H
+#define EALCDBOARDGPIO_H
+
+#include "EaLcdBoard.h"
+
+/** An interface to Embedded Artists LCD Boards
+ *
+ */
+class EaLcdBoardGPIO : public EaLcdBoard {
+public:
+
+    EaLcdBoardGPIO(PinName sda, PinName scl);
+
+    void setBC(uint32_t val) { setBacklightContrast(val); };
+
+protected:
+    virtual void setWriteProtect(bool enable);
+    virtual void set3V3Signal(bool enabled);
+    virtual void set5VSignal(bool enabled);
+    virtual void setDisplayEnableSignal(bool enabled);
+    virtual void setBacklightContrast(uint32_t value);
+
+private:
+    //DigitalOut pinWP;
+    DigitalOut pin3v3;
+    DigitalOut pin5v;
+    DigitalOut pinDE;
+    //DigitalOut pinContrast;
+    PwmOut pinContrast;
+};
+
+#endif
+
+
diff -r 000000000000 -r 4839ec6c350d Graphics.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Graphics.cpp	Fri Oct 03 13:09:11 2014 +0000
@@ -0,0 +1,241 @@
+
+#include "mbed.h"
+#include "Graphics.h"
+
+
+Graphics::Graphics(uint16_t *pFrmBuf, uint16_t dispWidth, uint16_t dispHeight)
+{
+	this->windowX = dispWidth;
+	this->windowY = dispHeight;
+	this->pFrmBuf = pFrmBuf;
+}
+
+void Graphics::setFrameBuffer( uint16_t *pFrmBuf )
+{
+	this->pFrmBuf = pFrmBuf;
+}
+
+int32_t Graphics::abs(int32_t v1) const
+{
+   if (v1 > 0)
+     return v1;
+
+  return -v1;
+}
+
+/***********************************************************************
+ *
+ * Function: swim_put_line_raw
+ *
+ * Purpose: Draw a line on the physical display
+ *
+ * Processing:
+ *     See function.
+ *
+ * Parameters:
+ *     win : Window identifier
+ *     x1  : Physical X position of X line start
+ *     y1  : Physical Y position of Y line start
+ *     x2  : Physical X position of X line end
+ *     y2  : Physical Y position of Y line end
+ *
+ * Outputs: None
+ *
+ * Returns: Nothing
+ *
+ * Notes: None
+ *
+ **********************************************************************/
+void Graphics::put_line(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int16_t color)
+{
+    int32_t e2, sx, sy, dx, dy, err;
+    
+    /* calculate delta_x and delta_y */
+    dx = abs(x2 - x1);
+    dy = abs(y2 - y1);
+
+    /* set the direction for the step for both x and y, and
+       initialize the error */
+    if (x1 < x2)
+        sx = 1;
+    else
+        sx = -1;
+
+    if (y1 < y2)
+        sy = 1;
+    else
+        sy = -1;
+
+    err = dx - dy;
+   
+    while (1)
+    {
+        if ((x1 >= 0) && (x1 < this->windowX) && 
+            (y1 >= 0) && (y1 < this->windowY))
+            this->pFrmBuf[x1 + (this->windowX*y1)] = color;
+
+        if ((x1 == x2) && (y1 == y2))
+            return;
+
+        e2 = 2 * err;
+        if (e2 > -dy)
+        {
+            err -= dy;
+            x1 += sx;
+        }
+        if (e2 < dx)
+        {
+            err += dx;
+            y1 += sy;
+        }
+    }
+}
+
+/***********************************************************************
+ *
+ * Function: plot4points
+ *
+ * Purpose:
+ *
+ * Processing:
+ *     See function.
+ *
+ * Parameters:
+ *     win    : Window identifier
+ *     cx     :
+ *     cy     :
+ *     x      :
+ *     y      :
+ *     Filled :
+ *
+ * Outputs: None
+ *
+ * Returns: Nothing
+ *
+ * Notes: None
+ *
+ **********************************************************************/
+void Graphics::plot4points( int32_t cx, int32_t cy, int32_t x, int32_t y, int16_t color, int32_t Filled )
+   {
+   int16_t x0, x1, y0, y1;
+
+   y0 = cy + y;
+   y1 = cy - y;
+   if( Filled )
+      {
+      for( x0=cx - x; x0<=cx + x; x0++ )
+         {
+         if ((x0>=0) && (x0<this->windowX) && (y0>=0) && (y0<this->windowY))
+            this->pFrmBuf[x0 + (this->windowX*y0)] = color;
+         if ((x0>=0) && (x0<this->windowX) && (y1>=0) && (y1<this->windowY))
+            this->pFrmBuf[x0 + (this->windowX*y1)] = color;
+         }
+      }
+   else
+      {
+      x0 = cx + x;
+      x1 = cx - x;
+      if ((x0>=0) && (x0<this->windowX) && (y0>=0) && (y0<this->windowY))
+         this->pFrmBuf[x0 + (this->windowX*y0)] = color;
+      if ((x != 0) && 
+          (x1>=0) && (x1<this->windowX) && (y0>=0) && (y0<this->windowY))
+         this->pFrmBuf[x1 + (this->windowX*y0)] = color;
+      if ((y != 0) &&
+          (x0>=0) && (x0<this->windowX) && (y1>=0) && (y1<this->windowY))
+         this->pFrmBuf[x0 + (this->windowX*y1)] = color;
+      if ((x != 0 && y != 0) &&
+          (x1>=0) && (x1<this->windowX) && (y1>=0) && (y1<this->windowY))
+         this->pFrmBuf[x1 + (this->windowX*y1)] = color;
+      }
+   }
+
+/***********************************************************************
+ *
+ * Function: plot8points
+ *
+ * Purpose:
+ *
+ * Processing:
+ *     See function.
+ *
+ * Parameters:
+ *     win    : Window identifier
+ *     cx     :
+ *     cy     :
+ *     x      :
+ *     y      :
+ *     Filled :
+ *
+ * Outputs: None
+ *
+ * Returns: Nothing
+ *
+ * Notes: None
+ *
+ **********************************************************************/
+void Graphics::plot8points( int32_t cx, int32_t cy, int32_t x, int32_t y, int16_t color, int32_t Filled )
+   {
+   plot4points( cx, cy, x, y, color, Filled );
+   if (x != y)
+      plot4points( cx, cy, y, x, color, Filled );
+   }
+
+/***********************************************************************
+ *
+ * Function: swim_put_circle
+ *
+ * Purpose:
+ *
+ * Processing:
+ *     See function.
+ *
+ * Parameters:
+ *     win    : Window identifier
+ *     cx     :
+ *     cy     :
+ *     radius :
+ *     Filled :
+ *
+ * Outputs: None
+ *
+ * Returns: Nothing
+ *
+ * Notes: None
+ *
+ **********************************************************************/
+void Graphics::put_circle( int32_t cx, int32_t cy, int16_t color, int32_t radius, int32_t Filled )
+   {
+   int32_t Error = -radius;
+   int16_t x = radius;
+   int16_t y = 0;
+
+   while( x >= y )
+      {
+      plot8points( cx, cy, x, y, color, Filled );
+
+      Error += y;
+      ++y;
+      Error += y;
+
+      if( Error >= 0 )
+         {
+         --x;
+         Error -= x;
+         Error -= x;
+         }
+      }
+   }
+
+void Graphics::put_dot( int32_t cx, int32_t cy, int16_t color )
+{
+  int size = 1;
+  for (int y1 = cy - size; y1 <= (cy + size); y1++) {
+    for (int x1 = cx - size; x1 <= (cx + size); x1++) {
+      if ((x1 >= 0) && (x1 < this->windowX) && (y1 >= 0) && (y1 < this->windowY)) {
+          this->pFrmBuf[x1 + (this->windowX*y1)] = color;
+      }
+    }
+  }
+}
+
+
diff -r 000000000000 -r 4839ec6c350d Graphics.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Graphics.h	Fri Oct 03 13:09:11 2014 +0000
@@ -0,0 +1,29 @@
+
+#ifndef GRAPHICS_H
+#define GRAPHICS_H
+
+class Graphics {
+public:
+
+	Graphics(uint16_t *pFrmBuf, uint16_t dispWidth, uint16_t dispHeight);
+
+	void setFrameBuffer( uint16_t *pFrmBuf );
+	void put_line(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int16_t color);
+    void put_circle( int32_t cx, int32_t cy, int16_t color, int32_t radius, int32_t Filled );
+    void put_dot( int32_t cx, int32_t cy, int16_t color );
+
+protected:
+	uint16_t windowX;
+	uint16_t windowY;
+	uint16_t *pFrmBuf;
+	
+    int32_t abs(int32_t v1) const;
+    
+    virtual void plot4points( int32_t cx, int32_t cy, int32_t x, int32_t y, int16_t color, int32_t Filled );
+    void plot8points( int32_t cx, int32_t cy, int32_t x, int32_t y, int16_t color, int32_t Filled );
+    
+};
+
+#endif
+
+
diff -r 000000000000 -r 4839ec6c350d TestDisplay.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TestDisplay.cpp	Fri Oct 03 13:09:11 2014 +0000
@@ -0,0 +1,216 @@
+/*
+ *  Copyright 2013 Embedded Artists AB
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/******************************************************************************
+ * Includes
+ *****************************************************************************/
+
+#include "mbed.h"
+#include "TestDisplay.h"
+#include "sdram.h"
+
+#include "BubbleDemo.h"
+
+/******************************************************************************
+ * Defines and typedefs
+ *****************************************************************************/
+
+#define LCD_CONFIGURATION_43 \
+        40,                         /* horizontalBackPorch */ \
+        5,                          /* horizontalFrontPorch */ \
+        2,                          /* hsync */ \
+        480,                        /* width */ \
+        8,                          /* verticalBackPorch */ \
+        8,                          /* verticalFrontPorch */ \
+        2,                          /* vsync */ \
+        272,                        /* height */ \
+        false,                      /* invertOutputEnable */ \
+        false,                      /* invertPanelClock */ \
+        true,                       /* invertHsync */ \
+        true,                       /* invertVsync */ \
+        1,                          /* acBias */ \
+        LcdController::Bpp_16_565,  /* bpp */ \
+        9000000,                    /* optimalClock */ \
+        LcdController::Tft,         /* panelType */ \
+        false                       /* dualPanel */
+
+#define LCD_INIT_STRING_43  (char*)"v1,cd0,c50,cc0,c30,d100,c31,d100,cd1,d10,o,c51,cc100"
+
+#define LCD_CONFIGURATION_50 \
+        46,                         /* horizontalBackPorch */ \
+        20,                          /* horizontalFrontPorch */ \
+        2,                          /* hsync */ \
+        800,                        /* width */ \
+        23,                          /* verticalBackPorch */ \
+        10,                          /* verticalFrontPorch */ \
+        3,                          /* vsync */ \
+        480,                        /* height */ \
+        false,                      /* invertOutputEnable */ \
+        false,                      /* invertPanelClock */ \
+        true,                       /* invertHsync */ \
+        true,                       /* invertVsync */ \
+        1,                          /* acBias */ \
+        LcdController::Bpp_16_565,  /* bpp */ \
+        30000000,                   /* optimalClock */ \
+        LcdController::Tft,         /* panelType */ \
+        false                       /* dualPanel */
+
+#define LCD_INIT_STRING_50  (char*)"v1,cc0,c31,d50,o,d200,c51,cc100"
+
+#define MY_ABS(__a)  (((__a) < 0) ? -(__a) : (__a))
+
+/******************************************************************************
+ * Public Functions
+ *****************************************************************************/
+
+/*
+   Prerequisites:
+ 
+   - A display must be connected to the LPC4088 Experiment Base Board
+     with the FPC connector
+
+   - The touch controller uses the I2C bus so for this test to work 
+     jumpers JP8 and JP9 on the LPC4088 Experiment Base Board must 
+     both be in positions 1-2
+
+*/
+
+TestDisplay::TestDisplay(WhichDisplay which) : 
+    _initStr(NULL),
+    _lcdCfg(NULL),
+    _lcdBoard(P0_27, P0_28),
+    _touch(P0_27, P0_28, P2_25) {
+    
+    switch (which) {
+        case TFT_5:
+            _lcdCfg = new LcdController::Config(LCD_CONFIGURATION_50);
+            _initStr = LCD_INIT_STRING_50;
+            break;
+        case TFT_4_3:
+            _lcdCfg = new LcdController::Config(LCD_CONFIGURATION_43);
+            _initStr = LCD_INIT_STRING_43;
+            break;
+        default:
+            mbed_die();
+    }
+            
+    if (sdram_init() == 1) {
+        printf("Failed to initialize SDRAM\n");
+        _framebuffer = 0;
+    } else {
+        _framebuffer = (uint32_t) malloc(_lcdCfg->width * _lcdCfg->height * 2 * 3); // 2 is for 16 bit color, 3 is the number of buffers
+        if (_framebuffer != 0) {
+            memset((uint8_t*)_framebuffer, 0, _lcdCfg->width * _lcdCfg->height * 2 * 3);
+        }
+    }
+}
+
+TestDisplay::~TestDisplay() {
+    if (_framebuffer != 0) {
+        free((void*)_framebuffer);
+        _framebuffer = 0;
+    }
+}
+
+bool TestDisplay::runTest() {
+    do {
+        if (_framebuffer == 0) {
+            printf("Failed to allocate memory for framebuffer\n");
+            break;
+        }
+        
+        EaLcdBoard::Result result = _lcdBoard.open(_lcdCfg, _initStr);
+        if (result != EaLcdBoard::Ok) {
+            printf("Failed to open display, error %d\n", result);
+            break;
+        }
+
+        result = _lcdBoard.setFrameBuffer(_framebuffer);
+        if (result != EaLcdBoard::Ok) {
+            printf("Failed to set framebuffer, error %d\n", result);
+            break;
+        }
+        
+        BubbleDemo bubbleDemo((uint8_t *)_framebuffer, _lcdCfg->width, _lcdCfg->height);
+        while (1) {
+            bubbleDemo.run(_lcdBoard, 750, 20);
+        }
+    } while(0);
+    
+    return false;
+}
+
+void TestDisplay::calibrate_drawMarker(Graphics &g, uint16_t x, uint16_t y, bool erase) {
+    uint16_t color = (erase ? 0x0000 : 0xffff);
+    g.put_line(x-15, y, x+15, y, color);
+    g.put_line(x, y-15, x, y+15, color);
+    g.put_circle(x, y, color, 10, false);
+}
+
+bool TestDisplay::calibrate_display() {
+    bool morePoints = true;
+    uint16_t x, y;
+    int point = 0;
+    Graphics g((uint16_t*)_framebuffer, _lcdCfg->width, _lcdCfg->height);
+    
+    do {
+        if (!_touch.init(_lcdCfg->width, _lcdCfg->height)) {
+            printf("Failed to initialize touch controller\n");
+            break;
+        }
+        if (!_touch.calibrateStart()) {
+            printf("Failed to start calibration\n");
+            break;
+        }  
+        while (morePoints) {
+            if (point++ > 0) {
+                // erase old location
+                calibrate_drawMarker(g, x, y, true);
+            }
+            if (!_touch.getNextCalibratePoint(&x, &y)) {
+                printf("Failed to get calibration point\n");
+                break;
+            }
+            calibrate_drawMarker(g, x, y, false);
+            if (!_touch.waitForCalibratePoint(&morePoints, 0)) {
+                printf("Failed to get user click\n");
+                break;
+            }
+        }
+        if (morePoints) {
+            // aborted calibration due to error(s)
+            break;
+        }
+
+        // erase old location
+        calibrate_drawMarker(g, x, y, true);
+
+        // allow user to draw for 5999 seconds
+        Timer t;
+        t.start();
+        TouchPanel::touchCoordinate_t tc;
+        while(t.read() < 6000) {
+            if (_touch.read(tc)) {
+                //printf("TC: x,y,z = {%5d, %5d, %5d}\n", tc.x, tc.y, tc.z);
+                if (tc.z) {
+                    g.put_dot(tc.x, tc.y, 0xffff);
+                }
+            }
+        }
+    } while(0);
+    
+    return !morePoints;
+}
diff -r 000000000000 -r 4839ec6c350d TestDisplay.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TestDisplay.h	Fri Oct 03 13:09:11 2014 +0000
@@ -0,0 +1,63 @@
+/*
+ *  Copyright 2013 Embedded Artists AB
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef TESTDISPLAY_H
+#define TESTDISPLAY_H
+
+#include "AR1021I2C.h"
+#include "Graphics.h"
+#include "LcdController.h"
+#include "EaLcdBoardGPIO.h"
+
+/**
+ * Test the display connected with a FPC cable to the LPC4088 Experiment Base Board
+ * as well as the AR1021 touch sensor on the board.
+ */
+class TestDisplay {
+public:
+	enum WhichDisplay {
+		TFT_5,    // 5" display
+		TFT_4_3,  // 4.3" display
+	};
+
+    /**
+     * Create an interface to the display
+     */
+    TestDisplay(WhichDisplay which);
+    ~TestDisplay();
+
+    /**
+     * Test the display
+     *
+     * @return true if the test was successful; otherwise false
+     */
+	bool runTest();
+
+private:
+
+    void calibrate_drawMarker(Graphics &g, uint16_t x, uint16_t y, bool erase);
+    bool calibrate_display();
+
+	char* _initStr;
+    LcdController::Config* _lcdCfg;
+    EaLcdBoardGPIO _lcdBoard;
+    AR1021I2C _touch;
+
+    uint32_t _framebuffer;
+};
+
+#endif
+
diff -r 000000000000 -r 4839ec6c350d main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Fri Oct 03 13:09:11 2014 +0000
@@ -0,0 +1,46 @@
+/******************************************************************************
+ * Includes
+ *****************************************************************************/
+#include "mbed.h"
+
+#include "TestDisplay.h"
+
+/******************************************************************************
+ * Typedefs and defines
+ *****************************************************************************/
+
+
+/******************************************************************************
+ * Local variables
+ *****************************************************************************/
+
+DigitalOut myled(LED1);
+
+/******************************************************************************
+ * Local functions
+ *****************************************************************************/
+
+/*
+ * Test program for the 4.3" and 5" displays. This test is supposed to run
+ * on a LPC4088QSB board on an LPC4088 Experiment Base Board.
+ */
+
+
+int main() {
+    printf("\n"
+           "---\n"
+           "Display Demo Program for 4.3 and 5 inch display on the LPC4088 Experiment Base Board\n"
+           "Build Date: " __DATE__ " at " __TIME__ "\n"
+           "\n");
+
+    //TestDisplay display(TestDisplay::TFT_4_3);
+    TestDisplay display(TestDisplay::TFT_5);
+    display.runTest();
+    
+    while(1) {
+        myled = 1;
+        wait(0.2);
+        myled = 0;
+        wait(0.2);
+    }
+}
diff -r 000000000000 -r 4839ec6c350d mbed.bld
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Fri Oct 03 13:09:11 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/552587b429a1
\ No newline at end of file