diff --git a/boards/pinetime/include/board.h b/boards/pinetime/include/board.h index b56237317f..43d3850966 100644 --- a/boards/pinetime/include/board.h +++ b/boards/pinetime/include/board.h @@ -100,6 +100,17 @@ extern mtd_dev_t *mtd0; #define MTD_0 mtd0 /** @} */ +/** + * @name Touch screen configuration + * @{ + */ +#define CST816S_PARAM_I2C_DEV I2C_DEV(0) +#define CST816S_PARAM_I2C_ADDR (0x15) +#define CST816S_PARAM_IRQ GPIO_PIN(0, 28) +#define CST816S_PARAM_IRQ_FLANK GPIO_FALLING +#define CST816S_PARAM_RESET GPIO_PIN(0, 10) +/** @} */ + #ifdef __cplusplus } #endif diff --git a/drivers/cst816s/Makefile b/drivers/cst816s/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/cst816s/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/cst816s/Makefile.dep b/drivers/cst816s/Makefile.dep new file mode 100644 index 0000000000..ac9f3060a5 --- /dev/null +++ b/drivers/cst816s/Makefile.dep @@ -0,0 +1,3 @@ +FEATURES_REQUIRED += periph_gpio_irq +FEATURES_REQUIRED += periph_i2c +USEMODULE += xtimer diff --git a/drivers/cst816s/Makefile.include b/drivers/cst816s/Makefile.include new file mode 100644 index 0000000000..24f7b96e6c --- /dev/null +++ b/drivers/cst816s/Makefile.include @@ -0,0 +1,2 @@ +USEMODULE_INCLUDES_cst816s := $(LAST_MAKEFILEDIR)/include +USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_cst816s) diff --git a/drivers/cst816s/cst816s.c b/drivers/cst816s/cst816s.c new file mode 100644 index 0000000000..6aecd207c2 --- /dev/null +++ b/drivers/cst816s/cst816s.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2020 Koen Zandberg + * + * 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_cst816s + * @{ + * + * @file + * @brief Device driver implementation for cst816s touch screen + * + * @author Koen Zandberg + * + * @} + */ + +#include "log.h" +#include "periph/gpio.h" +#include "periph/i2c.h" +#include "xtimer.h" + +#include "cst816s.h" +#include "cst816s_internal.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +const char *cst816s_gesture_str[] = { + [CST816S_GESTURE_NONE] = "none", + [CST816S_GESTURE_SLIDE_DOWN] = "slide down", + [CST816S_GESTURE_SLIDE_UP] = "slide up", + [CST816S_GESTURE_SLIDE_LEFT] = "slide left", + [CST816S_GESTURE_SLIDE_RIGHT] = "slide right", + [CST816S_GESTURE_SINGLE_CLICK] = "single click", + [CST816S_GESTURE_DOUBLE_CLICK] = "double click", + [CST816S_GESTURE_LONG_PRESS] = "long press", +}; + +static void _gpio_irq(void *arg) +{ + cst816s_t *dev = arg; + assert(dev); + + if (dev->cb) { + dev->cb(dev->cb_arg); + } +} + +static void _cst816s_reset(const cst816s_t *dev) +{ + /* Reset, sleep durations based on + * https://github.com/lupyuen/hynitron_i2c_cst0xxse/blob/master/cst0xx_core.c#L1078-L1085 */ + gpio_clear(dev->params->reset); + xtimer_usleep(CST816S_RESET_DURATION_LOW); + gpio_set(dev->params->reset); + xtimer_usleep(CST816S_RESET_DURATION_HIGH); +} + +int cst816s_read(const cst816s_t *dev, cst816s_touch_data_t *data) +{ + uint8_t buf[9]; /* 3 bytes "header" and 6 bytes touch info */ + + i2c_acquire(dev->params->i2c_dev); + int res = i2c_read_regs(dev->params->i2c_dev, dev->params->i2c_addr, + 0, buf, sizeof(buf), 0); + i2c_release(dev->params->i2c_dev); + + if (res < 0) { + return res; + } + + data->gesture = buf[1]; + data->action = buf[3] >> 6; + data->x = ((buf[3] & 0x0f) << 8) | buf[4]; + data->y = ((buf[5] & 0x0f) << 8) | buf[6]; + + return 0; +} + +int cst816s_init(cst816s_t *dev, const cst816s_params_t *params, + cst816s_irq_cb_t cb, void *arg) +{ + assert(dev && params); + dev->params = params; + dev->cb = cb; + dev->cb_arg = arg; + + if (dev->params->reset != GPIO_UNDEF) { + gpio_init(dev->params->reset, GPIO_OUT); + _cst816s_reset(dev); + } + + if ((dev->params->irq != GPIO_UNDEF) && cb) { + if (gpio_init_int(dev->params->irq, GPIO_IN, + dev->params->irq_flank, + _gpio_irq, dev) < 0) { + return CST816S_ERR_IRQ; + } + } + return CST816S_OK; + /* The device will not respond until the first touch event */ +} diff --git a/drivers/cst816s/include/cst816s_internal.h b/drivers/cst816s/include/cst816s_internal.h new file mode 100644 index 0000000000..24dec4886d --- /dev/null +++ b/drivers/cst816s/include/cst816s_internal.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 Koen Zandberg + * + * 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_cst816s + * + * @{ + * @file + * @brief Internal constants for cst816s + * + * @author Koen Zandberg + */ +#ifndef CST816S_INTERNAL_H +#define CST816S_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name cst816s timing constants + * @{ + */ +#define CST816S_RESET_DURATION_LOW (20 * US_PER_MS) +#define CST816S_RESET_DURATION_HIGH (400 * US_PER_MS) +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* CST816S_INTERNAL_H */ +/** @} */ diff --git a/drivers/cst816s/include/cst816s_params.h b/drivers/cst816s/include/cst816s_params.h new file mode 100644 index 0000000000..83791eb9a6 --- /dev/null +++ b/drivers/cst816s/include/cst816s_params.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 Koen Zandberg + * + * 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_cst816s + * + * @{ + * @file + * + * @brief Default configuration for the CST816S touch screen driver + * + * @author Koen Zandberg + */ + +#ifndef CST816S_PARAMS_H +#define CST816S_PARAMS_H + +#include "board.h" +#include "cst816s.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Set default configuration parameters for the CST816S + * @{ + */ +/* I2C configuration */ +#ifndef CST816S_PARAM_I2C_DEV +#define CST816S_PARAM_I2C_DEV I2C_DEV(0) +#endif + +#ifndef CST816S_PARAM_I2C_ADDR +#define CST816S_PARAM_I2C_ADDR (0x15) +#endif + +#ifndef CST816S_PARAM_IRQ +#define CST816S_PARAM_IRQ GPIO_PIN(0, 28) +#endif + +#ifndef CST816S_PARAM_IRQ_FLANK +#define CST816S_PARAM_IRQ_FLANK GPIO_FALLING +#endif + +#ifndef CST816S_PARAM_RESET +#define CST816S_PARAM_RESET GPIO_PIN(0, 10) +#endif + +#define CST816S_PARAMS \ + { \ + .i2c_dev = CST816S_PARAM_I2C_DEV, \ + .i2c_addr = CST816S_PARAM_I2C_ADDR, \ + .irq = CST816S_PARAM_IRQ, \ + .irq_flank = CST816S_PARAM_IRQ_FLANK, \ + .reset = CST816S_PARAM_RESET, \ + } +/**@}*/ + +/** + * @brief Configure CST816S + */ +static const cst816s_params_t cst816s_params[] = +{ + CST816S_PARAMS +}; + +/** + * @brief The number of configured sensors + */ +#define CST816S_NUMOF ARRAY_SIZE(cst816s_params) + +#ifdef __cplusplus +} +#endif + +#endif /* CST816S_PARAMS_H */ +/** @} */ diff --git a/drivers/include/cst816s.h b/drivers/include/cst816s.h new file mode 100644 index 0000000000..9a21ca2318 --- /dev/null +++ b/drivers/include/cst816s.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2020 Koen Zandberg + * + * 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. + */ + +/** + * @defgroup drivers_cst816s Cst816S touch screen driver + * + * @ingroup drivers_sensors + * @brief Device driver interface for the Hynitron CST816S touch screen + * + * The CST816S is a touch sensor from Hynitron with integrated gesture + * detection. It is able to measure both the position of a single finger and a + * number of basic gestures. The PineTime board has one of these for the touch + * screen. + * + * Documentation about the specifics is very limited and most of this driver is + * based on experimenting with the chip and from community effort on the + * PineTime. + * + * Two things about the driver are noteworthy: + * 1. It only responds to I2C commands after an event, such as a touch + * detection. Do not expect it to respond on init. Instead after a touch + * event, it will assert the IRQ and respond to I2C reads for a short time. + * 2. While it should be able to detect multiple finger events, this version of + * the chip always returns only a single finger event and a gesture. + * + * Reading the display data multiple times during a single event will return the + * last sampled finger position. + * + * @{ + * @file + * @brief Device driver interface for the CST816S touch screen + * + * @author Koen Zandberg + */ + +#ifndef CST816S_H +#define CST816S_H + +#include + +#include "periph/i2c.h" +#include "periph/gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief callback definition + */ +typedef void (*cst816s_irq_cb_t)(void *arg); + +/** + * @brief cst816s touch event touch state + */ +typedef enum { + CST816S_TOUCH_DOWN = 0, /**< Touch press */ + CST816S_TOUCH_UP = 1, /**< Touch release */ + CST816S_TOUCH_CONTACT = 2, /**< Touch contact */ +} cst816s_touch_t; + +/** + * @brief CST816S Gesture types + */ +typedef enum { + CST816S_GESTURE_NONE = 0x00, /**< no gesture detected */ + CST816S_GESTURE_SLIDE_DOWN = 0x01, /**< downward slide detected */ + CST816S_GESTURE_SLIDE_UP = 0x02, /**< upward slide detected */ + CST816S_GESTURE_SLIDE_LEFT = 0x03, /**< left slide detected */ + CST816S_GESTURE_SLIDE_RIGHT = 0x04, /**< right slide detected */ + CST816S_GESTURE_SINGLE_CLICK = 0x05, /**< single click detected */ + CST816S_GESTURE_DOUBLE_CLICK = 0x0b, /**< double click detected */ + CST816S_GESTURE_LONG_PRESS = 0x0c, /**< long press detected */ +} cst816s_gesture_t; + +/** + * @brief string versions of the cst816 gestures + */ +extern const char *cst816s_gesture_str[]; + +/** + * @brief cst816s touch event data + */ +typedef struct { + cst816s_gesture_t gesture; /**< Detected gesture */ + cst816s_touch_t action; /**< Press or release event */ + uint16_t x; /**< X coordinate */ + uint16_t y; /**< Y coordinate */ +} cst816s_touch_data_t; + +/** + * @brief cst816s driver struct + */ +typedef struct { + i2c_t i2c_dev; /**< I2C device which is used */ + uint8_t i2c_addr; /**< I2C address */ + gpio_t irq; /**< IRQ pin */ + gpio_flank_t irq_flank; /**< IRQ flank */ + gpio_t reset; /**< Device reset GPIO */ +} cst816s_params_t; + +/** + * @brief cst816s device descriptor + */ +typedef struct { + const cst816s_params_t *params; /**< Device parameters */ + cst816s_irq_cb_t cb; /**< Configured IRQ event callback */ + void *cb_arg; /**< Extra argument for the callback */ +} cst816s_t; + +/** + * @brief Status and error return codes + */ +enum { + CST816S_OK = 0, /**< everything was fine */ + CST816S_ERR_IRQ = -1, /**< IRQ initialization error */ +}; + +/** + * @brief Initialize the given cst816s device + * + * @param[out] dev device descriptor of the given cst816s device + * @param[in] params static configuration parameters + * @param[in] cb callback for the cst816s event interrupt, may be NULL + * @param[in] arg extra argument passed to the event interrupt. + * + * @returns CST816S_OK on success + * @returns CST816S_ERR_IRQ on IRQ initialization error + */ +int cst816s_init(cst816s_t *dev, const cst816s_params_t *params, + cst816s_irq_cb_t cb, void *arg); + +/** + * @brief Read touch data from the cst816s device + * + * @param[in] dev device descriptor + * @param[out] data Touch data + * + * @returns 0 on success + * @returns negative on I2C access error + */ +int cst816s_read(const cst816s_t *dev, cst816s_touch_data_t *data); + +#ifdef __cplusplus +} +#endif + +#endif /* CST816S_H */ +/** @} */ diff --git a/tests/driver_cst816s/Makefile b/tests/driver_cst816s/Makefile new file mode 100644 index 0000000000..119614342d --- /dev/null +++ b/tests/driver_cst816s/Makefile @@ -0,0 +1,6 @@ +include ../Makefile.tests_common + +USEMODULE += cst816s +USEMODULE += core_thread_flags + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_cst816s/main.c b/tests/driver_cst816s/main.c new file mode 100644 index 0000000000..30090e4b58 --- /dev/null +++ b/tests/driver_cst816s/main.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2020 Koen Zandberg + * + * 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 tests + * @{ + * + * @file + * @brief Test application for the CST816S touch screen driver + * + * @author Koen Zandberg + * + * @} + */ + +#include +#include + +#include "cst816s_params.h" +#include "cst816s.h" +#include "thread.h" +#include "thread_flags.h" + +#define CST816S_THREAD_FLAG (1 << 8) +#define CST816S_NUM_TOUCHES 5 + +static void _cb(void *arg) +{ + kernel_pid_t *pid = arg; + thread_flags_set((thread_t *)sched_threads[*pid], CST816S_THREAD_FLAG); +} + +static void _dump_cst816s(cst816s_t *dev) +{ + puts("Reading data:"); + cst816s_touch_data_t touches; + if (cst816s_read(dev, &touches) == 0) { + printf("Touch at %03u, %03u with gesture type \"%s\"\n", touches.x, + touches.y, cst816s_gesture_str[touches.gesture]); + } + else { + puts("Device not responding"); + } +} + +int main(void) +{ + cst816s_t dev; + + kernel_pid_t pid = thread_getpid(); + + puts("CST816S test application\n"); + cst816s_init(&dev, &cst816s_params[0], _cb, &pid); + + while (1) { + thread_flags_t flags = thread_flags_wait_any(CST816S_THREAD_FLAG); + if (flags & CST816S_THREAD_FLAG) { + _dump_cst816s(&dev); + } + } + return 0; +}