#include "FT6X36.h" #include "freertos/FreeRTOS.h" #define CONFIG_FT6X36_DEBUG false FT6X36 *FT6X36::_instance = nullptr; static const char *TAG = "i2c-touch"; //Handle indicating I2C is ready to read the touch SemaphoreHandle_t TouchSemaphore = xSemaphoreCreateBinary(); FT6X36::FT6X36(i2c_port_t port, gpio_num_t interruptPin) { _instance = this; _port = port; _intPin = interruptPin; } // Destructor should detach interrupt to the pin FT6X36::~FT6X36() { if (_intPin >= 0) gpio_isr_handler_remove((gpio_num_t)_intPin); } bool FT6X36::begin(uint8_t threshold, uint16_t width, uint16_t height) { _touch_width = width; _touch_height = height; if (width == 0 || height ==0) { ESP_LOGE(TAG,"begin(uint8_t threshold, uint16_t width, uint16_t height) did not receive the width / height so touch cannot be rotation aware"); } uint8_t data_panel_id; readRegister8(FT6X36_REG_PANEL_ID, &data_panel_id); if (data_panel_id != FT6X36_VENDID) { ESP_LOGE(TAG,"FT6X36_VENDID does not match. Received:0x%x Expected:0x%x\n",data_panel_id,FT6X36_VENDID); return false; } ESP_LOGI(TAG, "\tDevice ID: 0x%02x", data_panel_id); uint8_t chip_id; readRegister8(FT6X36_REG_CHIPID, &chip_id); if (chip_id != FT6206_CHIPID && chip_id != FT6236_CHIPID && chip_id != FT6336_CHIPID) { ESP_LOGE(TAG,"FT6206_CHIPID does not match. Received:0x%x\n",chip_id); return false; } ESP_LOGI(TAG, "\tFound touch controller with Chip ID: 0x%02x", chip_id); if (_intPin >= 0) { // INT pin triggers the callback function on the Falling edge of the GPIO gpio_config_t io_conf; io_conf.intr_type = GPIO_INTR_NEGEDGE; // GPIO_INTR_NEGEDGE repeats always interrupt io_conf.pin_bit_mask = 1ULL<<_intPin; io_conf.mode = GPIO_MODE_INPUT; io_conf.pull_down_en = (gpio_pulldown_t) 0; // disable pull-down mode io_conf.pull_up_en = (gpio_pullup_t) 1; // pull-up mode gpio_config(&io_conf); esp_err_t isr_service = gpio_install_isr_service(0); printf("ISR trigger install response: 0x%x %s\n", isr_service, (isr_service==0)?"ESP_OK":""); gpio_isr_handler_add((gpio_num_t)_intPin, isr, (void*) 1); } writeRegister8(FT6X36_REG_DEVICE_MODE, 0x00); writeRegister8(FT6X36_REG_THRESHHOLD, threshold); writeRegister8(FT6X36_REG_TOUCHRATE_ACTIVE, 0x0E); return true; } void FT6X36::registerTouchHandler(void (*fn)(TPoint point, TEvent e)) { _touchHandler = fn; if (CONFIG_FT6X36_DEBUG) printf("Touch handler function registered\n"); } uint8_t FT6X36::touched() { uint8_t data_buf; esp_err_t ret = readRegister8(FT6X36_REG_NUM_TOUCHES, &data_buf); if (ret != ESP_OK) { ESP_LOGE(TAG, "Error reading from device: %s", esp_err_to_name(ret)); } if (data_buf > 2) { data_buf = 0; } return data_buf; } void FT6X36::loop() { processTouch(); } void IRAM_ATTR FT6X36::isr(void* arg) { /* Un-block the interrupt processing task now */ xSemaphoreGive(TouchSemaphore); //xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); } void FT6X36::processTouch() { /* Task move to Block state to wait for interrupt event */ if (_intPin >= 0) { if (xSemaphoreTake(TouchSemaphore, portMAX_DELAY) == false) return; } readData(); uint8_t n = 0; TRawEvent event = (TRawEvent)_touchEvent[n]; TPoint point{_touchX[n], _touchY[n]}; switch (event) { case TRawEvent::PressDown: _points[0] = point; _dragMode = false; // Note: Is in microseconds. Ref https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/esp_timer.html _touchStartTime = esp_timer_get_time()/1000; fireEvent(point, TEvent::TouchStart); break; case TRawEvent::Contact: // Dragging makes no sense IMHO. Since the X & Y are not getting updated while dragging // Dragging && _points[0].aboutEqual(point) - Not used IDEA 2: && (lastEvent == 2) if (!_dragMode && (abs(lastX-_touchX[n]) <= maxDeviation || abs(lastY-_touchY[n])<=maxDeviation) && esp_timer_get_time()/1000 - _touchStartTime > 300) { _dragMode = true; fireEvent(point, TEvent::DragStart); #if defined(CONFIG_FT6X36_DEBUG_EVENTS) && CONFIG_FT6X36_DEBUG_EVENTS==1 printf("EV: DragStart\n"); #endif } else if (_dragMode) { fireEvent(point, TEvent::DragMove); #if defined(CONFIG_FT6X36_DEBUG_EVENTS) && CONFIG_FT6X36_DEBUG_EVENTS==1 printf("EV: DragMove\n"); #endif } fireEvent(point, TEvent::TouchMove); // For me the _touchStartTime shouold be set in both PressDown & Contact events, but after Drag detection _touchStartTime = esp_timer_get_time()/1000; break; case TRawEvent::LiftUp: _points[9] = point; _touchEndTime = esp_timer_get_time()/1000; //printf("TIMEDIFF: %lu End: %lu\n", _touchEndTime - _touchStartTime, _touchEndTime); fireEvent(point, TEvent::TouchEnd); if (_dragMode) { fireEvent(point, TEvent::DragEnd); #if defined(CONFIG_FT6X36_DEBUG_EVENTS) && CONFIG_FT6X36_DEBUG_EVENTS==1 printf("EV: DragEnd\n"); #endif _dragMode = false; } if ( _touchEndTime - _touchStartTime <= 900) { // Do not get why this: _points[0].aboutEqual(point) (Original library) fireEvent(point, TEvent::Tap); _points[0] = {0, 0}; _touchStartTime = 0; #if defined(CONFIG_FT6X36_DEBUG_EVENTS) && CONFIG_FT6X36_DEBUG_EVENTS==1 printf("EV: Tap\n"); #endif _dragMode = false; } break; case TRawEvent::NoEvent: #if defined(CONFIG_FT6X36_DEBUG_EVENTS) && CONFIG_FT6X36_DEBUG_EVENTS==1 printf("EV: NoEvent\n"); #endif break; } // Store lastEvent lastEvent = (int) event; lastX = _touchX[0]; lastY = _touchY[0]; } void FT6X36::poll(TPoint * point, TEvent * e) { readData(); // TPoint point{_touchX[0], _touchY[0]}; TRawEvent event = (TRawEvent)_touchEvent[0]; if (point != NULL) { point->x = _touchX[0]; point->y = _touchY[0]; } if (e != NULL) { switch (event) { case TRawEvent::PressDown: *e = TEvent::TouchStart; break; case TRawEvent::Contact: *e = TEvent::TouchMove; break; case TRawEvent::LiftUp: default: *e = TEvent::TouchEnd; break; } } } uint8_t FT6X36::read8(uint8_t regName) { uint8_t buf; readRegister8(regName, &buf); return buf; } #define data_size 16 // Discarding last 2: 0x0E & 0x0F as not relevant bool FT6X36::readData(void) { esp_err_t ret; uint8_t data[data_size]; uint8_t touch_pnt_cnt; // Number of detected touch points readRegister8(FT6X36_REG_NUM_TOUCHES, &touch_pnt_cnt); // Read data i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (FT6X36_ADDR<<1), ACK_CHECK_EN); i2c_master_write_byte(cmd, 0, ACK_CHECK_EN); i2c_master_stop(cmd); ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); if (ret != ESP_OK) { return ret; } cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (FT6X36_ADDR<<1)|1, ACK_CHECK_EN); i2c_master_read(cmd, data, data_size, I2C_MASTER_LAST_NACK); i2c_master_stop(cmd); ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); if (CONFIG_FT6X36_DEBUG) { //printf("REGISTERS:\n"); for (int16_t i = 0; i < data_size; i++) { printf("%x:%x ", i, data[i]); } printf("\n"); } const uint8_t addrShift = 6; // READ X, Y and Touch events (X 2) for (uint8_t i = 0; i < 2; i++) { _touchX[i] = data[FT6X36_REG_P1_XH + i * addrShift] & 0x0F; _touchX[i] <<= 8; _touchX[i] |= data[FT6X36_REG_P1_XL + i * addrShift]; _touchY[i] = data[FT6X36_REG_P1_YH + i * addrShift] & 0x0F; _touchY[i] <<= 8; _touchY[i] |= data[FT6X36_REG_P1_YL + i * addrShift]; _touchEvent[i] = data[FT6X36_REG_P1_XH + i * addrShift] >> 6; } // Make _touchX[idx] and _touchY[idx] rotation aware switch (_rotation) { case 1: swap(_touchX[0], _touchY[0]); swap(_touchX[1], _touchY[1]); _touchY[0] = _touch_width - _touchY[0] -1; _touchY[1] = _touch_width - _touchY[1] -1; break; case 2: _touchX[0] = _touch_width - _touchX[0] - 1; _touchX[1] = _touch_width - _touchX[1] - 1; _touchY[0] = _touch_height - _touchY[0] - 1; _touchY[1] = _touch_height - _touchY[1] - 1; break; case 3: swap(_touchX[0], _touchY[0]); swap(_touchX[1], _touchY[1]); _touchX[0] = _touch_height - _touchX[0] - 1; _touchX[1] = _touch_height - _touchX[1] - 1; break; } if (CONFIG_FT6X36_DEBUG) { printf("X0:%d Y0:%d EVENT:%d\n", _touchX[0], _touchY[0], _touchEvent[0]); //printf("X1:%d Y1:%d EVENT:%d\n", _touchX[1], _touchY[1], _touchEvent[1]); } return true; } void FT6X36::writeRegister8(uint8_t reg, uint8_t value) { i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, FT6X36_ADDR << 1 | I2C_MASTER_WRITE, ACK_CHECK_EN); i2c_master_write_byte(cmd, reg , ACK_CHECK_EN); i2c_master_write_byte(cmd, value , ACK_CHECK_EN); i2c_master_stop(cmd); i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); } uint8_t FT6X36::readRegister8(uint8_t reg, uint8_t *data_buf) { i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, FT6X36_ADDR << 1 | I2C_MASTER_WRITE, ACK_CHECK_EN); i2c_master_write_byte(cmd, reg, I2C_MASTER_ACK); // Research: Why it's started a 2nd time here i2c_master_start(cmd); i2c_master_write_byte(cmd, (FT6X36_ADDR << 1) | I2C_MASTER_READ, true); i2c_master_read_byte(cmd, data_buf, I2C_MASTER_NACK); i2c_master_stop(cmd); esp_err_t ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); //FT6X36_REG_GESTURE_ID. Check if it can be read! #if defined(FT6X36_DEBUG) && FT6X36_DEBUG==1 printf("REG 0x%x: 0x%x\n",reg,ret); #endif return ret; } void FT6X36::fireEvent(TPoint point, TEvent e) { if (_touchHandler) _touchHandler(point, e); } void FT6X36::debugInfo() { printf(" TH_DIFF: %d CTRL: %d\n", read8(FT6X36_REG_FILTER_COEF), read8(FT6X36_REG_CTRL)); printf(" TIMEENTERMONITOR: %d PERIODACTIVE: %d\n", read8(FT6X36_REG_TIME_ENTER_MONITOR), read8(FT6X36_REG_TOUCHRATE_ACTIVE)); printf(" PERIODMONITOR: %d RADIAN_VALUE: %d\n", read8(FT6X36_REG_TOUCHRATE_MONITOR), read8(FT6X36_REG_RADIAN_VALUE)); printf(" OFFSET_LEFT_RIGHT: %d OFFSET_UP_DOWN: %d\n", read8(FT6X36_REG_OFFSET_LEFT_RIGHT), read8(FT6X36_REG_OFFSET_UP_DOWN)); printf("DISTANCE_LEFT_RIGHT: %d DISTANCE_UP_DOWN: %d\n", read8(FT6X36_REG_DISTANCE_LEFT_RIGHT), read8(FT6X36_REG_DISTANCE_UP_DOWN)); printf(" DISTANCE_ZOOM: %d CIPHER: %d\n", read8(FT6X36_REG_DISTANCE_ZOOM), read8(FT6X36_REG_CHIPID)); printf(" G_MODE: %d PWR_MODE: %d\n", read8(FT6X36_REG_INTERRUPT_MODE), read8(FT6X36_REG_POWER_MODE)); printf(" FIRMID: %d FOCALTECH_ID: %d STATE: %d\n", read8(FT6X36_REG_FIRMWARE_VERSION), read8(FT6X36_REG_PANEL_ID), read8(FT6X36_REG_STATE)); } void FT6X36::setRotation(uint8_t rotation) { _rotation = rotation; } void FT6X36::setTouchWidth(uint16_t width) { printf("touch w:%d\n",width); _touch_width = width; } void FT6X36::setTouchHeight(uint16_t height) { printf("touch h:%d\n",height); _touch_height = height; }