504 lines
16 KiB
C
504 lines
16 KiB
C
#include "st_lsm6dsox.h"
|
|
|
|
#include <bsp/mmio.h>
|
|
#include <bsp/plic.h>
|
|
#include <bsp/pmod_spi.h>
|
|
#include <bsp/soc.h>
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
|
|
#define LSM6DSOX_WHO_AM_I 0x0fu
|
|
#define LSM6DSOX_COUNTER_BDR_REG1 0x0bu
|
|
#define LSM6DSOX_INT1_CTRL 0x0du
|
|
#define LSM6DSOX_INT2_CTRL 0x0eu
|
|
#define LSM6DSOX_CTRL1_XL 0x10u
|
|
#define LSM6DSOX_CTRL2_G 0x11u
|
|
#define LSM6DSOX_CTRL3_C 0x12u
|
|
#define LSM6DSOX_STATUS_REG 0x1eu
|
|
#define LSM6DSOX_TAP_CFG2 0x58u
|
|
#define LSM6DSOX_OUT_TEMP_L 0x20u
|
|
#define LSM6DSOX_OUTX_L_G 0x22u
|
|
#define LSM6DSOX_OUTX_L_A 0x28u
|
|
|
|
#define LSM6DSOX_SPI_READ 0x80u
|
|
#define LSM6DSOX_SPI_DUMMY 0xffu
|
|
#define LSM6DSOX_WHO_AM_I_VALUE 0x6cu
|
|
#define LSM6DSOX_CTRL3_SW_RESET 0x01u
|
|
#define LSM6DSOX_CTRL3_IF_INC 0x04u
|
|
#define LSM6DSOX_CTRL3_BDU 0x40u
|
|
#define LSM6DSOX_STATUS_XLDA 0x01u
|
|
#define LSM6DSOX_STATUS_GDA 0x02u
|
|
#define LSM6DSOX_INT1_DRDY_XL 0x01u
|
|
#define LSM6DSOX_DATAREADY_PULSED 0x80u
|
|
#define LSM6DSOX_INTERRUPTS_ENABLE 0x80u
|
|
#define LSM6DSOX_GYRO_READY_TIMEOUT_US 100u
|
|
|
|
#define ST_GRAVITY_MPS2 9.80665
|
|
#define ST_DEGREES_TO_RADIANS 0.017453292519943295769236907684886
|
|
|
|
#if (ST_IMU_RAW_QUEUE_CAPACITY < 2u)
|
|
#error "ST_IMU_RAW_QUEUE_CAPACITY must be at least 2"
|
|
#endif
|
|
|
|
static st_lsm6dsox_config_t active_config;
|
|
static st_lsm6dsox_raw_sample_t sample_queue[ST_IMU_RAW_QUEUE_CAPACITY];
|
|
static volatile unsigned int sample_head;
|
|
static volatile unsigned int sample_tail;
|
|
static volatile st_lsm6dsox_diagnostics_t diagnostics;
|
|
|
|
static uint64_t time_now(void)
|
|
{
|
|
return bsp_read64(BSP_CLINT_MTIME);
|
|
}
|
|
|
|
static void delay_ms(uint32_t milliseconds)
|
|
{
|
|
uint64_t ticks = ((uint64_t)BSP_CLINT_TIMEBASE_HZ * milliseconds + 999u) /
|
|
1000u;
|
|
uint64_t start = time_now();
|
|
while((time_now() - start) < ticks)
|
|
{
|
|
}
|
|
}
|
|
|
|
static void write_register(uint8_t reg, uint8_t value)
|
|
{
|
|
bsp_pmod_spi_write_reg8(reg & (uint8_t)~LSM6DSOX_SPI_READ, value);
|
|
}
|
|
|
|
static uint8_t read_register(uint8_t reg)
|
|
{
|
|
return bsp_pmod_spi_read_reg8(reg | LSM6DSOX_SPI_READ,
|
|
LSM6DSOX_SPI_DUMMY);
|
|
}
|
|
|
|
static int16_t little_endian_i16(const uint8_t* bytes)
|
|
{
|
|
return (int16_t)((uint16_t)bytes[0] | ((uint16_t)bytes[1] << 8));
|
|
}
|
|
|
|
static bool odr_register_value(uint32_t odr_hz, uint8_t* value)
|
|
{
|
|
if(value == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
switch(odr_hz)
|
|
{
|
|
case 52u: *value = 0x30u; return true;
|
|
case 104u: *value = 0x40u; return true;
|
|
case 208u: *value = 0x50u; return true;
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
static bool accel_full_scale_bits(unsigned int full_scale_g, uint8_t* bits,
|
|
double* mg_per_lsb)
|
|
{
|
|
if((bits == NULL) || (mg_per_lsb == NULL))
|
|
{
|
|
return false;
|
|
}
|
|
switch(full_scale_g)
|
|
{
|
|
case 2u: *bits = 0x00u; *mg_per_lsb = 0.061; return true;
|
|
case 4u: *bits = 0x08u; *mg_per_lsb = 0.122; return true;
|
|
case 8u: *bits = 0x0cu; *mg_per_lsb = 0.244; return true;
|
|
case 16u: *bits = 0x04u; *mg_per_lsb = 0.488; return true;
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
static bool gyro_full_scale_bits(unsigned int full_scale_dps, uint8_t* bits,
|
|
double* mdps_per_lsb)
|
|
{
|
|
if((bits == NULL) || (mdps_per_lsb == NULL))
|
|
{
|
|
return false;
|
|
}
|
|
switch(full_scale_dps)
|
|
{
|
|
case 125u: *bits = 0x02u; *mdps_per_lsb = 4.375; return true;
|
|
case 250u: *bits = 0x00u; *mdps_per_lsb = 8.750; return true;
|
|
case 500u: *bits = 0x04u; *mdps_per_lsb = 17.50; return true;
|
|
case 1000u: *bits = 0x08u; *mdps_per_lsb = 35.00; return true;
|
|
case 2000u: *bits = 0x0cu; *mdps_per_lsb = 70.00; return true;
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
static unsigned int next_queue_index(unsigned int index)
|
|
{
|
|
index++;
|
|
return (index == ST_IMU_RAW_QUEUE_CAPACITY) ? 0u : index;
|
|
}
|
|
|
|
static bool raw_sample_valid(const st_lsm6dsox_raw_sample_t* sample)
|
|
{
|
|
if((sample == NULL) || (sample->temperature_raw == INT16_MIN))
|
|
{
|
|
return false;
|
|
}
|
|
for(unsigned int axis = 0u ; axis < 3u ; axis++)
|
|
{
|
|
if((sample->accel_raw[axis] == INT16_MIN) ||
|
|
(sample->gyro_raw[axis] == INT16_MIN))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool queue_push_isr(const st_lsm6dsox_raw_sample_t* sample)
|
|
{
|
|
unsigned int head = __atomic_load_n(&sample_head, __ATOMIC_RELAXED);
|
|
unsigned int next = next_queue_index(head);
|
|
if(next == __atomic_load_n(&sample_tail, __ATOMIC_ACQUIRE))
|
|
{
|
|
__atomic_fetch_add(&diagnostics.queue_overflow_count, 1u,
|
|
__ATOMIC_RELAXED);
|
|
return false;
|
|
}
|
|
sample_queue[head] = *sample;
|
|
__atomic_store_n(&sample_head, next, __ATOMIC_RELEASE);
|
|
return true;
|
|
}
|
|
|
|
static bool queue_pop(st_lsm6dsox_raw_sample_t* sample)
|
|
{
|
|
unsigned int tail = __atomic_load_n(&sample_tail, __ATOMIC_RELAXED);
|
|
if(tail == __atomic_load_n(&sample_head, __ATOMIC_ACQUIRE))
|
|
{
|
|
return false;
|
|
}
|
|
*sample = sample_queue[tail];
|
|
__atomic_store_n(&sample_tail, next_queue_index(tail), __ATOMIC_RELEASE);
|
|
return true;
|
|
}
|
|
|
|
static void read_raw_outputs(st_lsm6dsox_raw_sample_t* sample,
|
|
uint64_t timestamp_ticks)
|
|
{
|
|
uint8_t bytes[14];
|
|
bsp_pmod_spi_read_regs(LSM6DSOX_OUT_TEMP_L | LSM6DSOX_SPI_READ,
|
|
LSM6DSOX_SPI_DUMMY, bytes, 2u);
|
|
/* Temperature and gyro/accel are separate internal output blocks. */
|
|
bsp_pmod_spi_read_regs(LSM6DSOX_OUTX_L_G | LSM6DSOX_SPI_READ,
|
|
LSM6DSOX_SPI_DUMMY, &bytes[2], 12u);
|
|
sample->timestamp_ticks = timestamp_ticks;
|
|
sample->temperature_raw = little_endian_i16(&bytes[0]);
|
|
for(unsigned int axis = 0u ; axis < 3u ; axis++)
|
|
{
|
|
sample->gyro_raw[axis] = little_endian_i16(&bytes[2u + 2u * axis]);
|
|
sample->accel_raw[axis] = little_endian_i16(&bytes[8u + 2u * axis]);
|
|
}
|
|
}
|
|
|
|
static void read_raw_accel(st_lsm6dsox_raw_sample_t* sample)
|
|
{
|
|
uint8_t bytes[6];
|
|
bsp_pmod_spi_read_regs(LSM6DSOX_OUTX_L_A | LSM6DSOX_SPI_READ,
|
|
LSM6DSOX_SPI_DUMMY, bytes, sizeof(bytes));
|
|
for(unsigned int axis = 0u ; axis < 3u ; axis++)
|
|
{
|
|
sample->accel_raw[axis] = little_endian_i16(&bytes[2u * axis]);
|
|
}
|
|
}
|
|
|
|
static void read_raw_gyro_temperature(st_lsm6dsox_raw_sample_t* sample)
|
|
{
|
|
uint8_t bytes[8];
|
|
bsp_pmod_spi_read_regs(LSM6DSOX_OUT_TEMP_L | LSM6DSOX_SPI_READ,
|
|
LSM6DSOX_SPI_DUMMY, bytes, 2u);
|
|
bsp_pmod_spi_read_regs(LSM6DSOX_OUTX_L_G | LSM6DSOX_SPI_READ,
|
|
LSM6DSOX_SPI_DUMMY, &bytes[2], 6u);
|
|
sample->temperature_raw = little_endian_i16(&bytes[0]);
|
|
for(unsigned int axis = 0u ; axis < 3u ; axis++)
|
|
{
|
|
sample->gyro_raw[axis] = little_endian_i16(&bytes[2u + 2u * axis]);
|
|
}
|
|
}
|
|
|
|
void st_lsm6dsox_default_config(st_lsm6dsox_config_t* config)
|
|
{
|
|
if(config == NULL)
|
|
{
|
|
return;
|
|
}
|
|
config->odr_hz = 104u;
|
|
config->accel_full_scale_g = 2u;
|
|
config->gyro_full_scale_dps = 250u;
|
|
}
|
|
|
|
bool st_lsm6dsox_init(const st_lsm6dsox_config_t* config)
|
|
{
|
|
uint8_t odr_bits;
|
|
uint8_t accel_bits;
|
|
uint8_t gyro_bits;
|
|
double unused_accel_scale;
|
|
double unused_gyro_scale;
|
|
if((config == NULL) || !odr_register_value(config->odr_hz, &odr_bits) ||
|
|
!accel_full_scale_bits(config->accel_full_scale_g, &accel_bits,
|
|
&unused_accel_scale) ||
|
|
!gyro_full_scale_bits(config->gyro_full_scale_dps, &gyro_bits,
|
|
&unused_gyro_scale))
|
|
{
|
|
return false;
|
|
}
|
|
memset((void*)&diagnostics, 0, sizeof(diagnostics));
|
|
sample_head = 0u;
|
|
sample_tail = 0u;
|
|
active_config = *config;
|
|
|
|
bsp_pmod_spi_init();
|
|
bsp_pmod_spi_set_mode(BSP_PMOD_SPI_MODE_SINGLE);
|
|
bsp_pmod_spi_set_clock_mode(BSP_PMOD_SPI_CLOCK_MODE0);
|
|
bsp_pmod_spi_set_timing(2u, 2u, 2u, 5u);
|
|
delay_ms(20u);
|
|
diagnostics.who_am_i = read_register(LSM6DSOX_WHO_AM_I);
|
|
if(diagnostics.who_am_i != LSM6DSOX_WHO_AM_I_VALUE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
write_register(LSM6DSOX_CTRL3_C, LSM6DSOX_CTRL3_SW_RESET);
|
|
uint64_t reset_start = time_now();
|
|
uint64_t reset_timeout =
|
|
((uint64_t)BSP_CLINT_TIMEBASE_HZ * 100u + 999u) / 1000u;
|
|
while((read_register(LSM6DSOX_CTRL3_C) & LSM6DSOX_CTRL3_SW_RESET) != 0u)
|
|
{
|
|
if((time_now() - reset_start) >= reset_timeout)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
delay_ms(10u);
|
|
|
|
write_register(LSM6DSOX_CTRL3_C,
|
|
LSM6DSOX_CTRL3_BDU | LSM6DSOX_CTRL3_IF_INC);
|
|
/* Active-high push-pull is the CTRL3_C reset polarity/driver setting. */
|
|
write_register(LSM6DSOX_INT1_CTRL, 0u);
|
|
write_register(LSM6DSOX_INT2_CTRL, 0u);
|
|
write_register(LSM6DSOX_TAP_CFG2,
|
|
read_register(LSM6DSOX_TAP_CFG2) &
|
|
(uint8_t)~LSM6DSOX_INTERRUPTS_ENABLE);
|
|
write_register(LSM6DSOX_COUNTER_BDR_REG1, 0u);
|
|
write_register(LSM6DSOX_CTRL1_XL, (uint8_t)(odr_bits | accel_bits));
|
|
write_register(LSM6DSOX_CTRL2_G, (uint8_t)(odr_bits | gyro_bits));
|
|
delay_ms(100u);
|
|
if((read_register(LSM6DSOX_CTRL3_C) !=
|
|
(LSM6DSOX_CTRL3_BDU | LSM6DSOX_CTRL3_IF_INC)) ||
|
|
(read_register(LSM6DSOX_CTRL1_XL) != (uint8_t)(odr_bits | accel_bits)) ||
|
|
(read_register(LSM6DSOX_CTRL2_G) != (uint8_t)(odr_bits | gyro_bits)) ||
|
|
(read_register(LSM6DSOX_INT1_CTRL) != 0u) ||
|
|
(read_register(LSM6DSOX_INT2_CTRL) != 0u) ||
|
|
((read_register(LSM6DSOX_TAP_CFG2) &
|
|
LSM6DSOX_INTERRUPTS_ENABLE) != 0u) ||
|
|
((read_register(LSM6DSOX_COUNTER_BDR_REG1) &
|
|
LSM6DSOX_DATAREADY_PULSED) != 0u))
|
|
{
|
|
st_lsm6dsox_stop();
|
|
return false;
|
|
}
|
|
diagnostics.initialized = true;
|
|
return true;
|
|
}
|
|
|
|
bool st_lsm6dsox_start(void)
|
|
{
|
|
if(!diagnostics.initialized || diagnostics.running)
|
|
{
|
|
return diagnostics.running;
|
|
}
|
|
bsp_plic_disable(BSP_PMOD_IRQ);
|
|
write_register(LSM6DSOX_INT1_CTRL, 0u);
|
|
write_register(LSM6DSOX_INT2_CTRL, 0u);
|
|
write_register(LSM6DSOX_TAP_CFG2,
|
|
read_register(LSM6DSOX_TAP_CFG2) &
|
|
(uint8_t)~LSM6DSOX_INTERRUPTS_ENABLE);
|
|
write_register(LSM6DSOX_COUNTER_BDR_REG1, 0u);
|
|
/* Clear an old latched data-ready before exposing the PLIC source. */
|
|
if((read_register(LSM6DSOX_STATUS_REG) & LSM6DSOX_STATUS_XLDA) != 0u)
|
|
{
|
|
st_lsm6dsox_raw_sample_t stale;
|
|
read_raw_outputs(&stale, time_now());
|
|
}
|
|
/* Route accel DRDY to the wired sensor INT1 pad, then globally enable the
|
|
* physical interrupt pads as required by ST's reference driver. */
|
|
write_register(LSM6DSOX_INT1_CTRL, LSM6DSOX_INT1_DRDY_XL);
|
|
write_register(LSM6DSOX_TAP_CFG2,
|
|
read_register(LSM6DSOX_TAP_CFG2) |
|
|
LSM6DSOX_INTERRUPTS_ENABLE);
|
|
if((read_register(LSM6DSOX_INT1_CTRL) != LSM6DSOX_INT1_DRDY_XL) ||
|
|
(read_register(LSM6DSOX_INT2_CTRL) != 0u) ||
|
|
((read_register(LSM6DSOX_TAP_CFG2) &
|
|
LSM6DSOX_INTERRUPTS_ENABLE) == 0u) ||
|
|
((read_register(LSM6DSOX_COUNTER_BDR_REG1) &
|
|
LSM6DSOX_DATAREADY_PULSED) != 0u))
|
|
{
|
|
write_register(LSM6DSOX_INT1_CTRL, 0u);
|
|
write_register(LSM6DSOX_INT2_CTRL, 0u);
|
|
write_register(LSM6DSOX_TAP_CFG2,
|
|
read_register(LSM6DSOX_TAP_CFG2) &
|
|
(uint8_t)~LSM6DSOX_INTERRUPTS_ENABLE);
|
|
return false;
|
|
}
|
|
bsp_plic_set_priority(BSP_PMOD_IRQ, 1u);
|
|
diagnostics.running = true;
|
|
bsp_plic_enable(BSP_PMOD_IRQ);
|
|
return true;
|
|
}
|
|
|
|
void st_lsm6dsox_stop(void)
|
|
{
|
|
bsp_plic_disable(BSP_PMOD_IRQ);
|
|
if(diagnostics.who_am_i == LSM6DSOX_WHO_AM_I_VALUE)
|
|
{
|
|
write_register(LSM6DSOX_INT1_CTRL, 0u);
|
|
write_register(LSM6DSOX_INT2_CTRL, 0u);
|
|
write_register(LSM6DSOX_TAP_CFG2,
|
|
read_register(LSM6DSOX_TAP_CFG2) &
|
|
(uint8_t)~LSM6DSOX_INTERRUPTS_ENABLE);
|
|
write_register(LSM6DSOX_CTRL1_XL, 0u);
|
|
write_register(LSM6DSOX_CTRL2_G, 0u);
|
|
}
|
|
diagnostics.running = false;
|
|
}
|
|
|
|
void st_lsm6dsox_irq_handler(void)
|
|
{
|
|
const uint64_t timestamp = time_now();
|
|
__atomic_fetch_add(&diagnostics.irq_count, 1u, __ATOMIC_RELAXED);
|
|
if(!__atomic_load_n(&diagnostics.running, __ATOMIC_RELAXED))
|
|
{
|
|
__atomic_fetch_add(&diagnostics.spurious_irq_count, 1u,
|
|
__ATOMIC_RELAXED);
|
|
return;
|
|
}
|
|
uint8_t status = read_register(LSM6DSOX_STATUS_REG);
|
|
st_lsm6dsox_raw_sample_t sample;
|
|
sample.timestamp_ticks = timestamp;
|
|
/* Always clear the routed, latched accel source before returning. */
|
|
read_raw_accel(&sample);
|
|
if((status & LSM6DSOX_STATUS_XLDA) == 0u)
|
|
{
|
|
__atomic_fetch_add(&diagnostics.spurious_irq_count, 1u,
|
|
__ATOMIC_RELAXED);
|
|
return;
|
|
}
|
|
uint64_t wait_start = time_now();
|
|
uint64_t timeout_ticks =
|
|
((uint64_t)BSP_CLINT_TIMEBASE_HZ * LSM6DSOX_GYRO_READY_TIMEOUT_US +
|
|
999999u) / 1000000u;
|
|
while((read_register(LSM6DSOX_STATUS_REG) & LSM6DSOX_STATUS_GDA) == 0u)
|
|
{
|
|
if((time_now() - wait_start) >= timeout_ticks)
|
|
{
|
|
__atomic_fetch_add(&diagnostics.invalid_sample_count, 1u,
|
|
__ATOMIC_RELAXED);
|
|
return;
|
|
}
|
|
}
|
|
read_raw_gyro_temperature(&sample);
|
|
if(!raw_sample_valid(&sample))
|
|
{
|
|
__atomic_fetch_add(&diagnostics.invalid_sample_count, 1u,
|
|
__ATOMIC_RELAXED);
|
|
return;
|
|
}
|
|
sample.sequence = (uint32_t)(
|
|
__atomic_load_n(&diagnostics.sample_count, __ATOMIC_RELAXED) + 1u);
|
|
uint64_t previous_timestamp = __atomic_exchange_n(
|
|
&diagnostics.last_timestamp_ticks, timestamp, __ATOMIC_RELAXED);
|
|
if(previous_timestamp != 0u)
|
|
{
|
|
uint64_t expected_ticks = BSP_CLINT_TIMEBASE_HZ / active_config.odr_hz;
|
|
uint64_t elapsed = timestamp - previous_timestamp;
|
|
if((expected_ticks != 0u) &&
|
|
(elapsed > expected_ticks + expected_ticks / 2u))
|
|
{
|
|
uint64_t periods = (elapsed + expected_ticks / 2u) / expected_ticks;
|
|
if(periods > 1u)
|
|
{
|
|
__atomic_fetch_add(&diagnostics.estimated_missed_sample_count,
|
|
periods - 1u, __ATOMIC_RELAXED);
|
|
}
|
|
}
|
|
}
|
|
(void)queue_push_isr(&sample);
|
|
__atomic_fetch_add(&diagnostics.sample_count, 1u, __ATOMIC_RELAXED);
|
|
}
|
|
|
|
void st_lsm6dsox_service(void)
|
|
{
|
|
}
|
|
|
|
bool st_lsm6dsox_take_measurement(
|
|
fusion_imu_measurement_t* measurement_out)
|
|
{
|
|
if(measurement_out == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
st_lsm6dsox_raw_sample_t raw;
|
|
if(!queue_pop(&raw))
|
|
{
|
|
return false;
|
|
}
|
|
double accel_mg_per_lsb;
|
|
double gyro_mdps_per_lsb;
|
|
uint8_t unused_bits;
|
|
if(!accel_full_scale_bits(active_config.accel_full_scale_g, &unused_bits,
|
|
&accel_mg_per_lsb) ||
|
|
!gyro_full_scale_bits(active_config.gyro_full_scale_dps, &unused_bits,
|
|
&gyro_mdps_per_lsb))
|
|
{
|
|
__atomic_fetch_add(&diagnostics.invalid_sample_count, 1u,
|
|
__ATOMIC_RELAXED);
|
|
return false;
|
|
}
|
|
memset(measurement_out, 0, sizeof(*measurement_out));
|
|
measurement_out->timestamp_ticks = raw.timestamp_ticks;
|
|
for(unsigned int axis = 0 ; axis < 3u ; axis++)
|
|
{
|
|
measurement_out->accel_mps2[axis] =
|
|
(double)raw.accel_raw[axis] * accel_mg_per_lsb *
|
|
ST_GRAVITY_MPS2 / 1000.0;
|
|
measurement_out->gyro_rps[axis] =
|
|
(double)raw.gyro_raw[axis] * gyro_mdps_per_lsb *
|
|
ST_DEGREES_TO_RADIANS / 1000.0;
|
|
}
|
|
measurement_out->temperature_c =
|
|
25.0 + (double)raw.temperature_raw / 256.0;
|
|
measurement_out->sequence = raw.sequence;
|
|
return true;
|
|
}
|
|
|
|
void st_lsm6dsox_get_diagnostics(
|
|
st_lsm6dsox_diagnostics_t* diagnostics_out)
|
|
{
|
|
if(diagnostics_out != NULL)
|
|
{
|
|
diagnostics_out->irq_count =
|
|
__atomic_load_n(&diagnostics.irq_count, __ATOMIC_RELAXED);
|
|
diagnostics_out->sample_count =
|
|
__atomic_load_n(&diagnostics.sample_count, __ATOMIC_RELAXED);
|
|
diagnostics_out->invalid_sample_count =
|
|
__atomic_load_n(&diagnostics.invalid_sample_count, __ATOMIC_RELAXED);
|
|
diagnostics_out->spurious_irq_count =
|
|
__atomic_load_n(&diagnostics.spurious_irq_count, __ATOMIC_RELAXED);
|
|
diagnostics_out->queue_overflow_count =
|
|
__atomic_load_n(&diagnostics.queue_overflow_count, __ATOMIC_RELAXED);
|
|
diagnostics_out->estimated_missed_sample_count = __atomic_load_n(
|
|
&diagnostics.estimated_missed_sample_count, __ATOMIC_RELAXED);
|
|
diagnostics_out->last_timestamp_ticks =
|
|
__atomic_load_n(&diagnostics.last_timestamp_ticks, __ATOMIC_RELAXED);
|
|
diagnostics_out->who_am_i = diagnostics.who_am_i;
|
|
diagnostics_out->initialized = diagnostics.initialized;
|
|
diagnostics_out->running = diagnostics.running;
|
|
}
|
|
}
|