The wait in mci_WaitForEvent will delay all card transactions.

Dependencies:   FATFileSystem

Fork of EALib by EmbeddedArtists AB

Committer:
embeddedartists
Date:
Thu Sep 26 06:37:02 2013 +0000
Revision:
0:0fdadbc3d852
Child:
4:b32cf4ef45c5
First version

Who changed what in which revision?

UserRevisionLine numberNew contents of line
embeddedartists 0:0fdadbc3d852 1
embeddedartists 0:0fdadbc3d852 2 #include "mbed.h"
embeddedartists 0:0fdadbc3d852 3 #include "TSC2046.h"
embeddedartists 0:0fdadbc3d852 4
embeddedartists 0:0fdadbc3d852 5 #ifndef ABS
embeddedartists 0:0fdadbc3d852 6 #define ABS(x) ( ((int32_t)(x)) < 0 ? (-(x)) : (x))
embeddedartists 0:0fdadbc3d852 7 #endif
embeddedartists 0:0fdadbc3d852 8
embeddedartists 0:0fdadbc3d852 9 #define ADS_START (1 << 7)
embeddedartists 0:0fdadbc3d852 10 #define ADS_A2A1A0_d_y (1 << 4) /* differential */
embeddedartists 0:0fdadbc3d852 11 #define ADS_A2A1A0_d_z1 (3 << 4) /* differential */
embeddedartists 0:0fdadbc3d852 12 #define ADS_A2A1A0_d_z2 (4 << 4) /* differential */
embeddedartists 0:0fdadbc3d852 13 #define ADS_A2A1A0_d_x (5 << 4) /* differential */
embeddedartists 0:0fdadbc3d852 14 #define ADS_A2A1A0_temp0 (0 << 4) /* non-differential */
embeddedartists 0:0fdadbc3d852 15 #define ADS_A2A1A0_vbatt (2 << 4) /* non-differential */
embeddedartists 0:0fdadbc3d852 16 #define ADS_A2A1A0_vaux (6 << 4) /* non-differential */
embeddedartists 0:0fdadbc3d852 17 #define ADS_A2A1A0_temp1 (7 << 4) /* non-differential */
embeddedartists 0:0fdadbc3d852 18 #define ADS_8_BIT (1 << 3)
embeddedartists 0:0fdadbc3d852 19 #define ADS_12_BIT (0 << 3)
embeddedartists 0:0fdadbc3d852 20 #define ADS_SER (1 << 2) /* non-differential */
embeddedartists 0:0fdadbc3d852 21 #define ADS_DFR (0 << 2) /* differential */
embeddedartists 0:0fdadbc3d852 22 #define ADS_PD10_PDOWN (0 << 0) /* lowpower mode + penirq */
embeddedartists 0:0fdadbc3d852 23 #define ADS_PD10_ADC_ON (1 << 0) /* ADC on */
embeddedartists 0:0fdadbc3d852 24 #define ADS_PD10_REF_ON (2 << 0) /* vREF on + penirq */
embeddedartists 0:0fdadbc3d852 25 #define ADS_PD10_ALL_ON (3 << 0) /* ADC + vREF on */
embeddedartists 0:0fdadbc3d852 26
embeddedartists 0:0fdadbc3d852 27
embeddedartists 0:0fdadbc3d852 28 #define READ_12BIT_DFR(d, adc, vref) (ADS_START | d \
embeddedartists 0:0fdadbc3d852 29 | ADS_12_BIT | ADS_DFR | \
embeddedartists 0:0fdadbc3d852 30 (adc ? ADS_PD10_ADC_ON : 0) | (vref ? ADS_PD10_REF_ON : 0))
embeddedartists 0:0fdadbc3d852 31
embeddedartists 0:0fdadbc3d852 32 #define READ_Y(vref) (READ_12BIT_DFR(ADS_A2A1A0_d_y, 1, vref))
embeddedartists 0:0fdadbc3d852 33 #define READ_Z1(vref) (READ_12BIT_DFR(ADS_A2A1A0_d_z1, 1, vref))
embeddedartists 0:0fdadbc3d852 34 #define READ_Z2(vref) (READ_12BIT_DFR(ADS_A2A1A0_d_z2, 1, vref))
embeddedartists 0:0fdadbc3d852 35 #define READ_X(vref) (READ_12BIT_DFR(ADS_A2A1A0_d_x, 1, vref))
embeddedartists 0:0fdadbc3d852 36 #define PWRDOWN (READ_12BIT_DFR(ADS_A2A1A0_d_y, 0, 0)) /* LAST */
embeddedartists 0:0fdadbc3d852 37
embeddedartists 0:0fdadbc3d852 38 /* single-ended samples need to first power up reference voltage;
embeddedartists 0:0fdadbc3d852 39 * we leave both ADC and VREF powered
embeddedartists 0:0fdadbc3d852 40 */
embeddedartists 0:0fdadbc3d852 41 #define READ_12BIT_SER(x) (ADS_START | x \
embeddedartists 0:0fdadbc3d852 42 | ADS_12_BIT | ADS_SER)
embeddedartists 0:0fdadbc3d852 43
embeddedartists 0:0fdadbc3d852 44 #define REF_ON (READ_12BIT_DFR(ADS_A2A1A0_d_x, 1, 1))
embeddedartists 0:0fdadbc3d852 45 #define REF_OFF (READ_12BIT_DFR(ADS_A2A1A0_d_y, 0, 0))
embeddedartists 0:0fdadbc3d852 46
embeddedartists 0:0fdadbc3d852 47 #define DEBOUNCE_MAX 10
embeddedartists 0:0fdadbc3d852 48 #define DEBOUNCE_TOL 3
embeddedartists 0:0fdadbc3d852 49
embeddedartists 0:0fdadbc3d852 50 TSC2046::TSC2046(PinName mosi, PinName miso, PinName sck, PinName cs) :
embeddedartists 0:0fdadbc3d852 51 _spi(mosi, miso, sck), _cs(cs)
embeddedartists 0:0fdadbc3d852 52 {
embeddedartists 0:0fdadbc3d852 53 _cs = 1; // active low
embeddedartists 0:0fdadbc3d852 54
embeddedartists 0:0fdadbc3d852 55 _spi.format(8, 3);
embeddedartists 0:0fdadbc3d852 56 _spi.frequency(1500000);
embeddedartists 0:0fdadbc3d852 57 _calibrated = false;
embeddedartists 0:0fdadbc3d852 58 _initialized = false;
embeddedartists 0:0fdadbc3d852 59 }
embeddedartists 0:0fdadbc3d852 60
embeddedartists 0:0fdadbc3d852 61
embeddedartists 0:0fdadbc3d852 62 void TSC2046::read(touchCoordinate_t &coord) {
embeddedartists 0:0fdadbc3d852 63
embeddedartists 0:0fdadbc3d852 64 touchCoordinate_t tmpCoord;
embeddedartists 0:0fdadbc3d852 65 calibPoint_t displayPoint;
embeddedartists 0:0fdadbc3d852 66 calibPoint_t screenSample;
embeddedartists 0:0fdadbc3d852 67
embeddedartists 0:0fdadbc3d852 68 if (!_initialized) {
embeddedartists 0:0fdadbc3d852 69 init();
embeddedartists 0:0fdadbc3d852 70 _initialized = true;
embeddedartists 0:0fdadbc3d852 71 }
embeddedartists 0:0fdadbc3d852 72
embeddedartists 0:0fdadbc3d852 73 readAndFilter(tmpCoord);
embeddedartists 0:0fdadbc3d852 74
embeddedartists 0:0fdadbc3d852 75 _cs = 0;
embeddedartists 0:0fdadbc3d852 76 _spi.write(PWRDOWN);
embeddedartists 0:0fdadbc3d852 77 _cs = 1;
embeddedartists 0:0fdadbc3d852 78
embeddedartists 0:0fdadbc3d852 79 coord.z = tmpCoord.z;
embeddedartists 0:0fdadbc3d852 80
embeddedartists 0:0fdadbc3d852 81 if (_calibrated) {
embeddedartists 0:0fdadbc3d852 82 screenSample.x = tmpCoord.x;
embeddedartists 0:0fdadbc3d852 83 screenSample.y = tmpCoord.y;
embeddedartists 0:0fdadbc3d852 84
embeddedartists 0:0fdadbc3d852 85 getDisplayPoint(&displayPoint, &screenSample, &_calibMatrix);
embeddedartists 0:0fdadbc3d852 86
embeddedartists 0:0fdadbc3d852 87 coord.x = displayPoint.x;
embeddedartists 0:0fdadbc3d852 88 coord.y = displayPoint.y;
embeddedartists 0:0fdadbc3d852 89 }
embeddedartists 0:0fdadbc3d852 90 else {
embeddedartists 0:0fdadbc3d852 91 coord.x = tmpCoord.x;
embeddedartists 0:0fdadbc3d852 92 coord.y = tmpCoord.y;
embeddedartists 0:0fdadbc3d852 93 }
embeddedartists 0:0fdadbc3d852 94
embeddedartists 0:0fdadbc3d852 95 }
embeddedartists 0:0fdadbc3d852 96
embeddedartists 0:0fdadbc3d852 97 void TSC2046::calibrate(touchCoordinate_t &ref1,
embeddedartists 0:0fdadbc3d852 98 touchCoordinate_t &ref2,
embeddedartists 0:0fdadbc3d852 99 touchCoordinate_t &ref3,
embeddedartists 0:0fdadbc3d852 100 touchCoordinate_t &scr1,
embeddedartists 0:0fdadbc3d852 101 touchCoordinate_t &scr2,
embeddedartists 0:0fdadbc3d852 102 touchCoordinate_t &scr3) {
embeddedartists 0:0fdadbc3d852 103
embeddedartists 0:0fdadbc3d852 104 calibPoint_t disp[3];
embeddedartists 0:0fdadbc3d852 105 calibPoint_t scr[3];
embeddedartists 0:0fdadbc3d852 106
embeddedartists 0:0fdadbc3d852 107 disp[0].x = ref1.x;
embeddedartists 0:0fdadbc3d852 108 disp[0].y = ref1.y;
embeddedartists 0:0fdadbc3d852 109 disp[1].x = ref2.x;
embeddedartists 0:0fdadbc3d852 110 disp[1].y = ref2.y;
embeddedartists 0:0fdadbc3d852 111 disp[2].x = ref3.x;
embeddedartists 0:0fdadbc3d852 112 disp[2].y = ref3.y;
embeddedartists 0:0fdadbc3d852 113
embeddedartists 0:0fdadbc3d852 114 scr[0].x = scr1.x;
embeddedartists 0:0fdadbc3d852 115 scr[0].y = scr1.y;
embeddedartists 0:0fdadbc3d852 116 scr[1].x = scr2.x;
embeddedartists 0:0fdadbc3d852 117 scr[1].y = scr2.y;
embeddedartists 0:0fdadbc3d852 118 scr[2].x = scr3.x;
embeddedartists 0:0fdadbc3d852 119 scr[2].y = scr3.y;
embeddedartists 0:0fdadbc3d852 120
embeddedartists 0:0fdadbc3d852 121 setCalibrationMatrix(disp, scr, &_calibMatrix);
embeddedartists 0:0fdadbc3d852 122
embeddedartists 0:0fdadbc3d852 123 _calibrated = true;
embeddedartists 0:0fdadbc3d852 124
embeddedartists 0:0fdadbc3d852 125 }
embeddedartists 0:0fdadbc3d852 126
embeddedartists 0:0fdadbc3d852 127 void TSC2046::uncalibrate() {
embeddedartists 0:0fdadbc3d852 128 _calibrated = false;
embeddedartists 0:0fdadbc3d852 129 }
embeddedartists 0:0fdadbc3d852 130
embeddedartists 0:0fdadbc3d852 131
embeddedartists 0:0fdadbc3d852 132 void TSC2046::init() {
embeddedartists 0:0fdadbc3d852 133
embeddedartists 0:0fdadbc3d852 134 _cs = 0;
embeddedartists 0:0fdadbc3d852 135
embeddedartists 0:0fdadbc3d852 136 _spi.write(REF_ON);
embeddedartists 0:0fdadbc3d852 137 _spi.write((READ_12BIT_SER(ADS_A2A1A0_vaux) | ADS_PD10_ALL_ON));
embeddedartists 0:0fdadbc3d852 138 _spi.write(PWRDOWN);
embeddedartists 0:0fdadbc3d852 139
embeddedartists 0:0fdadbc3d852 140 _cs = 1;
embeddedartists 0:0fdadbc3d852 141 }
embeddedartists 0:0fdadbc3d852 142
embeddedartists 0:0fdadbc3d852 143 void TSC2046::readAndFilter(touchCoordinate_t &coord)
embeddedartists 0:0fdadbc3d852 144 {
embeddedartists 0:0fdadbc3d852 145 int32_t ix, iy, iz1, iz2 = 0;
embeddedartists 0:0fdadbc3d852 146 int32_t lastx, lasty, lastz1, lastz2 = 0;
embeddedartists 0:0fdadbc3d852 147 int i = 0;
embeddedartists 0:0fdadbc3d852 148
embeddedartists 0:0fdadbc3d852 149 coord.x = 0;
embeddedartists 0:0fdadbc3d852 150 coord.y = 0;
embeddedartists 0:0fdadbc3d852 151 coord.z = 0;
embeddedartists 0:0fdadbc3d852 152
embeddedartists 0:0fdadbc3d852 153 lasty = getFilteredValue(READ_Y(0));
embeddedartists 0:0fdadbc3d852 154 lasty >>= 3;
embeddedartists 0:0fdadbc3d852 155 if (lasty >= 4095) {
embeddedartists 0:0fdadbc3d852 156 lasty = 0;
embeddedartists 0:0fdadbc3d852 157 }
embeddedartists 0:0fdadbc3d852 158
embeddedartists 0:0fdadbc3d852 159 lastx = getFilteredValue(READ_X(0));
embeddedartists 0:0fdadbc3d852 160 lastx >>= 3;
embeddedartists 0:0fdadbc3d852 161 if (lastx >= 4095) {
embeddedartists 0:0fdadbc3d852 162 lastx = 0;
embeddedartists 0:0fdadbc3d852 163 }
embeddedartists 0:0fdadbc3d852 164
embeddedartists 0:0fdadbc3d852 165 lastz1 = getFilteredValue(READ_Z1(0));
embeddedartists 0:0fdadbc3d852 166 lastz1 >>= 3;
embeddedartists 0:0fdadbc3d852 167
embeddedartists 0:0fdadbc3d852 168 lastz2 = getFilteredValue(READ_Z2(0));
embeddedartists 0:0fdadbc3d852 169 lastz2 >>= 3;
embeddedartists 0:0fdadbc3d852 170
embeddedartists 0:0fdadbc3d852 171
embeddedartists 0:0fdadbc3d852 172 if (lastx && lastz1) {
embeddedartists 0:0fdadbc3d852 173 coord.z = (lastx * ABS(lastz2 - lastz1)) / lastz1;
embeddedartists 0:0fdadbc3d852 174 }
embeddedartists 0:0fdadbc3d852 175 else {
embeddedartists 0:0fdadbc3d852 176 coord.z = 0;
embeddedartists 0:0fdadbc3d852 177 }
embeddedartists 0:0fdadbc3d852 178
embeddedartists 0:0fdadbc3d852 179 if (coord.z > 20000) {
embeddedartists 0:0fdadbc3d852 180 coord.z = 0;
embeddedartists 0:0fdadbc3d852 181 }
embeddedartists 0:0fdadbc3d852 182
embeddedartists 0:0fdadbc3d852 183 if (coord.z == 0) {
embeddedartists 0:0fdadbc3d852 184 return;
embeddedartists 0:0fdadbc3d852 185 }
embeddedartists 0:0fdadbc3d852 186
embeddedartists 0:0fdadbc3d852 187 for (i = 0; i < DEBOUNCE_MAX; i++) {
embeddedartists 0:0fdadbc3d852 188 iy = getFilteredValue(READ_Y(0));
embeddedartists 0:0fdadbc3d852 189 iy >>= 3;
embeddedartists 0:0fdadbc3d852 190
embeddedartists 0:0fdadbc3d852 191 if (ABS (lasty - iy) <= DEBOUNCE_TOL) {
embeddedartists 0:0fdadbc3d852 192 break;
embeddedartists 0:0fdadbc3d852 193 }
embeddedartists 0:0fdadbc3d852 194
embeddedartists 0:0fdadbc3d852 195 lasty = iy;
embeddedartists 0:0fdadbc3d852 196 }
embeddedartists 0:0fdadbc3d852 197
embeddedartists 0:0fdadbc3d852 198 for (i = 0; i < DEBOUNCE_MAX; i++) {
embeddedartists 0:0fdadbc3d852 199 ix = getFilteredValue(READ_X(0));
embeddedartists 0:0fdadbc3d852 200 ix >>= 3;
embeddedartists 0:0fdadbc3d852 201 if (ix > 4095) {
embeddedartists 0:0fdadbc3d852 202 ix = 0;
embeddedartists 0:0fdadbc3d852 203 }
embeddedartists 0:0fdadbc3d852 204
embeddedartists 0:0fdadbc3d852 205 if (ABS (lastx - ix) <= DEBOUNCE_TOL) {
embeddedartists 0:0fdadbc3d852 206 break;
embeddedartists 0:0fdadbc3d852 207 }
embeddedartists 0:0fdadbc3d852 208
embeddedartists 0:0fdadbc3d852 209 lastx = ix;
embeddedartists 0:0fdadbc3d852 210 }
embeddedartists 0:0fdadbc3d852 211
embeddedartists 0:0fdadbc3d852 212 for (i = 0; i < DEBOUNCE_MAX; i++) {
embeddedartists 0:0fdadbc3d852 213 iz1 = getFilteredValue(READ_Z1(0));
embeddedartists 0:0fdadbc3d852 214 iz1 >>= 3;
embeddedartists 0:0fdadbc3d852 215
embeddedartists 0:0fdadbc3d852 216 if (ABS (lastz1 - iz1) <= DEBOUNCE_TOL) {
embeddedartists 0:0fdadbc3d852 217 break;
embeddedartists 0:0fdadbc3d852 218 }
embeddedartists 0:0fdadbc3d852 219
embeddedartists 0:0fdadbc3d852 220 lastz1 = iz1;
embeddedartists 0:0fdadbc3d852 221 }
embeddedartists 0:0fdadbc3d852 222
embeddedartists 0:0fdadbc3d852 223 for (i = 0; i < DEBOUNCE_MAX; i++) {
embeddedartists 0:0fdadbc3d852 224 iz2 = getFilteredValue(READ_Z2(0));
embeddedartists 0:0fdadbc3d852 225 iz2 >>= 3;
embeddedartists 0:0fdadbc3d852 226
embeddedartists 0:0fdadbc3d852 227 if (ABS (lastz2 - iz2) <= DEBOUNCE_TOL) {
embeddedartists 0:0fdadbc3d852 228 break;
embeddedartists 0:0fdadbc3d852 229 }
embeddedartists 0:0fdadbc3d852 230
embeddedartists 0:0fdadbc3d852 231 lastz2 = iz2;
embeddedartists 0:0fdadbc3d852 232 }
embeddedartists 0:0fdadbc3d852 233
embeddedartists 0:0fdadbc3d852 234 coord.x = ix;
embeddedartists 0:0fdadbc3d852 235 coord.y = iy;
embeddedartists 0:0fdadbc3d852 236
embeddedartists 0:0fdadbc3d852 237 if (ix && iz1) {
embeddedartists 0:0fdadbc3d852 238 coord.z = (ix * ABS(iz2 - iz1)) / iz1;
embeddedartists 0:0fdadbc3d852 239 }
embeddedartists 0:0fdadbc3d852 240 else {
embeddedartists 0:0fdadbc3d852 241 coord.z = 0;
embeddedartists 0:0fdadbc3d852 242 }
embeddedartists 0:0fdadbc3d852 243
embeddedartists 0:0fdadbc3d852 244 if (coord.z > 20000) {
embeddedartists 0:0fdadbc3d852 245 coord.z = 0;
embeddedartists 0:0fdadbc3d852 246 }
embeddedartists 0:0fdadbc3d852 247
embeddedartists 0:0fdadbc3d852 248 }
embeddedartists 0:0fdadbc3d852 249
embeddedartists 0:0fdadbc3d852 250 int32_t TSC2046::getFilteredValue(int cmd)
embeddedartists 0:0fdadbc3d852 251 {
embeddedartists 0:0fdadbc3d852 252 int32_t a[7];
embeddedartists 0:0fdadbc3d852 253 int32_t tmp = 0;
embeddedartists 0:0fdadbc3d852 254 int i = 0, j = 0;
embeddedartists 0:0fdadbc3d852 255
embeddedartists 0:0fdadbc3d852 256 /*
embeddedartists 0:0fdadbc3d852 257 * Median and averaging filter
embeddedartists 0:0fdadbc3d852 258 *
embeddedartists 0:0fdadbc3d852 259 * 1. Get 7 values
embeddedartists 0:0fdadbc3d852 260 * 2. Sort these values
embeddedartists 0:0fdadbc3d852 261 * 3. Take average of the 3 values in the middle
embeddedartists 0:0fdadbc3d852 262 */
embeddedartists 0:0fdadbc3d852 263
embeddedartists 0:0fdadbc3d852 264 for (i = 0; i < 7; i++) {
embeddedartists 0:0fdadbc3d852 265 a[i] = spiTransfer(cmd);
embeddedartists 0:0fdadbc3d852 266 }
embeddedartists 0:0fdadbc3d852 267
embeddedartists 0:0fdadbc3d852 268 // bubble sort
embeddedartists 0:0fdadbc3d852 269 for (i = 0; i < 7; i++) {
embeddedartists 0:0fdadbc3d852 270 for (j = 0; j < (7-(i+1)); j++) {
embeddedartists 0:0fdadbc3d852 271 if (a[j] > a[j+1]) {
embeddedartists 0:0fdadbc3d852 272 // swap
embeddedartists 0:0fdadbc3d852 273 tmp = a[j];
embeddedartists 0:0fdadbc3d852 274 a[j] = a[j+1];
embeddedartists 0:0fdadbc3d852 275 a[j+1] = tmp;
embeddedartists 0:0fdadbc3d852 276 }
embeddedartists 0:0fdadbc3d852 277 }
embeddedartists 0:0fdadbc3d852 278 }
embeddedartists 0:0fdadbc3d852 279
embeddedartists 0:0fdadbc3d852 280 // average of 3 values in the middle
embeddedartists 0:0fdadbc3d852 281 return ((a[2]+a[3]+a[4])/3);
embeddedartists 0:0fdadbc3d852 282 }
embeddedartists 0:0fdadbc3d852 283
embeddedartists 0:0fdadbc3d852 284 uint16_t TSC2046::spiTransfer(uint8_t cmd)
embeddedartists 0:0fdadbc3d852 285 {
embeddedartists 0:0fdadbc3d852 286 uint8_t data[3];
embeddedartists 0:0fdadbc3d852 287
embeddedartists 0:0fdadbc3d852 288 _cs = 0;
embeddedartists 0:0fdadbc3d852 289
embeddedartists 0:0fdadbc3d852 290 /*data[0] = */_spi.write(cmd);
embeddedartists 0:0fdadbc3d852 291 data[0] = _spi.write(0xff);
embeddedartists 0:0fdadbc3d852 292 data[1] = _spi.write(0xff);
embeddedartists 0:0fdadbc3d852 293
embeddedartists 0:0fdadbc3d852 294 _cs = 1;
embeddedartists 0:0fdadbc3d852 295
embeddedartists 0:0fdadbc3d852 296 return ((data[0] << 8) | data[1]);
embeddedartists 0:0fdadbc3d852 297 }
embeddedartists 0:0fdadbc3d852 298
embeddedartists 0:0fdadbc3d852 299
embeddedartists 0:0fdadbc3d852 300 // ############################################################################
embeddedartists 0:0fdadbc3d852 301 // >>>>>>>> Calibrate code >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
embeddedartists 0:0fdadbc3d852 302 // ############################################################################
embeddedartists 0:0fdadbc3d852 303
embeddedartists 0:0fdadbc3d852 304
embeddedartists 0:0fdadbc3d852 305 /*
embeddedartists 0:0fdadbc3d852 306 *
embeddedartists 0:0fdadbc3d852 307 * Copyright (c) 2001, Carlos E. Vidales. All rights reserved.
embeddedartists 0:0fdadbc3d852 308 *
embeddedartists 0:0fdadbc3d852 309 * This sample program was written and put in the public domain
embeddedartists 0:0fdadbc3d852 310 * by Carlos E. Vidales. The program is provided "as is"
embeddedartists 0:0fdadbc3d852 311 * without warranty of any kind, either expressed or implied.
embeddedartists 0:0fdadbc3d852 312 * If you choose to use the program within your own products
embeddedartists 0:0fdadbc3d852 313 * you do so at your own risk, and assume the responsibility
embeddedartists 0:0fdadbc3d852 314 * for servicing, repairing or correcting the program should
embeddedartists 0:0fdadbc3d852 315 * it prove defective in any manner.
embeddedartists 0:0fdadbc3d852 316 * You may copy and distribute the program's source code in any
embeddedartists 0:0fdadbc3d852 317 * medium, provided that you also include in each copy an
embeddedartists 0:0fdadbc3d852 318 * appropriate copyright notice and disclaimer of warranty.
embeddedartists 0:0fdadbc3d852 319 * You may also modify this program and distribute copies of
embeddedartists 0:0fdadbc3d852 320 * it provided that you include prominent notices stating
embeddedartists 0:0fdadbc3d852 321 * that you changed the file(s) and the date of any change,
embeddedartists 0:0fdadbc3d852 322 * and that you do not charge any royalties or licenses for
embeddedartists 0:0fdadbc3d852 323 * its use.
embeddedartists 0:0fdadbc3d852 324 *
embeddedartists 0:0fdadbc3d852 325 *
embeddedartists 0:0fdadbc3d852 326 *
embeddedartists 0:0fdadbc3d852 327 * File Name: calibrate.c
embeddedartists 0:0fdadbc3d852 328 *
embeddedartists 0:0fdadbc3d852 329 *
embeddedartists 0:0fdadbc3d852 330 * This file contains functions that implement calculations
embeddedartists 0:0fdadbc3d852 331 * necessary to obtain calibration factors for a touch screen
embeddedartists 0:0fdadbc3d852 332 * that suffers from multiple distortion effects: namely,
embeddedartists 0:0fdadbc3d852 333 * translation, scaling and rotation.
embeddedartists 0:0fdadbc3d852 334 *
embeddedartists 0:0fdadbc3d852 335 * The following set of equations represent a valid display
embeddedartists 0:0fdadbc3d852 336 * point given a corresponding set of touch screen points:
embeddedartists 0:0fdadbc3d852 337 *
embeddedartists 0:0fdadbc3d852 338 *
embeddedartists 0:0fdadbc3d852 339 * /- -\
embeddedartists 0:0fdadbc3d852 340 * /- -\ /- -\ | |
embeddedartists 0:0fdadbc3d852 341 * | | | | | Xs |
embeddedartists 0:0fdadbc3d852 342 * | Xd | | A B C | | |
embeddedartists 0:0fdadbc3d852 343 * | | = | | * | Ys |
embeddedartists 0:0fdadbc3d852 344 * | Yd | | D E F | | |
embeddedartists 0:0fdadbc3d852 345 * | | | | | 1 |
embeddedartists 0:0fdadbc3d852 346 * \- -/ \- -/ | |
embeddedartists 0:0fdadbc3d852 347 * \- -/
embeddedartists 0:0fdadbc3d852 348 *
embeddedartists 0:0fdadbc3d852 349 *
embeddedartists 0:0fdadbc3d852 350 * where:
embeddedartists 0:0fdadbc3d852 351 *
embeddedartists 0:0fdadbc3d852 352 * (Xd,Yd) represents the desired display point
embeddedartists 0:0fdadbc3d852 353 * coordinates,
embeddedartists 0:0fdadbc3d852 354 *
embeddedartists 0:0fdadbc3d852 355 * (Xs,Ys) represents the available touch screen
embeddedartists 0:0fdadbc3d852 356 * coordinates, and the matrix
embeddedartists 0:0fdadbc3d852 357 *
embeddedartists 0:0fdadbc3d852 358 * /- -\
embeddedartists 0:0fdadbc3d852 359 * |A,B,C|
embeddedartists 0:0fdadbc3d852 360 * |D,E,F| represents the factors used to translate
embeddedartists 0:0fdadbc3d852 361 * \- -/ the available touch screen point values
embeddedartists 0:0fdadbc3d852 362 * into the corresponding display
embeddedartists 0:0fdadbc3d852 363 * coordinates.
embeddedartists 0:0fdadbc3d852 364 *
embeddedartists 0:0fdadbc3d852 365 *
embeddedartists 0:0fdadbc3d852 366 * Note that for practical considerations, the utilitities
embeddedartists 0:0fdadbc3d852 367 * within this file do not use the matrix coefficients as
embeddedartists 0:0fdadbc3d852 368 * defined above, but instead use the following
embeddedartists 0:0fdadbc3d852 369 * equivalents, since floating point math is not used:
embeddedartists 0:0fdadbc3d852 370 *
embeddedartists 0:0fdadbc3d852 371 * A = An/Divider
embeddedartists 0:0fdadbc3d852 372 * B = Bn/Divider
embeddedartists 0:0fdadbc3d852 373 * C = Cn/Divider
embeddedartists 0:0fdadbc3d852 374 * D = Dn/Divider
embeddedartists 0:0fdadbc3d852 375 * E = En/Divider
embeddedartists 0:0fdadbc3d852 376 * F = Fn/Divider
embeddedartists 0:0fdadbc3d852 377 *
embeddedartists 0:0fdadbc3d852 378 *
embeddedartists 0:0fdadbc3d852 379 *
embeddedartists 0:0fdadbc3d852 380 * The functions provided within this file are:
embeddedartists 0:0fdadbc3d852 381 *
embeddedartists 0:0fdadbc3d852 382 * setCalibrationMatrix() - calculates the set of factors
embeddedartists 0:0fdadbc3d852 383 * in the above equation, given
embeddedartists 0:0fdadbc3d852 384 * three sets of test points.
embeddedartists 0:0fdadbc3d852 385 * getDisplayPoint() - returns the actual display
embeddedartists 0:0fdadbc3d852 386 * coordinates, given a set of
embeddedartists 0:0fdadbc3d852 387 * touch screen coordinates.
embeddedartists 0:0fdadbc3d852 388 * translateRawScreenCoordinates() - helper function to transform
embeddedartists 0:0fdadbc3d852 389 * raw screen points into values
embeddedartists 0:0fdadbc3d852 390 * scaled to the desired display
embeddedartists 0:0fdadbc3d852 391 * resolution.
embeddedartists 0:0fdadbc3d852 392 *
embeddedartists 0:0fdadbc3d852 393 *
embeddedartists 0:0fdadbc3d852 394 */
embeddedartists 0:0fdadbc3d852 395
embeddedartists 0:0fdadbc3d852 396
embeddedartists 0:0fdadbc3d852 397 /**********************************************************************
embeddedartists 0:0fdadbc3d852 398 *
embeddedartists 0:0fdadbc3d852 399 * Function: setCalibrationMatrix()
embeddedartists 0:0fdadbc3d852 400 *
embeddedartists 0:0fdadbc3d852 401 * Description: Calling this function with valid input data
embeddedartists 0:0fdadbc3d852 402 * in the display and screen input arguments
embeddedartists 0:0fdadbc3d852 403 * causes the calibration factors between the
embeddedartists 0:0fdadbc3d852 404 * screen and display points to be calculated,
embeddedartists 0:0fdadbc3d852 405 * and the output argument - matrixPtr - to be
embeddedartists 0:0fdadbc3d852 406 * populated.
embeddedartists 0:0fdadbc3d852 407 *
embeddedartists 0:0fdadbc3d852 408 * This function needs to be called only when new
embeddedartists 0:0fdadbc3d852 409 * calibration factors are desired.
embeddedartists 0:0fdadbc3d852 410 *
embeddedartists 0:0fdadbc3d852 411 *
embeddedartists 0:0fdadbc3d852 412 * Argument(s): displayPtr (input) - Pointer to an array of three
embeddedartists 0:0fdadbc3d852 413 * sample, reference points.
embeddedartists 0:0fdadbc3d852 414 * screenPtr (input) - Pointer to the array of touch
embeddedartists 0:0fdadbc3d852 415 * screen points corresponding
embeddedartists 0:0fdadbc3d852 416 * to the reference display points.
embeddedartists 0:0fdadbc3d852 417 * matrixPtr (output) - Pointer to the calibration
embeddedartists 0:0fdadbc3d852 418 * matrix computed for the set
embeddedartists 0:0fdadbc3d852 419 * of points being provided.
embeddedartists 0:0fdadbc3d852 420 *
embeddedartists 0:0fdadbc3d852 421 *
embeddedartists 0:0fdadbc3d852 422 * From the article text, recall that the matrix coefficients are
embeddedartists 0:0fdadbc3d852 423 * resolved to be the following:
embeddedartists 0:0fdadbc3d852 424 *
embeddedartists 0:0fdadbc3d852 425 *
embeddedartists 0:0fdadbc3d852 426 * Divider = (Xs0 - Xs2)*(Ys1 - Ys2) - (Xs1 - Xs2)*(Ys0 - Ys2)
embeddedartists 0:0fdadbc3d852 427 *
embeddedartists 0:0fdadbc3d852 428 *
embeddedartists 0:0fdadbc3d852 429 *
embeddedartists 0:0fdadbc3d852 430 * (Xd0 - Xd2)*(Ys1 - Ys2) - (Xd1 - Xd2)*(Ys0 - Ys2)
embeddedartists 0:0fdadbc3d852 431 * A = ---------------------------------------------------
embeddedartists 0:0fdadbc3d852 432 * Divider
embeddedartists 0:0fdadbc3d852 433 *
embeddedartists 0:0fdadbc3d852 434 *
embeddedartists 0:0fdadbc3d852 435 * (Xs0 - Xs2)*(Xd1 - Xd2) - (Xd0 - Xd2)*(Xs1 - Xs2)
embeddedartists 0:0fdadbc3d852 436 * B = ---------------------------------------------------
embeddedartists 0:0fdadbc3d852 437 * Divider
embeddedartists 0:0fdadbc3d852 438 *
embeddedartists 0:0fdadbc3d852 439 *
embeddedartists 0:0fdadbc3d852 440 * Ys0*(Xs2*Xd1 - Xs1*Xd2) +
embeddedartists 0:0fdadbc3d852 441 * Ys1*(Xs0*Xd2 - Xs2*Xd0) +
embeddedartists 0:0fdadbc3d852 442 * Ys2*(Xs1*Xd0 - Xs0*Xd1)
embeddedartists 0:0fdadbc3d852 443 * C = ---------------------------------------------------
embeddedartists 0:0fdadbc3d852 444 * Divider
embeddedartists 0:0fdadbc3d852 445 *
embeddedartists 0:0fdadbc3d852 446 *
embeddedartists 0:0fdadbc3d852 447 * (Yd0 - Yd2)*(Ys1 - Ys2) - (Yd1 - Yd2)*(Ys0 - Ys2)
embeddedartists 0:0fdadbc3d852 448 * D = ---------------------------------------------------
embeddedartists 0:0fdadbc3d852 449 * Divider
embeddedartists 0:0fdadbc3d852 450 *
embeddedartists 0:0fdadbc3d852 451 *
embeddedartists 0:0fdadbc3d852 452 * (Xs0 - Xs2)*(Yd1 - Yd2) - (Yd0 - Yd2)*(Xs1 - Xs2)
embeddedartists 0:0fdadbc3d852 453 * E = ---------------------------------------------------
embeddedartists 0:0fdadbc3d852 454 * Divider
embeddedartists 0:0fdadbc3d852 455 *
embeddedartists 0:0fdadbc3d852 456 *
embeddedartists 0:0fdadbc3d852 457 * Ys0*(Xs2*Yd1 - Xs1*Yd2) +
embeddedartists 0:0fdadbc3d852 458 * Ys1*(Xs0*Yd2 - Xs2*Yd0) +
embeddedartists 0:0fdadbc3d852 459 * Ys2*(Xs1*Yd0 - Xs0*Yd1)
embeddedartists 0:0fdadbc3d852 460 * F = ---------------------------------------------------
embeddedartists 0:0fdadbc3d852 461 * Divider
embeddedartists 0:0fdadbc3d852 462 *
embeddedartists 0:0fdadbc3d852 463 *
embeddedartists 0:0fdadbc3d852 464 * Return: OK - the calibration matrix was correctly
embeddedartists 0:0fdadbc3d852 465 * calculated and its value is in the
embeddedartists 0:0fdadbc3d852 466 * output argument.
embeddedartists 0:0fdadbc3d852 467 * NOT_OK - an error was detected and the
embeddedartists 0:0fdadbc3d852 468 * function failed to return a valid
embeddedartists 0:0fdadbc3d852 469 * set of matrix values.
embeddedartists 0:0fdadbc3d852 470 * The only time this sample code returns
embeddedartists 0:0fdadbc3d852 471 * NOT_OK is when Divider == 0
embeddedartists 0:0fdadbc3d852 472 *
embeddedartists 0:0fdadbc3d852 473 *
embeddedartists 0:0fdadbc3d852 474 *
embeddedartists 0:0fdadbc3d852 475 * NOTE! NOTE! NOTE!
embeddedartists 0:0fdadbc3d852 476 *
embeddedartists 0:0fdadbc3d852 477 * setCalibrationMatrix() and getDisplayPoint() will do fine
embeddedartists 0:0fdadbc3d852 478 * for you as they are, provided that your digitizer
embeddedartists 0:0fdadbc3d852 479 * resolution does not exceed 10 bits (1024 values). Higher
embeddedartists 0:0fdadbc3d852 480 * resolutions may cause the integer operations to overflow
embeddedartists 0:0fdadbc3d852 481 * and return incorrect values. If you wish to use these
embeddedartists 0:0fdadbc3d852 482 * functions with digitizer resolutions of 12 bits (4096
embeddedartists 0:0fdadbc3d852 483 * values) you will either have to a) use 64-bit signed
embeddedartists 0:0fdadbc3d852 484 * integer variables and math, or b) judiciously modify the
embeddedartists 0:0fdadbc3d852 485 * operations to scale results by a factor of 2 or even 4.
embeddedartists 0:0fdadbc3d852 486 *
embeddedartists 0:0fdadbc3d852 487 *
embeddedartists 0:0fdadbc3d852 488 */
embeddedartists 0:0fdadbc3d852 489 int TSC2046::setCalibrationMatrix( calibPoint_t * displayPtr,
embeddedartists 0:0fdadbc3d852 490 calibPoint_t * screenPtr,
embeddedartists 0:0fdadbc3d852 491 calibMatrix_t * matrixPtr)
embeddedartists 0:0fdadbc3d852 492 {
embeddedartists 0:0fdadbc3d852 493 int retValue = 0 ;
embeddedartists 0:0fdadbc3d852 494
embeddedartists 0:0fdadbc3d852 495
embeddedartists 0:0fdadbc3d852 496 matrixPtr->Divider = ((screenPtr[0].x - screenPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) -
embeddedartists 0:0fdadbc3d852 497 ((screenPtr[1].x - screenPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ;
embeddedartists 0:0fdadbc3d852 498 if( matrixPtr->Divider == 0 )
embeddedartists 0:0fdadbc3d852 499 {
embeddedartists 0:0fdadbc3d852 500 retValue = 1 ;
embeddedartists 0:0fdadbc3d852 501 }
embeddedartists 0:0fdadbc3d852 502 else
embeddedartists 0:0fdadbc3d852 503 {
embeddedartists 0:0fdadbc3d852 504 matrixPtr->An = ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) -
embeddedartists 0:0fdadbc3d852 505 ((displayPtr[1].x - displayPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ;
embeddedartists 0:0fdadbc3d852 506 matrixPtr->Bn = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].x - displayPtr[2].x)) -
embeddedartists 0:0fdadbc3d852 507 ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].x - screenPtr[2].x)) ;
embeddedartists 0:0fdadbc3d852 508 matrixPtr->Cn = (screenPtr[2].x * displayPtr[1].x - screenPtr[1].x * displayPtr[2].x) * screenPtr[0].y +
embeddedartists 0:0fdadbc3d852 509 (screenPtr[0].x * displayPtr[2].x - screenPtr[2].x * displayPtr[0].x) * screenPtr[1].y +
embeddedartists 0:0fdadbc3d852 510 (screenPtr[1].x * displayPtr[0].x - screenPtr[0].x * displayPtr[1].x) * screenPtr[2].y ;
embeddedartists 0:0fdadbc3d852 511 matrixPtr->Dn = ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].y - screenPtr[2].y)) -
embeddedartists 0:0fdadbc3d852 512 ((displayPtr[1].y - displayPtr[2].y) * (screenPtr[0].y - screenPtr[2].y)) ;
embeddedartists 0:0fdadbc3d852 513
embeddedartists 0:0fdadbc3d852 514 matrixPtr->En = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].y - displayPtr[2].y)) -
embeddedartists 0:0fdadbc3d852 515 ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].x - screenPtr[2].x)) ;
embeddedartists 0:0fdadbc3d852 516 matrixPtr->Fn = (screenPtr[2].x * displayPtr[1].y - screenPtr[1].x * displayPtr[2].y) * screenPtr[0].y +
embeddedartists 0:0fdadbc3d852 517 (screenPtr[0].x * displayPtr[2].y - screenPtr[2].x * displayPtr[0].y) * screenPtr[1].y +
embeddedartists 0:0fdadbc3d852 518 (screenPtr[1].x * displayPtr[0].y - screenPtr[0].x * displayPtr[1].y) * screenPtr[2].y ;
embeddedartists 0:0fdadbc3d852 519 }
embeddedartists 0:0fdadbc3d852 520
embeddedartists 0:0fdadbc3d852 521 return( retValue ) ;
embeddedartists 0:0fdadbc3d852 522 }
embeddedartists 0:0fdadbc3d852 523
embeddedartists 0:0fdadbc3d852 524 /**********************************************************************
embeddedartists 0:0fdadbc3d852 525 *
embeddedartists 0:0fdadbc3d852 526 * Function: getDisplayPoint()
embeddedartists 0:0fdadbc3d852 527 *
embeddedartists 0:0fdadbc3d852 528 * Description: Given a valid set of calibration factors and a point
embeddedartists 0:0fdadbc3d852 529 * value reported by the touch screen, this function
embeddedartists 0:0fdadbc3d852 530 * calculates and returns the true (or closest to true)
embeddedartists 0:0fdadbc3d852 531 * display point below the spot where the touch screen
embeddedartists 0:0fdadbc3d852 532 * was touched.
embeddedartists 0:0fdadbc3d852 533 *
embeddedartists 0:0fdadbc3d852 534 *
embeddedartists 0:0fdadbc3d852 535 *
embeddedartists 0:0fdadbc3d852 536 * Argument(s): displayPtr (output) - Pointer to the calculated
embeddedartists 0:0fdadbc3d852 537 * (true) display point.
embeddedartists 0:0fdadbc3d852 538 * screenPtr (input) - Pointer to the reported touch
embeddedartists 0:0fdadbc3d852 539 * screen point.
embeddedartists 0:0fdadbc3d852 540 * matrixPtr (input) - Pointer to calibration factors
embeddedartists 0:0fdadbc3d852 541 * matrix previously calculated
embeddedartists 0:0fdadbc3d852 542 * from a call to
embeddedartists 0:0fdadbc3d852 543 * setCalibrationMatrix()
embeddedartists 0:0fdadbc3d852 544 *
embeddedartists 0:0fdadbc3d852 545 *
embeddedartists 0:0fdadbc3d852 546 * The function simply solves for Xd and Yd by implementing the
embeddedartists 0:0fdadbc3d852 547 * computations required by the translation matrix.
embeddedartists 0:0fdadbc3d852 548 *
embeddedartists 0:0fdadbc3d852 549 * /- -\
embeddedartists 0:0fdadbc3d852 550 * /- -\ /- -\ | |
embeddedartists 0:0fdadbc3d852 551 * | | | | | Xs |
embeddedartists 0:0fdadbc3d852 552 * | Xd | | A B C | | |
embeddedartists 0:0fdadbc3d852 553 * | | = | | * | Ys |
embeddedartists 0:0fdadbc3d852 554 * | Yd | | D E F | | |
embeddedartists 0:0fdadbc3d852 555 * | | | | | 1 |
embeddedartists 0:0fdadbc3d852 556 * \- -/ \- -/ | |
embeddedartists 0:0fdadbc3d852 557 * \- -/
embeddedartists 0:0fdadbc3d852 558 *
embeddedartists 0:0fdadbc3d852 559 * It must be kept brief to avoid consuming CPU cycles.
embeddedartists 0:0fdadbc3d852 560 *
embeddedartists 0:0fdadbc3d852 561 *
embeddedartists 0:0fdadbc3d852 562 * Return: OK - the display point was correctly calculated
embeddedartists 0:0fdadbc3d852 563 * and its value is in the output argument.
embeddedartists 0:0fdadbc3d852 564 * NOT_OK - an error was detected and the function
embeddedartists 0:0fdadbc3d852 565 * failed to return a valid point.
embeddedartists 0:0fdadbc3d852 566 *
embeddedartists 0:0fdadbc3d852 567 *
embeddedartists 0:0fdadbc3d852 568 *
embeddedartists 0:0fdadbc3d852 569 * NOTE! NOTE! NOTE!
embeddedartists 0:0fdadbc3d852 570 *
embeddedartists 0:0fdadbc3d852 571 * setCalibrationMatrix() and getDisplayPoint() will do fine
embeddedartists 0:0fdadbc3d852 572 * for you as they are, provided that your digitizer
embeddedartists 0:0fdadbc3d852 573 * resolution does not exceed 10 bits (1024 values). Higher
embeddedartists 0:0fdadbc3d852 574 * resolutions may cause the integer operations to overflow
embeddedartists 0:0fdadbc3d852 575 * and return incorrect values. If you wish to use these
embeddedartists 0:0fdadbc3d852 576 * functions with digitizer resolutions of 12 bits (4096
embeddedartists 0:0fdadbc3d852 577 * values) you will either have to a) use 64-bit signed
embeddedartists 0:0fdadbc3d852 578 * integer variables and math, or b) judiciously modify the
embeddedartists 0:0fdadbc3d852 579 * operations to scale results by a factor of 2 or even 4.
embeddedartists 0:0fdadbc3d852 580 *
embeddedartists 0:0fdadbc3d852 581 *
embeddedartists 0:0fdadbc3d852 582 */
embeddedartists 0:0fdadbc3d852 583 int TSC2046::getDisplayPoint( calibPoint_t * displayPtr,
embeddedartists 0:0fdadbc3d852 584 calibPoint_t * screenPtr,
embeddedartists 0:0fdadbc3d852 585 calibMatrix_t * matrixPtr )
embeddedartists 0:0fdadbc3d852 586 {
embeddedartists 0:0fdadbc3d852 587 int retValue = 0 ;
embeddedartists 0:0fdadbc3d852 588
embeddedartists 0:0fdadbc3d852 589 if( matrixPtr->Divider != 0 )
embeddedartists 0:0fdadbc3d852 590 {
embeddedartists 0:0fdadbc3d852 591 /* Operation order is important since we are doing integer */
embeddedartists 0:0fdadbc3d852 592 /* math. Make sure you add all terms together before */
embeddedartists 0:0fdadbc3d852 593 /* dividing, so that the remainder is not rounded off */
embeddedartists 0:0fdadbc3d852 594 /* prematurely. */
embeddedartists 0:0fdadbc3d852 595 displayPtr->x = ( (matrixPtr->An * screenPtr->x) +
embeddedartists 0:0fdadbc3d852 596 (matrixPtr->Bn * screenPtr->y) +
embeddedartists 0:0fdadbc3d852 597 matrixPtr->Cn
embeddedartists 0:0fdadbc3d852 598 ) / matrixPtr->Divider ;
embeddedartists 0:0fdadbc3d852 599 displayPtr->y = ( (matrixPtr->Dn * screenPtr->x) +
embeddedartists 0:0fdadbc3d852 600 (matrixPtr->En * screenPtr->y) +
embeddedartists 0:0fdadbc3d852 601 matrixPtr->Fn
embeddedartists 0:0fdadbc3d852 602 ) / matrixPtr->Divider ;
embeddedartists 0:0fdadbc3d852 603 }
embeddedartists 0:0fdadbc3d852 604 else
embeddedartists 0:0fdadbc3d852 605 {
embeddedartists 0:0fdadbc3d852 606 retValue = 1 ;
embeddedartists 0:0fdadbc3d852 607 }
embeddedartists 0:0fdadbc3d852 608 return( retValue ) ;
embeddedartists 0:0fdadbc3d852 609 }
embeddedartists 0:0fdadbc3d852 610
embeddedartists 0:0fdadbc3d852 611
embeddedartists 0:0fdadbc3d852 612 // ############################################################################
embeddedartists 0:0fdadbc3d852 613 // <<<<<<<< Calibrate code <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
embeddedartists 0:0fdadbc3d852 614 // ############################################################################
embeddedartists 0:0fdadbc3d852 615
embeddedartists 0:0fdadbc3d852 616
embeddedartists 0:0fdadbc3d852 617