diff --git a/STM32F1/libraries/Serasidis_XPT2046_touch/examples/TouchCalibration/TouchCalibration.ino b/STM32F1/libraries/Serasidis_XPT2046_touch/examples/TouchCalibration/TouchCalibration.ino new file mode 100644 index 000000000..66ecc16eb --- /dev/null +++ b/STM32F1/libraries/Serasidis_XPT2046_touch/examples/TouchCalibration/TouchCalibration.ino @@ -0,0 +1,240 @@ +#include +#include +#include +#include +#include +#include "XPT2046_touch.h" +#include +#include // Hardware-specific library +Adafruit_TFTLCD_16bit_STM32 tft; + +//definitions, initialize parameters + +#define CS_PIN PA3 // Chip Select pin + +SPIClass mySPI(1); //Create an SPI instance on SPI1 port. +XPT2046_touch ts(CS_PIN, mySPI); // Chip Select pin, SPI port + + +char s[30]; //character arrays for text strings +char s2[30]; + +uint16_t calA[] = {15, 10}; //calibration points must be independent, i.e. not line up +uint16_t calB[] = {80, 280}; +uint16_t calC[] = {200, 170}; + +uint16_t calA_raw[] = {0, 0}; //store raw touch data for calibration points +uint16_t calB_raw[] = {0, 0}; +uint16_t calC_raw[] = {0, 0}; + +uint8_t calCount = 0; //track which point has been activated + + +uint16_t xy[2]; + +//SETUP + +void setup() { + pinMode(1, OUTPUT); + + + tft.begin(0x9341); //set up our screen + tft.fillScreen(BLACK); + tft.setTextColor(WHITE); + + tft.setTextSize(1); //these should be self-explanatory + tft.setCursor(20,43); + tft.print("touch stylus to target centre"); + + tft.setTextSize(2); + tft.setCursor(20,25); + tft.print("Calibrate Touch:"); + tft.setCursor(35,63); + tft.print("X:"); + tft.setCursor(35,82); + tft.print("Y:"); + tft.setCursor(35,101); + tft.print("Z:"); + + calibrationPoint(calA[0], calA[1]); //display first calibration point + + ts.begin(); //initialize touch screen + //ts.calibrate(-.00114, -.0653, 254, -.0885, -.00125, 348, 240, 320); +} + + +//MAIN LOOP + +void loop() { + if (ts.read_XY(xy)) { //check for screen touch + tft.fillRect(59, 63, 48, 54, BLACK); + char buf[30]; + //black out X, Y, Z values + TS_Point p = ts.getPoint(); //get touch data + sprintf(s, "%d", p.x); //convert X data to char array + tft.setCursor(59,63); + tft.print(s); //and display + sprintf(s, "%d", p.y); //same for Y value + //itoa(p.y); + tft.setCursor(59,82); + tft.print(s); + sprintf(s, "%d", p.z); //and Z (pressure) + tft.setCursor(59,101); + tft.print(s); + + if (calCount == 0) { //first calibration point? + calA_raw[0] = p.x; //store values + calA_raw[1] = p.y; + + tft.fillRect(calA[0]-6, calA[1]-6, 13, 13, BLACK); + //black out calibration point + delay(500); //give user time to take stylus off screen so as not to + //read same values into next calibration point + calibrationPoint(calB[0], calB[1]); + //and display the next calibration point + } + else if (calCount == 1) { //do the same for the second calibration point + calB_raw[0] = p.x; + calB_raw[1] = p.y; + + tft.fillRect(calB[0]-6, calB[1]-6, 13, 13, BLACK); + delay(500); + calibrationPoint(calC[0], calC[1]); + } + else if (calCount == 2) { //and third + calC_raw[0] = p.x; + calC_raw[1] = p.y; + + tft.fillRect(calC[0]-6, calC[1]-6, 13, 13, BLACK); + + tft.setTextSize(2); //get ready to display calibration parameters + tft.setCursor(20,130); + tft.print("Parameters:"); + tft.setCursor(35,155); + tft.print("X:"); + tft.setCursor(35,209); + tft.print("Y:"); + + //calculate calibration parameters + + int32_t delta = (calA_raw[0]-calC_raw[0])*(calB_raw[1]-calC_raw[1]) - + (calB_raw[0]-calC_raw[0])*(calA_raw[1]-calC_raw[1]); + float alpha_x = (float)((calA[0]-calC[0])*(calB_raw[1]-calC_raw[1]) - + (calB[0]-calC[0])*(calA_raw[1]-calC_raw[1])) / delta; + float beta_x = (float)((calA_raw[0]-calC_raw[0])*(calB[0]-calC[0]) - + (calB_raw[0]-calC_raw[0])*(calA[0]-calC[0])) / delta; + uint16_t delta_x = ((uint64_t)calA[0]*(calB_raw[0]*calC_raw[1]-calC_raw[0]*calB_raw[1]) - + (uint64_t)calB[0]*(calA_raw[0]*calC_raw[1]-calC_raw[0]*calA_raw[1]) + + (uint64_t)calC[0]*(calA_raw[0]*calB_raw[1]-calB_raw[0]*calA_raw[1])) / delta; + float alpha_y = (float)((calA[1]-calC[1])*(calB_raw[1]-calC_raw[1]) - + (calB[1]-calC[1])*(calA_raw[1]-calC_raw[1])) / delta; + float beta_y = (float)((calA_raw[0]-calC_raw[0])*(calB[1]-calC[1]) - + (calB_raw[0]-calC_raw[0])*(calA[1]-calC[1])) / delta; + uint16_t delta_y = ((uint64_t)calA[1]*(calB_raw[0]*calC_raw[1]-calC_raw[0]*calB_raw[1]) - + (uint64_t)calB[1]*(calA_raw[0]*calC_raw[1]-calC_raw[0]*calA_raw[1]) + + (uint64_t)calC[1]*(calA_raw[0]*calB_raw[1]-calB_raw[0]*calA_raw[1])) / delta; + + ftoa(alpha_x, s, 3); //convert first parameter, a float value, to char array + tft.setCursor(59,155); + tft.print(s); //and display + ftoa(beta_x, s, 3); + tft.setCursor(59,173); + tft.print(s); + sprintf(s, "%d", delta_x); //etc. + tft.setCursor(59,191); + tft.print(s); + ftoa(alpha_y, s, 3); + tft.setCursor(59,209); + tft.print(s); + ftoa(beta_y, s, 3); + tft.setCursor(59,227); + tft.print(s); + sprintf(s, "%d", delta_y); + tft.setCursor(59,245); + tft.print(s); + + tft.setCursor(20,275); + tft.print("[ Restart ]"); + } + + calCount++; + } + + if (calCount == 4) { //finally, if another touch is detected + tft.fillRect(0, 130, 240, 190, BLACK); + //clear parameters from screen + calibrationPoint(calA[0], calA[1]); //and start over + calCount = 0; + } + + delay(200); +} + + +//calibrationPoint() displays a calibration target on screen, using the Adafruit graphics functions + +void calibrationPoint(uint16_t calX, uint16_t calY) { + tft.drawCircle(calX, calY, 6, WHITE); + tft.drawFastHLine(calX-4, calY, 9, WHITE); + tft.drawFastVLine(calX, calY-4, 9, WHITE); +} + + + +void ftoa(float f, char *str, uint8_t precision) { + uint8_t i, j, divisor = 1; + int8_t log_f; + int32_t int_digits = (int)f; //store the integer digits + float decimals; + char s1[12]; + + memset(str, 0, sizeof(s)); + memset(s1, 0, 10); + + if (f < 0) { //if a negative number + str[0] = '-'; //start the char array with '-' + f = abs(f); //store its positive absolute value + } + log_f = ceil(log10(f)); //get number of digits before the decimal + if (log_f > 0) { //log value > 0 indicates a number > 1 + if (log_f == precision) { //if number of digits = significant figures + f += 0.5; //add 0.5 to round up decimals >= 0.5 + sprintf(s1, "%d", f); //itoa converts the number to a char array + strcat(str, s1); //add to the number string + } + else if ((log_f - precision) > 0) { //if more integer digits than significant digits + i = log_f - precision; //count digits to discard + divisor = 10; + for (j = 0; j < i; j++) divisor *= 10; //divisor isolates our desired integer digits + f /= divisor; //divide + f += 0.5; //round when converting to int + int_digits = (int)f; + int_digits *= divisor; //and multiply back to the adjusted value + sprintf(s1, "%d", int_digits); + strcat(str, s1); + } + else { //if more precision specified than integer digits, + sprintf(s1, "%d", int_digits); //convert + strcat(str, s1); //and append + } + } + + else { //decimal fractions between 0 and 1: leading 0 + s1[0] = '0'; + strcat(str, s1); + } + + if (log_f < precision) { //if precision exceeds number of integer digits, + decimals = f - (int)f; //get decimal value as float + strcat(str, "."); //append decimal point to char array + + i = precision - log_f; //number of decimals to read + for (j = 0; j < i; j++) { //for each, + decimals *= 10; //multiply decimals by 10 + if (j == (i-1)) decimals += 0.5; //and if it's the last, add 0.5 to round it + sprintf(s1, "%d", (int)decimals); //convert as integer to character array + strcat(str, s1); //append to string + decimals -= (int)decimals; //and remove, moving to the next + } + } +} \ No newline at end of file diff --git a/STM32F1/libraries/Serasidis_XPT2046_touch/src/XPT2046_touch.cpp b/STM32F1/libraries/Serasidis_XPT2046_touch/src/XPT2046_touch.cpp index f24c67ad5..3c5730c70 100644 --- a/STM32F1/libraries/Serasidis_XPT2046_touch/src/XPT2046_touch.cpp +++ b/STM32F1/libraries/Serasidis_XPT2046_touch/src/XPT2046_touch.cpp @@ -1,103 +1,161 @@ -/** - * - * - */ - - - #include "Arduino.h" #include "XPT2046_touch.h" -XPT2046_touch::XPT2046_touch(uint8_t _cs_pin, SPIClass _spiChan) : cs_pin(_cs_pin), my_SPI(_spiChan){ +XPT2046_touch::XPT2046_touch(uint8_t _cs_pin, SPIClass _spiChan) : cs_pin(_cs_pin), my_SPI(_spiChan) +{ setOversampling(); setThreshold(); } -void XPT2046_touch::begin(){ +void XPT2046_touch::begin() +{ pinMode(cs_pin, OUTPUT); digitalWrite(cs_pin, HIGH); my_SPI.begin(); } -uint16_t XPT2046_touch::gatherSamples(uint8_t command) { +uint16_t XPT2046_touch::gatherSamples(uint8_t command) +{ uint32_t sample_sum = 0; uint16_t samples[MAX_OVERSAMPLING]; my_SPI.transfer16(command); // discard first reading after switching MUX - for (int i = 0; i < oversampling; ++i) { + for (int i = 0; i < oversampling; ++i) + { samples[i] = my_SPI.transfer16(command); sample_sum += samples[i]; } int32_t avg = sample_sum / oversampling; - if (oversampling < 3) { + if (oversampling < 3) + { return avg >> 3; } // occasionally, we get a reading that is a _far_ outlier. // checking for, and removing those improves quality a lot. uint8_t n = oversampling; - for (int i = 0; i < oversampling; ++i) { - if (abs(avg - samples[i]) > 80) { // NOTE: data is left shifted by 3 at this point. I.e. the test is for 10+ pixels deviation from average + for (int i = 0; i < oversampling; ++i) + { + if (abs(avg - samples[i]) > 80) + { // NOTE: data is left shifted by 3 at this point. I.e. the test is for 10+ pixels deviation from average sample_sum -= samples[i]; --n; } } - if (n < 2) return avg >> 3; - else return (sample_sum / n) >> 3; + if (n < 2) + return avg >> 3; + else + return (sample_sum / n) >> 3; } -TS_Point XPT2046_touch::getPoint() { - uint16_t z1, z2; +TS_Point XPT2046_touch::getPoint() +{ + uint16_t z1, z2, x, y; - my_SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0)); // max clock freq for XPT2046 + my_SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0)); // max clock freq for XPT2046 digitalWrite(cs_pin, LOW); TS_Point ret; //Check if touch screen is pressed. - my_SPI.transfer(B10110011); // trigger Z1 reading + my_SPI.transfer(B10110011); // trigger Z1 reading z1 = my_SPI.transfer16(B11000011) >> 3; // read Z1, and trigger Z2 reading z2 = my_SPI.transfer16(B10010011) >> 3; // read Z2, and trigger Y reading ret.z = z1 + 4095 - z2; - if(ret.z >= threshold){ //If the touch screen is pressed, read the X,Y coordinates from XPT2046. - ret.x = gatherSamples(B10010011); - ret.y = gatherSamples(B11010011); - } else { + if (ret.z >= threshold) + { //If the touch screen is pressed, read the X,Y coordinates from XPT2046. + x = gatherSamples(B10010011); + y = gatherSamples(B11010011); + if (alpha_x) + { //if calibration parameters have been defined, calculate pixel coordinates + ret.x = alpha_x * x + beta_x * y + delta_x; + ret.y = alpha_y * x + beta_y * y + delta_y; + if (displ_rot) + { //and if the screen has been rotated + uint16_t rot_x; + uint16_t rot_y; + //calculate pixel positions for rotated screen + if (displ_rot == 1) + { + rot_x = ret.y; + rot_y = displ_height - ret.x; + } + else if (displ_rot == 2) + { + rot_x = displ_width - ret.x; + rot_y = displ_height - ret.y; + } + else if (displ_rot == 3) + { + rot_x = displ_width - ret.y; + rot_y = ret.x; + } + ret.x = rot_x; + ret.y = rot_y; + } + } + else + { + ret.x = x; + ret.y = y; + } + } + else + { ret.z = 0; } my_SPI.transfer(B00000000); // enter power saving (and IRQ enable) digitalWrite(cs_pin, HIGH); my_SPI.endTransaction(); + return ret; } -boolean XPT2046_touch::read_XY(uint16_t *xy) { +boolean XPT2046_touch::read_XY(uint16_t *xy) +{ TS_Point p = getPoint(); xy[0] = p.x; xy[1] = p.y; return p.z > 0; } + +void XPT2046_touch::calibrate(float ax, float bx, int16_t dx, float ay, float by, int16_t dy, uint16_t dwidth, uint16_t dheight, uint8_t drot) +{ + alpha_x = ax; + beta_x = bx; + delta_x = dx; + alpha_y = ay; + beta_y = by; + delta_y = dy; + displ_width = dwidth; + displ_height = dheight; + displ_rot = drot; +} + ////////////////// Buttons //////////////// -void XPT2046_touch::setButtonsNumber(byte columnButtons, byte rowButtons ){ +void XPT2046_touch::setButtonsNumber(byte columnButtons, byte rowButtons) +{ _rowButtons = rowButtons; _columnButtons = columnButtons; } -uint8_t XPT2046_touch::getButtonNumber(){ +uint8_t XPT2046_touch::getButtonNumber() +{ uint16_t xy[2]; uint8_t tmp, buttonNum; int div; - - if(read_XY(xy)){ + + if (read_XY(xy)) + { div = (X_MAX + X_MIN) / (_columnButtons + 1); buttonNum = ((xy[1] / div)); - div = (Y_MAX + Y_MIN) / _rowButtons; + div = (Y_MAX + Y_MIN) / _rowButtons; tmp = ((xy[0] / div)); - return ((buttonNum * _rowButtons) + tmp + 1); //Return the button number. + return ((buttonNum * _rowButtons) + tmp + 1); //Return the button number. } return 0; //Touch screen is not pressed. diff --git a/STM32F1/libraries/Serasidis_XPT2046_touch/src/XPT2046_touch.h b/STM32F1/libraries/Serasidis_XPT2046_touch/src/XPT2046_touch.h index 9fbea6a82..bb1634d66 100644 --- a/STM32F1/libraries/Serasidis_XPT2046_touch/src/XPT2046_touch.h +++ b/STM32F1/libraries/Serasidis_XPT2046_touch/src/XPT2046_touch.h @@ -1,11 +1,3 @@ -/** - * - * - * - * - */ - - #ifndef XPT2046_touch_h #define XPT2046_touch_h #include @@ -35,14 +27,15 @@ class TS_Point { */ class XPT2046_touch { private: - uint8_t cs_pin; + uint8_t cs_pin, displ_rot; SPIClass my_SPI; uint8_t _rowButtons = 1; uint8_t _columnButtons = 1; uint8_t oversampling; - uint16_t threshold; + uint16_t threshold, delta_x, delta_y, displ_width, displ_height; uint16_t gatherSamples(uint8_t command); - + float alpha_x, beta_x, alpha_y, beta_y; + public: /** c'tor. Note that no IRQ pin is supported, here. You can easily do that yourself: * \code if(digitalRead(irq_pin)) { @@ -68,6 +61,8 @@ class XPT2046_touch { uint8_t getButtonNumber(); boolean read_XY(uint16_t *xy); TS_Point getPoint(); + + void calibrate(float ax, float bx, int16_t dx, float ay, float by, int16_t dy, uint16_t dwidth, uint16_t dheight, uint8_t drot=0); }; #endif