pebble/src/fw/drivers/accel.h
2025-01-27 11:38:16 -08:00

210 lines
8.9 KiB
C

/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "services/imu/units.h"
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
/*! Accelerometer driver interface
* ==============================
*
* The accelerometer driver is simply an interface to the accelerometer
* hardware. It is dumb; it does not contain any circular buffers, has no
* knowledge of clients, threads, subsampling or even other hardware. It is up
* to higher level code (read: the accelerometer service) to deal with
* that. The reason for that is to maximize code reuse: anything which could
* possibly need to be copy-pasted from one accel driver to another should be
* moved outside of the driver.
*
* The accelerometer knows (almost) nothing about the OS, events, analytics or
* the vibe motor. It does not even keep around a sample buffer for any reason.
* All of that code is handled externally. The interface for the accelerometer
* driver is a set of functions implemented by the accelerometer, and a set of
* external functions that it will call in response to certain events. While OS
* services may be employed internally by a driver, they are not part of the
* public interface.
*
* One of the goals of the accelerometer interface is to hide the state of the
* accelerometer hardware as much as possible (e.g. FIFO mode) and use
* higher-level constructs to allow the driver to make its own decisions on what
* state the hardware should be in. This way the interface is (hopefully)
* generic enough that accelerometers with vastly different operating and
* power-saving modes can have all of those details hidden away in the driver,
* and the higher-level code can work unmodified with different accelerometers.
*/
//! Information which varies by accelerometer driver.
typedef struct {
//! Highest sample interval (slowest rate) supported by the driver.
uint32_t sample_interval_max;
//! Recommended sample interval for low-power use, around 100,000 us.
uint32_t sample_interval_low_power;
//! Recommended sample interval for interactive use, around 250,000 us.
uint32_t sample_interval_ui;
//! Recommended sample interval for games and fast interactivity,
//! around 20,000 us.
uint32_t sample_interval_game;
//! Lowest sample interval (fastest rate) supported by the driver.
uint32_t sample_interval_min;
} AccelDriverInfo;
extern const AccelDriverInfo ACCEL_DRIVER_INFO;
typedef struct {
//! Timestamp of when the sample was taken in microseconds since the epoch.
//! The precision of the timestamp is not guaranteed.
uint64_t timestamp_us;
//! Acceleration along the x axis.
int16_t x;
//! Acceleration along the y axis.
int16_t y;
//! Acceleration along the z axis.
int16_t z;
} AccelDriverSample;
//! Sets the accelerometer sampling interval.
//!
//! Not all sampling intervals are supported by all drivers. The driver must
//! select a sampling interval which is equal to or shorter than the requested
//! interval, saturating at the shortest interval supported by the hardware.
//!
//! The new sampling rate takes effect immediately. The driver may flush any
//! queued samples before changing the sampling rate to ensure that timestamps
//! remain accurate.
//!
//! @param interval_us The requested sampling interval in microseconds.
//!
//! @return The actual sampling interval set by the driver.
uint32_t accel_set_sampling_interval(uint32_t interval_us);
//! Returns the currently set accelerometer sampling interval.
uint32_t accel_get_sampling_interval(void);
//! Set the max number of samples the driver may batch.
//!
//! @param n Maximum number of samples the driver can batch
//!
//! When n=0, the accelerometer driver must not call accel_cb_new_sample().
//!
//! When n=1, the accelerometer driver must call accel_cb_new_sample() for
//! each sample as soon as the hardware has acquired it.
//!
//! When n>1, the accelerometer driver may batch up to n samples before
//! calling accel_cb_new_sample() up to n times in rapid succession with
//! all of the queued samples. The last item in a batch must be the most
//! recently acquired sample from the hardware. This is used by the driver
//! as a hint for power saving or other optimizations; it only sets an
//! upper bound on the number of samples the driver may batch up.
//!
//! When n is set to a value smaller than the number of samples already
//! queued up, the driver must flush all of the queued samples to
//! accel_cb_new_sample() before the new value of n takes effect. The
//! accel_cb_new_sample() function may be called from within
//! accel_set_num_samples().
//!
//! @see accel_cb_new_sample
void accel_set_num_samples(uint32_t num_samples);
//! Peek at the most recent accelerometer sample.
//!
//! @param[out] data Pointer to a buffer to write accelerometer data
//!
//! @return 0 if successful, nonzero on failure
//!
//! During the execution of this function, the driver may call
//! accel_cb_new_sample() one or more times iff accel_set_num_samples(n) was
//! called most recently with a value of n >= 1.
int accel_peek(AccelDriverSample *data);
//! Enable or disable shake detection
//!
//! @param on Enable shake detection when true, disable when false
//!
//! When shake detection is enabled, accel_cb_shake_detected must be called every
//! time the accelerometer hardware detects a shake. When shake detection is
//! disabled, accel_cb_shake_detected must not be called.
//!
//! @see accel_cb_shake_detected
void accel_enable_shake_detection(bool on);
//! Returns whether shake detection is enabled
bool accel_get_shake_detection_enabled(void);
//! Enable or disable double tap detection
//!
//! @param on Enable double tap detection when true, disable when false
//!
//! When double tap detection is enabled, accel_cb_double_tap_detected must be called every
//! time the accelerometer hardware detects a double tap. When double tap detection is
//! disabled, accel_cb_double_tap_detected must not be called.
//!
//! @see accel_cb_double_tap_detected
void accel_enable_double_tap_detection(bool on);
//! Returns whether double tap detection is enabled
bool accel_get_double_tap_detection_enabled(void);
//! Function called by the driver whenever a new accel sample is available.
//!
//! @param[in] data pointer to a populated AccelDriverSample struct. The pointer is
//! only valid for the duration of the function call.
//!
//! This function will always be called with samples monotonically increasing in
//! time. It will always be called from within a thread context.
//!
//! @note This function may be called from within any of the functions in the
//! accelerometer driver interface. To prevent reentrancy issues, avoid
//! calling accelerometer driver functions from within this function.
extern void accel_cb_new_sample(AccelDriverSample const *data);
//! Function called by driver whenever shake is detected.
//!
//! @param axis Axis which the shake was detected on
//! @param direction The sign indicates whether the shake was on the positive or
//! negative axis
//!
//! @note It is up to the implementer to filter out shake events triggered by the
//! vibrate motor.
extern void accel_cb_shake_detected(IMUCoordinateAxis axis, int32_t direction);
//! Function called by driver whenever a double tap is detected.
//!
//! @param axis Axis which the double tap was detected on
//! @param direction The sign indicates whether the double tap was on the positive or
//! negative axis
extern void accel_cb_double_tap_detected(IMUCoordinateAxis axis, int32_t direction);
//! Function called by driver when it needs to offload work from an ISR context.
//! It is up to the implementer to decide how this should work
//!
//! @param cb The callback to be invoked from a thread context
//! @param data Data to be passed to the callback, NULL if none
typedef void (*AccelOffloadCallback)(void);
extern void accel_offload_work_from_isr(AccelOffloadCallback cb, bool *should_context_switch);
//! Function runs a diagnostic test on the accelerometer hardware to confirm it
//! works as expected
extern bool accel_run_selftest(void);
//! The accelerometer supports a changeable sensitivity for shake detection. This call will
//! select whether we want the accelerometer to enter a highly sensitive state with a low
//! threshold, where any minor amount of motion would trigger the system shake event.
//! Note: Setting this value does not ensure that shake detection is enabled.
void accel_set_shake_sensitivity_high(bool sensitivity_high);