/* * Author: LoBo (loboris@gmail.com, loboris.github) * * Module supporting STMPE touch screen controller. * * HIGH SPEED LOW LEVEL DISPLAY FUNCTIONS * USING DIRECT or DMA SPI TRANSFER MODEs * */ #include "mgos.h" #include "lobo_spi.h" #include "stmpe610.h" #define SPI_BUS VSPI_HOST #define STMPE_SPI_MODE 1 static spi_lobo_device_handle_t s_stmpe610_spi = NULL; static mgos_stmpe610_event_t s_event_handler = NULL; static enum mgos_stmpe610_rotation_t s_rotation = STMPE610_PORTRAIT; static void IRAM_ATTR _spi_transfer_start(spi_lobo_device_handle_t spi_dev, int wrbits, int rdbits) { // Load send buffer spi_dev->host->hw->user.usr_mosi_highpart = 0; spi_dev->host->hw->mosi_dlen.usr_mosi_dbitlen = wrbits-1; spi_dev->host->hw->user.usr_mosi = 1; if (rdbits) { spi_dev->host->hw->miso_dlen.usr_miso_dbitlen = rdbits; spi_dev->host->hw->user.usr_miso = 1; } else { spi_dev->host->hw->miso_dlen.usr_miso_dbitlen = 0; spi_dev->host->hw->user.usr_miso = 0; } // Start transfer spi_dev->host->hw->cmd.usr = 1; // Wait for SPI bus ready while (spi_dev->host->hw->cmd.usr); } static void IRAM_ATTR stmpe610_write_reg(uint8_t reg, uint8_t val) { spi_lobo_device_select(s_stmpe610_spi, 0); s_stmpe610_spi->host->hw->data_buf[0] = (val << 8) | reg; _spi_transfer_start(s_stmpe610_spi, 16, 0); spi_lobo_device_deselect(s_stmpe610_spi); } static uint8_t IRAM_ATTR stmpe610_read_byte(uint8_t reg) { spi_lobo_device_select(s_stmpe610_spi, 0); s_stmpe610_spi->host->hw->data_buf[0] = (reg << 8) | (reg | 0x80); _spi_transfer_start(s_stmpe610_spi, 16, 16); uint8_t res = s_stmpe610_spi->host->hw->data_buf[0] >> 8; spi_lobo_device_deselect(s_stmpe610_spi); return res; } static uint16_t IRAM_ATTR stmpe610_read_word(uint8_t reg) { spi_lobo_device_select(s_stmpe610_spi, 0); s_stmpe610_spi->host->hw->data_buf[0] = ((((reg+1) << 8) | ((reg+1) | 0x80)) << 16) | (reg << 8) | (reg | 0x80); _spi_transfer_start(s_stmpe610_spi, 32, 32); uint16_t res = (uint16_t)(s_stmpe610_spi->host->hw->data_buf[0] & 0xFF00); res |= (uint16_t)(s_stmpe610_spi->host->hw->data_buf[0] >> 24); spi_lobo_device_deselect(s_stmpe610_spi); return res; } static uint32_t stmpe610_getID() { uint16_t tid = stmpe610_read_word(0); uint8_t tver = stmpe610_read_byte(2); return (tid << 8) | tver; } static uint8_t stmpe610_bufferLength(void) { return stmpe610_read_byte(STMPE_FIFO_SIZE); } static uint8_t stmpe610_readData(uint16_t *x, uint16_t *y, uint8_t *z) { uint8_t samples, cnt; uint32_t sum_sample_x = 0, sum_sample_y = 0; uint16_t sum_sample_z = 0; samples = cnt = stmpe610_bufferLength(); LOG(LL_DEBUG, ("Touch sensed with %d samples", samples)); if (samples == 0) return 0; while (cnt>0) { uint16_t sample_x, sample_y; uint8_t sample_z; sample_x = stmpe610_read_word(0x4D); sample_y = stmpe610_read_word(0x4F); sample_z = stmpe610_read_byte(0x51); sum_sample_x += sample_x; sum_sample_y += sample_y; sum_sample_z += sample_z; LOG(LL_DEBUG, ("Sample at (%d,%d) pressure=%d, bufferLength=%d", sample_x, sample_y, sample_z, stmpe610_bufferLength())); cnt--; } *x = sum_sample_x / samples; *y = sum_sample_y / samples; *z = sum_sample_z / samples; stmpe610_write_reg(STMPE_FIFO_STA, STMPE_FIFO_STA_RESET); // clear FIFO stmpe610_write_reg(STMPE_FIFO_STA, 0); // unreset return samples; } static long map(long x, long in_min, long in_max, long out_min, long out_max) { if (xin_max) x=in_max; return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } static void stmpe610_map_rotation(uint16_t x, uint16_t y, uint16_t *x_out, uint16_t *y_out) { switch(s_rotation) { case STMPE610_LANDSCAPE: *x_out = map(y, 150, 3800, 0, 4095); *y_out = map(x, 250, 3700, 0, 4095); break; case STMPE610_PORTRAIT_FLIP: *x_out = map(x, 250, 3800, 0, 4095); *y_out = 4095-map(y, 150, 3700, 0, 4095); break; case STMPE610_LANDSCAPE_FLIP: *x_out = 4095-map(y, 150, 3800, 0, 4095); *y_out = 4095-map(x, 250, 3700, 0, 4095); break; default: // STMPE610_PORTRAIT *x_out = 4095-map(x, 250, 3800, 0, 4095); *y_out = map(y, 150, 3700, 0, 4095); } } static void stmpe610_irq(int pin, void *arg) { struct mgos_stmpe610_event_data ed; uint16_t x, y; uint8_t z; if (stmpe610_bufferLength()==0) { uint8_t i; LOG(LL_DEBUG, ("Touch DOWN")); for (i=0; i<10; i++) { mgos_msleep(5); if (stmpe610_bufferLength()>0) { stmpe610_readData(&x, &y, &z); LOG(LL_DEBUG, ("Touch DOWN at (%d,%d) pressure=%d, length=%d, iteration=%d", x, y, z, ed.length, i)); ed.length=1; ed.direction = TOUCH_DOWN; stmpe610_map_rotation(x, y, &ed.x, &ed.y); ed.z = z; if (s_event_handler) s_event_handler(&ed); break; } } stmpe610_write_reg(STMPE_INT_STA, 0xFF); // reset all ints return; } ed.length = stmpe610_readData(&x, &y, &z); LOG(LL_DEBUG, ("Touch UP at (%d,%d) pressure=%d, length=%d", x, y, z, ed.length)); ed.direction = TOUCH_UP; stmpe610_map_rotation(x, y, &ed.x, &ed.y); ed.z = z; if (s_event_handler) s_event_handler(&ed); stmpe610_write_reg(STMPE_INT_STA, 0xFF); // reset all ints (void) pin; (void) arg; } void mgos_stmpe610_set_handler(mgos_stmpe610_event_t handler) { s_event_handler = handler; } void mgos_stmpe610_set_rotation(enum mgos_stmpe610_rotation_t rotation) { s_rotation = rotation; } bool mgos_stmpe610_init(void) { esp_err_t ret; gpio_pad_select_gpio(mgos_sys_config_get_stmpe610_cs_pin()); gpio_set_direction(mgos_sys_config_get_stmpe610_cs_pin(), GPIO_MODE_OUTPUT); spi_lobo_bus_config_t buscfg={ .miso_io_num=mgos_sys_config_get_spi_miso(), .mosi_io_num=mgos_sys_config_get_spi_mosi(), .sclk_io_num=mgos_sys_config_get_spi_sck(), .quadwp_io_num=-1, .quadhd_io_num=-1, .max_transfer_sz = 6*1024, }; spi_lobo_device_interface_config_t tsdevcfg={ .clock_speed_hz=1000000, .mode=STMPE_SPI_MODE, .spics_io_num=mgos_sys_config_get_stmpe610_cs_pin(), //Touch CS pin .spics_ext_io_num=-1, //Not using the external CS .flags = 0, }; ret=spi_lobo_bus_add_device(SPI_BUS, &buscfg, &tsdevcfg, &s_stmpe610_spi); assert(ret==ESP_OK); ret = spi_lobo_device_select(s_stmpe610_spi, 1); assert(ret==ESP_OK); ret = spi_lobo_device_deselect(s_stmpe610_spi); assert(ret==ESP_OK); uint32_t tver = stmpe610_getID(); if (tver >> 8 != 0x0811) { LOG(LL_ERROR, ("STMPE SPI init failed, disabling")); return true; } LOG(LL_INFO, ("SPI init ok (cs=%d, speed=%u, ver=%04x, rev=%02x); irq=%d", mgos_sys_config_get_stmpe610_cs_pin(), spi_lobo_get_speed(s_stmpe610_spi), tver>>8, tver&0xFF, mgos_sys_config_get_stmpe610_irq_pin())); stmpe610_write_reg(STMPE_SYS_CTRL1, STMPE_SYS_CTRL1_RESET); mgos_msleep(10); for (uint8_t i=0; i<65; i++) stmpe610_read_byte(i); stmpe610_write_reg(STMPE_SYS_CTRL2, 0x0); // turn on clocks! stmpe610_write_reg(STMPE_INT_EN, STMPE_INT_EN_TOUCHDET); stmpe610_write_reg(STMPE_ADC_CTRL1, STMPE_ADC_CTRL1_10BIT | (0x6 << 4)); // 96 clocks per conversion stmpe610_write_reg(STMPE_ADC_CTRL2, STMPE_ADC_CTRL2_6_5MHZ); stmpe610_write_reg(STMPE_TSC_CFG, STMPE_TSC_CFG_4SAMPLE | STMPE_TSC_CFG_DELAY_1MS | STMPE_TSC_CFG_SETTLE_5MS); stmpe610_write_reg(STMPE_TSC_FRACTION_Z, 0x6); stmpe610_write_reg(STMPE_FIFO_TH, 1); stmpe610_write_reg(STMPE_FIFO_STA, STMPE_FIFO_STA_RESET); stmpe610_write_reg(STMPE_FIFO_STA, 0); // unreset stmpe610_write_reg(STMPE_TSC_I_DRIVE, STMPE_TSC_I_DRIVE_50MA); stmpe610_write_reg(STMPE_TSC_CTRL, 0x30); // X&Y&Z, 16 reading window stmpe610_write_reg(STMPE_TSC_CTRL, 0x31); // X&Y&Z, 16 reading window, TSC enable stmpe610_write_reg(STMPE_INT_STA, 0xFF); // reset all ints stmpe610_write_reg(STMPE_INT_CTRL, STMPE_INT_CTRL_POL_LOW | STMPE_INT_CTRL_EDGE | STMPE_INT_CTRL_ENABLE); mgos_gpio_set_mode(mgos_sys_config_get_stmpe610_irq_pin(), MGOS_GPIO_MODE_INPUT); mgos_gpio_set_pull(mgos_sys_config_get_stmpe610_irq_pin(), MGOS_GPIO_PULL_UP); mgos_gpio_set_int_handler(mgos_sys_config_get_stmpe610_irq_pin(), MGOS_GPIO_INT_EDGE_NEG, stmpe610_irq, NULL); mgos_gpio_enable_int(mgos_sys_config_get_stmpe610_irq_pin()); return true; }