Step 32 of 56
Sensors: the QMI8658C IMU and SHT41
One register-mapped device and one command-based device — the two shapes almost every I2C sensor takes, and how each reports that its reading is trustworthy.
The shared shape
Every I2C device driver here follows the same pattern, and it is worth stating once because it repeats across the rest of these pages:
The driver hard-codes no board wiring. You tell
Setup which host, which SDA/SCL pins, optionally which INT pin, and
the address; the Device remembers them. Each operation then opens a
short-lived I2C session for one complete
transaction and lets it release the host on scope exit. So devices share a bus
safely, and a fault mid-transaction cannot leak the lock. A Device
is limited — it owns the wiring it was set up with and cannot be copied.
QMI8658C: a register-mapped IMU
A 6-axis part — 3-axis accelerometer plus 3-axis gyroscope. SA0 selects
address 0x6A or 0x6B.
type Accel_Range is (Range_2G, Range_4G, Range_8G, Range_16G);
type Gyro_Range is ...;
type Output_Rate is ...;
type Axes is record ... end record;
type Status is (OK, Bus_Error);
procedure Read_Accelerometer (Dev : Device; A : out Axes; Result : out Status);
procedure Read_Gyroscope (Dev : Device; G : out Axes; Result : out Status);
procedure Read_Temperature (Dev : Device; Raw : out Interfaces.Integer_16; Result : out Status);
procedure Data_Ready (Dev : Device; Accel, Gyro : out Boolean; Result : out Status);
Reads use the chip's auto-incrementing address pointer, set by
Configure via CTRL1.ADDR_AI: a one-byte write sets the
pointer and the following read streams from it. The driver runs the device
little-endian (CTRL1.BE = 0).
Raw counts are not physical units, and the scale depends on the range you configured. That is what these two are for:
function Accel_LSB_Per_G (Dev : Device) return Positive;
function Gyro_LSB_Per_DPS (Dev : Device) return Positive;
Ask the device rather than hard-coding a divisor, and changing
Accel_Range later cannot silently scale every reading wrong. Both are
SPARK-mode functions.
SHT41: a command-based sensor
The SHT4x has no registers at all. A measurement
is: write a one-byte command, wait the conversion time, read six bytes. That is a
genuinely different protocol shape from the IMU, and it is why the driver exposes
Measure rather than register accessors.
type Precision is (Low, Medium, High);
type Measurement is record ... end record;
type Status is (OK, Bus_Error, CRC_Error);
procedure Measure (...);
procedure Read_Serial_Number (...);
procedure Reset (Dev : Device; Result : out Status);
Precision trades conversion time against noise. The six returned
bytes are two CRC-8-protected words, and the driver checks them — hence the third
Status value:
CRC_Error is not Bus_Error. The
device ACKed and returned data, but the checksum failed — so the wiring is
probably fine and you are looking at interference, an over-long cable, or too
fast a bus. Treat it as "retry", not "device missing".
There is no interrupt line: the sensor is read on request.