/** * @file XPT2046.c * */ /********************* * INCLUDES *********************/ #include "xpt2046.h" #include "esp_system.h" #include "esp_log.h" #include "driver/gpio.h" #include "tp_spi.h" #include /********************* * DEFINES *********************/ #define TAG "XPT2046" #define CMD_X_READ 0b10010000 // NOTE: XPT2046 data sheet says this is actually Y #define CMD_Y_READ 0b11010000 // NOTE: XPT2046 data sheet says this is actually X #define CMD_Z1_READ 0b10110000 #define CMD_Z2_READ 0b11000000 /********************** * TYPEDEFS **********************/ typedef enum { TOUCH_NOT_DETECTED = 0, TOUCH_DETECTED = 1, } xpt2046_touch_detect_t; /********************** * STATIC PROTOTYPES **********************/ static void xpt2046_corr(int16_t * x, int16_t * y); static void xpt2046_avg(int16_t * x, int16_t * y); static int16_t xpt2046_cmd(uint8_t cmd); static xpt2046_touch_detect_t xpt2048_is_touch_detected(); /********************** * STATIC VARIABLES **********************/ int16_t avg_buf_x[XPT2046_AVG]; int16_t avg_buf_y[XPT2046_AVG]; uint8_t avg_last; /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ /** * Initialize the XPT2046 */ void xpt2046_init(void) { ESP_LOGI(TAG, "XPT2046 Initialization"); #if XPT2046_TOUCH_IRQ || XPT2046_TOUCH_IRQ_PRESS gpio_config_t irq_config = { .pin_bit_mask = BIT64(XPT2046_IRQ), .mode = GPIO_MODE_INPUT, .pull_up_en = GPIO_PULLUP_DISABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE, }; esp_err_t ret = gpio_config(&irq_config); assert(ret == ESP_OK); #endif } /** * Get the current position and state of the touchpad * @param data store the read data here * @return false: because no more data to be read */ bool xpt2046_read(lv_indev_drv_t * drv, lv_indev_data_t * data) { static int16_t last_x = 0; static int16_t last_y = 0; bool valid = false; int16_t x = last_x; int16_t y = last_y; if (xpt2048_is_touch_detected() == TOUCH_DETECTED) { valid = true; x = xpt2046_cmd(CMD_X_READ); y = xpt2046_cmd(CMD_Y_READ); ESP_LOGI(TAG, "P(%d,%d)", x, y); /*Normalize Data back to 12-bits*/ x = x >> 4; y = y >> 4; ESP_LOGI(TAG, "P_norm(%d,%d)", x, y); xpt2046_corr(&x, &y); xpt2046_avg(&x, &y); last_x = x; last_y = y; ESP_LOGI(TAG, "x = %d, y = %d", x, y); } else { avg_last = 0; } data->point.x = x; data->point.y = y; data->state = valid == false ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; return false; } /********************** * STATIC FUNCTIONS **********************/ static xpt2046_touch_detect_t xpt2048_is_touch_detected() { // check IRQ pin if we IRQ or IRQ and preessure #if XPT2046_TOUCH_IRQ || XPT2046_TOUCH_IRQ_PRESS uint8_t irq = gpio_get_level(XPT2046_IRQ); if (irq != 0) { return TOUCH_NOT_DETECTED; } #endif // check pressure if we are pressure or IRQ and pressure #if XPT2046_TOUCH_PRESS || XPT2046_TOUCH_IRQ_PRESS int16_t z1 = xpt2046_cmd(CMD_Z1_READ) >> 3; int16_t z2 = xpt2046_cmd(CMD_Z2_READ) >> 3; // this is not what the confusing datasheet says but it seems to // be enough to detect real touches on the panel int16_t z = z1 + 4096 - z2; if (z < XPT2046_TOUCH_THRESHOLD) { return TOUCH_NOT_DETECTED; } #endif return TOUCH_DETECTED; } static int16_t xpt2046_cmd(uint8_t cmd) { uint8_t data[2]; tp_spi_read_reg(cmd, data, 2); int16_t val = (data[0] << 8) | data[1]; return val; } static void xpt2046_corr(int16_t * x, int16_t * y) { #if XPT2046_XY_SWAP != 0 int16_t swap_tmp; swap_tmp = *x; *x = *y; *y = swap_tmp; #endif if((*x) > XPT2046_X_MIN)(*x) -= XPT2046_X_MIN; else(*x) = 0; if((*y) > XPT2046_Y_MIN)(*y) -= XPT2046_Y_MIN; else(*y) = 0; (*x) = (uint32_t)((uint32_t)(*x) * LV_HOR_RES) / (XPT2046_X_MAX - XPT2046_X_MIN); (*y) = (uint32_t)((uint32_t)(*y) * LV_VER_RES) / (XPT2046_Y_MAX - XPT2046_Y_MIN); #if XPT2046_X_INV != 0 (*x) = LV_HOR_RES - (*x); #endif #if XPT2046_Y_INV != 0 (*y) = LV_VER_RES - (*y); #endif } static void xpt2046_avg(int16_t * x, int16_t * y) { /*Shift out the oldest data*/ uint8_t i; for(i = XPT2046_AVG - 1; i > 0 ; i--) { avg_buf_x[i] = avg_buf_x[i - 1]; avg_buf_y[i] = avg_buf_y[i - 1]; } /*Insert the new point*/ avg_buf_x[0] = *x; avg_buf_y[0] = *y; if(avg_last < XPT2046_AVG) avg_last++; /*Sum the x and y coordinates*/ int32_t x_sum = 0; int32_t y_sum = 0; for(i = 0; i < avg_last ; i++) { x_sum += avg_buf_x[i]; y_sum += avg_buf_y[i]; } /*Normalize the sums*/ (*x) = (int32_t)x_sum / avg_last; (*y) = (int32_t)y_sum / avg_last; }