mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
5cf32002f5
19880: drivers/ft5x06: fix initialization if callback function parameter is NULL r=aabadie a=gschorcht ### Contribution description This PR fixes the `ft5x06` driver initialization if the callback function parameter `cb` is `NULL. This might be the case for example if the application uses the touch device in polling mode. If the interrupt pin is initialized if the callback function parameter `cb` is `NULL`, the driver crashes the first time an interrupt is triggered. Therefore, the INT pin must be initialized only if also the callback function parameter `cb` is not `NULL`. To be able to test the polling mode, this PR also includes a change of the `tests/drivers/ft5x06` application which introduces the environment variables `FT5X06_POLLING_MODE` `FT5X06_POLLING_PERIOD` and in the makefile. ### Testing procedure 1. Use any board with a FTXXXX touch device and test it in polling mode, for example: ``` FT5X06_POLLING_MODE=1 BOARD=stm32f746g-disco make -C tests/drivers/ft5x06 flash term ``` It should work as expected. ``` main(): This is RIOT! (Version: 2023.10-devel-119-g92a44a-drivers/ft5x06_fix_cb_null) FT5x06 test application +------------Initializing------------+ Initialization successful 1 touch detected Touch 1 - X: 236, Y:111 Touch 1 - X: 236, Y:111 ... Touch 1 - X: 236, Y:111 Released! ``` 2. Checkout master branch and cerry-pick commit 691a5e6308426ddc685e5a2c297238529211c258. The test application `tests/drivers/ft5x06` will crash once a touch event occur: ``` +------------Initializing------------+ Initialization successful 1 touch detected Context before hardfault: ``` ### Issues/PRs references Co-authored-by: Gunar Schorcht <gunar@schorcht.net>
200 lines
5.8 KiB
C
200 lines
5.8 KiB
C
/*
|
|
* Copyright (C) 2021 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_ft5x06
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief Device driver implementation for the FT5x06 touch driver
|
|
*
|
|
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
|
|
#include "periph/i2c.h"
|
|
#include "periph/gpio.h"
|
|
|
|
#include "ztimer.h"
|
|
|
|
#include "ft5x06.h"
|
|
#include "ft5x06_internal.h"
|
|
#include "ft5x06_constants.h"
|
|
#include "ft5x06_params.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
#define FT5X06_BUS (dev->params->i2c)
|
|
#define FT5X06_ADDR (dev->params->addr)
|
|
|
|
#define FT5X06_RESET_DELAY_MS (200)
|
|
|
|
int ft5x06_init(ft5x06_t *dev, const ft5x06_params_t *params, ft5x06_event_cb_t cb, void *arg)
|
|
{
|
|
assert(dev);
|
|
assert(params);
|
|
|
|
dev->params = params;
|
|
|
|
/* Wait at least 200ms after power up before accessing registers */
|
|
ztimer_sleep(ZTIMER_MSEC, FT5X06_RESET_DELAY_MS);
|
|
|
|
i2c_acquire(FT5X06_BUS);
|
|
|
|
uint8_t vendor_id = 0;
|
|
if (i2c_read_reg(FT5X06_BUS, FT5X06_ADDR, FT5X06_G_VENDOR_ID_REG, &vendor_id, 0) != 0) {
|
|
i2c_release(FT5X06_BUS);
|
|
return -EPROTO;
|
|
}
|
|
|
|
if (dev->params->type == FT5X06_TYPE_FT6X06 || dev->params->type == FT5X06_TYPE_FT6X36) {
|
|
if ((vendor_id != FT5X06_VENDOR_ID_2) && (vendor_id != FT5X06_VENDOR_ID_3)) {
|
|
DEBUG("[ft5x06] init: invalid vendor ID: '0x%02x' (expected: 0x%02x or 0x%02x)\n",
|
|
vendor_id, FT5X06_VENDOR_ID_2, FT5X06_VENDOR_ID_3);
|
|
i2c_release(FT5X06_BUS);
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
else if (vendor_id != FT5X06_VENDOR_ID_1) {
|
|
DEBUG("[ft5x06] init: invalid vendor ID: '0x%02x' (expected: 0x%02x)\n",
|
|
vendor_id, FT5X06_VENDOR_ID_1);
|
|
i2c_release(FT5X06_BUS);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Auto-calibrate if needed */
|
|
if (dev->params->type == FT5X06_TYPE_FT5606|| dev->params->type == FT5X06_TYPE_FT5X16 ||
|
|
dev->params->type == FT5X06_TYPE_FT5X06I) {
|
|
DEBUG("[ft5x06] init: enable device auto-calibration\n");
|
|
i2c_write_reg(FT5X06_BUS, FT5X06_ADDR, FT5X06_G_AUTO_CLB_MODE_REG, 0, 0);
|
|
}
|
|
|
|
/* Configure interrupt */
|
|
if (gpio_is_valid(dev->params->int_pin) && cb) {
|
|
DEBUG("[ft5x06] init: configuring touchscreen interrupt\n");
|
|
gpio_init_int(dev->params->int_pin, GPIO_IN, GPIO_RISING, cb, arg);
|
|
}
|
|
|
|
i2c_write_reg(FT5X06_BUS, FT5X06_ADDR, FT5X06_G_MODE_REG, FT5X06_G_MODE_INTERRUPT_TRIGGER & 0x01, 0);
|
|
|
|
i2c_release(FT5X06_BUS);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const uint8_t touch_reg_map[FT5X06_TOUCHES_COUNT_MAX] = {
|
|
FT5X06_TOUCH1_XH_REG,
|
|
FT5X06_TOUCH2_XH_REG,
|
|
FT5X06_TOUCH3_XH_REG,
|
|
FT5X06_TOUCH4_XH_REG,
|
|
FT5X06_TOUCH5_XH_REG,
|
|
};
|
|
|
|
int ft5x06_read_touch_positions(const ft5x06_t *dev, ft5x06_touch_position_t *positions, size_t len)
|
|
{
|
|
assert(dev);
|
|
assert(positions);
|
|
|
|
i2c_acquire(FT5X06_BUS);
|
|
for (uint8_t touch = 0; touch < len; touch++) {
|
|
uint8_t regs[4];
|
|
uint16_t pos_x = 0, pos_y = 0;
|
|
i2c_read_regs(FT5X06_BUS, FT5X06_ADDR, touch_reg_map[touch], ®s, 4, 0);
|
|
pos_x = (uint16_t)((regs[1] & FT5X06_TOUCH_POS_LSB_MASK) | (uint16_t)(regs[0] & FT5X06_TOUCH_POS_MSB_MASK) << 8);
|
|
pos_y = (uint16_t)((regs[3] & FT5X06_TOUCH_POS_LSB_MASK) | (uint16_t)(regs[2] & FT5X06_TOUCH_POS_MSB_MASK) << 8);
|
|
|
|
if (dev->params->xyconv & FT5X06_SWAP_XY) {
|
|
positions[touch].x = pos_y;
|
|
positions[touch].y = pos_x;
|
|
}
|
|
else {
|
|
positions[touch].x = pos_x;
|
|
positions[touch].y = pos_y;
|
|
}
|
|
|
|
if (dev->params->xyconv & FT5X06_MIRROR_X) {
|
|
/* X position is mirrored */
|
|
assert(positions[touch].x <= dev->params->xmax);
|
|
positions[touch].x = dev->params->xmax - positions[touch].x;
|
|
}
|
|
|
|
if (dev->params->xyconv & FT5X06_MIRROR_Y) {
|
|
/* Y position is mirrored */
|
|
assert(positions[touch].y <= dev->params->ymax);
|
|
positions[touch].y = dev->params->ymax - positions[touch].y;
|
|
}
|
|
|
|
DEBUG("[ft5x06] read position X=%u y=%u'\n",
|
|
positions[touch].x, positions[touch].y);
|
|
}
|
|
i2c_release(FT5X06_BUS);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ft5x06_read_touch_count(const ft5x06_t *dev, uint8_t *count)
|
|
{
|
|
assert(dev);
|
|
assert(count);
|
|
|
|
i2c_acquire(FT5X06_BUS);
|
|
i2c_read_reg(FT5X06_BUS, FT5X06_ADDR, FT5X06_TD_STATUS_REG, count, 0);
|
|
i2c_release(FT5X06_BUS);
|
|
*count &= FT5X06_TD_STATUS_MASK;
|
|
|
|
if (*count > ft5x06_get_touches_count_max(dev)) {
|
|
*count = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ft5x06_read_touch_gesture(const ft5x06_t *dev, ft5x06_touch_gesture_t *gesture)
|
|
{
|
|
assert(dev);
|
|
assert(gesture);
|
|
|
|
uint8_t gesture_id = 0;
|
|
i2c_acquire(FT5X06_BUS);
|
|
i2c_read_reg(FT5X06_BUS, FT5X06_ADDR, FT5X06_GESTURE_ID_REG, &gesture_id, 0);
|
|
i2c_release(FT5X06_BUS);
|
|
|
|
DEBUG("[ft5x06] read gesture_id '0x%02X'\n", gesture_id);
|
|
|
|
switch (gesture_id) {
|
|
case FT5X06_GESTURE_ID_MOVE_UP:
|
|
*gesture = FT5X06_TOUCH_MOVE_UP;
|
|
break;
|
|
case FT5X06_GESTURE_ID_MOVE_LEFT:
|
|
*gesture = FT5X06_TOUCH_MOVE_LEFT;
|
|
break;
|
|
case FT5X06_GESTURE_ID_MOVE_DOWN:
|
|
*gesture = FT5X06_TOUCH_MOVE_DOWN;
|
|
break;
|
|
case FT5X06_GESTURE_ID_MOVE_RIGHT:
|
|
*gesture = FT5X06_TOUCH_MOVE_RIGHT;
|
|
break;
|
|
case FT5X06_GESTURE_ID_ZOOM_IN:
|
|
*gesture = FT5X06_TOUCH_ZOOM_IN;
|
|
break;
|
|
case FT5X06_GESTURE_ID_ZOOM_OUT:
|
|
*gesture = FT5X06_TOUCH_ZOOM_OUT;
|
|
break;
|
|
default: /* Fallback to None */
|
|
*gesture = FT5X06_TOUCH_NO_GESTURE;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|