1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/ft5x06/ft5x06.c
bors[bot] 5cf32002f5
Merge #19880
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>
2023-08-29 08:19:52 +00:00

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], &regs, 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;
}