Skip to content

Commit 4344e71

Browse files
Add Zephyr tap wake example
1 parent 150f0e5 commit 4344e71

File tree

7 files changed

+314
-0
lines changed

7 files changed

+314
-0
lines changed

examples/zephyr-tapwake/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.pio
2+
.vscode/.browse.c_cpp.db*
3+
.vscode/c_cpp_properties.json
4+
.vscode/launch.json
5+
.vscode/ipch

examples/zephyr-tapwake/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
How to build PlatformIO based project
2+
=====================================
3+
4+
1. [Install PlatformIO Core](https://docs.platformio.org/page/core.html)
5+
2. Download [development platform with examples](https://github.com/Seeed-Studio/platform-seeedboards/archive/refs/heads/main.zip)
6+
3. Extract ZIP archive
7+
4. Run these commands:
8+
9+
```shell
10+
# Change directory to example
11+
$ cd platform-seeedboards/examples/zephyr-imu
12+
13+
# Build project
14+
$ pio run
15+
16+
# Upload firmware
17+
$ pio run --target upload
18+
19+
# Build specific environment
20+
$ pio run -e seeed-xiao-nrf54l15
21+
22+
# Upload firmware for the specific environment
23+
$ pio run -e seeed-xiao-nrf54l15 --target upload
24+
25+
# Clean build files
26+
$ pio run --target clean
27+
```
28+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
; PlatformIO Project Configuration File
2+
;
3+
; Build options: build flags, source filter, extra scripting
4+
; Upload options: custom port, speed and extra flags
5+
; Library options: dependencies, extra library storages
6+
;
7+
; Please visit documentation for the other options and examples
8+
; https://docs.platformio.org/page/projectconf.html
9+
10+
[env:seeed-xiao-nrf54l15]
11+
platform = https://github.com/Seeed-Studio/platform-seeedboards.git
12+
framework = zephyr
13+
board = seeed-xiao-nrf54l15
14+
monitor_speed = 115200

examples/zephyr-tapwake/src/main.c

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
#include <stdio.h>
2+
#include <zephyr/kernel.h>
3+
#include <zephyr/device.h>
4+
#include <zephyr/drivers/gpio.h>
5+
#include <zephyr/drivers/i2c.h>
6+
#include <zephyr/sys/util.h>
7+
#include <zephyr/logging/log.h>
8+
9+
LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
10+
11+
/* Use DT_ALIAS to get the nodes */
12+
static const struct i2c_dt_spec imu_i2c = I2C_DT_SPEC_GET(DT_ALIAS(imu0));
13+
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
14+
static const struct gpio_dt_spec imu_int = GPIO_DT_SPEC_GET(DT_NODELABEL(lsm6ds3tr_c), irq_gpios);
15+
16+
/* Register addresses */
17+
#define LSM6DS3TR_C_CTRL1_XL 0x10
18+
#define LSM6DS3TR_C_TAP_SRC 0x1C
19+
#define LSM6DS3TR_C_TAP_CFG 0x58
20+
#define LSM6DS3TR_C_TAP_THS_6D 0x59
21+
#define LSM6DS3TR_C_INT_DUR2 0x5A
22+
#define LSM6DS3TR_C_WAKE_UP_THS 0x5B
23+
#define LSM6DS3TR_C_MD1_CFG 0x5E
24+
25+
/* Register bit definitions and configuration values */
26+
#define LSM6DS3TR_C_ACCEL_ODR_104HZ (0x40)
27+
28+
#define LSM6DS3TR_C_TAP_CFG_INT_ENABLE BIT(7)
29+
#define LSM6DS3TR_C_TAP_CFG_TAP_X_EN BIT(3)
30+
#define LSM6DS3TR_C_TAP_CFG_TAP_Y_EN BIT(2)
31+
#define LSM6DS3TR_C_TAP_CFG_TAP_Z_EN BIT(1)
32+
#define LSM6DS3TR_C_TAP_CFG_LATCH_INT BIT(0)
33+
34+
#define LSM6DS3TR_C_TAP_CONFIG (LSM6DS3TR_C_TAP_CFG_INT_ENABLE | \
35+
LSM6DS3TR_C_TAP_CFG_TAP_X_EN | \
36+
LSM6DS3TR_C_TAP_CFG_TAP_Y_EN | \
37+
LSM6DS3TR_C_TAP_CFG_TAP_Z_EN | \
38+
LSM6DS3TR_C_TAP_CFG_LATCH_INT)
39+
40+
#define LSM6DS3TR_C_TAP_THRESHOLD (0x0A) /* Adjust sensitivity as needed */
41+
#define LSM6DS3TR_C_TAP_TIMING (0x80)
42+
#define LSM6DS3TR_C_WAKE_UP_THS_SINGLE_DOUBLE_EN BIT(7)
43+
#define LSM6DS3TR_C_MD1_CFG_INT1_SINGLE_TAP_EN BIT(6)
44+
#define LSM6DS3TR_C_INT1_ROUTING (LSM6DS3TR_C_MD1_CFG_INT1_SINGLE_TAP_EN)
45+
46+
#define DOUBLE_TAP_WINDOW_MS 500
47+
48+
/* Forward declarations */
49+
static void imu_work_handler(struct k_work *work);
50+
static void led_off_work_handler(struct k_work *work);
51+
static void tap_timer_expiry_function(struct k_timer *timer_id);
52+
53+
/* Work items and timers */
54+
static K_WORK_DEFINE(imu_work, imu_work_handler);
55+
static K_WORK_DELAYABLE_DEFINE(led_off_work, led_off_work_handler);
56+
static K_TIMER_DEFINE(tap_timer, tap_timer_expiry_function, NULL);
57+
58+
/* GPIO callback struct for the IMU interrupt pin */
59+
static struct gpio_callback imu_cb_data;
60+
61+
/* Helper to flash the LED */
62+
static void trigger_led_flash(void)
63+
{
64+
gpio_pin_set_dt(&led, 1);
65+
k_work_schedule(&led_off_work, K_MSEC(150));
66+
}
67+
68+
/* Called by timer when the double-tap window expires */
69+
static void tap_timer_expiry_function(struct k_timer *timer_id)
70+
{
71+
LOG_INF("Single tap event detected!");
72+
trigger_led_flash();
73+
}
74+
75+
/* Work handler to process IMU interrupt in thread context */
76+
static void imu_work_handler(struct k_work *work)
77+
{
78+
/* An interrupt means a tap occurred. Check timer to classify it. */
79+
if (k_timer_remaining_get(&tap_timer) > 0)
80+
{
81+
/* Timer is running: this is the second tap of a double tap */
82+
k_timer_stop(&tap_timer);
83+
LOG_INF("Double tap event detected!");
84+
trigger_led_flash();
85+
}
86+
else
87+
{
88+
/* Timer is not running: this is the first tap. Start the window. */
89+
k_timer_start(&tap_timer, K_MSEC(DOUBLE_TAP_WINDOW_MS), K_NO_WAIT);
90+
}
91+
92+
/* Reading TAP_SRC is still good practice to clear the latched interrupt */
93+
uint8_t tap_src;
94+
i2c_reg_read_byte_dt(&imu_i2c, LSM6DS3TR_C_TAP_SRC, &tap_src);
95+
}
96+
97+
/* ISR: only submits work to a thread. Non-blocking and fast. */
98+
static void gpio_interrupt_handler(const struct device *port, struct gpio_callback *cb,
99+
gpio_port_pins_t pins)
100+
{
101+
k_work_submit(&imu_work);
102+
}
103+
104+
static void led_off_work_handler(struct k_work *work)
105+
{
106+
gpio_pin_set_dt(&led, 0);
107+
}
108+
109+
static int setup_gpio_interrupt(void)
110+
{
111+
if (!gpio_is_ready_dt(&imu_int))
112+
{
113+
LOG_ERR("IMU interrupt pin not ready.");
114+
return -ENODEV;
115+
}
116+
117+
int ret = gpio_pin_configure_dt(&imu_int, GPIO_INPUT);
118+
if (ret)
119+
{
120+
LOG_ERR("Error configuring interrupt pin: %d", ret);
121+
return ret;
122+
}
123+
124+
ret = gpio_pin_interrupt_configure_dt(&imu_int, GPIO_INT_EDGE_TO_ACTIVE);
125+
if (ret)
126+
{
127+
LOG_ERR("Error configuring interrupt: %d", ret);
128+
return ret;
129+
}
130+
131+
gpio_init_callback(&imu_cb_data, gpio_interrupt_handler, BIT(imu_int.pin));
132+
gpio_add_callback(imu_int.port, &imu_cb_data);
133+
134+
LOG_INF("GPIO interrupt configured on %s, pin %d", imu_int.port->name, imu_int.pin);
135+
return 0;
136+
}
137+
138+
static int configure_lsm6ds3_tap(void)
139+
{
140+
int ret = 0;
141+
142+
/* Enable accelerometer */
143+
ret = i2c_reg_write_byte_dt(&imu_i2c, LSM6DS3TR_C_CTRL1_XL, LSM6DS3TR_C_ACCEL_ODR_104HZ);
144+
if (ret)
145+
{
146+
return ret;
147+
}
148+
k_msleep(20);
149+
150+
/* Enable interrupts, latch them, and enable tap on all axes */
151+
ret = i2c_reg_write_byte_dt(&imu_i2c, LSM6DS3TR_C_TAP_CFG, LSM6DS3TR_C_TAP_CONFIG);
152+
if (ret)
153+
{
154+
return ret;
155+
}
156+
157+
/* Set tap threshold */
158+
ret = i2c_reg_write_byte_dt(&imu_i2c, LSM6DS3TR_C_TAP_THS_6D, LSM6DS3TR_C_TAP_THRESHOLD);
159+
if (ret)
160+
{
161+
return ret;
162+
}
163+
164+
/* Set tap timing parameters (DUR field is ignored in single-tap only mode) */
165+
ret = i2c_reg_write_byte_dt(&imu_i2c, LSM6DS3TR_C_INT_DUR2, LSM6DS3TR_C_TAP_TIMING);
166+
if (ret)
167+
{
168+
return ret;
169+
}
170+
171+
/* Configure for single-tap ONLY by clearing the SINGLE_DOUBLE_TAP bit */
172+
ret = i2c_reg_update_byte_dt(&imu_i2c, LSM6DS3TR_C_WAKE_UP_THS,
173+
LSM6DS3TR_C_WAKE_UP_THS_SINGLE_DOUBLE_EN,
174+
0);
175+
if (ret)
176+
{
177+
return ret;
178+
}
179+
180+
/* Route ONLY single tap interrupt to INT1 */
181+
ret = i2c_reg_write_byte_dt(&imu_i2c, LSM6DS3TR_C_MD1_CFG, LSM6DS3TR_C_INT1_ROUTING);
182+
if (ret)
183+
{
184+
return ret;
185+
}
186+
187+
LOG_INF("LSM6DS3TR-C tap detection configured for single-tap events.");
188+
return 0;
189+
}
190+
191+
int main(void)
192+
{
193+
if (!i2c_is_ready_dt(&imu_i2c))
194+
{
195+
LOG_ERR("I2C bus for IMU not ready.");
196+
return 0;
197+
}
198+
199+
if (!gpio_is_ready_dt(&led))
200+
{
201+
LOG_ERR("LED device not found!");
202+
return 0;
203+
}
204+
gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);
205+
206+
LOG_INF("Blinking LED to indicate startup...");
207+
gpio_pin_set_dt(&led, 1);
208+
k_msleep(500);
209+
gpio_pin_set_dt(&led, 0);
210+
211+
if (configure_lsm6ds3_tap() != 0)
212+
{
213+
LOG_ERR("Failed to configure IMU for tap detection.");
214+
return 0;
215+
}
216+
217+
if (setup_gpio_interrupt() != 0)
218+
{
219+
LOG_ERR("Failed to set up GPIO interrupt.");
220+
return 0;
221+
}
222+
223+
LOG_INF("Setup complete. Entering sleep mode.");
224+
LOG_INF("Tap the board to wake it up.");
225+
226+
while (1)
227+
{
228+
k_sleep(K_FOREVER);
229+
}
230+
231+
return 0;
232+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
set(BOARD_ROOT "$ENV{ZEPHYR_BASE}/../../platforms/Seeed Studio/zephyr")
3+
4+
cmake_minimum_required(VERSION 3.13.1)
5+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
project(imu)
7+
8+
target_sources(app PRIVATE ../src/main.c)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
&lsm6ds3tr_c{
2+
wakeup-source;
3+
};
4+
5+
&rfsw_pwr{
6+
/delete-property/ regulator-boot-on;
7+
};
8+
9+
&vbat_pwr{
10+
/delete-property/ regulator-boot-on;
11+
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Standard output
2+
CONFIG_STDOUT_CONSOLE=y
3+
CONFIG_CBPRINTF_FP_SUPPORT=y
4+
5+
# Logging
6+
CONFIG_LOG=y
7+
8+
# I2C and Sensor drivers
9+
CONFIG_I2C=y
10+
11+
# GPIO for LED
12+
CONFIG_GPIO=y
13+
14+
# Enable Zephyr Power Management
15+
CONFIG_PM=y
16+
CONFIG_PM_DEVICE=y

0 commit comments

Comments
 (0)