text: /**
   * @file XPT2046.c
   */
  /*********************
   *      INCLUDES
   *********************/
  #include "XPT2046.h"
  #include "board_config.h"
  #include "stdio.h"
  #include <string.h>
  #include "drivers/spi.h"
  
  #include "zephyr.h"
  #include "kernel.h"
  
  #if USE_XPT2046
  
  #include <stddef.h>
  
  #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
  