Production Test Program (PTP) for the LPC4088 Experiment Base Board
Dependencies: EALib I2S LM75B SDFileSystem mbed
AR1021I2C.cpp
- Committer:
- embeddedartists
- Date:
- 2014-09-25
- Revision:
- 7:48375cb50f3a
- Parent:
- 1:47680ec5d783
File content as of revision 7:48375cb50f3a:
/* * 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, ®Offset, ®OffLen); 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, ®Offset, ®OffLen); 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); }