Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of EALib by
Diff: AR1021.cpp
- Revision:
- 4:b32cf4ef45c5
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);
+
+ }
+}
+
