#include // M5Touch class /* static */ M5Touch* M5Touch::instance; M5Touch::M5Touch() { if (!instance) instance = this; } void M5Touch::begin() { Wire1.begin(21, 22); pinMode(CST_INT, INPUT); // By default, the FT6336 will pulse the INT line for every touch // event. But because it shares the Wire1 TwoWire/I2C with other // devices, we cannot easily create an interrupt service routine to // handle these events. So instead, we set the INT wire to polled mode, // so it simply goes low as long as there is at least one valid touch. ft6336(0xA4, 0x00); Serial.print("touch: "); if (interval(DEFAULT_INTERVAL) == DEFAULT_INTERVAL) { Serial.printf("FT6336 ready (fw id 0x%02X rel %d, lib 0x%02X%02X)\n", ft6336(0xA6), ft6336(0xAF), ft6336(0xA1), ft6336(0xA2)); } else { Serial.println("ERROR - FT6336 not responding"); } } bool M5Touch::ispressed() { return (digitalRead(CST_INT) == LOW); } // Single register read and write uint8_t M5Touch::ft6336(uint8_t reg) { Wire1.beginTransmission((uint8_t)CST_DEVICE_ADDR); Wire1.write(reg); Wire1.endTransmission(); Wire1.requestFrom((uint8_t)CST_DEVICE_ADDR, uint8_t(1)); return Wire1.read(); } void M5Touch::ft6336(uint8_t reg, uint8_t value) { Wire1.beginTransmission(CST_DEVICE_ADDR); Wire1.write(reg); Wire1.write((uint8_t)value); Wire1.endTransmission(); } // Reading size bytes into data void M5Touch::ft6336(uint8_t reg, uint8_t size, uint8_t* data) { Wire1.beginTransmission((uint8_t)CST_DEVICE_ADDR); Wire1.write(reg); Wire1.endTransmission(); Wire1.requestFrom((uint8_t)CST_DEVICE_ADDR, size); for (uint8_t i = 0; i < size; i++) data[i] = Wire1.read(); } uint8_t M5Touch::interval(uint8_t ivl) { ft6336(0x88, ivl); return interval(); } uint8_t M5Touch::interval() { _interval = ft6336(0x88); return _interval; } // This is normally called from update() bool M5Touch::read() { // true if real read, not a "come back later" wasRead = false; // true is something actually changed on the touchpad changed = false; // Return immediately if read() is called more frequently than the // touch sensor updates. This prevents unnecessary I2C reads, and the // data can also get corrupted if reads are too close together. if (millis() - _lastRead < _interval) return false; _lastRead = millis(); Point p[2]; uint8_t pts = 0; uint8_t p0f = 0; if (ispressed()) { uint8_t data[11]; ft6336(0x02, 11, data); pts = data[0]; if (pts > 2) return false; if (pts) { // Read the data. Never mind trying to read the "weight" and // "size" properties or using the built-in gestures: they // are always set to zero. p0f = (data[3] >> 4) ? 1 : 0; p[0].x = ((data[1] << 8) | data[2]) & 0x0fff; p[0].y = ((data[3] << 8) | data[4]) & 0x0fff; if (p[0].x >= TOUCH_W || p[0].y >= TOUCH_H) return false; if (pts == 2) { p[1].x = ((data[7] << 8) | data[8]) & 0x0fff; p[1].y = ((data[9] << 8) | data[10]) & 0x0fff; if (p[1].x >= TOUCH_W || p[1].y >= TOUCH_H) return false; } } } #ifdef TFT p[0].rotate(TFT->rotation); p[1].rotate(TFT->rotation); #endif /* TFT */ if (p[0] != point[0] || p[1] != point[1]) { changed = true; point[0] = p[0]; point[1] = p[1]; points = pts; point0finger = p0f; } wasRead = true; return true; } Point M5Touch::getPressPoint() { read(); if (point[0]) return point[0]; return Point(-1, -1); // -1, -1 is old API's definition of invalid } void M5Touch::update() { read(); } void M5Touch::dump() { uint8_t data[256] = {0}; ft6336(0x00, 0x80, data); ft6336(0x80, 0x80, data + 0x80); Serial.printf("\n "); for (uint8_t i = 0; i < 16; i++) Serial.printf(".%1X ", i); Serial.printf("\n"); for (uint16_t i = 0; i < 0x100; i++) { if (!(i % 16)) Serial.printf("\n%1X. ", i / 16); Serial.printf("%02X ", data[i]); } Serial.printf("\n\n\n"); } // HotZone class (for compatibility with older M5Core2 code) HotZone::HotZone(int16_t x0_, int16_t y0_, int16_t x1_, int16_t y1_, void (*fun_)() /* = nullptr */ ) : Zone(x0_, y0_, x1_ - x0_, y1_ - y0_) { fun = fun_; } void HotZone::setZone(int16_t x0_, int16_t y0_, int16_t x1_, int16_t y1_, void (*fun_)() /*= nullptr */ ) { set(x0_, y0_, x1_ - x0_, y1_ - y0_); fun = fun_; } bool HotZone::inHotZone(Point& p) { return contains(p); } bool HotZone::inHotZoneDoFun(Point& p) { if (contains(p)) { if (fun) fun(); return true; } else { return false; } }