I was doing a demo code to implement a wake up triggered by motion. I was able to make it work with the integrated low power accelerometer on thingy52 but it only worked 1 time after turning off and on the board. After some research I found out that the problem might be in the internal latch of the accelerometer and it was suggested to read the register to clear that latch. I started doing that before configuring the interrupt, but the behavior is not consistent. Sometimes it works, other times not.
Here is the code I amusing:
main.c
#include "bt_service.h"
#include "accelerometer.h"
#include <zephyr/sys/poweroff.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(STATE_MACHINE, LOG_LEVEL_INF);
int main(void)
{
init_bt_service();
init_advertise();
k_sleep(K_SECONDS(10));
LOG_INF("%s system off demo\n", CONFIG_BOARD);
init_accelerometer();
k_sleep(K_SECONDS(10));
sys_poweroff();
return 0;
}
accelerometer.c
#include <zephyr/drivers/gpio.h>
#include "accelerometer.h"
LOG_MODULE_REGISTER(ACCELEROMETER, LOG_LEVEL_INF);
const struct device *accelerometer = DEVICE_DT_GET_ANY(st_lis2dh12);
const struct gpio_dt_spec accelerometer_gpio = GPIO_DT_SPEC_GET(DT_NODELABEL(lis2dh12), irq_gpios);
const struct i2c_dt_spec accelerometer_i2c = I2C_DT_SPEC_GET(DT_NODELABEL(lis2dh12));
static void lis2dh_trigger_handler(const struct device *accelerometer, const struct sensor_trigger *trig)
{
struct sensor_value xyz[3];
if (sensor_sample_fetch(accelerometer)) {
LOG_ERR("Failed to fetch sensor sample");
return;
}
if (sensor_channel_get(accelerometer, SENSOR_CHAN_ACCEL_XYZ, xyz)) {
LOG_ERR("Failed to get sensor channel: accel");
return;
}
LOG_INF("Movement detected: X=%d mg, Y=%d mg, Z=%d mg",
(int)(sensor_value_to_double(&xyz[0]) * 1000),
(int)(sensor_value_to_double(&xyz[1]) * 1000),
(int)(sensor_value_to_double(&xyz[2]) * 1000));
stop_accelerometer();
}
static void clear_accelerometer_interrupt_line()
{
uint8_t src;
if (!device_is_ready(accelerometer_i2c.bus)) {
LOG_ERR("I2C bus not ready to clear LIS2DH interrupt");
return;
}
if (i2c_reg_read_byte(accelerometer_i2c.bus, LIS2DH_ADDR, LIS2DH_REG_INT1_SRC, &src)) {
LOG_ERR("Failed to read INT1_SRC");
}
LOG_INF("Cleared LIS2DH interrupt, INT1_SRC=0x%02X", src);
}
void configure_accelerometer_wakeup_gpio() {
if(gpio_pin_configure_dt(&accelerometer_gpio, GPIO_INPUT)) {
LOG_INF("Could not configure accelerometer GPIO");
return;
}
if (gpio_pin_interrupt_configure_dt(&accelerometer_gpio, GPIO_INT_LEVEL_ACTIVE)) {
LOG_INF("Could not configure accelerometer GPIO interrupt");
return;
}
NRF_GPIO->PIN_CNF[accelerometer_gpio.pin] |= (GPIO_PIN_CNF_SENSE_High << GPIO_PIN_CNF_SENSE_Pos);
}
void init_accelerometer(void)
{
if (accelerometer == NULL) {
LOG_ERR("Accelerometer device not found");
return;
}
if (!device_is_ready(accelerometer)) {
LOG_ERR("Accelerometer not ready");
return;
}
clear_accelerometer_interrupt_line();
k_sleep(K_SECONDS(1));
double sensitivity_value = ACCELEROMETER_TRIGGER_SENSITIVITY_VALUE * GRAVITY_CONSTANT;
struct sensor_value attr;
attr.val1 = (int32_t)sensitivity_value;
attr.val2 = (int32_t)((double)(sensitivity_value - (int32_t)(sensitivity_value)) * 1000000);
if(sensor_attr_set(accelerometer, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_SLOPE_TH, &attr)){
LOG_ERR("Failed to set slope");
return;
}
struct sensor_trigger trig = {
.type = SENSOR_TRIG_DELTA,
.chan = SENSOR_CHAN_ACCEL_XYZ,
};
if (IS_ENABLED(CONFIG_LIS2DH_ODR_RUNTIME)) {
struct sensor_value odr = {
.val1 = SAMPLE_FREQUENCY,
.val2 = 0,
};
if (sensor_attr_set(accelerometer, trig.chan, SENSOR_ATTR_SAMPLING_FREQUENCY, &odr)) {
LOG_ERR("Failed to set odr");
return;
}
}
if (sensor_trigger_set(accelerometer, &trig, lis2dh_trigger_handler)) {
LOG_ERR("Failed to set trigger");
return;
}
configure_accelerometer_wakeup_gpio();
LOG_INF("Accelerometer device is ready");
}
void stop_accelerometer() {
struct sensor_value odr = {
.val1 = 0,
.val2 = 0
};
if (sensor_attr_set(accelerometer, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_SAMPLING_FREQUENCY, &odr)) {
LOG_ERR("Failed to set ODR to 0");
}
LOG_INF("Accelerometer stopped");
}
The Bluetooth initialization is standard and it's just to see when the device wakes up without needing the terminal.