A library with drivers for different peripherals on the LPC4088 QuickStart Board or related add-on boards.
Dependents: LPC4088test LPC4088test_ledonly LPC4088test_deleteall LPC4088_RAMtest ... more
Diff: AR1021.cpp
- Revision:
- 4:b32cf4ef45c5
- Child:
- 12:15597e45eea0
diff -r 9d31a3c5013e -r b32cf4ef45c5 AR1021.cpp --- /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, + ®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, &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, + ®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 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); + + } +} +