/** * @file XPT2046.c */ /********************* * INCLUDES *********************/ #include "XPT2046.h" #include "board_config.h" #include "stdio.h" #include #include "drivers/spi.h" #if KERNEL_VERSION_NUMBER < 0x030200 /* version 3.2.0 */ #include #include #else #include #endif #if USE_XPT2046 #include #define abs(x) ((x) < 0 ? -(x) : (x)) /********************* * DEFINES *********************/ /********************** * TYPEDEFS **********************/ /********************** * STATIC PROTOTYPES **********************/ static void xpt2046_corr(int16_t *x, int16_t *y); #if 0 static void xpt2046_avg(int16_t * x, int16_t * y); #endif /********************** * 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 */ struct device *input_dev; struct spi_config spi_conf_xpt2046; struct spi_cs_control xpt2046_cs_ctrl; struct device *xpt2046_pen_gpio_dev; static struct gpio_callback gpio_cb; lv_indev_data_t touch_point; lv_indev_data_t last_touch_point; #define TOUCH_READ_THREAD_STACK_SIZE 4096 static K_THREAD_STACK_DEFINE(touch_read_thread_stack, TOUCH_READ_THREAD_STACK_SIZE); static struct k_thread touch_thread_data; static struct k_sem sem_touch_read; K_MUTEX_DEFINE(spi_display_touch_mutex); int cnt = 0; int touch_read_times = 0; int last_pen_interrupt_time = 0; void xpt2046_pen_gpio_callback(struct device *port, struct gpio_callback *cb, u32_t pins) { cnt++; if ((k_uptime_get_32() - last_pen_interrupt_time) > 500) { k_sem_give(&sem_touch_read); touch_read_times++; last_pen_interrupt_time = k_uptime_get_32(); } } void disable_pen_interrupt() { int ret = 0; ret = gpio_disable_callback(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN); if (ret != 0) { printf("gpio_pin_configure GPIO_INPUT failed\n"); } } void enable_pen_interrupt() { int ret = 0; ret = gpio_enable_callback(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN); if (ret != 0) { printf("gpio_pin_configure failed\n"); } } void touch_screen_read_thread() { int i; bool ret = false; for (;;) { k_sem_take(&sem_touch_read, K_FOREVER); memset(&last_touch_point, 0, sizeof(lv_indev_data_t)); memset(&touch_point, 0, sizeof(lv_indev_data_t)); memset(avg_buf_x, 0, sizeof(avg_buf_x)); memset(avg_buf_y, 0, sizeof(avg_buf_y)); k_mutex_lock(&spi_display_touch_mutex, K_FOREVER); disable_pen_interrupt(); for (i = 0; i < 100; i++) { ret = xpt2046_read(&touch_point); if (ret) { if ((abs(last_touch_point.point.x - touch_point.point.x) < 4) && (abs(last_touch_point.point.y - touch_point.point.y) < 4)) { break; } last_touch_point = touch_point; } } enable_pen_interrupt(); k_mutex_unlock(&spi_display_touch_mutex); } } void xpt2046_init(void) { int ret; input_dev = device_get_binding(XPT2046_SPI_DEVICE_NAME); if (input_dev == NULL) { printf("device not found. Aborting test."); return; } memset((void *)&touch_point, 0, sizeof(lv_indev_data_t)); spi_conf_xpt2046.frequency = XPT2046_SPI_MAX_FREQUENCY; spi_conf_xpt2046.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8); spi_conf_xpt2046.slave = 0; spi_conf_xpt2046.cs = NULL; #ifdef XPT2046_CS_GPIO_CONTROLLER xpt2046_cs_ctrl.gpio_dev = device_get_binding(XPT2046_CS_GPIO_CONTROLLER); if (xpt2046_cs_ctrl.gpio_dev == NULL) { printk("Cannot find %s!\n", XPT2046_CS_GPIO_CONTROLLER); return; } gpio_pin_configure(xpt2046_cs_ctrl.gpio_dev, XPT2046_CS_GPIO_PIN, GPIO_OUTPUT); gpio_pin_set(xpt2046_cs_ctrl.gpio_dev, XPT2046_CS_GPIO_PIN, 1); xpt2046_cs_ctrl.gpio_pin = XPT2046_CS_GPIO_PIN; xpt2046_cs_ctrl.delay = 0; spi_conf_xpt2046.cs = &xpt2046_cs_ctrl; #endif #ifdef XPT2046_PEN_GPIO_CONTROLLER xpt2046_pen_gpio_dev = device_get_binding(XPT2046_PEN_GPIO_CONTROLLER); if (!xpt2046_pen_gpio_dev) { printk("Cannot find %s!\n", XPT2046_PEN_GPIO_CONTROLLER); return; } /* Setup GPIO input */ ret = gpio_pin_configure(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN, (GPIO_INPUT | GPIO_INT_ENABLE | GPIO_INT_EDGE | GPIO_INT_LOW_0 | GPIO_INT_DEBOUNCE)); if (ret) { printk("Error configuring pin %d!\n", XPT2046_PEN_GPIO_PIN); } gpio_init_callback(&gpio_cb, xpt2046_pen_gpio_callback, BIT(XPT2046_PEN_GPIO_PIN)); ret = gpio_add_callback(xpt2046_pen_gpio_dev, &gpio_cb); if (ret) { printk("gpio_add_callback error\n"); } ret = gpio_enable_callback(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN); if (ret) { printk("gpio_enable_callback error\n"); } #endif k_sem_init(&sem_touch_read, 0, 1); k_thread_create(&touch_thread_data, touch_read_thread_stack, TOUCH_READ_THREAD_STACK_SIZE, touch_screen_read_thread, NULL, NULL, NULL, 5, 0, K_NO_WAIT); printf("xpt2046_init ok \n"); } /** * Get the current position and state of the touchpad * @param data store the read data here * @return false: because no ore data to be read */ bool xpt2046_read(lv_indev_data_t *data) { static int16_t last_x = 0; static int16_t last_y = 0; bool valid = true; int s32_ret = 0; int16_t x = 0; int16_t y = 0; char tx1[16] = { 0 }; char rx1[16] = { 0 }; struct spi_buf tx_buf = { .buf = &tx1, .len = 3 }; struct spi_buf_set tx_bufs = { .buffers = &tx_buf, .count = 1 }; struct spi_buf rx_buf = { .buf = &rx1, .len = 3 }; struct spi_buf_set rx_bufs = { .buffers = &rx_buf, .count = 1 }; tx1[0] = CMD_X_READ; s32_ret = spi_transceive(input_dev, &spi_conf_xpt2046, &tx_bufs, &rx_bufs); if (s32_ret != 0) { printf("spi_transceive return failed:%d\n", s32_ret); } x = rx1[1] << 8; x += rx1[2]; tx1[0] = CMD_Y_READ; s32_ret = spi_transceive(input_dev, &spi_conf_xpt2046, &tx_bufs, &rx_bufs); if (s32_ret != 0) { printf("spi_transceive return failed:%d\n", s32_ret); } y = rx1[1] << 8; y += rx1[2]; x = x >> 3; y = y >> 3; xpt2046_corr(&x, &y); if (y <= 0 || (x > 320)) { valid = false; } last_x = x; last_y = y; data->point.x = x; data->point.y = y; data->state = valid == false ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; return valid; } /********************** * STATIC FUNCTIONS **********************/ 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) * XPT2046_HOR_RES) / (XPT2046_X_MAX - XPT2046_X_MIN); (*y) = (uint32_t)((uint32_t)(*y) * XPT2046_VER_RES) / (XPT2046_Y_MAX - XPT2046_Y_MIN); #if XPT2046_X_INV != 0 (*x) = XPT2046_HOR_RES - (*x); #endif #if XPT2046_Y_INV != 0 (*y) = XPT2046_VER_RES - (*y); #endif } #if 0 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; } #endif bool touchscreen_read(lv_indev_data_t *data) { /*Store the collected data*/ data->point.x = last_touch_point.point.x; data->point.y = last_touch_point.point.y; data->state = last_touch_point.state; if (last_touch_point.state == LV_INDEV_STATE_PR) { last_touch_point.state = LV_INDEV_STATE_REL; } return false; } #endif