316 lines
7.8 KiB
C
316 lines
7.8 KiB
C
#include "touch.h"
|
|
|
|
#include "driver/gpio.h"
|
|
#include "esp_log.h"
|
|
#include "esp_rom_sys.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "ili9341.h"
|
|
|
|
#define SCREEN_W 320
|
|
#define SCREEN_H 240
|
|
#define PREV_W 240
|
|
#define PREV_H 320
|
|
|
|
#define TOUCH_PIN_CLK GPIO_NUM_25
|
|
#define TOUCH_PIN_MOSI GPIO_NUM_32
|
|
#define TOUCH_PIN_CS GPIO_NUM_33
|
|
#define TOUCH_PIN_MISO GPIO_NUM_39
|
|
#define TOUCH_PIN_IRQ GPIO_NUM_36
|
|
#define TOUCH_SAMPLES 7
|
|
|
|
static const char *TAG = "touch";
|
|
|
|
static touch_calib_t s_cal = {
|
|
.x_min = 281,
|
|
.x_max = 1781,
|
|
.y_min = 162,
|
|
.y_max = 1725,
|
|
.invert_x = false,
|
|
.invert_y = true,
|
|
};
|
|
|
|
static inline void touch_delay_us(int us)
|
|
{
|
|
esp_rom_delay_us(us);
|
|
}
|
|
|
|
static inline void touch_clk_pulse(void)
|
|
{
|
|
gpio_set_level(TOUCH_PIN_CLK, 1);
|
|
touch_delay_us(1);
|
|
gpio_set_level(TOUCH_PIN_CLK, 0);
|
|
touch_delay_us(1);
|
|
}
|
|
|
|
static uint16_t xpt2046_transfer_12b(uint8_t cmd)
|
|
{
|
|
uint16_t value = 0;
|
|
|
|
gpio_set_level(TOUCH_PIN_CS, 0);
|
|
touch_delay_us(2);
|
|
|
|
for (int i = 7; i >= 0; --i) {
|
|
gpio_set_level(TOUCH_PIN_MOSI, (cmd >> i) & 0x1);
|
|
touch_clk_pulse();
|
|
}
|
|
|
|
for (int i = 0; i < 16; ++i) {
|
|
gpio_set_level(TOUCH_PIN_CLK, 1);
|
|
touch_delay_us(1);
|
|
value = (uint16_t)((value << 1) | gpio_get_level(TOUCH_PIN_MISO));
|
|
gpio_set_level(TOUCH_PIN_CLK, 0);
|
|
touch_delay_us(1);
|
|
}
|
|
|
|
gpio_set_level(TOUCH_PIN_CS, 1);
|
|
touch_delay_us(2);
|
|
|
|
return (uint16_t)(value >> 4);
|
|
}
|
|
|
|
static int map_clamped(int v, int in_min, int in_max, int out_min, int out_max)
|
|
{
|
|
if (v < in_min) {
|
|
v = in_min;
|
|
}
|
|
if (v > in_max) {
|
|
v = in_max;
|
|
}
|
|
|
|
int den = (in_max - in_min);
|
|
if (den == 0) {
|
|
return out_min;
|
|
}
|
|
|
|
int num = (v - in_min) * (out_max - out_min);
|
|
return out_min + (num / den);
|
|
}
|
|
|
|
static void draw_crosshair(int x, int y, uint16_t color)
|
|
{
|
|
const int arm = 10;
|
|
|
|
for (int dx = -arm; dx <= arm; ++dx) {
|
|
int px = x + dx;
|
|
if (px >= 0 && px < SCREEN_W && y >= 0 && y < SCREEN_H) {
|
|
(void)ili9341_draw_pixel((uint16_t)px, (uint16_t)y, color);
|
|
}
|
|
}
|
|
|
|
for (int dy = -arm; dy <= arm; ++dy) {
|
|
int py = y + dy;
|
|
if (x >= 0 && x < SCREEN_W && py >= 0 && py < SCREEN_H) {
|
|
(void)ili9341_draw_pixel((uint16_t)x, (uint16_t)py, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool wait_for_stable_touch(uint16_t *out_x, uint16_t *out_y)
|
|
{
|
|
uint32_t sx = 0;
|
|
uint32_t sy = 0;
|
|
int count = 0;
|
|
int idle_loops = 0;
|
|
|
|
while (count < 20) {
|
|
uint16_t rx = 0;
|
|
uint16_t ry = 0;
|
|
uint16_t rz = 0;
|
|
|
|
if (touch_read_raw(&rx, &ry, &rz)) {
|
|
sx += rx;
|
|
sy += ry;
|
|
++count;
|
|
idle_loops = 0;
|
|
} else {
|
|
++idle_loops;
|
|
if (count > 0 && idle_loops > 25) {
|
|
sx = 0;
|
|
sy = 0;
|
|
count = 0;
|
|
}
|
|
}
|
|
|
|
vTaskDelay(pdMS_TO_TICKS(8));
|
|
}
|
|
|
|
*out_x = (uint16_t)(sx / count);
|
|
*out_y = (uint16_t)(sy / count);
|
|
return true;
|
|
}
|
|
|
|
esp_err_t touch_init(void)
|
|
{
|
|
gpio_config_t out_cfg = {
|
|
.pin_bit_mask = (1ULL << TOUCH_PIN_CLK) | (1ULL << TOUCH_PIN_MOSI) | (1ULL << TOUCH_PIN_CS),
|
|
.mode = GPIO_MODE_OUTPUT,
|
|
.pull_up_en = GPIO_PULLUP_DISABLE,
|
|
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
|
.intr_type = GPIO_INTR_DISABLE,
|
|
};
|
|
ESP_ERROR_CHECK(gpio_config(&out_cfg));
|
|
|
|
gpio_config_t in_cfg = {
|
|
.pin_bit_mask = (1ULL << TOUCH_PIN_MISO) | (1ULL << TOUCH_PIN_IRQ),
|
|
.mode = GPIO_MODE_INPUT,
|
|
.pull_up_en = GPIO_PULLUP_DISABLE,
|
|
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
|
.intr_type = GPIO_INTR_DISABLE,
|
|
};
|
|
ESP_ERROR_CHECK(gpio_config(&in_cfg));
|
|
|
|
gpio_set_level(TOUCH_PIN_CS, 1);
|
|
gpio_set_level(TOUCH_PIN_CLK, 0);
|
|
gpio_set_level(TOUCH_PIN_MOSI, 0);
|
|
|
|
ESP_LOGI(TAG,
|
|
"touch ready x=[%d,%d] y=[%d,%d] invert=(%d,%d)",
|
|
s_cal.x_min,
|
|
s_cal.x_max,
|
|
s_cal.y_min,
|
|
s_cal.y_max,
|
|
s_cal.invert_x,
|
|
s_cal.invert_y);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
bool touch_read_raw(uint16_t *x, uint16_t *y, uint16_t *z)
|
|
{
|
|
if (x == NULL || y == NULL || z == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (gpio_get_level(TOUCH_PIN_IRQ) != 0) {
|
|
return false;
|
|
}
|
|
|
|
uint32_t sx = 0;
|
|
uint32_t sy = 0;
|
|
uint32_t sz = 0;
|
|
int valid = 0;
|
|
|
|
for (int i = 0; i < TOUCH_SAMPLES; ++i) {
|
|
uint16_t z1 = xpt2046_transfer_12b(0xB0);
|
|
uint16_t z2 = xpt2046_transfer_12b(0xC0);
|
|
uint16_t pressure = (z1 > 0 && z2 > z1) ? (uint16_t)(z1 + (4095 - z2)) : 0;
|
|
if (pressure < 80) {
|
|
continue;
|
|
}
|
|
|
|
sx += xpt2046_transfer_12b(0xD0);
|
|
sy += xpt2046_transfer_12b(0x90);
|
|
sz += pressure;
|
|
++valid;
|
|
}
|
|
|
|
if (valid == 0) {
|
|
return false;
|
|
}
|
|
|
|
*x = (uint16_t)(sx / valid);
|
|
*y = (uint16_t)(sy / valid);
|
|
*z = (uint16_t)(sz / valid);
|
|
return true;
|
|
}
|
|
|
|
bool touch_read_screen(int *x, int *y, uint16_t *z)
|
|
{
|
|
if (x == NULL || y == NULL || z == NULL) {
|
|
return false;
|
|
}
|
|
|
|
uint16_t raw_x = 0;
|
|
uint16_t raw_y = 0;
|
|
uint16_t raw_z = 0;
|
|
|
|
if (!touch_read_raw(&raw_x, &raw_y, &raw_z)) {
|
|
return false;
|
|
}
|
|
|
|
int old_x = map_clamped((int)raw_x,
|
|
s_cal.x_min,
|
|
s_cal.x_max,
|
|
s_cal.invert_x ? (PREV_W - 1) : 0,
|
|
s_cal.invert_x ? 0 : (PREV_W - 1));
|
|
|
|
int old_y = map_clamped((int)raw_y,
|
|
s_cal.y_min,
|
|
s_cal.y_max,
|
|
s_cal.invert_y ? (PREV_H - 1) : 0,
|
|
s_cal.invert_y ? 0 : (PREV_H - 1));
|
|
|
|
/* Keep 320x240 landscape, rotated 180° from prior orientation. */
|
|
*x = (PREV_H - 1) - old_y;
|
|
*y = old_x;
|
|
|
|
*z = raw_z;
|
|
return true;
|
|
}
|
|
|
|
esp_err_t touch_calibrate(void)
|
|
{
|
|
const int margin = 18;
|
|
const int tx[4] = {margin, SCREEN_W - margin, SCREEN_W - margin, margin};
|
|
const int ty[4] = {margin, margin, SCREEN_H - margin, SCREEN_H - margin};
|
|
const char *name[4] = {"TOP-LEFT", "TOP-RIGHT", "BOTTOM-RIGHT", "BOTTOM-LEFT"};
|
|
|
|
uint16_t raw_x[4] = {0};
|
|
uint16_t raw_y[4] = {0};
|
|
|
|
ili9341_fill_screen(ILI9341_COLOR_BLACK);
|
|
ili9341_draw_text(6, 6, "TOUCH CAL", ILI9341_COLOR_WHITE, ILI9341_COLOR_BLACK);
|
|
|
|
ESP_LOGI(TAG, "Calibration start");
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
ili9341_fill_screen(ILI9341_COLOR_BLACK);
|
|
draw_crosshair(tx[i], ty[i], ILI9341_COLOR_GREEN);
|
|
ESP_LOGI(TAG, "Tap and hold %s", name[i]);
|
|
|
|
wait_for_stable_touch(&raw_x[i], &raw_y[i]);
|
|
|
|
draw_crosshair(tx[i], ty[i], ILI9341_COLOR_BLUE);
|
|
vTaskDelay(pdMS_TO_TICKS(350));
|
|
}
|
|
|
|
int x_left = ((int)raw_x[0] + (int)raw_x[3]) / 2;
|
|
int x_right = ((int)raw_x[1] + (int)raw_x[2]) / 2;
|
|
int y_top = ((int)raw_y[0] + (int)raw_y[1]) / 2;
|
|
int y_bottom = ((int)raw_y[2] + (int)raw_y[3]) / 2;
|
|
|
|
s_cal.x_min = (x_left < x_right) ? x_left : x_right;
|
|
s_cal.x_max = (x_left > x_right) ? x_left : x_right;
|
|
s_cal.y_min = (y_top < y_bottom) ? y_top : y_bottom;
|
|
s_cal.y_max = (y_top > y_bottom) ? y_top : y_bottom;
|
|
s_cal.invert_x = !(x_right > x_left);
|
|
s_cal.invert_y = !(y_bottom > y_top);
|
|
|
|
ESP_LOGI(TAG,
|
|
"Calibration done: x=[%d,%d] y=[%d,%d] invert=(%d,%d)",
|
|
s_cal.x_min,
|
|
s_cal.x_max,
|
|
s_cal.y_min,
|
|
s_cal.y_max,
|
|
s_cal.invert_x,
|
|
s_cal.invert_y);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
const touch_calib_t *touch_get_calibration(void)
|
|
{
|
|
return &s_cal;
|
|
}
|
|
|
|
esp_err_t touch_set_calibration(const touch_calib_t *calib)
|
|
{
|
|
if (calib == NULL) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
s_cal = *calib;
|
|
return ESP_OK;
|
|
}
|