1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/stmpe811/stmpe811.c

472 lines
13 KiB
C
Raw Normal View History

2019-04-28 17:54:09 +02:00
/*
* Copyright (C) 2019 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_stmpe811
* @{
*
* @file
* @brief Device driver implementation for the STMPE811 touchscreen controller.
*
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*
* @}
*/
#include <inttypes.h>
2021-12-01 17:10:43 +01:00
#include "ztimer.h"
2021-10-23 12:09:27 +02:00
#if IS_USED(MODULE_STMPE811_SPI)
#include "periph/spi.h"
#else
2019-04-28 17:54:09 +02:00
#include "periph/i2c.h"
2021-10-23 12:09:27 +02:00
#endif
2019-04-28 17:54:09 +02:00
#include "periph/gpio.h"
#include "stmpe811.h"
#include "stmpe811_constants.h"
#include "stmpe811_params.h"
2020-10-22 11:34:31 +02:00
#define ENABLE_DEBUG 0
2019-04-28 17:54:09 +02:00
#include "debug.h"
2021-10-23 12:09:27 +02:00
#if IS_USED(MODULE_STMPE811_SPI)
#define BUS (dev->params.spi)
#define CS (dev->params.cs)
#define CLK (dev->params.clk)
#define MODE (dev->params.mode)
#define WRITE_MASK (0x7F)
#else
#define BUS (dev->params.i2c)
#define ADDR (dev->params.addr)
#endif
#ifndef STMPE811_FIFO_THRESHOLD_ENABLED
#define STMPE811_FIFO_THRESHOLD_ENABLED (0)
#endif
/* The driver only works reliably with FIFO threshold interrupts if the FIFO
* threshold is at least 2. The reason is that all interrupts are cleared when
* the status is checked in `stmpe811_read_touch_state` but the FIFO Threshold
* interrupt is asserted again immediately since the FIFO is not read so that
* a new interrupt is pending. On the other hand, the Touch Detected interrupt
* does not work reliably for the release event if only the Touch Detected
* interrupt in `stmpe811_read_touch_state` is cleared. The workaround is
* to set the FIFO threshold to at least 2 to introduce a small delay between
* the Touch Detect interrupt on a touch and the first FIFO threshold
* interrupt. */
#ifndef STMPE811_FIFO_THRESHOLD
#define STMPE811_FIFO_THRESHOLD (STMPE811_FIFO_THRESHOLD_ENABLED ? 2 : 1)
#endif
2021-10-23 12:09:27 +02:00
#if IS_USED(MODULE_STMPE811_SPI) /* using SPI mode */
static inline void _acquire(const stmpe811_t *dev)
{
spi_acquire(BUS, CS, MODE, CLK);
}
static inline void _release(const stmpe811_t *dev)
{
spi_release(BUS);
}
static int _read_reg(const stmpe811_t *dev, uint8_t reg, uint8_t *data)
{
*data = spi_transfer_reg(BUS, CS, reg | 0x80, 0x00);
return 0;
}
static int _write_reg(const stmpe811_t *dev, uint8_t reg, uint8_t data)
{
(void)spi_transfer_reg(BUS, CS, (reg & WRITE_MASK), data);
return 0;
}
static int _read_burst(const stmpe811_t *dev, uint8_t reg, void *buf, size_t len)
{
uint8_t reg_read = reg | 0x80;
/* since SPI is in auto-increment mode subsequent reads will ignore the
content of the reg_read buffer, and itself can't overflow since it matches
the amount of data per register */
spi_transfer_regs(BUS, CS, reg_read, &reg_read, buf, len);
return 0;
}
#else /* using I2C mode */
static inline void _acquire(const stmpe811_t *dev)
{
i2c_acquire(BUS);
}
static inline void _release(const stmpe811_t *dev)
{
i2c_release(BUS);
}
static int _read_reg(const stmpe811_t *dev, uint8_t reg, uint8_t *data)
{
if (i2c_read_reg(BUS, ADDR, reg, data, 0) != 0) {
return -EIO;
}
return 0;
}
static int _write_reg(const stmpe811_t *dev, uint8_t reg, uint8_t data)
{
if (i2c_write_reg(BUS, ADDR, reg, data, 0) != 0) {
return -EIO;
}
return 0;
}
static int _read_burst(const stmpe811_t *dev, uint8_t reg, void *buf, size_t len)
{
if (i2c_read_regs(BUS, ADDR, reg, buf, len, 0) != 0) {
return -EIO;
}
return 0;
}
#endif /* bus mode selection */
2019-04-28 17:54:09 +02:00
static int _soft_reset(const stmpe811_t *dev)
{
2021-10-23 12:09:27 +02:00
if (_write_reg(dev, STMPE811_SYS_CTRL1, STMPE811_SYS_CTRL1_SOFT_RESET ) < 0) {
2019-04-28 17:54:09 +02:00
DEBUG("[stmpe811] soft reset: cannot write soft reset bit to SYS_CTRL1 register\n");
2021-10-23 11:50:02 +02:00
return -EPROTO;
2019-04-28 17:54:09 +02:00
}
2021-12-01 17:10:43 +01:00
ztimer_sleep(ZTIMER_MSEC, 10);
2019-04-28 17:54:09 +02:00
2021-10-23 12:09:27 +02:00
if (_write_reg(dev, STMPE811_SYS_CTRL1, 0) < 0) {
2019-04-28 17:54:09 +02:00
DEBUG("[stmpe811] soft reset: cannot clear SYS_CTRL1 register\n");
2021-10-23 11:50:02 +02:00
return -EPROTO;
2019-04-28 17:54:09 +02:00
}
2021-12-01 17:10:43 +01:00
ztimer_sleep(ZTIMER_MSEC, 2);
2019-04-28 17:54:09 +02:00
2021-10-23 11:50:02 +02:00
return 0;
2019-04-28 17:54:09 +02:00
}
static void _reset_fifo(const stmpe811_t *dev)
{
2021-10-23 12:09:27 +02:00
_write_reg(dev, STMPE811_FIFO_CTRL_STA, STMPE811_FIFO_CTRL_STA_RESET);
_write_reg(dev, STMPE811_FIFO_CTRL_STA, 0);
2019-04-28 17:54:09 +02:00
}
static void _clear_interrupt_status(const stmpe811_t *dev)
{
2021-10-23 12:09:27 +02:00
_write_reg(dev, STMPE811_INT_STA, 0xff);
}
#if IS_USED(MODULE_STMPE811_SPI)
static int _stmpe811_check_mode(stmpe811_t *dev)
{
/* can iterate directly through the enum since they might not be
monotonically incrementing */
uint8_t modes[] = { SPI_MODE_0, SPI_MODE_1, SPI_MODE_2, SPI_MODE_3};
uint8_t reg;
for (uint8_t i = 0; i < sizeof(modes); i++) {
DEBUG("[stmpe811] init: set spi mode to 0x%02x ... ", modes[i]);
dev->params.mode = modes[i];
/* acquire */
_acquire(dev);
/* configure auto increment SPI */
_read_reg(dev, STMPE811_SPI_CFG, &reg);
reg = STMPE811_SPI_CFG_AUTO_INCR;
_write_reg(dev, STMPE811_SPI_CFG, reg);
_read_reg(dev, STMPE811_SPI_CFG, &reg);
if (reg & STMPE811_SPI_CFG_AUTO_INCR) {
DEBUG("success\n");
_release(dev);
return 0;
}
DEBUG("failed\n");
_release(dev);
}
return 1;
2019-04-28 17:54:09 +02:00
}
2021-10-23 12:09:27 +02:00
#endif
2019-04-28 17:54:09 +02:00
2021-10-23 12:09:27 +02:00
int stmpe811_init(stmpe811_t *dev, const stmpe811_params_t *params, stmpe811_event_cb_t cb,
void *arg)
2019-04-28 17:54:09 +02:00
{
dev->params = *params;
2021-10-23 12:09:27 +02:00
dev->prev_x = 0;
dev->prev_y = 0;
2021-10-23 11:50:02 +02:00
int ret = 0;
2021-10-23 12:09:27 +02:00
uint8_t reg;
2019-04-28 17:54:09 +02:00
2021-10-23 12:09:27 +02:00
#if IS_USED(MODULE_STMPE811_SPI)
/* configure the chip-select pin */
if (spi_init_cs(BUS, CS) != SPI_OK) {
DEBUG("[stmpe811] error: unable to configure chip the select pin\n");
return -EIO;
}
/* check mode configuration */
if (_stmpe811_check_mode(dev) != 0) {
2021-10-23 12:09:27 +02:00
DEBUG("[stmpe811] error: couldn't setup SPI\n");
return -EIO;
}
#endif
/* acquire bus */
_acquire(dev);
2019-04-28 17:54:09 +02:00
uint16_t device_id;
2021-10-23 12:09:27 +02:00
if (_read_burst(dev, STMPE811_CHIP_ID, &device_id, 2) < 0) {
2019-04-28 17:54:09 +02:00
DEBUG("[stmpe811] init: cannot read CHIP_ID register\n");
2021-10-23 12:09:27 +02:00
_release(dev);
2021-10-23 11:50:02 +02:00
return -EPROTO;
2019-04-28 17:54:09 +02:00
}
device_id = (device_id << 8) | (device_id >> 8);
if (device_id != STMPE811_CHIP_ID_VALUE) {
DEBUG("[stmpe811] init: invalid device id (actual: 0x%04X, expected: 0x%04X)\n",
device_id, STMPE811_CHIP_ID_VALUE);
2021-10-23 12:09:27 +02:00
_release(dev);
2021-10-23 11:50:02 +02:00
return -ENODEV;
2019-04-28 17:54:09 +02:00
}
DEBUG("[stmpe811] init: valid device\n");
ret = _soft_reset(dev);
2021-10-23 11:50:02 +02:00
if (ret != 0) {
2019-04-28 17:54:09 +02:00
DEBUG("[stmpe811] init: reset failed\n");
2021-10-23 12:09:27 +02:00
_release(dev);
2021-10-23 11:50:02 +02:00
return -EIO;
2019-04-28 17:54:09 +02:00
}
DEBUG("[stmpe811] init: soft reset done\n");
/* Initialization sequence */
/* disable temperature sensor and GPIO */
2021-10-23 12:09:27 +02:00
ret =
_write_reg(dev, STMPE811_SYS_CTRL2,
(STMPE811_SYS_CTRL2_TS_OFF | STMPE811_SYS_CTRL2_GPIO_OFF));
2019-04-28 17:54:09 +02:00
/* set to 80 cycles and adc resolution to 12 bit*/
2021-10-23 12:09:27 +02:00
reg =
((uint8_t)(STMPE811_ADC_CTRL1_SAMPLE_TIME_80 << STMPE811_ADC_CTRL1_SAMPLE_TIME_POS) |
STMPE811_ADC_CTRL1_MOD_12B);
ret += _write_reg(dev, STMPE811_ADC_CTRL1, reg);
2019-04-28 17:54:09 +02:00
/* set adc clock speed to 3.25 MHz */
2021-10-23 12:09:27 +02:00
ret += _write_reg(dev, STMPE811_ADC_CTRL2, STMPE811_ADC_CTRL2_FREQ_3_25MHZ);
2019-04-28 17:54:09 +02:00
/* set GPIO AF to function as ts/adc */
2021-10-23 12:09:27 +02:00
ret += _write_reg(dev, STMPE811_GPIO_ALT_FUNCTION, 0x00);
2019-04-28 17:54:09 +02:00
/* set touchscreen configuration */
reg = ((uint8_t)(STMPE811_TSC_CFG_AVE_CTRL_4 << STMPE811_TSC_CFG_AVE_CTRL_POS) |
2021-10-23 12:09:27 +02:00
(uint8_t)(STMPE811_TSC_CFG_TOUCH_DET_DELAY_500US <<
STMPE811_TSC_CFG_TOUCH_DET_DELAY_POS) |
(STMPE811_TSC_CFG_SETTLING_500US));
ret += _write_reg(dev, STMPE811_TSC_CFG, reg);
2019-04-28 17:54:09 +02:00
/* set fifo threshold */
ret += _write_reg(dev, STMPE811_FIFO_TH, STMPE811_FIFO_THRESHOLD);
2019-04-28 17:54:09 +02:00
/* reset fifo */
_reset_fifo(dev);
/* set fractional part to 7, whole part to 1 */
2021-10-23 12:09:27 +02:00
ret += _write_reg(dev, STMPE811_TSC_FRACTION_Z, STMPE811_TSC_FRACTION_Z_7_1);
2019-04-28 17:54:09 +02:00
/* set current limit value to 50 mA */
2021-10-23 12:09:27 +02:00
ret += _write_reg(dev, STMPE811_TSC_I_DRIVE, STMPE811_TSC_I_DRIVE_50MA);
2019-04-28 17:54:09 +02:00
/* enable touchscreen clock */
2021-10-23 12:09:27 +02:00
ret += _read_reg(dev, STMPE811_SYS_CTRL2, &reg);
2019-04-28 17:54:09 +02:00
reg &= ~STMPE811_SYS_CTRL2_TSC_OFF;
2021-10-23 12:09:27 +02:00
ret += _write_reg(dev, STMPE811_SYS_CTRL2, reg);
2019-04-28 17:54:09 +02:00
2021-10-23 12:09:27 +02:00
ret += _read_reg(dev, STMPE811_TSC_CTRL, &reg);
2019-04-28 17:54:09 +02:00
reg |= STMPE811_TSC_CTRL_EN;
2021-10-23 12:09:27 +02:00
ret += _write_reg(dev, STMPE811_TSC_CTRL, reg);
2019-04-28 17:54:09 +02:00
/* clear interrupt status */
_clear_interrupt_status(dev);
if (gpio_is_valid(dev->params.int_pin)) {
2019-04-28 17:54:09 +02:00
DEBUG("[stmpe811] init: configuring touchscreen interrupt\n");
if (cb) {
gpio_init_int(dev->params.int_pin, GPIO_IN, GPIO_FALLING, cb, arg);
}
2019-04-28 17:54:09 +02:00
/* Enable touchscreen interrupt */
ret += _write_reg(dev, STMPE811_INT_EN,
STMPE811_INT_EN_TOUCH_DET |
(STMPE811_FIFO_THRESHOLD_ENABLED ? STMPE811_INT_EN_FIFO_TH : 0));
2019-04-28 17:54:09 +02:00
/* Enable global interrupt */
2021-10-23 12:09:27 +02:00
ret += _write_reg(dev, STMPE811_INT_CTRL,
STMPE811_INT_CTRL_GLOBAL_INT | STMPE811_INT_CTRL_INT_TYPE);
2019-04-28 17:54:09 +02:00
}
if (ret < 0) {
2021-10-23 12:09:27 +02:00
_release(dev);
2019-04-28 17:54:09 +02:00
DEBUG("[stmpe811] init: initialization sequence failed\n");
2021-10-23 11:50:02 +02:00
return -EPROTO;
2019-04-28 17:54:09 +02:00
}
/* Release I2C device */
2021-10-23 12:09:27 +02:00
_release(dev);
2019-04-28 17:54:09 +02:00
DEBUG("[stmpe811] initialization successful\n");
return ret;
}
int stmpe811_read_touch_position(stmpe811_t *dev, stmpe811_touch_position_t *position)
{
uint16_t tmp_x, tmp_y;
2021-10-23 12:09:27 +02:00
/* Acquire device bus */
_acquire(dev);
2019-04-28 17:54:09 +02:00
/* Ensure there's a least one position measured in the FIFO */
uint8_t fifo_size = 0;
2021-10-23 12:09:27 +02:00
do {
2021-10-23 12:09:27 +02:00
_read_reg(dev, STMPE811_FIFO_SIZE, &fifo_size);
} while (!fifo_size);
2021-10-23 12:09:27 +02:00
uint8_t xyz[4];
2019-04-28 17:54:09 +02:00
uint32_t xyz_ul;
2021-10-23 12:09:27 +02:00
#if IS_USED(MODULE_STMPE811_SPI)
for (uint8_t i = 0; i < sizeof(xyz); i++) {
if (_read_reg(dev, STMPE811_TSC_DATA_NON_INC, &xyz[i]) < 0) {
DEBUG("[stmpe811] position: cannot read position\n");
_release(dev);
return -EPROTO;
}
}
#else
if (_read_burst(dev, STMPE811_TSC_DATA_NON_INC, xyz, sizeof(xyz)) < 0) {
2019-04-28 17:54:09 +02:00
DEBUG("[stmpe811] position: cannot read position\n");
2021-10-23 12:09:27 +02:00
_release(dev);
2021-10-23 11:50:02 +02:00
return -EPROTO;
2019-04-28 17:54:09 +02:00
}
2021-10-23 12:09:27 +02:00
#endif
2019-04-28 17:54:09 +02:00
/* Reset the FIFO, otherwise new touch data will be processed with a delay
* if the rate of calling this function to read the FIFO is slower than
* the rate at which the FIFO is filled. The reason for this is that with
* each call of this function only the oldest touch data is read
* value by value from the FIFO. Gestures, for example, can't be
* implemented with such a behavior. */
_reset_fifo(dev);
2021-10-23 12:09:27 +02:00
/* Release device bus */
_release(dev);
2019-04-28 17:54:09 +02:00
xyz_ul = ((uint32_t)xyz[0] << 24) | ((uint32_t)xyz[1] << 16) | \
2021-10-23 12:09:27 +02:00
((uint32_t)xyz[2] << 8) | (xyz[3] << 0);
2019-04-28 17:54:09 +02:00
tmp_x = (xyz_ul >> 20) & 0xfff;
tmp_y = (xyz_ul >> 8) & 0xfff;
/* Y value first correction */
tmp_y -= 360;
/* Y value second correction */
tmp_y /= 11;
/* maximum values in device coordinates */
uint16_t tmp_xmax;
uint16_t tmp_ymax;
if (dev->params.xyconv & STMPE811_SWAP_XY) {
tmp_xmax = dev->params.ymax;
tmp_ymax = dev->params.xmax;
}
else {
tmp_xmax = dev->params.xmax;
tmp_ymax = dev->params.ymax;
}
2019-04-28 17:54:09 +02:00
/* clamp y position */
if (tmp_y > tmp_ymax) {
2019-04-28 17:54:09 +02:00
tmp_y = dev->prev_y;
}
/* X value first correction */
if (tmp_x <= 3000) {
tmp_x = 3870 - tmp_x;
}
else {
tmp_x = 3800 - tmp_x;
}
/* X value second correction */
tmp_x /= 15;
/* clamp x position */
if (tmp_x > tmp_xmax) {
2019-04-28 17:54:09 +02:00
tmp_x = dev->prev_x;
}
dev->prev_x = tmp_x;
dev->prev_y = tmp_y;
/* conversion to screen coordinates */
if (dev->params.xyconv & STMPE811_SWAP_XY) {
position->x = tmp_y;
position->y = tmp_x;
}
else {
position->x = tmp_x;
position->y = tmp_y;
}
if (dev->params.xyconv & STMPE811_MIRROR_X) {
assert(position->x <= dev->params.xmax);
position->x = dev->params.xmax - position->x;
}
if (dev->params.xyconv & STMPE811_MIRROR_Y) {
assert(position->y <= dev->params.ymax);
position->y = dev->params.ymax - position->y;
}
2019-04-28 17:54:09 +02:00
2021-10-23 11:50:02 +02:00
return 0;
2019-04-28 17:54:09 +02:00
}
int stmpe811_read_touch_state(const stmpe811_t *dev, stmpe811_touch_state_t *state)
{
uint8_t val;
2021-10-23 12:09:27 +02:00
/* Acquire device bus */
_acquire(dev);
2019-04-28 17:54:09 +02:00
2021-10-23 12:09:27 +02:00
if (_read_reg(dev, STMPE811_TSC_CTRL, &val) < 0) {
2019-04-28 17:54:09 +02:00
DEBUG("[stmpe811] position: cannot read touch state\n");
2021-10-23 12:09:27 +02:00
_release(dev);
2021-10-23 11:50:02 +02:00
return -EPROTO;
2019-04-28 17:54:09 +02:00
}
_clear_interrupt_status(dev);
2019-04-28 17:54:09 +02:00
if ((val & STMPE811_TSC_CTRL_STA)) {
*state = STMPE811_TOUCH_STATE_PRESSED;
}
else {
_reset_fifo(dev);
*state = STMPE811_TOUCH_STATE_RELEASED;
}
/* Release I2C device */
2021-10-23 12:09:27 +02:00
_release(dev);
2019-04-28 17:54:09 +02:00
2021-10-23 11:50:02 +02:00
return 0;
2019-04-28 17:54:09 +02:00
}