Add STEVAL-MKI217V1 SPI logger and replay
This commit is contained in:
@@ -196,6 +196,8 @@ class TLFpgaSPI(params: FpgaSPIParams, beatBytes: Int)(implicit p: Parameters) e
|
|||||||
val csn = RegInit(true.B)
|
val csn = RegInit(true.B)
|
||||||
val sck = RegInit(false.B)
|
val sck = RegInit(false.B)
|
||||||
val quad = RegInit(false.B)
|
val quad = RegInit(false.B)
|
||||||
|
val cpol = RegInit(false.B)
|
||||||
|
val cpha = RegInit(false.B)
|
||||||
val txShift = RegInit(0.U(32.W))
|
val txShift = RegInit(0.U(32.W))
|
||||||
val rxShift = RegInit(0.U(32.W))
|
val rxShift = RegInit(0.U(32.W))
|
||||||
val bitsLeft = RegInit(0.U(6.W))
|
val bitsLeft = RegInit(0.U(6.W))
|
||||||
@@ -211,13 +213,29 @@ class TLFpgaSPI(params: FpgaSPIParams, beatBytes: Int)(implicit p: Parameters) e
|
|||||||
when(div === (params.clockDiv - 1).U) {
|
when(div === (params.clockDiv - 1).U) {
|
||||||
div := 0.U
|
div := 0.U
|
||||||
sck := ~sck
|
sck := ~sck
|
||||||
when(sck) {
|
val step = Mux(quad, 4.U, 1.U)
|
||||||
val step = Mux(quad, 4.U, 1.U)
|
/*
|
||||||
|
* `sck` is the pre-toggle level. The edge away from CPOL is the
|
||||||
|
* leading edge; CPHA selects leading (0) or trailing (1) sampling.
|
||||||
|
* TX advances only after the active sample edge, so all four standard
|
||||||
|
* SPI modes retain a full half-cycle of setup time.
|
||||||
|
*/
|
||||||
|
val leadingEdge = sck === cpol
|
||||||
|
val sampleEdge = Mux(cpha, !leadingEdge, leadingEdge)
|
||||||
|
when(sampleEdge) {
|
||||||
rxShift := Mux(quad, Cat(rxShift(27, 0), dq_i), Cat(rxShift(30, 0), dq_i(1)))
|
rxShift := Mux(quad, Cat(rxShift(27, 0), dq_i), Cat(rxShift(30, 0), dq_i(1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CPHA=0 completes on the trailing edge after the final leading-edge
|
||||||
|
* sample. CPHA=1 samples and completes on the final trailing edge.
|
||||||
|
*/
|
||||||
|
val advanceEdge = Mux(cpha, sampleEdge, !sampleEdge)
|
||||||
|
when(advanceEdge) {
|
||||||
txShift := Mux(quad, Cat(txShift(27, 0), 0.U(4.W)), Cat(txShift(30, 0), 0.U(1.W)))
|
txShift := Mux(quad, Cat(txShift(27, 0), 0.U(4.W)), Cat(txShift(30, 0), 0.U(1.W)))
|
||||||
when(bitsLeft <= step) {
|
when(bitsLeft <= step) {
|
||||||
busy := false.B
|
busy := false.B
|
||||||
sck := false.B
|
sck := cpol
|
||||||
bitsLeft := 0.U
|
bitsLeft := 0.U
|
||||||
}.otherwise {
|
}.otherwise {
|
||||||
bitsLeft := bitsLeft - step
|
bitsLeft := bitsLeft - step
|
||||||
@@ -229,12 +247,15 @@ class TLFpgaSPI(params: FpgaSPIParams, beatBytes: Int)(implicit p: Parameters) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
val ctrl = Wire(UInt(32.W))
|
val ctrl = Wire(UInt(32.W))
|
||||||
ctrl := Cat(0.U(29.W), quad, csn, busy)
|
ctrl := Cat(0.U(27.W), cpha, cpol, quad, csn, busy)
|
||||||
|
|
||||||
val ctrlWrite = RegWriteFn { (valid, data) =>
|
val ctrlWrite = RegWriteFn { (valid, data) =>
|
||||||
when(valid && !busy) {
|
when(valid && !busy) {
|
||||||
csn := data(1)
|
csn := data(1)
|
||||||
quad := data(2)
|
quad := data(2)
|
||||||
|
cpol := data(3)
|
||||||
|
cpha := data(4)
|
||||||
|
sck := data(3)
|
||||||
}
|
}
|
||||||
true.B
|
true.B
|
||||||
}
|
}
|
||||||
@@ -246,13 +267,13 @@ class TLFpgaSPI(params: FpgaSPIParams, beatBytes: Int)(implicit p: Parameters) e
|
|||||||
bitsLeft := Mux(quad, 32.U, 8.U)
|
bitsLeft := Mux(quad, 32.U, 8.U)
|
||||||
busy := true.B
|
busy := true.B
|
||||||
div := 0.U
|
div := 0.U
|
||||||
sck := false.B
|
sck := cpol
|
||||||
}
|
}
|
||||||
true.B
|
true.B
|
||||||
}
|
}
|
||||||
|
|
||||||
node.regmap(
|
node.regmap(
|
||||||
0x00 -> Seq(RegField(32, ctrl, ctrlWrite, RegFieldDesc("ctrl", "bit0 busy RO, bit1 csn RW, bit2 quad RW"))),
|
0x00 -> Seq(RegField(32, ctrl, ctrlWrite, RegFieldDesc("ctrl", "bit0 busy RO, bit1 csn RW, bit2 quad RW, bit3 CPOL RW, bit4 CPHA RW"))),
|
||||||
0x04 -> Seq(RegField.w(32, txWrite, RegFieldDesc("tx", "start transfer"))),
|
0x04 -> Seq(RegField.w(32, txWrite, RegFieldDesc("tx", "start transfer"))),
|
||||||
0x08 -> Seq(RegField.r(32, rxShift, RegFieldDesc("rx", "last received shift register"))),
|
0x08 -> Seq(RegField.r(32, rxShift, RegFieldDesc("rx", "last received shift register"))),
|
||||||
0x0c -> Seq(RegField.r(1, !busy, RegFieldDesc("ready", "controller ready")))
|
0x0c -> Seq(RegField.r(1, !busy, RegFieldDesc("ready", "controller ready")))
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ OBJCOPY := $(TOOLCHAIN)/riscv64-unknown-elf-objcopy
|
|||||||
READELF := $(TOOLCHAIN)/riscv64-unknown-elf-readelf
|
READELF := $(TOOLCHAIN)/riscv64-unknown-elf-readelf
|
||||||
MAKE_FLASH_IMAGE := $(SOC_DIR)/software/tools/make_flash_image.py
|
MAKE_FLASH_IMAGE := $(SOC_DIR)/software/tools/make_flash_image.py
|
||||||
|
|
||||||
.PHONY: help all app bootloader hello pmod-spi-test gps-system flash-image clean
|
.PHONY: help all app bootloader hello pmod-spi-test steval-mki217v1-test gps-system flash-image clean
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@echo "Common Rocket bare-metal software targets:"
|
@echo "Common Rocket bare-metal software targets:"
|
||||||
@@ -29,6 +29,7 @@ help:
|
|||||||
@echo " make bootloader # build only BootROM flash bootloader"
|
@echo " make bootloader # build only BootROM flash bootloader"
|
||||||
@echo " make hello # build only hello UART app"
|
@echo " make hello # build only hello UART app"
|
||||||
@echo " make pmod-spi-test # build PMOD SPI smoke test app"
|
@echo " make pmod-spi-test # build PMOD SPI smoke test app"
|
||||||
|
@echo " make steval-mki217v1-test # build STEVAL-MKI217V1 LSM6DSOX polling app"
|
||||||
@echo " make gps-system # build imported GPS bare-metal app"
|
@echo " make gps-system # build imported GPS bare-metal app"
|
||||||
@echo " make flash-image APP=name # build bootloader-readable app flash image"
|
@echo " make flash-image APP=name # build bootloader-readable app flash image"
|
||||||
@echo " make clean # remove software build products"
|
@echo " make clean # remove software build products"
|
||||||
@@ -51,6 +52,9 @@ hello:
|
|||||||
pmod-spi-test:
|
pmod-spi-test:
|
||||||
@make -C pmod_spi_test TOOLCHAIN="$(TOOLCHAIN)" BUILD_DIR="$(OUT_DIR)/pmod_spi_test" BSP_UART_CLOCK_HZ="$(BSP_UART_CLOCK_HZ)" BSP_UART_BAUDRATE="$(BSP_UART_BAUDRATE)" BSP_CLINT_TIMEBASE_HZ="$(BSP_CLINT_TIMEBASE_HZ)" BSP_DTIM_BASE="$(BSP_DTIM_BASE)" BSP_DTIM_SIZE="$(BSP_DTIM_SIZE)" all
|
@make -C pmod_spi_test TOOLCHAIN="$(TOOLCHAIN)" BUILD_DIR="$(OUT_DIR)/pmod_spi_test" BSP_UART_CLOCK_HZ="$(BSP_UART_CLOCK_HZ)" BSP_UART_BAUDRATE="$(BSP_UART_BAUDRATE)" BSP_CLINT_TIMEBASE_HZ="$(BSP_CLINT_TIMEBASE_HZ)" BSP_DTIM_BASE="$(BSP_DTIM_BASE)" BSP_DTIM_SIZE="$(BSP_DTIM_SIZE)" all
|
||||||
|
|
||||||
|
steval-mki217v1-test:
|
||||||
|
@make -C steval_mki217v1_test TOOLCHAIN="$(TOOLCHAIN)" BUILD_DIR="$(OUT_DIR)/steval_mki217v1_test" BSP_UART_CLOCK_HZ="$(BSP_UART_CLOCK_HZ)" BSP_UART_BAUDRATE="$(BSP_UART_BAUDRATE)" BSP_CLINT_TIMEBASE_HZ="$(BSP_CLINT_TIMEBASE_HZ)" BSP_DTIM_BASE="$(BSP_DTIM_BASE)" BSP_DTIM_SIZE="$(BSP_DTIM_SIZE)" all
|
||||||
|
|
||||||
gps-system:
|
gps-system:
|
||||||
@make -C gps_system TOOLCHAIN="$(TOOLCHAIN)" BUILD_DIR="$(OUT_DIR)/gps_system" GPS_DEBUG="$(GPS_DEBUG)" GPS_DEBUG_LEVEL="$(GPS_DEBUG_LEVEL)" BSP_UART_CLOCK_HZ="$(BSP_UART_CLOCK_HZ)" BSP_UART_BAUDRATE="$(BSP_UART_BAUDRATE)" BSP_CLINT_TIMEBASE_HZ="$(BSP_CLINT_TIMEBASE_HZ)" BSP_DTIM_BASE="$(BSP_DTIM_BASE)" BSP_DTIM_SIZE="$(BSP_DTIM_SIZE)" all
|
@make -C gps_system TOOLCHAIN="$(TOOLCHAIN)" BUILD_DIR="$(OUT_DIR)/gps_system" GPS_DEBUG="$(GPS_DEBUG)" GPS_DEBUG_LEVEL="$(GPS_DEBUG_LEVEL)" BSP_UART_CLOCK_HZ="$(BSP_UART_CLOCK_HZ)" BSP_UART_BAUDRATE="$(BSP_UART_BAUDRATE)" BSP_CLINT_TIMEBASE_HZ="$(BSP_CLINT_TIMEBASE_HZ)" BSP_DTIM_BASE="$(BSP_DTIM_BASE)" BSP_DTIM_SIZE="$(BSP_DTIM_SIZE)" all
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,11 @@
|
|||||||
#define PMOD_SPI_CTRL_BUSY 0x1u
|
#define PMOD_SPI_CTRL_BUSY 0x1u
|
||||||
#define PMOD_SPI_CTRL_CSN 0x2u
|
#define PMOD_SPI_CTRL_CSN 0x2u
|
||||||
#define PMOD_SPI_CTRL_QUAD 0x4u
|
#define PMOD_SPI_CTRL_QUAD 0x4u
|
||||||
|
#define PMOD_SPI_CTRL_CPOL 0x8u
|
||||||
|
#define PMOD_SPI_CTRL_CPHA 0x10u
|
||||||
|
#define PMOD_SPI_CTRL_WRITABLE \
|
||||||
|
(PMOD_SPI_CTRL_CSN | PMOD_SPI_CTRL_QUAD | \
|
||||||
|
PMOD_SPI_CTRL_CPOL | PMOD_SPI_CTRL_CPHA)
|
||||||
|
|
||||||
static uint32_t pmod_spi_ctrl_shadow = PMOD_SPI_CTRL_CSN;
|
static uint32_t pmod_spi_ctrl_shadow = PMOD_SPI_CTRL_CSN;
|
||||||
static uint32_t pmod_spi_cs_setup_us;
|
static uint32_t pmod_spi_cs_setup_us;
|
||||||
@@ -42,7 +47,7 @@ static void pmod_spi_wait_ready(void)
|
|||||||
static void pmod_spi_write_ctrl(uint32_t ctrl)
|
static void pmod_spi_write_ctrl(uint32_t ctrl)
|
||||||
{
|
{
|
||||||
pmod_spi_wait_ready();
|
pmod_spi_wait_ready();
|
||||||
pmod_spi_ctrl_shadow = ctrl & (PMOD_SPI_CTRL_CSN | PMOD_SPI_CTRL_QUAD);
|
pmod_spi_ctrl_shadow = ctrl & PMOD_SPI_CTRL_WRITABLE;
|
||||||
bsp_write32(PMOD_SPI_CTRL, pmod_spi_ctrl_shadow);
|
bsp_write32(PMOD_SPI_CTRL, pmod_spi_ctrl_shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +77,44 @@ void bsp_pmod_spi_set_quad(int enabled)
|
|||||||
bsp_pmod_spi_set_mode(enabled ? BSP_PMOD_SPI_MODE_QUAD : BSP_PMOD_SPI_MODE_SINGLE);
|
bsp_pmod_spi_set_mode(enabled ? BSP_PMOD_SPI_MODE_QUAD : BSP_PMOD_SPI_MODE_SINGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bsp_pmod_spi_set_clock_mode(bsp_pmod_spi_clock_mode_t mode)
|
||||||
|
{
|
||||||
|
uint32_t ctrl =
|
||||||
|
pmod_spi_ctrl_shadow & ~(PMOD_SPI_CTRL_CPOL | PMOD_SPI_CTRL_CPHA);
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case BSP_PMOD_SPI_CLOCK_MODE1:
|
||||||
|
ctrl |= PMOD_SPI_CTRL_CPHA;
|
||||||
|
break;
|
||||||
|
case BSP_PMOD_SPI_CLOCK_MODE2:
|
||||||
|
ctrl |= PMOD_SPI_CTRL_CPOL;
|
||||||
|
break;
|
||||||
|
case BSP_PMOD_SPI_CLOCK_MODE3:
|
||||||
|
ctrl |= PMOD_SPI_CTRL_CPOL | PMOD_SPI_CTRL_CPHA;
|
||||||
|
break;
|
||||||
|
case BSP_PMOD_SPI_CLOCK_MODE0:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pmod_spi_write_ctrl(ctrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
bsp_pmod_spi_clock_mode_t bsp_pmod_spi_clock_mode(void)
|
||||||
|
{
|
||||||
|
const uint32_t bits =
|
||||||
|
pmod_spi_ctrl_shadow & (PMOD_SPI_CTRL_CPOL | PMOD_SPI_CTRL_CPHA);
|
||||||
|
if (bits == (PMOD_SPI_CTRL_CPOL | PMOD_SPI_CTRL_CPHA)) {
|
||||||
|
return BSP_PMOD_SPI_CLOCK_MODE3;
|
||||||
|
}
|
||||||
|
if (bits == PMOD_SPI_CTRL_CPOL) {
|
||||||
|
return BSP_PMOD_SPI_CLOCK_MODE2;
|
||||||
|
}
|
||||||
|
if (bits == PMOD_SPI_CTRL_CPHA) {
|
||||||
|
return BSP_PMOD_SPI_CLOCK_MODE1;
|
||||||
|
}
|
||||||
|
return BSP_PMOD_SPI_CLOCK_MODE0;
|
||||||
|
}
|
||||||
|
|
||||||
void bsp_pmod_spi_set_timing(uint32_t cs_setup_us,
|
void bsp_pmod_spi_set_timing(uint32_t cs_setup_us,
|
||||||
uint32_t inter_byte_us,
|
uint32_t inter_byte_us,
|
||||||
uint32_t cs_hold_us,
|
uint32_t cs_hold_us,
|
||||||
|
|||||||
@@ -9,11 +9,25 @@ typedef enum {
|
|||||||
BSP_PMOD_SPI_MODE_QUAD = 1
|
BSP_PMOD_SPI_MODE_QUAD = 1
|
||||||
} bsp_pmod_spi_mode_t;
|
} bsp_pmod_spi_mode_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BSP_PMOD_SPI_CLOCK_MODE0 = 0,
|
||||||
|
BSP_PMOD_SPI_CLOCK_MODE1 = 1,
|
||||||
|
BSP_PMOD_SPI_CLOCK_MODE2 = 2,
|
||||||
|
BSP_PMOD_SPI_CLOCK_MODE3 = 3
|
||||||
|
} bsp_pmod_spi_clock_mode_t;
|
||||||
|
|
||||||
void bsp_pmod_spi_init(void);
|
void bsp_pmod_spi_init(void);
|
||||||
void bsp_pmod_spi_set_mode(bsp_pmod_spi_mode_t mode);
|
void bsp_pmod_spi_set_mode(bsp_pmod_spi_mode_t mode);
|
||||||
bsp_pmod_spi_mode_t bsp_pmod_spi_mode(void);
|
bsp_pmod_spi_mode_t bsp_pmod_spi_mode(void);
|
||||||
void bsp_pmod_spi_set_quad(int enabled);
|
void bsp_pmod_spi_set_quad(int enabled);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Select the standard CPOL/CPHA SPI clock mode independently of data width.
|
||||||
|
* Mode 0 is hardware-validated; modes 1-3 require target/device validation.
|
||||||
|
*/
|
||||||
|
void bsp_pmod_spi_set_clock_mode(bsp_pmod_spi_clock_mode_t mode);
|
||||||
|
bsp_pmod_spi_clock_mode_t bsp_pmod_spi_clock_mode(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Configure delays used by the chip-select-managed transaction helpers.
|
* Configure delays used by the chip-select-managed transaction helpers.
|
||||||
* All values are in microseconds. Zero disables the corresponding delay.
|
* All values are in microseconds. Zero disables the corresponding delay.
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Offline GPS/ICM-4268x fusion for the stationary Genesys2 capture.
|
"""Offline GPS/IMU fusion for the stationary Genesys2 capture.
|
||||||
|
|
||||||
The two UART logs are captured sequentially, so their elapsed-time axes are
|
The two UART logs are captured sequentially, so their elapsed-time axes are
|
||||||
replayed together. That is valid for the explicitly stationary experiment,
|
replayed together. That is valid for the explicitly stationary experiment,
|
||||||
but is not a substitute for a synchronous moving-platform capture.
|
but is not a substitute for a synchronous moving-platform capture.
|
||||||
|
|
||||||
|
The IMU parser accepts both the original averaged ICM-4268x report and the
|
||||||
|
timestamped raw LSM6DSOX stream from ``steval_mki217v1_test``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -98,8 +101,69 @@ def parse_gps(path: Path) -> list[GpsSample]:
|
|||||||
|
|
||||||
def parse_imu(path: Path) -> list[ImuSample]:
|
def parse_imu(path: Path) -> list[ImuSample]:
|
||||||
samples: list[ImuSample] = []
|
samples: list[ImuSample] = []
|
||||||
|
raw_meta: dict[str, float] | None = None
|
||||||
|
first_raw_tick: int | None = None
|
||||||
for row in read_uart_csv(path):
|
for row in read_uart_csv(path):
|
||||||
match = IMU_RE.search(row["uart_line"])
|
line = row["uart_line"]
|
||||||
|
|
||||||
|
if line.startswith("IMU_META,LSM6DSOX,"):
|
||||||
|
fields = line.split(",")[2:]
|
||||||
|
values: dict[str, float] = {}
|
||||||
|
for field in fields:
|
||||||
|
key, separator, value = field.partition("=")
|
||||||
|
if not separator:
|
||||||
|
continue
|
||||||
|
values[key] = float(value)
|
||||||
|
required = {
|
||||||
|
"tick_hz",
|
||||||
|
"odr_hz",
|
||||||
|
"accel_ug_lsb",
|
||||||
|
"gyro_udps_lsb",
|
||||||
|
"temp_lsb_c",
|
||||||
|
"temp_offset_millic",
|
||||||
|
}
|
||||||
|
missing = required.difference(values)
|
||||||
|
if missing:
|
||||||
|
raise ValueError(
|
||||||
|
f"LSM6DSOX metadata missing {sorted(missing)} in {path}"
|
||||||
|
)
|
||||||
|
if values["tick_hz"] <= 0.0 or values["temp_lsb_c"] <= 0.0:
|
||||||
|
raise ValueError(f"invalid LSM6DSOX metadata in {path}")
|
||||||
|
raw_meta = values
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.startswith("IMU_RAW,LSM6DSOX,"):
|
||||||
|
if raw_meta is None:
|
||||||
|
raise ValueError(f"LSM6DSOX raw sample precedes metadata in {path}")
|
||||||
|
fields = line.split(",")
|
||||||
|
if len(fields) != 11:
|
||||||
|
raise ValueError(f"malformed LSM6DSOX raw sample in {path}: {line}")
|
||||||
|
tick = int(fields[3])
|
||||||
|
if first_raw_tick is None:
|
||||||
|
first_raw_tick = tick
|
||||||
|
raw_accel = np.array([int(x) for x in fields[4:7]], dtype=float)
|
||||||
|
raw_gyro = np.array([int(x) for x in fields[7:10]], dtype=float)
|
||||||
|
raw_temperature = int(fields[10])
|
||||||
|
samples.append(
|
||||||
|
ImuSample(
|
||||||
|
t=(tick - first_raw_tick) / raw_meta["tick_hz"],
|
||||||
|
unix_s=float(row["host_unix_s"]),
|
||||||
|
samples=1,
|
||||||
|
temperature_c=(
|
||||||
|
raw_meta["temp_offset_millic"] / 1000.0
|
||||||
|
+ raw_temperature / raw_meta["temp_lsb_c"]
|
||||||
|
),
|
||||||
|
accel_mps2=(
|
||||||
|
G * raw_accel * raw_meta["accel_ug_lsb"] * 1e-6
|
||||||
|
),
|
||||||
|
gyro_rps=np.deg2rad(
|
||||||
|
raw_gyro * raw_meta["gyro_udps_lsb"] * 1e-6
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
match = IMU_RE.search(line)
|
||||||
if not match:
|
if not match:
|
||||||
continue
|
continue
|
||||||
samples.append(
|
samples.append(
|
||||||
@@ -119,9 +183,10 @@ def parse_imu(path: Path) -> list[ImuSample]:
|
|||||||
)
|
)
|
||||||
if len(samples) < 4:
|
if len(samples) < 4:
|
||||||
raise ValueError(f"only {len(samples)} IMU windows found in {path}")
|
raise ValueError(f"only {len(samples)} IMU windows found in {path}")
|
||||||
t0 = samples[0].t
|
if first_raw_tick is None:
|
||||||
for sample in samples:
|
t0 = samples[0].t
|
||||||
sample.t -= t0
|
for sample in samples:
|
||||||
|
sample.t -= t0
|
||||||
return samples
|
return samples
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -221,6 +221,7 @@ bool tdk_icm42688_init(const tdk_icm42688_config_t* config)
|
|||||||
|
|
||||||
bsp_pmod_spi_init();
|
bsp_pmod_spi_init();
|
||||||
bsp_pmod_spi_set_mode(BSP_PMOD_SPI_MODE_SINGLE);
|
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(10u, 10u, 10u, 10u);
|
bsp_pmod_spi_set_timing(10u, 10u, 10u, 10u);
|
||||||
delay_us(1000u);
|
delay_us(1000u);
|
||||||
diagnostics.who_am_i = read_register(ICM_WHO_AM_I);
|
diagnostics.who_am_i = read_register(ICM_WHO_AM_I);
|
||||||
|
|||||||
@@ -406,6 +406,7 @@ int main(void)
|
|||||||
|
|
||||||
bsp_pmod_spi_init();
|
bsp_pmod_spi_init();
|
||||||
bsp_pmod_spi_set_mode(BSP_PMOD_SPI_MODE_SINGLE);
|
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(10u, 10u, 10u, 10u);
|
bsp_pmod_spi_set_timing(10u, 10u, 10u, 10u);
|
||||||
delay_us(1000u);
|
delay_us(1000u);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
TARGET := steval_mki217v1_test
|
||||||
|
|
||||||
|
APP_SRCS := \
|
||||||
|
main.c \
|
||||||
|
src/lsm6dsox.c
|
||||||
|
|
||||||
|
BSP_SRCS := init.c uart.c pmod_spi.c
|
||||||
|
|
||||||
|
# Genesys2 fabric/UART and CLINT defaults. Top-level board builds may override.
|
||||||
|
BSP_UART_CLOCK_HZ ?= 50000000
|
||||||
|
BSP_CLINT_TIMEBASE_HZ ?= 500000
|
||||||
|
|
||||||
|
LSM6DSOX_ODR_HZ ?= 104
|
||||||
|
CAPTURE_DURATION_S ?= 60
|
||||||
|
|
||||||
|
include ../common/app.mk
|
||||||
|
|
||||||
|
CFLAGS += -Iheader
|
||||||
|
CFLAGS += -DLSM6DSOX_ODR_HZ=$(LSM6DSOX_ODR_HZ) \
|
||||||
|
-DCAPTURE_DURATION_S=$(CAPTURE_DURATION_S)
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
# STEVAL-MKI217V1 polling test (Genesys2)
|
||||||
|
|
||||||
|
This standalone bare-metal application reads the LSM6DSOX on the
|
||||||
|
STEVAL-MKI217V1 over the existing Genesys2 PMOD SPI peripheral. It does not
|
||||||
|
use INT1/INT2. The LIS2MDL magnetometer on the module is not enabled in this
|
||||||
|
first electrical/readout smoke test; it is internally reachable through the
|
||||||
|
LSM6DSOX sensor hub and will be integrated separately after the IMU link is
|
||||||
|
verified.
|
||||||
|
|
||||||
|
## Wiring
|
||||||
|
|
||||||
|
| STEVAL-MKI217V1 | Genesys2 | Function |
|
||||||
|
|---|---|---|
|
||||||
|
| CS | JC1 | active-low chip select |
|
||||||
|
| SDA | JC2 | SPI MOSI / SDI |
|
||||||
|
| SDO | JC3 | SPI MISO |
|
||||||
|
| SCL | JC4 | SPI clock / SPC |
|
||||||
|
| VDD | 3.3 V | sensor supply |
|
||||||
|
| VDDIO | 3.3 V | digital I/O supply |
|
||||||
|
| GND | GND | common ground |
|
||||||
|
|
||||||
|
`CS` is mandatory for four-wire SPI. Never feed 5 V to VDDIO. JC9 is the
|
||||||
|
optional sensor interrupt pin but is deliberately unused here.
|
||||||
|
|
||||||
|
## Hardware bring-up results (2026-07-13)
|
||||||
|
|
||||||
|
The Genesys2/Rocket ELF was loaded over GDB and the UART output was captured
|
||||||
|
on `/dev/ttyUSB0`. All eight identity reads returned `0xff` instead of the
|
||||||
|
expected `0x6c`. In that trial SCL, SDA, SDO, VDDIO, VDD and GND were connected
|
||||||
|
but CS was not. An all-`0xff` response is therefore the expected open/unselected
|
||||||
|
SPI-bus result, not a valid sensor sample.
|
||||||
|
|
||||||
|
After CS was connected to JC1, all eight identity reads returned `0x6c` and
|
||||||
|
configuration read-back returned `CTRL1_XL=0x40`, `CTRL2_G=0x40`,
|
||||||
|
`CTRL3_C=0x44` and `STATUS=0x07`.
|
||||||
|
|
||||||
|
The original FPGA SPI implementation sampled MISO on the data-change edge.
|
||||||
|
A first 60-second diagnostic log consequently contained about 68.4% bit-shifted
|
||||||
|
samples. The generic SPI control register was extended with independent CPOL
|
||||||
|
and CPHA bits while retaining the single/quad-width bit. Mode 0 is selected by
|
||||||
|
this driver and has been verified on hardware. A Mode-3 probe returned an
|
||||||
|
incorrect identity byte (`0x28`), so the CPHA=1 path is not yet production
|
||||||
|
validated.
|
||||||
|
|
||||||
|
One additional LSM6DSOX-specific issue was found: a 14-byte burst crossing the
|
||||||
|
`OUT_TEMP_H` to `OUTX_L_G` internal output-block boundary shifted the remaining
|
||||||
|
stream by one bit. Delaying between bytes did not help. The permanent readout
|
||||||
|
therefore uses a 2-byte temperature burst followed by one coherent 12-byte
|
||||||
|
gyro+accelerometer burst. This is not sample filtering or post-processing; it
|
||||||
|
matches the sensor's output block structure and preserves BDU coherence.
|
||||||
|
|
||||||
|
The final real-hardware capture is
|
||||||
|
`../fusion/logs/steval_mki217v1_clean_60s.csv`:
|
||||||
|
|
||||||
|
- 6349 samples over 59.994462 seconds (105.810 Hz measured)
|
||||||
|
- sequence discontinuities: 0
|
||||||
|
- bit-shifted, saturated or invalid samples: 0
|
||||||
|
- maximum timestamp gap: 4742 CLINT ticks (9.484 ms)
|
||||||
|
- acceleration norm: 9.94245 +/- 0.00687 m/s^2
|
||||||
|
- gyro mean XYZ: +0.20395, -0.07578, -0.27628 deg/s
|
||||||
|
- gyro standard deviation XYZ: 0.02600, 0.02379, 0.02630 deg/s
|
||||||
|
- temperature: 25.909 +/- 0.056 degC
|
||||||
|
|
||||||
|
These measurements are for the stationary board in its test orientation. The
|
||||||
|
nonzero gyro mean is startup bias to be estimated by stationary calibration,
|
||||||
|
not angular motion.
|
||||||
|
|
||||||
|
## Build and load
|
||||||
|
|
||||||
|
From the repository root:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make FPGA_BOARD=genesys2 fpga-elf-flash-image \
|
||||||
|
APP=steval_mki217v1_test
|
||||||
|
|
||||||
|
make FPGA_BOARD=genesys2 fpga-debug-load \
|
||||||
|
APP=steval_mki217v1_test
|
||||||
|
```
|
||||||
|
|
||||||
|
The default run is 104 Hz for 60 seconds. Alternatives can be passed through
|
||||||
|
the application directory build:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make -C soc/software/steval_mki217v1_test \
|
||||||
|
BUILD_DIR=../../out/software/steval_mki217v1_test \
|
||||||
|
LSM6DSOX_ODR_HZ=52 CAPTURE_DURATION_S=60
|
||||||
|
```
|
||||||
|
|
||||||
|
Supported smoke-test ODR values are 52, 104 and 208 Hz. At 115200 baud, use
|
||||||
|
52 or 104 Hz for loss-free line-oriented logging. The firmware verifies
|
||||||
|
`WHO_AM_I=0x6c`, selects SPI mode 0, resets the device, enables
|
||||||
|
block-data-update and register auto-increment, and configures +/-2 g and
|
||||||
|
+/-250 dps. The FPGA SPI controller exposes CPOL/CPHA independently from its
|
||||||
|
single/quad data-width selection.
|
||||||
|
|
||||||
|
## UART/replay format
|
||||||
|
|
||||||
|
Each sample is timestamped from the local CLINT/CPU time domain before its SPI
|
||||||
|
burst and emitted without floating-point formatting:
|
||||||
|
|
||||||
|
```text
|
||||||
|
IMU_META,LSM6DSOX,tick_hz=500000,odr_hz=104,accel_ug_lsb=61,gyro_udps_lsb=8750,temp_lsb_c=256,temp_offset_millic=25000
|
||||||
|
IMU_RAW,LSM6DSOX,0,12345678,ax,ay,az,gx,gy,gz,temp
|
||||||
|
```
|
||||||
|
|
||||||
|
Scale factors are 61 ug/LSB, 8750 udps/LSB and 256 LSB/degC with a 25 degC
|
||||||
|
offset. `fusion/fusion_eskf.py` accepts this raw format directly, so a captured
|
||||||
|
log can be replayed with an existing GPS UART capture. Sequential GPS and IMU
|
||||||
|
captures are meaningful only for a stationary test. A moving-platform replay
|
||||||
|
requires GPS and IMU samples stamped in the same firmware/run.
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#ifndef LSM6DSOX_H
|
||||||
|
#define LSM6DSOX_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define LSM6DSOX_WHO_AM_I_VALUE 0x6cu
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t timestamp_ticks;
|
||||||
|
int16_t accel[3];
|
||||||
|
int16_t gyro[3];
|
||||||
|
int16_t temperature;
|
||||||
|
} lsm6dsox_sample_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t odr_hz;
|
||||||
|
uint32_t accel_ug_per_lsb;
|
||||||
|
uint32_t gyro_udps_per_lsb;
|
||||||
|
uint32_t temperature_lsb_per_c;
|
||||||
|
int32_t temperature_offset_millic;
|
||||||
|
} lsm6dsox_scale_t;
|
||||||
|
|
||||||
|
/* Initialize the PMOD SPI peripheral, identify and configure the IMU. */
|
||||||
|
int lsm6dsox_init(uint32_t odr_hz);
|
||||||
|
|
||||||
|
/* Read-only helpers used by the smoke-test diagnostics. */
|
||||||
|
uint8_t lsm6dsox_read_who_am_i(void);
|
||||||
|
uint8_t lsm6dsox_read_register(uint8_t reg);
|
||||||
|
|
||||||
|
/* Return 1 for a new coherent gyro+accelerometer sample, 0 if not ready. */
|
||||||
|
int lsm6dsox_poll_sample(lsm6dsox_sample_t *sample);
|
||||||
|
|
||||||
|
void lsm6dsox_power_down(void);
|
||||||
|
const lsm6dsox_scale_t *lsm6dsox_scale(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "bsp/mmio.h"
|
||||||
|
#include "bsp/soc.h"
|
||||||
|
#include "bsp/uart.h"
|
||||||
|
#include "lsm6dsox.h"
|
||||||
|
|
||||||
|
#ifndef LSM6DSOX_ODR_HZ
|
||||||
|
#define LSM6DSOX_ODR_HZ 104u
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CAPTURE_DURATION_S
|
||||||
|
#define CAPTURE_DURATION_S 60u
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static uint64_t time_now(void)
|
||||||
|
{
|
||||||
|
return bsp_read64(BSP_CLINT_MTIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_identity_probe(void)
|
||||||
|
{
|
||||||
|
printf("WHO_AM_I probes:");
|
||||||
|
for (unsigned i = 0u; i < 8u; ++i) {
|
||||||
|
printf(" %02x", lsm6dsox_read_who_am_i());
|
||||||
|
}
|
||||||
|
printf(" (expected 6c)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
printf("\nSTEVAL-MKI217V1 / LSM6DSOX SPI polling test\n");
|
||||||
|
printf("Genesys2 JC: CS=JC1 SDA/MOSI=JC2 SDO/MISO=JC3 SCL/SCK=JC4\n");
|
||||||
|
printf("Power: VDD=3.3V VDDIO=3.3V common GND; INT is not used\n");
|
||||||
|
printf("SPI MMIO=0x%08lx CLINT=%lu Hz UART=%lu baud\n",
|
||||||
|
(unsigned long)BSP_PMOD_SPI_BASE,
|
||||||
|
(unsigned long)BSP_CLINT_TIMEBASE_HZ,
|
||||||
|
(unsigned long)bsp_uart_baudrate());
|
||||||
|
|
||||||
|
if (!lsm6dsox_init(LSM6DSOX_ODR_HZ)) {
|
||||||
|
print_identity_probe();
|
||||||
|
printf("INIT FAIL: check CS, MOSI, MISO, SCK, 3.3V and common GND.\n");
|
||||||
|
printf("0x00 usually means MISO held low; 0xff usually means no selected device.\n");
|
||||||
|
bsp_uart_wait_tx_idle();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_identity_probe();
|
||||||
|
printf("CONFIG CTRL1_XL=%02x CTRL2_G=%02x CTRL3_C=%02x STATUS=%02x\n",
|
||||||
|
lsm6dsox_read_register(0x10u),
|
||||||
|
lsm6dsox_read_register(0x11u),
|
||||||
|
lsm6dsox_read_register(0x12u),
|
||||||
|
lsm6dsox_read_register(0x1eu));
|
||||||
|
|
||||||
|
const lsm6dsox_scale_t *scale = lsm6dsox_scale();
|
||||||
|
printf("IMU_META,LSM6DSOX,tick_hz=%lu,odr_hz=%lu,"
|
||||||
|
"accel_ug_lsb=%lu,gyro_udps_lsb=%lu,temp_lsb_c=%lu,"
|
||||||
|
"temp_offset_millic=%ld\n",
|
||||||
|
(unsigned long)BSP_CLINT_TIMEBASE_HZ,
|
||||||
|
(unsigned long)scale->odr_hz,
|
||||||
|
(unsigned long)scale->accel_ug_per_lsb,
|
||||||
|
(unsigned long)scale->gyro_udps_per_lsb,
|
||||||
|
(unsigned long)scale->temperature_lsb_per_c,
|
||||||
|
(long)scale->temperature_offset_millic);
|
||||||
|
printf("IMU_COLUMNS,sensor,seq,tick,ax_raw,ay_raw,az_raw,"
|
||||||
|
"gx_raw,gy_raw,gz_raw,temp_raw\n");
|
||||||
|
|
||||||
|
const uint64_t start = time_now();
|
||||||
|
const uint64_t duration_ticks =
|
||||||
|
(uint64_t)BSP_CLINT_TIMEBASE_HZ * CAPTURE_DURATION_S;
|
||||||
|
uint32_t samples = 0u;
|
||||||
|
uint64_t previous_tick = 0u;
|
||||||
|
uint64_t maximum_gap = 0u;
|
||||||
|
|
||||||
|
while ((time_now() - start) < duration_ticks) {
|
||||||
|
lsm6dsox_sample_t sample;
|
||||||
|
if (!lsm6dsox_poll_sample(&sample)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previous_tick != 0u) {
|
||||||
|
const uint64_t gap = sample.timestamp_ticks - previous_tick;
|
||||||
|
if (gap > maximum_gap) {
|
||||||
|
maximum_gap = gap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previous_tick = sample.timestamp_ticks;
|
||||||
|
|
||||||
|
printf("IMU_RAW,LSM6DSOX,%lu,%lu,%d,%d,%d,%d,%d,%d,%d\n",
|
||||||
|
(unsigned long)samples,
|
||||||
|
(unsigned long)sample.timestamp_ticks,
|
||||||
|
(int)sample.accel[0], (int)sample.accel[1],
|
||||||
|
(int)sample.accel[2], (int)sample.gyro[0],
|
||||||
|
(int)sample.gyro[1], (int)sample.gyro[2],
|
||||||
|
(int)sample.temperature);
|
||||||
|
samples++;
|
||||||
|
}
|
||||||
|
|
||||||
|
lsm6dsox_power_down();
|
||||||
|
printf("IMU_DONE,samples=%lu,duration_s=%u,max_gap_ticks=%lu\n",
|
||||||
|
(unsigned long)samples, (unsigned)CAPTURE_DURATION_S,
|
||||||
|
(unsigned long)maximum_gap);
|
||||||
|
bsp_uart_wait_tx_idle();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
#include "lsm6dsox.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "bsp/mmio.h"
|
||||||
|
#include "bsp/pmod_spi.h"
|
||||||
|
#include "bsp/soc.h"
|
||||||
|
|
||||||
|
#define LSM6DSOX_SPI_READ 0x80u
|
||||||
|
#define LSM6DSOX_SPI_DUMMY 0xffu
|
||||||
|
|
||||||
|
#define LSM6DSOX_CTRL1_XL 0x10u
|
||||||
|
#define LSM6DSOX_CTRL2_G 0x11u
|
||||||
|
#define LSM6DSOX_CTRL3_C 0x12u
|
||||||
|
#define LSM6DSOX_STATUS_REG 0x1eu
|
||||||
|
#define LSM6DSOX_OUT_TEMP_L 0x20u
|
||||||
|
#define LSM6DSOX_OUTX_L_G 0x22u
|
||||||
|
#define LSM6DSOX_WHO_AM_I 0x0fu
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
/* CTRL1_XL/CTRL2_G ODR fields (bits 7:4). */
|
||||||
|
#define LSM6DSOX_ODR_52 0x30u
|
||||||
|
#define LSM6DSOX_ODR_104 0x40u
|
||||||
|
#define LSM6DSOX_ODR_208 0x50u
|
||||||
|
|
||||||
|
/* This first integration uses +-2 g and +-250 dps. */
|
||||||
|
static const lsm6dsox_scale_t sensor_scale = {
|
||||||
|
0u,
|
||||||
|
61u, /* 0.061 mg/LSB = 61 ug/LSB. */
|
||||||
|
8750u, /* 8.75 mdps/LSB = 8750 udps/LSB. */
|
||||||
|
256u,
|
||||||
|
25000
|
||||||
|
};
|
||||||
|
|
||||||
|
static lsm6dsox_scale_t configured_scale;
|
||||||
|
|
||||||
|
static uint64_t time_now(void)
|
||||||
|
{
|
||||||
|
return bsp_read64(BSP_CLINT_MTIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void delay_ms(uint32_t ms)
|
||||||
|
{
|
||||||
|
const uint64_t ticks =
|
||||||
|
((uint64_t)BSP_CLINT_TIMEBASE_HZ * ms + 999u) / 1000u;
|
||||||
|
const uint64_t start = time_now();
|
||||||
|
while ((time_now() - start) < ticks) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_reg(uint8_t reg, uint8_t value)
|
||||||
|
{
|
||||||
|
bsp_pmod_spi_write_reg8(reg & (uint8_t)~LSM6DSOX_SPI_READ, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t read_reg(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 int odr_register_value(uint32_t odr_hz, uint8_t *value)
|
||||||
|
{
|
||||||
|
switch (odr_hz) {
|
||||||
|
case 52u:
|
||||||
|
*value = LSM6DSOX_ODR_52;
|
||||||
|
return 1;
|
||||||
|
case 104u:
|
||||||
|
*value = LSM6DSOX_ODR_104;
|
||||||
|
return 1;
|
||||||
|
case 208u:
|
||||||
|
*value = LSM6DSOX_ODR_208;
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t lsm6dsox_read_who_am_i(void)
|
||||||
|
{
|
||||||
|
return read_reg(LSM6DSOX_WHO_AM_I);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t lsm6dsox_read_register(uint8_t reg)
|
||||||
|
{
|
||||||
|
return read_reg(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lsm6dsox_init(uint32_t odr_hz)
|
||||||
|
{
|
||||||
|
uint8_t odr_value;
|
||||||
|
if (!odr_register_value(odr_hz, &odr_value)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
/* Conservative bring-up timing; SPI SCK remains controlled by the FPGA IP. */
|
||||||
|
bsp_pmod_spi_set_timing(2u, 2u, 2u, 5u);
|
||||||
|
delay_ms(20u);
|
||||||
|
|
||||||
|
if (read_reg(LSM6DSOX_WHO_AM_I) != LSM6DSOX_WHO_AM_I_VALUE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_reg(LSM6DSOX_CTRL3_C, LSM6DSOX_CTRL3_SW_RESET);
|
||||||
|
const uint64_t reset_start = time_now();
|
||||||
|
const uint64_t reset_timeout =
|
||||||
|
((uint64_t)BSP_CLINT_TIMEBASE_HZ * 100u + 999u) / 1000u;
|
||||||
|
while ((read_reg(LSM6DSOX_CTRL3_C) & LSM6DSOX_CTRL3_SW_RESET) != 0u) {
|
||||||
|
if ((time_now() - reset_start) >= reset_timeout) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delay_ms(10u);
|
||||||
|
|
||||||
|
/* Block partial output updates and auto-increment during burst reads. */
|
||||||
|
write_reg(LSM6DSOX_CTRL3_C,
|
||||||
|
LSM6DSOX_CTRL3_BDU | LSM6DSOX_CTRL3_IF_INC);
|
||||||
|
|
||||||
|
/* ODR plus FS=00: +-2 g accelerometer, +-250 dps gyroscope. */
|
||||||
|
write_reg(LSM6DSOX_CTRL1_XL, odr_value);
|
||||||
|
write_reg(LSM6DSOX_CTRL2_G, odr_value);
|
||||||
|
delay_ms(100u);
|
||||||
|
|
||||||
|
if ((read_reg(LSM6DSOX_CTRL3_C) !=
|
||||||
|
(LSM6DSOX_CTRL3_BDU | LSM6DSOX_CTRL3_IF_INC)) ||
|
||||||
|
(read_reg(LSM6DSOX_CTRL1_XL) != odr_value) ||
|
||||||
|
(read_reg(LSM6DSOX_CTRL2_G) != odr_value)) {
|
||||||
|
lsm6dsox_power_down();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
configured_scale = sensor_scale;
|
||||||
|
configured_scale.odr_hz = odr_hz;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lsm6dsox_poll_sample(lsm6dsox_sample_t *sample)
|
||||||
|
{
|
||||||
|
uint8_t raw[14];
|
||||||
|
if (sample == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t status = read_reg(LSM6DSOX_STATUS_REG);
|
||||||
|
if ((status & (LSM6DSOX_STATUS_XLDA | LSM6DSOX_STATUS_GDA)) !=
|
||||||
|
(LSM6DSOX_STATUS_XLDA | LSM6DSOX_STATUS_GDA)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Timestamp the measurement before the foreground SPI burst. */
|
||||||
|
sample->timestamp_ticks = time_now();
|
||||||
|
bsp_pmod_spi_read_regs(LSM6DSOX_OUT_TEMP_L | LSM6DSOX_SPI_READ,
|
||||||
|
LSM6DSOX_SPI_DUMMY,
|
||||||
|
raw,
|
||||||
|
2u);
|
||||||
|
/* Do not cross the temperature-to-gyroscope internal output-block boundary
|
||||||
|
* in one SPI burst. Gyroscope and accelerometer form one 12-byte block. */
|
||||||
|
bsp_pmod_spi_read_regs(LSM6DSOX_OUTX_L_G | LSM6DSOX_SPI_READ,
|
||||||
|
LSM6DSOX_SPI_DUMMY,
|
||||||
|
&raw[2],
|
||||||
|
sizeof(raw) - 2u);
|
||||||
|
|
||||||
|
sample->temperature = little_endian_i16(&raw[0]);
|
||||||
|
sample->gyro[0] = little_endian_i16(&raw[2]);
|
||||||
|
sample->gyro[1] = little_endian_i16(&raw[4]);
|
||||||
|
sample->gyro[2] = little_endian_i16(&raw[6]);
|
||||||
|
sample->accel[0] = little_endian_i16(&raw[8]);
|
||||||
|
sample->accel[1] = little_endian_i16(&raw[10]);
|
||||||
|
sample->accel[2] = little_endian_i16(&raw[12]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lsm6dsox_power_down(void)
|
||||||
|
{
|
||||||
|
write_reg(LSM6DSOX_CTRL1_XL, 0u);
|
||||||
|
write_reg(LSM6DSOX_CTRL2_G, 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
const lsm6dsox_scale_t *lsm6dsox_scale(void)
|
||||||
|
{
|
||||||
|
return &configured_scale;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user