Skip to content

Commit 503f638

Browse files
paultimkekartben
authored andcommitted
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 <[email protected]>
1 parent bb849bd commit 503f638

File tree

10 files changed

+752
-0
lines changed

10 files changed

+752
-0
lines changed

drivers/sensor/pixart/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Copyright (c) 2025 Croxel Inc.
22
# Copyright (c) 2025 CogniPilot Foundation
3+
# Copyright (c) 2025 Paul Timke <[email protected]>
34
# SPDX-License-Identifier: Apache-2.0
45

6+
# zephyr-keep-sorted-start
57
add_subdirectory_ifdef(CONFIG_PAA3905 paa3905)
8+
add_subdirectory_ifdef(CONFIG_PAJ7620 paj7620)
69
add_subdirectory_ifdef(CONFIG_PAT9136 pat9136)
10+
# zephyr-keep-sorted-stop

drivers/sensor/pixart/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# Copyright (c) 2025 Croxel Inc.
22
# Copyright (c) 2025 CogniPilot Foundation
3+
# Copyright (c) 2025 Paul Timke <[email protected]>
34
# SPDX-License-Identifier: Apache-2.0
45

56
# zephyr-keep-sorted-start
67
source "drivers/sensor/pixart/paa3905/Kconfig"
8+
source "drivers/sensor/pixart/paj7620/Kconfig"
79
source "drivers/sensor/pixart/pat9136/Kconfig"
810
# zephyr-keep-sorted-stop
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright (c) 2025 Paul Timke <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
zephyr_library()
5+
6+
zephyr_library_sources(paj7620.c)
7+
zephyr_library_sources_ifdef(CONFIG_PAJ7620_TRIGGER paj7620_trigger.c)

drivers/sensor/pixart/paj7620/Kconfig

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# PAJ7620 gesture sensor configuration options
2+
3+
# Copyright (c) 2025 Paul Timke <[email protected]>
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
menuconfig PAJ7620
7+
bool "PAJ7620 gesture sensor driver"
8+
default y
9+
depends on DT_HAS_PIXART_PAJ7620_ENABLED
10+
select I2C
11+
help
12+
Enable driver for the PAJ7620 gesture sensor
13+
14+
if PAJ7620
15+
16+
module = PAJ7620
17+
thread_priority = 10
18+
thread_stack_size = 1024
19+
source "drivers/sensor/Kconfig.trigger_template"
20+
21+
endif # PAJ7620
Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
/*
2+
* Copyright (c) 2025 Paul Timke <[email protected]>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT pixart_paj7620
8+
9+
#include <zephyr/drivers/i2c.h>
10+
#include <zephyr/devicetree.h>
11+
#include <zephyr/drivers/sensor.h>
12+
#include <zephyr/sys/byteorder.h>
13+
#include <zephyr/sys/util.h>
14+
#include <zephyr/logging/log.h>
15+
#include <zephyr/drivers/sensor/paj7620.h>
16+
17+
#include "paj7620.h"
18+
#include "paj7620_reg.h"
19+
20+
LOG_MODULE_REGISTER(paj7620, CONFIG_SENSOR_LOG_LEVEL);
21+
22+
static int paj7620_select_register_bank(const struct device *dev, enum paj7620_mem_bank bank)
23+
{
24+
int ret;
25+
uint8_t bank_selection;
26+
const struct paj7620_config *config = dev->config;
27+
28+
switch (bank) {
29+
case PAJ7620_MEMBANK_0:
30+
bank_selection = PAJ7620_VAL_BANK_SEL_BANK_0;
31+
break;
32+
case PAJ7620_MEMBANK_1:
33+
bank_selection = PAJ7620_VAL_BANK_SEL_BANK_1;
34+
break;
35+
default:
36+
LOG_ERR("Nonexistent memory bank %d", (int)bank);
37+
return -EINVAL;
38+
}
39+
40+
ret = i2c_reg_write_byte_dt(&config->i2c, PAJ7620_REG_BANK_SEL, bank_selection);
41+
if (ret < 0) {
42+
LOG_ERR("Failed to change memory bank");
43+
return ret;
44+
}
45+
46+
return 0;
47+
}
48+
49+
static int paj7620_get_hw_id(const struct device *dev, uint16_t *result)
50+
{
51+
uint8_t ret;
52+
uint8_t hw_id[2];
53+
const struct paj7620_config *config = dev->config;
54+
55+
/* Part ID is stored in bank 0 */
56+
ret = paj7620_select_register_bank(dev, PAJ7620_MEMBANK_0);
57+
if (ret < 0) {
58+
return ret;
59+
}
60+
61+
ret = i2c_reg_read_byte_dt(&config->i2c, PAJ7620_REG_PART_ID_LSB, &hw_id[0]);
62+
if (ret < 0) {
63+
LOG_ERR("Failed to read hardware ID");
64+
return ret;
65+
}
66+
67+
ret = i2c_reg_read_byte_dt(&config->i2c, PAJ7620_REG_PART_ID_MSB, &hw_id[1]);
68+
if (ret < 0) {
69+
LOG_ERR("Failed to read hardware ID");
70+
return ret;
71+
}
72+
73+
*result = sys_get_le16(hw_id);
74+
LOG_DBG("Obtained hardware ID 0x%04x", *result);
75+
76+
return 0;
77+
}
78+
79+
static int paj7620_write_initial_reg_settings(const struct device *dev)
80+
{
81+
int ret;
82+
uint8_t reg_addr;
83+
uint8_t value;
84+
const struct paj7620_config *config = dev->config;
85+
86+
/**
87+
* Initializes registers with default values according to section 8.1
88+
* from Datasheet v1.5:
89+
* https://files.seeedstudio.com/wiki/Grove_Gesture_V_1.0/res/PAJ7620U2_DS_v1.5_05012022_Confidential.pdf
90+
*/
91+
92+
for (int i = 0; i < ARRAY_SIZE(initial_register_array); i++) {
93+
reg_addr = initial_register_array[i][0];
94+
value = initial_register_array[i][1];
95+
96+
ret = i2c_reg_write_byte_dt(&config->i2c, reg_addr, value);
97+
if (ret < 0) {
98+
return ret;
99+
}
100+
}
101+
102+
return ret;
103+
}
104+
105+
static int paj7620_set_sampling_rate(const struct device *dev, const struct sensor_value *val)
106+
{
107+
int ret;
108+
int fps;
109+
const struct paj7620_config *config = dev->config;
110+
int64_t uval = val->val1 * 1000000 + val->val2;
111+
112+
if (uval <= 120000000) {
113+
fps = PAJ7620_NORMAL_SPEED;
114+
} else if (uval <= 240000000) {
115+
fps = PAJ7620_GAME_SPEED;
116+
} else {
117+
LOG_ERR("Unsupported sample rate");
118+
return -ENOTSUP;
119+
}
120+
121+
ret = paj7620_select_register_bank(dev, PAJ7620_MEMBANK_1);
122+
if (ret < 0) {
123+
return ret;
124+
}
125+
126+
ret = i2c_reg_write_byte_dt(&config->i2c, PAJ7620_REG_R_IDLE_TIME_LSB, fps);
127+
if (ret < 0) {
128+
LOG_ERR("Failed to set sample rate");
129+
return ret;
130+
}
131+
132+
ret = paj7620_select_register_bank(dev, PAJ7620_MEMBANK_0);
133+
if (ret < 0) {
134+
return ret;
135+
}
136+
137+
LOG_DBG("Sample rate set to %s mode", fps == PAJ7620_GAME_SPEED ? "game" : "normal");
138+
139+
return 0;
140+
}
141+
142+
static int paj7620_sample_fetch(const struct device *dev, enum sensor_channel chan)
143+
{
144+
int ret;
145+
struct paj7620_data *data = dev->data;
146+
const struct paj7620_config *config = dev->config;
147+
uint8_t gest_data[2];
148+
149+
if (chan != SENSOR_CHAN_ALL
150+
&& ((enum sensor_channel_paj7620)chan != SENSOR_CHAN_PAJ7620_GESTURES)) {
151+
return -ENOTSUP;
152+
}
153+
154+
/* We read from REG_INT_FLAG_1 and REG_INT_FLAG_2 even on polling mode
155+
* (without using interrupts) because that's where the gesture
156+
* detection flags are set.
157+
* NOTE: A set bit means that the corresponding gesture has been detected
158+
*/
159+
160+
ret = i2c_burst_read_dt(&config->i2c, PAJ7620_REG_INT_FLAG_1, gest_data, sizeof(gest_data));
161+
if (ret < 0) {
162+
LOG_ERR("Failed to read gesture data");
163+
return ret;
164+
}
165+
166+
data->gesture_flags = sys_get_le16(gest_data);
167+
168+
return 0;
169+
}
170+
171+
static int paj7620_channel_get(const struct device *dev,
172+
enum sensor_channel chan,
173+
struct sensor_value *val)
174+
{
175+
struct paj7620_data *data = dev->data;
176+
177+
switch ((uint32_t)chan) {
178+
case SENSOR_CHAN_PAJ7620_GESTURES:
179+
val->val1 = data->gesture_flags;
180+
val->val2 = 0;
181+
break;
182+
default:
183+
LOG_ERR("Unsupported sensor channel");
184+
return -ENOTSUP;
185+
}
186+
187+
return 0;
188+
}
189+
190+
static int paj7620_attr_set(const struct device *dev,
191+
enum sensor_channel chan,
192+
enum sensor_attribute attr,
193+
const struct sensor_value *val)
194+
{
195+
int ret;
196+
197+
if (chan != SENSOR_CHAN_ALL
198+
&& ((enum sensor_channel_paj7620)chan != SENSOR_CHAN_PAJ7620_GESTURES)) {
199+
return -ENOTSUP;
200+
}
201+
202+
switch ((uint32_t)attr) {
203+
case SENSOR_ATTR_SAMPLING_FREQUENCY:
204+
ret = paj7620_set_sampling_rate(dev, val);
205+
break;
206+
207+
default:
208+
return -ENOTSUP;
209+
}
210+
211+
return ret;
212+
}
213+
214+
static int paj7620_init(const struct device *dev)
215+
{
216+
int ret;
217+
uint16_t hw_id;
218+
const struct paj7620_config *config = dev->config;
219+
220+
if (!i2c_is_ready_dt(&config->i2c)) {
221+
LOG_ERR("I2C bus device not ready");
222+
return -ENODEV;
223+
}
224+
225+
/**
226+
* According to the datasheet section 8.1, we must wait this amount
227+
* of time for sensor to stabilize after power up
228+
*/
229+
k_usleep(PAJ7620_POWERUP_STABILIZATION_TIME_US);
230+
231+
/**
232+
* Make a write to the sensor to wake it up. After waking it, the
233+
* the sensor still needs some time to be ready to listen. Without it,
234+
* it may NACK subsequent transactions.
235+
*/
236+
(void)paj7620_select_register_bank(dev, PAJ7620_MEMBANK_0);
237+
k_usleep(PAJ7620_WAKEUP_TIME_US);
238+
239+
/** Verify this is not some other sensor with the same address */
240+
ret = paj7620_get_hw_id(dev, &hw_id);
241+
if (ret < 0) {
242+
return ret;
243+
}
244+
245+
if (hw_id != PAJ7620_PART_ID) {
246+
LOG_ERR("Hardware ID 0x%04x does not match for PAJ7620", hw_id);
247+
return -ENOTSUP;
248+
}
249+
250+
/** Initialize settings (it defaults to gesture mode) */
251+
ret = paj7620_write_initial_reg_settings(dev);
252+
if (ret < 0) {
253+
LOG_ERR("Failed to initialize device registers");
254+
return ret;
255+
}
256+
257+
if (IS_ENABLED(CONFIG_PAJ7620_TRIGGER)) {
258+
ret = paj7620_trigger_init(dev);
259+
if (ret < 0) {
260+
LOG_ERR("Failed to enable interrupts");
261+
return ret;
262+
}
263+
}
264+
265+
return 0;
266+
}
267+
268+
static DEVICE_API(sensor, paj7620_driver_api) = {
269+
.sample_fetch = paj7620_sample_fetch,
270+
.channel_get = paj7620_channel_get,
271+
.attr_set = paj7620_attr_set,
272+
#if CONFIG_PAJ7620_TRIGGER
273+
.trigger_set = paj7620_trigger_set,
274+
#endif
275+
};
276+
277+
#define PAJ7620_INIT(n) \
278+
static const struct paj7620_config paj7620_config_##n = { \
279+
.i2c = I2C_DT_SPEC_INST_GET(n), \
280+
IF_ENABLED(CONFIG_PAJ7620_TRIGGER, \
281+
(.int_gpio = GPIO_DT_SPEC_INST_GET_OR(n, int_gpios, {0}))) \
282+
}; \
283+
\
284+
static struct paj7620_data paj7620_data_##n; \
285+
\
286+
SENSOR_DEVICE_DT_INST_DEFINE(n, \
287+
paj7620_init, \
288+
NULL, \
289+
&paj7620_data_##n, \
290+
&paj7620_config_##n, \
291+
POST_KERNEL, \
292+
CONFIG_SENSOR_INIT_PRIORITY, \
293+
&paj7620_driver_api);
294+
295+
DT_INST_FOREACH_STATUS_OKAY(PAJ7620_INIT);

0 commit comments

Comments
 (0)