/** * @file XPT2046.ino * @brief XPT2046 SPI/Bitbang touchscreen interface for PacoMouseCYD throttle. * @author F. Cañada * @date 2025-2026 * @copyright https://usuaris.tinet.cat/fmco/ * * This file contains the implementation of the XPT2046 touchscreen interface, * including initialization, calibration, and touch detection for the PacoMouseCYD throttle. */ //////////////////////////////////////////////////////////// // API Documentation //////////////////////////////////////////////////////////// /** * @brief XPT2046 touchscreen class constructor. * @param mosiPin MOSI pin number. * @param misoPin MISO pin number. * @param clkPin Clock pin number. * @param csPin Chip select pin number. */ XPT2046_TS::XPT2046_TS(uint8_t mosiPin, uint8_t misoPin, uint8_t clkPin, uint8_t csPin); /** * @brief Initializes the touchscreen with given width and height. * @param width Screen width. * @param height Screen height. */ void XPT2046_TS::begin(uint16_t width, uint16_t height); /** * @brief Sets the touchscreen calibration values. * @param xMin Minimum X value. * @param xMax Maximum X value. * @param yMin Minimum Y value. * @param yMax Maximum Y value. */ void XPT2046_TS::setCalibration(uint16_t xMin, uint16_t xMax, uint16_t yMin, uint16_t yMax); /** * @brief Gets the current touchscreen calibration values. * @return The current TouchCalibration struct. */ TouchCalibration XPT2046_TS::getCalibration(); /** * @brief Sets the touchscreen rotation. * @param n Rotation value (0-3). */ void XPT2046_TS::setRotation(uint8_t n); /** * @brief Checks if the touchscreen is currently being touched. * @return True if touched, false otherwise. */ bool XPT2046_TS::touched(); // Add further function documentation here as needed for each public function. //////////////////////////////////////////////////////////// // End API Documentation //////////////////////////////////////////////////////////// /* PacoMouseCYD throttle -- F. Cañada 2025-2026 -- https://usuaris.tinet.cat/fmco/ Simple XPT2046 SPI/Bitbang interface for PacoMouseCYD */ #include "config.h" #include "XPT2046.h" #define Z_THRESHOLD 300 #define MSEC_THRESHOLD 4 XPT2046_TS::XPT2046_TS(uint8_t mosiPin, uint8_t misoPin, uint8_t clkPin, uint8_t csPin) : _mosiPin(mosiPin), _misoPin(misoPin), _clkPin(clkPin), _csPin(csPin) { cal = TouchCalibration{0, 4095, 0, 4095, 0}; // other initializations, if required _msraw = millis(); #ifdef USE_XPT2046_SPI hspi = new SPIClass(HSPI); // XPT2046 connected to HSPI in CYD 2.4" hspi->begin(); #endif } void XPT2046_TS::begin(uint16_t width, uint16_t height) { pinMode(_csPin, OUTPUT); digitalWrite(_csPin, HIGH); #ifdef USE_XPT2046_BITBANG pinMode(_clkPin, OUTPUT); // init all pins in bitbang mode only (CYD 2.8") digitalWrite(_clkPin, LOW); pinMode(_mosiPin, OUTPUT); pinMode(_misoPin, INPUT); #endif _width = width; _height = height; } void XPT2046_TS::setCalibration(uint16_t xMin, uint16_t xMax, uint16_t yMin, uint16_t yMax) { cal.xMin = xMin; cal.xMax = xMax; cal.yMin = yMin; cal.yMax = yMax; } TouchCalibration XPT2046_TS::getCalibration() { return cal; } void XPT2046_TS::setRotation(uint8_t n) { cal.rotation = n % 4; } bool XPT2046_TS::touched() { update(); return (_zraw > Z_THRESHOLD); } TSPoint XPT2046_TS::getTouch() { update(); uint16_t x = map(_xraw, cal.xMin, cal.xMax, 0, _width); uint16_t y = map(_yraw, cal.yMin, cal.yMax, 0, _height); if ((x >= _width) || (x <= 0) || (y >= _height) || (y <= 0)) _zraw = 0; return TSPoint{x, y, _zraw}; } void XPT2046_TS::readData(uint16_t *x, uint16_t *y, uint16_t *z) { update(); *x = _xraw; // read raw data *y = _yraw; *z = _zraw; } #ifdef USE_XPT2046_BITBANG uint16_t XPT2046_TS::readSPI(byte command) { uint16_t result = 0; for (int i = 7; i >= 0; i--) { digitalWrite(_mosiPin, command & (1 << i)); // send command digitalWrite(_clkPin, HIGH); delayMicroseconds(7); digitalWrite(_clkPin, LOW); delayMicroseconds(7); } for (int i = 11; i >= 0; i--) { // read data digitalWrite(_clkPin, HIGH); delayMicroseconds(7); digitalWrite(_clkPin, LOW); delayMicroseconds(7); result |= (digitalRead(_misoPin) << i); } return result; } void XPT2046_TS::update() { int t; uint32_t now = millis(); if (now - _msraw < MSEC_THRESHOLD) return; digitalWrite(_csPin, LOW); readSPI(0xB0); readSPI(0xB0); readSPI(0xB0); int z1 = readSPI(0xB0); _zraw = z1 + 4095; readSPI(0xC0); readSPI(0xC0); readSPI(0xC0); int z2 = readSPI(0xC0); _zraw -= z2; readSPI(0x90); readSPI(0x90); readSPI(0x90); _xraw = readSPI(0x90); readSPI(0xD0); readSPI(0xD0); readSPI(0xD0); _yraw = readSPI(0xD0); digitalWrite(_csPin, HIGH); _msraw = now; switch (cal.rotation) { case 0: t = 4095 - _yraw; _yraw = _xraw; _xraw = t; break; case 1: break; case 2: t = _xraw; _xraw = _yraw; _yraw = 4095 - t; break; default: _xraw = 4095 - _xraw; _yraw = 4095 - _yraw; break; } } #endif #ifdef USE_XPT2046_SPI void XPT2046_TS::update() { int t; uint32_t now = millis(); if (now - _msraw < MSEC_THRESHOLD) return; hspi->beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0)); digitalWrite(_csPin, LOW); hspi->transfer(0xB0); hspi->transfer16(0xB0); hspi->transfer16(0xB0); hspi->transfer16(0xB0); int z1 = hspi->transfer16(0xC0) >> 3; _zraw = z1 + 4095; hspi->transfer16(0xC0); hspi->transfer16(0xC0); hspi->transfer16(0xC0); int z2 = hspi->transfer16(0x90) >> 3; _zraw -= z2; hspi->transfer16(0x90); hspi->transfer16(0x90); hspi->transfer16(0x90); _xraw = hspi->transfer16(0xD0) >> 3; hspi->transfer16(0xD0); hspi->transfer16(0xD0); hspi->transfer16(0xD0); _yraw = hspi->transfer16(0x0) >> 3; digitalWrite(_csPin, HIGH); hspi->endTransaction(); _msraw = now; switch (cal.rotation) { case 0: _xraw = 4095 - _xraw; break; case 1: t = _yraw; _yraw = _xraw; _xraw = t; break; case 2: _yraw = 4095 - _yraw; break; default: t = _yraw; _yraw = 4095 - _xraw; _xraw = 4095 - t; break; } } #endif