Graphical demo for the LPC4088 Experiment Base Board with one of the Display Expansion Kits. This program decodes decodes and shows two png images.

Dependencies:   EALib mbed

Revision:
0:b567d56a59d7
diff -r 000000000000 -r b567d56a59d7 AR1021I2C.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AR1021I2C.cpp	Fri Oct 03 13:30:09 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);
+}