drivers: sensor: paj7620: added driver
Added driver for the PAJ7620 gesture sensor. For now, just added basic gesture mode, although sensor also has other modes (proximity and cursor modes). Signed-off-by: Paul Timke Contreras <ptimkec@live.com>
This commit is contained in:
parent
bb849bd3a4
commit
503f6388e4
10 changed files with 752 additions and 0 deletions
|
@ -1,6 +1,10 @@
|
|||
# Copyright (c) 2025 Croxel Inc.
|
||||
# Copyright (c) 2025 CogniPilot Foundation
|
||||
# Copyright (c) 2025 Paul Timke <ptimkec@live.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# zephyr-keep-sorted-start
|
||||
add_subdirectory_ifdef(CONFIG_PAA3905 paa3905)
|
||||
add_subdirectory_ifdef(CONFIG_PAJ7620 paj7620)
|
||||
add_subdirectory_ifdef(CONFIG_PAT9136 pat9136)
|
||||
# zephyr-keep-sorted-stop
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
# Copyright (c) 2025 Croxel Inc.
|
||||
# Copyright (c) 2025 CogniPilot Foundation
|
||||
# Copyright (c) 2025 Paul Timke <ptimkec@live.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# zephyr-keep-sorted-start
|
||||
source "drivers/sensor/pixart/paa3905/Kconfig"
|
||||
source "drivers/sensor/pixart/paj7620/Kconfig"
|
||||
source "drivers/sensor/pixart/pat9136/Kconfig"
|
||||
# zephyr-keep-sorted-stop
|
||||
|
|
7
drivers/sensor/pixart/paj7620/CMakeLists.txt
Normal file
7
drivers/sensor/pixart/paj7620/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Copyright (c) 2025 Paul Timke <ptimkec@live.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources(paj7620.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_PAJ7620_TRIGGER paj7620_trigger.c)
|
21
drivers/sensor/pixart/paj7620/Kconfig
Normal file
21
drivers/sensor/pixart/paj7620/Kconfig
Normal file
|
@ -0,0 +1,21 @@
|
|||
# PAJ7620 gesture sensor configuration options
|
||||
|
||||
# Copyright (c) 2025 Paul Timke <ptimkec@live.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig PAJ7620
|
||||
bool "PAJ7620 gesture sensor driver"
|
||||
default y
|
||||
depends on DT_HAS_PIXART_PAJ7620_ENABLED
|
||||
select I2C
|
||||
help
|
||||
Enable driver for the PAJ7620 gesture sensor
|
||||
|
||||
if PAJ7620
|
||||
|
||||
module = PAJ7620
|
||||
thread_priority = 10
|
||||
thread_stack_size = 1024
|
||||
source "drivers/sensor/Kconfig.trigger_template"
|
||||
|
||||
endif # PAJ7620
|
295
drivers/sensor/pixart/paj7620/paj7620.c
Normal file
295
drivers/sensor/pixart/paj7620/paj7620.c
Normal file
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Paul Timke <ptimkec@live.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT pixart_paj7620
|
||||
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/drivers/sensor/paj7620.h>
|
||||
|
||||
#include "paj7620.h"
|
||||
#include "paj7620_reg.h"
|
||||
|
||||
LOG_MODULE_REGISTER(paj7620, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static int paj7620_select_register_bank(const struct device *dev, enum paj7620_mem_bank bank)
|
||||
{
|
||||
int ret;
|
||||
uint8_t bank_selection;
|
||||
const struct paj7620_config *config = dev->config;
|
||||
|
||||
switch (bank) {
|
||||
case PAJ7620_MEMBANK_0:
|
||||
bank_selection = PAJ7620_VAL_BANK_SEL_BANK_0;
|
||||
break;
|
||||
case PAJ7620_MEMBANK_1:
|
||||
bank_selection = PAJ7620_VAL_BANK_SEL_BANK_1;
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Nonexistent memory bank %d", (int)bank);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = i2c_reg_write_byte_dt(&config->i2c, PAJ7620_REG_BANK_SEL, bank_selection);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to change memory bank");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int paj7620_get_hw_id(const struct device *dev, uint16_t *result)
|
||||
{
|
||||
uint8_t ret;
|
||||
uint8_t hw_id[2];
|
||||
const struct paj7620_config *config = dev->config;
|
||||
|
||||
/* Part ID is stored in bank 0 */
|
||||
ret = paj7620_select_register_bank(dev, PAJ7620_MEMBANK_0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_reg_read_byte_dt(&config->i2c, PAJ7620_REG_PART_ID_LSB, &hw_id[0]);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to read hardware ID");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_reg_read_byte_dt(&config->i2c, PAJ7620_REG_PART_ID_MSB, &hw_id[1]);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to read hardware ID");
|
||||
return ret;
|
||||
}
|
||||
|
||||
*result = sys_get_le16(hw_id);
|
||||
LOG_DBG("Obtained hardware ID 0x%04x", *result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int paj7620_write_initial_reg_settings(const struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
uint8_t reg_addr;
|
||||
uint8_t value;
|
||||
const struct paj7620_config *config = dev->config;
|
||||
|
||||
/**
|
||||
* Initializes registers with default values according to section 8.1
|
||||
* from Datasheet v1.5:
|
||||
* https://files.seeedstudio.com/wiki/Grove_Gesture_V_1.0/res/PAJ7620U2_DS_v1.5_05012022_Confidential.pdf
|
||||
*/
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(initial_register_array); i++) {
|
||||
reg_addr = initial_register_array[i][0];
|
||||
value = initial_register_array[i][1];
|
||||
|
||||
ret = i2c_reg_write_byte_dt(&config->i2c, reg_addr, value);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int paj7620_set_sampling_rate(const struct device *dev, const struct sensor_value *val)
|
||||
{
|
||||
int ret;
|
||||
int fps;
|
||||
const struct paj7620_config *config = dev->config;
|
||||
int64_t uval = val->val1 * 1000000 + val->val2;
|
||||
|
||||
if (uval <= 120000000) {
|
||||
fps = PAJ7620_NORMAL_SPEED;
|
||||
} else if (uval <= 240000000) {
|
||||
fps = PAJ7620_GAME_SPEED;
|
||||
} else {
|
||||
LOG_ERR("Unsupported sample rate");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
ret = paj7620_select_register_bank(dev, PAJ7620_MEMBANK_1);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_reg_write_byte_dt(&config->i2c, PAJ7620_REG_R_IDLE_TIME_LSB, fps);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to set sample rate");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = paj7620_select_register_bank(dev, PAJ7620_MEMBANK_0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
LOG_DBG("Sample rate set to %s mode", fps == PAJ7620_GAME_SPEED ? "game" : "normal");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int paj7620_sample_fetch(const struct device *dev, enum sensor_channel chan)
|
||||
{
|
||||
int ret;
|
||||
struct paj7620_data *data = dev->data;
|
||||
const struct paj7620_config *config = dev->config;
|
||||
uint8_t gest_data[2];
|
||||
|
||||
if (chan != SENSOR_CHAN_ALL
|
||||
&& ((enum sensor_channel_paj7620)chan != SENSOR_CHAN_PAJ7620_GESTURES)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* We read from REG_INT_FLAG_1 and REG_INT_FLAG_2 even on polling mode
|
||||
* (without using interrupts) because that's where the gesture
|
||||
* detection flags are set.
|
||||
* NOTE: A set bit means that the corresponding gesture has been detected
|
||||
*/
|
||||
|
||||
ret = i2c_burst_read_dt(&config->i2c, PAJ7620_REG_INT_FLAG_1, gest_data, sizeof(gest_data));
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to read gesture data");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->gesture_flags = sys_get_le16(gest_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int paj7620_channel_get(const struct device *dev,
|
||||
enum sensor_channel chan,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
struct paj7620_data *data = dev->data;
|
||||
|
||||
switch ((uint32_t)chan) {
|
||||
case SENSOR_CHAN_PAJ7620_GESTURES:
|
||||
val->val1 = data->gesture_flags;
|
||||
val->val2 = 0;
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Unsupported sensor channel");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int paj7620_attr_set(const struct device *dev,
|
||||
enum sensor_channel chan,
|
||||
enum sensor_attribute attr,
|
||||
const struct sensor_value *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (chan != SENSOR_CHAN_ALL
|
||||
&& ((enum sensor_channel_paj7620)chan != SENSOR_CHAN_PAJ7620_GESTURES)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
switch ((uint32_t)attr) {
|
||||
case SENSOR_ATTR_SAMPLING_FREQUENCY:
|
||||
ret = paj7620_set_sampling_rate(dev, val);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int paj7620_init(const struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
uint16_t hw_id;
|
||||
const struct paj7620_config *config = dev->config;
|
||||
|
||||
if (!i2c_is_ready_dt(&config->i2c)) {
|
||||
LOG_ERR("I2C bus device not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/**
|
||||
* According to the datasheet section 8.1, we must wait this amount
|
||||
* of time for sensor to stabilize after power up
|
||||
*/
|
||||
k_usleep(PAJ7620_POWERUP_STABILIZATION_TIME_US);
|
||||
|
||||
/**
|
||||
* Make a write to the sensor to wake it up. After waking it, the
|
||||
* the sensor still needs some time to be ready to listen. Without it,
|
||||
* it may NACK subsequent transactions.
|
||||
*/
|
||||
(void)paj7620_select_register_bank(dev, PAJ7620_MEMBANK_0);
|
||||
k_usleep(PAJ7620_WAKEUP_TIME_US);
|
||||
|
||||
/** Verify this is not some other sensor with the same address */
|
||||
ret = paj7620_get_hw_id(dev, &hw_id);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (hw_id != PAJ7620_PART_ID) {
|
||||
LOG_ERR("Hardware ID 0x%04x does not match for PAJ7620", hw_id);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/** Initialize settings (it defaults to gesture mode) */
|
||||
ret = paj7620_write_initial_reg_settings(dev);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to initialize device registers");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_PAJ7620_TRIGGER)) {
|
||||
ret = paj7620_trigger_init(dev);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to enable interrupts");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEVICE_API(sensor, paj7620_driver_api) = {
|
||||
.sample_fetch = paj7620_sample_fetch,
|
||||
.channel_get = paj7620_channel_get,
|
||||
.attr_set = paj7620_attr_set,
|
||||
#if CONFIG_PAJ7620_TRIGGER
|
||||
.trigger_set = paj7620_trigger_set,
|
||||
#endif
|
||||
};
|
||||
|
||||
#define PAJ7620_INIT(n) \
|
||||
static const struct paj7620_config paj7620_config_##n = { \
|
||||
.i2c = I2C_DT_SPEC_INST_GET(n), \
|
||||
IF_ENABLED(CONFIG_PAJ7620_TRIGGER, \
|
||||
(.int_gpio = GPIO_DT_SPEC_INST_GET_OR(n, int_gpios, {0}))) \
|
||||
}; \
|
||||
\
|
||||
static struct paj7620_data paj7620_data_##n; \
|
||||
\
|
||||
SENSOR_DEVICE_DT_INST_DEFINE(n, \
|
||||
paj7620_init, \
|
||||
NULL, \
|
||||
&paj7620_data_##n, \
|
||||
&paj7620_config_##n, \
|
||||
POST_KERNEL, \
|
||||
CONFIG_SENSOR_INIT_PRIORITY, \
|
||||
&paj7620_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(PAJ7620_INIT);
|
69
drivers/sensor/pixart/paj7620/paj7620.h
Normal file
69
drivers/sensor/pixart/paj7620/paj7620.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Paul Timke <ptimkec@live.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_PAJ7620_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_PAJ7620_H_
|
||||
|
||||
#include <zephyr/drivers/sensor/paj7620.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
|
||||
/** Sensor hardcoded Part ID */
|
||||
#define PAJ7620_PART_ID ((PAJ7620_VAL_PART_ID_MSB << 8) | (PAJ7620_VAL_PART_ID_LSB & 0x00FF))
|
||||
|
||||
/** Sensor FPS values */
|
||||
#define PAJ7620_NORMAL_SPEED 0xAC /* Normal speed 120 fps */
|
||||
#define PAJ7620_GAME_SPEED 0x30 /* Game mode speed 240 fps */
|
||||
|
||||
/** Sensor stabilization time microseconds (us) */
|
||||
#define PAJ7620_POWERUP_STABILIZATION_TIME_US 700
|
||||
|
||||
/** Sensor stabilization time after I2C wakeup (us).
|
||||
* PAJ7620 still needs some time to wake up after waking it
|
||||
* with an I2C write. This value was obtained experimentally
|
||||
*/
|
||||
#define PAJ7620_WAKEUP_TIME_US 200
|
||||
|
||||
enum paj7620_mem_bank {
|
||||
PAJ7620_MEMBANK_0 = 0,
|
||||
PAJ7620_MEMBANK_1,
|
||||
};
|
||||
|
||||
struct paj7620_config {
|
||||
const struct i2c_dt_spec i2c;
|
||||
#if CONFIG_PAJ7620_TRIGGER
|
||||
struct gpio_dt_spec int_gpio;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct paj7620_data {
|
||||
struct k_sem sem;
|
||||
uint16_t gesture_flags;
|
||||
|
||||
#ifdef CONFIG_PAJ7620_TRIGGER
|
||||
const struct device *dev;
|
||||
struct gpio_callback gpio_cb;
|
||||
sensor_trigger_handler_t motion_handler;
|
||||
const struct sensor_trigger *motion_trig;
|
||||
#endif
|
||||
#ifdef CONFIG_PAJ7620_TRIGGER_OWN_THREAD
|
||||
K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_PAJ7620_THREAD_STACK_SIZE);
|
||||
struct k_thread thread;
|
||||
struct k_sem trig_sem;
|
||||
#endif
|
||||
#ifdef CONFIG_PAJ7620_TRIGGER_GLOBAL_THREAD
|
||||
struct k_work work;
|
||||
#endif
|
||||
};
|
||||
|
||||
int paj7620_trigger_init(const struct device *dev);
|
||||
int paj7620_trigger_set(const struct device *dev,
|
||||
const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler);
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_SENSOR_PAJ7620_H_ */
|
151
drivers/sensor/pixart/paj7620/paj7620_reg.h
Normal file
151
drivers/sensor/pixart/paj7620/paj7620_reg.h
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Paul Timke <ptimkec@live.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_PAJ7620_REG_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_PAJ7620_REG_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief PAJ7620 Register addresses and values. Not all registers from the
|
||||
* data sheet are listed here, only the ones directly used by the driver.
|
||||
* For more information about the registers, refer to:
|
||||
* https://files.seeedstudio.com/wiki/Grove_Gesture_V_1.0/res/PAJ7620U2_DS_v1.5_05012022_Confidential.pdf
|
||||
*/
|
||||
|
||||
/**
|
||||
* BANK 0 REGISTER ADDRESSES
|
||||
*/
|
||||
|
||||
/* Chip / Version ID */
|
||||
#define PAJ7620_REG_PART_ID_LSB 0x00
|
||||
#define PAJ7620_REG_PART_ID_MSB 0x01
|
||||
|
||||
/** Register bank select */
|
||||
#define PAJ7620_REG_BANK_SEL 0xEF
|
||||
|
||||
/** Interrupt Controls */
|
||||
/* Note: If the corresponding bit is 1, the corresponding interrupt is enabled */
|
||||
#define PAJ7620_REG_MCU_INT_CTRL 0x40 /* Configure auto clean and active high/low */
|
||||
#define PAJ7620_REG_INT_1_EN 0x41 /* Enable int 1 interrupts */
|
||||
#define PAJ7620_REG_INT_2_EN 0x42 /* Enable int 2 interrupts */
|
||||
#define PAJ7620_REG_INT_FLAG_1 0x43 /* Gesture detection results */
|
||||
#define PAJ7620_REG_INT_FLAG_2 0x44 /* Gesture detection results (wave and others) */
|
||||
|
||||
/**
|
||||
* BANK 0 REGISTER VALUES AND MASKS
|
||||
*/
|
||||
#define PAJ7620_VAL_PART_ID_LSB 0x20
|
||||
#define PAJ7620_VAL_PART_ID_MSB 0x76
|
||||
|
||||
/** Register bank select values */
|
||||
#define PAJ7620_VAL_BANK_SEL_BANK_0 0x00
|
||||
#define PAJ7620_VAL_BANK_SEL_BANK_1 0x01
|
||||
|
||||
/** Interrupt controls masks and values */
|
||||
#define PAJ7620_MASK_MCU_INT_FLAG_GCLR BIT(1)
|
||||
#define PAJ7620_VAL_MCU_INT_FLAG_AUTO_CLEAN_DISABLE 0x00
|
||||
#define PAJ7620_VAL_MCU_INT_FLAG_AUTO_CLEAN_ENABLE 0x01
|
||||
|
||||
#define PAJ7620_MASK_MCU_INT_FLAG_INV BIT(4)
|
||||
#define PAJ7620_VAL_MCU_INT_FLAG_PIN_ACTIVE_LOW 0x00
|
||||
#define PAJ7620_VAL_MCU_INT_FLAG_PIN_ACTIVE_HIGH 0x01
|
||||
|
||||
#define PAJ7620_MASK_ALL_GESTURE_INTS_ENABLE 0xFF
|
||||
#define PAJ7620_MASK_ALL_GESTURE_INTS_DISABLE 0x00
|
||||
|
||||
/**
|
||||
* BANK 1 REGISTER ADDRESSES
|
||||
*/
|
||||
#define PAJ7620_REG_R_IDLE_TIME_LSB 0x65
|
||||
#define PAJ7620_REG_R_IDLE_TIME_MSB 0x66
|
||||
|
||||
/**
|
||||
* INITIALIZATION ARRAYS
|
||||
* The following 'initial_register_array' is taken 'as is' from Section 8
|
||||
* (Firmware Guides) of the PAJ7620 datasheet v1.5.
|
||||
* It encodes pairs of register addresses and values for those registers
|
||||
* needed to initialize the sensor or change its operation mode
|
||||
*
|
||||
* Reference:
|
||||
* https://files.seeedstudio.com/wiki/Grove_Gesture_V_1.0/res/PAJ7620U2_DS_v1.5_05012022_Confidential.pdf
|
||||
*/
|
||||
|
||||
static const uint8_t initial_register_array[][2] = {
|
||||
{0xEF, 0x00}, /* Select memory bank 0 */
|
||||
{0x41, 0xFF}, /* Enable gesture interrupts */
|
||||
{0x42, 0x01},
|
||||
{0x46, 0x2D},
|
||||
{0x47, 0x0F},
|
||||
{0x48, 0x80},
|
||||
{0x49, 0x00},
|
||||
{0x4A, 0x40},
|
||||
{0x4B, 0x00},
|
||||
{0x4C, 0x20},
|
||||
{0x4D, 0x00},
|
||||
{0x51, 0x10},
|
||||
{0x5C, 0x02},
|
||||
{0x5E, 0x10},
|
||||
{0x80, 0x41},
|
||||
{0x81, 0x44},
|
||||
{0x82, 0x0C},
|
||||
{0x83, 0x20},
|
||||
{0x84, 0x20},
|
||||
{0x85, 0x00},
|
||||
{0x86, 0x10},
|
||||
{0x87, 0x00},
|
||||
{0x8B, 0x01},
|
||||
{0x8D, 0x00},
|
||||
{0x90, 0x0C},
|
||||
{0x91, 0x0C},
|
||||
{0x93, 0x0D},
|
||||
{0x94, 0x0A},
|
||||
{0x95, 0x0A},
|
||||
{0x96, 0x0C},
|
||||
{0x97, 0x05},
|
||||
{0x9A, 0x14},
|
||||
{0x9C, 0x3F},
|
||||
{0x9F, 0xF9},
|
||||
{0xA0, 0x48},
|
||||
{0xA5, 0x19},
|
||||
{0xCC, 0x19},
|
||||
{0xCD, 0x0B},
|
||||
{0xCE, 0x13},
|
||||
{0xCF, 0x62},
|
||||
{0xD0, 0x21},
|
||||
{0xEF, 0x01}, /* Select memory bank 1 */
|
||||
{0x00, 0x1E},
|
||||
{0x01, 0x1E},
|
||||
{0x02, 0x0F},
|
||||
{0x03, 0x0F},
|
||||
{0x04, 0x02},
|
||||
{0x25, 0x01},
|
||||
{0x26, 0x00},
|
||||
{0x27, 0x39},
|
||||
{0x28, 0x7F},
|
||||
{0x29, 0x08},
|
||||
{0x30, 0x03},
|
||||
{0x3E, 0xFF},
|
||||
{0x5E, 0x3D},
|
||||
{0x65, 0xAC}, /* Set fps to 'normal' mode */
|
||||
{0x66, 0x00},
|
||||
{0x67, 0x97},
|
||||
{0x68, 0x01},
|
||||
{0x69, 0xCD},
|
||||
{0x6A, 0x01},
|
||||
{0x6B, 0xB0},
|
||||
{0x6C, 0x04},
|
||||
{0x6D, 0x2C},
|
||||
{0x6E, 0x01},
|
||||
{0x72, 0x01},
|
||||
{0x73, 0x35},
|
||||
{0x74, 0x00}, /* Set to gesture mode */
|
||||
{0x77, 0x01},
|
||||
{0xEF, 0x00}, /* Reselect memory bank 0 */
|
||||
};
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_SENSOR_PAJ7620_REG_H_ */
|
137
drivers/sensor/pixart/paj7620/paj7620_trigger.c
Normal file
137
drivers/sensor/pixart/paj7620/paj7620_trigger.c
Normal file
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Paul Timke <ptimkec@live.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT pixart_paj7620
|
||||
|
||||
#include "paj7620.h"
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
|
||||
LOG_MODULE_REGISTER(paj7620, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static void paj7620_gpio_callback(const struct device *dev,
|
||||
struct gpio_callback *cb,
|
||||
uint32_t pin_mask)
|
||||
{
|
||||
struct paj7620_data *data =
|
||||
CONTAINER_OF(cb, struct paj7620_data, gpio_cb);
|
||||
const struct paj7620_config *config = data->dev->config;
|
||||
|
||||
if ((pin_mask & BIT(config->int_gpio.pin)) == 0U) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PAJ7620_TRIGGER_OWN_THREAD
|
||||
k_sem_give(&data->trig_sem);
|
||||
#elif CONFIG_PAJ7620_TRIGGER_GLOBAL_THREAD
|
||||
k_work_submit(&data->work);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void paj7620_handle_int(const struct device *dev)
|
||||
{
|
||||
struct paj7620_data *data = dev->data;
|
||||
|
||||
if (data->motion_handler) {
|
||||
data->motion_handler(dev, data->motion_trig);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PAJ7620_TRIGGER_OWN_THREAD
|
||||
static void paj7620_thread_main(void *p1, void *p2, void *p3)
|
||||
{
|
||||
ARG_UNUSED(p1);
|
||||
ARG_UNUSED(p2);
|
||||
|
||||
struct paj7620_data *data = p1;
|
||||
|
||||
while (1) {
|
||||
k_sem_take(&data->trig_sem, K_FOREVER);
|
||||
paj7620_handle_int(data->dev);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PAJ7620_TRIGGER_GLOBAL_THREAD
|
||||
static void paj7620_work_handler(struct k_work *work)
|
||||
{
|
||||
struct paj7620_data *data =
|
||||
CONTAINER_OF(work, struct paj7620_data, work);
|
||||
|
||||
paj7620_handle_int(data->dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
int paj7620_trigger_set(const struct device *dev,
|
||||
const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler)
|
||||
{
|
||||
struct paj7620_data *data = dev->data;
|
||||
const struct paj7620_config *cfg = dev->config;
|
||||
|
||||
if (cfg->int_gpio.port == NULL) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (trig->type != SENSOR_TRIG_MOTION) {
|
||||
LOG_ERR("Unsupported sensor trigger");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
data->motion_handler = handler;
|
||||
data->motion_trig = trig;
|
||||
|
||||
if (handler == NULL) {
|
||||
return gpio_pin_interrupt_configure_dt(&cfg->int_gpio, GPIO_INT_DISABLE);
|
||||
}
|
||||
|
||||
return gpio_pin_interrupt_configure_dt(&cfg->int_gpio, GPIO_INT_EDGE_FALLING);
|
||||
}
|
||||
|
||||
int paj7620_trigger_init(const struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
const struct paj7620_config *config = dev->config;
|
||||
struct paj7620_data *data = dev->data;
|
||||
|
||||
data->dev = dev;
|
||||
|
||||
#ifdef CONFIG_PAJ7620_TRIGGER_OWN_THREAD
|
||||
k_sem_init(&data->trig_sem, 0, K_SEM_MAX_LIMIT);
|
||||
k_thread_create(&data->thread,
|
||||
data->thread_stack,
|
||||
K_KERNEL_STACK_SIZEOF(data->thread_stack),
|
||||
paj7620_thread_main,
|
||||
data,
|
||||
NULL,
|
||||
NULL,
|
||||
K_PRIO_COOP(CONFIG_PAJ7620_THREAD_PRIORITY),
|
||||
0,
|
||||
K_NO_WAIT);
|
||||
#elif CONFIG_PAJ7620_TRIGGER_GLOBAL_THREAD
|
||||
data->work.handler = paj7620_work_handler;
|
||||
#endif
|
||||
|
||||
/* Configure GPIO */
|
||||
if (!gpio_is_ready_dt(&config->int_gpio)) {
|
||||
LOG_ERR("GPIO device not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
gpio_init_callback(&data->gpio_cb, paj7620_gpio_callback, BIT(config->int_gpio.pin));
|
||||
|
||||
ret = gpio_add_callback(config->int_gpio.port, &data->gpio_cb);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
17
dts/bindings/sensor/pixart,paj7620.yaml
Normal file
17
dts/bindings/sensor/pixart,paj7620.yaml
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Copyright (c) 2025 Paul Timke <ptimkec@live.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Pixart PAJ7620 gesture sensor
|
||||
|
||||
compatible: "pixart,paj7620"
|
||||
|
||||
include: [sensor-device.yaml, i2c-device.yaml]
|
||||
|
||||
properties:
|
||||
int-gpios:
|
||||
type: phandle-array
|
||||
description: |
|
||||
INT pin
|
||||
This pin defaults to active low when produced by the sensor.
|
||||
The property value should ensure the flags properly describe
|
||||
the signal that is presented to the driver.
|
49
include/zephyr/drivers/sensor/paj7620.h
Normal file
49
include/zephyr/drivers/sensor/paj7620.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Paul Timke <ptimkec@live.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Extended Public API for PAJ7620 sensor
|
||||
*
|
||||
* Some capabilities of the sensor cannot be expressed
|
||||
* within the sensor driver abstraction
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_PAJ7620_H_
|
||||
#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_PAJ7620_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
|
||||
#define PAJ7620_FLAG_GES_UP BIT(0)
|
||||
#define PAJ7620_FLAG_GES_DOWN BIT(1)
|
||||
#define PAJ7620_FLAG_GES_LEFT BIT(2)
|
||||
#define PAJ7620_FLAG_GES_RIGHT BIT(3)
|
||||
#define PAJ7620_FLAG_GES_FORWARD BIT(4)
|
||||
#define PAJ7620_FLAG_GES_BACKWARD BIT(5)
|
||||
#define PAJ7620_FLAG_GES_CLOCKWISE BIT(6)
|
||||
#define PAJ7620_FLAG_GES_COUNTERCLOCKWISE BIT(7)
|
||||
#define PAJ7620_FLAG_GES_WAVE BIT(8)
|
||||
|
||||
enum sensor_channel_paj7620 {
|
||||
/**
|
||||
* This channel will contain gesture data as a bitmask where each
|
||||
* set bit represents a detected gesture. The possible gestures
|
||||
* that can be detected along with their corresponding bit are given
|
||||
* by the PAJ7620_FLAG_GES_X macros
|
||||
*/
|
||||
SENSOR_CHAN_PAJ7620_GESTURES = SENSOR_CHAN_PRIV_START
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_PAJ7620_H_ */
|
Loading…
Add table
Add a link
Reference in a new issue