mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
194 lines
5.2 KiB
Go
194 lines
5.2 KiB
Go
// Package qmi8658c provides a driver for the QMI8658C accelerometer and gyroscope
|
|
// made by QST Solutions.
|
|
//
|
|
// Datasheet:
|
|
// https://www.qstcorp.com/upload/pdf/202202/%EF%BC%88%E5%B7%B2%E4%BC%A0%EF%BC%89QMI8658C%20datasheet%20rev%200.9.pdf
|
|
package qmi8656c
|
|
|
|
import (
|
|
"tinygo.org/x/drivers"
|
|
"tinygo.org/x/drivers/internal/legacy"
|
|
)
|
|
|
|
// Device wraps the I2C connection to the QMIC8658 sensor
|
|
type Device struct {
|
|
bus drivers.I2C
|
|
Address uint16
|
|
AccLsbDiv uint16
|
|
GyroLsbDiv uint16
|
|
}
|
|
|
|
type Config struct {
|
|
// SPI Config
|
|
SPIMode byte // One of SPI_X_WIRE
|
|
SPIEndian byte // One of SPI_XXX_ENDIAN
|
|
SPIAutoInc byte // One of SPI_NOT_AUTO_INC or SPI_AUTO_INC
|
|
// Accelerometer
|
|
AccEnable byte // One of ACC_ENABLE or ACC_DISABLE
|
|
AccScale byte // One of ACC_XG
|
|
AccRate byte // One of ACC_XX_YYHZ
|
|
AccLowPass byte // One of ACC_LOW_PASS_X
|
|
// Gyro
|
|
GyroEnable byte // One of GYRO_X_ENABLE or GYRO_DISABLE
|
|
GyroScale byte // One of GYRO_XDPS
|
|
GyroRate byte // One of GYRO_X_YHZ
|
|
GyroLowPass byte // One of GYRO_LOW_PASS_X
|
|
}
|
|
|
|
// Create a new device with the I2C passed, correct address and nil values for
|
|
// AccLsbDiv and GyroLsbDiv, which will be corrected based on the config.
|
|
func New(bus drivers.I2C) Device {
|
|
return Device{
|
|
bus,
|
|
Address,
|
|
1,
|
|
1,
|
|
}
|
|
}
|
|
|
|
// Check if the device is connected by calling WHO_AM_I and checking the
|
|
// default identifier.
|
|
func (d *Device) Connected() bool {
|
|
data := []byte{0}
|
|
d.ReadRegister(WHO_AM_I, data)
|
|
return data[0] == IDENTIFIER
|
|
}
|
|
|
|
// Create a basic default configuration that works with the "WaveShare RP2040
|
|
// Round LCD 1.28in".
|
|
func DefaultConfig() (cfg Config) {
|
|
return Config{
|
|
SPIMode: SPI_4_WIRE,
|
|
SPIEndian: SPI_BIG_ENDIAN,
|
|
SPIAutoInc: SPI_AUTO_INC,
|
|
AccEnable: ACC_ENABLE,
|
|
AccScale: ACC_8G,
|
|
AccRate: ACC_NORMAL_1000HZ,
|
|
AccLowPass: ACC_LOW_PASS_2_62,
|
|
GyroEnable: GYRO_FULL_ENABLE,
|
|
GyroScale: GYRO_512DPS,
|
|
GyroRate: GYRO_1000HZ,
|
|
GyroLowPass: GYRO_LOW_PASS_2_62,
|
|
}
|
|
}
|
|
|
|
// Check if the user has defined a desired configuration, if not uses the
|
|
// DefaultConfig, then defines the AccLsbDiv and GyroLsbDiv based on the
|
|
// configurations and, finally, send the commands and configure the IMU.
|
|
func (d *Device) Configure(cfg Config) {
|
|
if cfg == (Config{}) {
|
|
cfg = DefaultConfig()
|
|
}
|
|
var val uint16
|
|
// Setting accelerometer LSB
|
|
switch cfg.AccScale {
|
|
case ACC_2G:
|
|
d.AccLsbDiv = 1 << 14
|
|
case ACC_4G:
|
|
d.AccLsbDiv = 1 << 13
|
|
case ACC_8G:
|
|
d.AccLsbDiv = 1 << 12
|
|
case ACC_16G:
|
|
d.AccLsbDiv = 1 << 11
|
|
default:
|
|
d.AccLsbDiv = 1 << 12
|
|
}
|
|
// Setting gyro LSB
|
|
switch cfg.GyroScale {
|
|
case GYRO_16DPS:
|
|
d.GyroLsbDiv = 2048
|
|
case GYRO_32DPS:
|
|
d.GyroLsbDiv = 1024
|
|
case GYRO_64DPS:
|
|
d.GyroLsbDiv = 512
|
|
case GYRO_128DPS:
|
|
d.GyroLsbDiv = 256
|
|
case GYRO_256DPS:
|
|
d.GyroLsbDiv = 128
|
|
case GYRO_512DPS:
|
|
d.GyroLsbDiv = 64
|
|
case GYRO_1024DPS:
|
|
d.GyroLsbDiv = 32
|
|
case GYRO_2048DPS:
|
|
d.GyroLsbDiv = 16
|
|
default:
|
|
d.GyroLsbDiv = 64
|
|
}
|
|
// SPI Modes
|
|
val = uint16((cfg.SPIMode | cfg.SPIEndian | cfg.SPIAutoInc))
|
|
d.WriteRegister(CTRL1, val)
|
|
// Accelerometer config
|
|
val = uint16(cfg.AccScale | cfg.AccRate)
|
|
d.WriteRegister(CTRL2, val)
|
|
// Gyro config
|
|
val = uint16(cfg.GyroScale | cfg.GyroRate)
|
|
d.WriteRegister(CTRL3, val)
|
|
// Sensor DSP config
|
|
val = uint16(cfg.GyroLowPass | cfg.AccLowPass)
|
|
d.WriteRegister(CTRL5, val)
|
|
// Sensors config
|
|
val = uint16(cfg.GyroEnable | cfg.AccEnable)
|
|
d.WriteRegister(CTRL7, val)
|
|
}
|
|
|
|
// Read the acceleration from the sensor, the values returned are in mg
|
|
// (milli gravity), which means that 1000 = 1g.
|
|
func (d *Device) ReadAcceleration() (x int32, y int32, z int32) {
|
|
data := make([]byte, 6)
|
|
raw := make([]int32, 3)
|
|
d.ReadRegister(ACC_XOUT_L, data)
|
|
for i := range raw {
|
|
raw[i] = int32(uint16(data[(2*i+1)])<<8 | uint16(data[i]))
|
|
if raw[i] >= 32767 {
|
|
raw[i] = raw[i] - 65535
|
|
}
|
|
}
|
|
x = -raw[0] * 1000 / int32(d.AccLsbDiv)
|
|
y = -raw[1] * 1000 / int32(d.AccLsbDiv)
|
|
z = -raw[2] * 1000 / int32(d.AccLsbDiv)
|
|
return x, y, z
|
|
}
|
|
|
|
// Read the rotation from the sensor, the values returned are in mdeg/sec
|
|
// (milli degress/second), which means that a full rotation is 360000.
|
|
func (d *Device) ReadRotation() (x int32, y int32, z int32) {
|
|
data := make([]byte, 6)
|
|
raw := make([]int32, 3)
|
|
d.ReadRegister(GYRO_XOUT_L, data)
|
|
for i := range raw {
|
|
raw[i] = int32(uint16(data[(2*i+1)])<<8 | uint16(data[i]))
|
|
if raw[i] >= 32767 {
|
|
raw[i] = raw[i] - 65535
|
|
}
|
|
}
|
|
x = raw[0] * 1000 / int32(d.GyroLsbDiv)
|
|
y = raw[1] * 1000 / int32(d.GyroLsbDiv)
|
|
z = raw[2] * 1000 / int32(d.GyroLsbDiv)
|
|
return x, y, z
|
|
}
|
|
|
|
// Read the temperature from the sensor, the values returned are in
|
|
// millidegrees Celsius.
|
|
func (d *Device) ReadTemperature() (int32, error) {
|
|
data := make([]byte, 2)
|
|
err := d.ReadRegister(TEMP_OUT_L, data)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
raw := uint16(data[1])<<8 | uint16(data[0])
|
|
t := int32(raw) * 1000 / 256
|
|
return t, err
|
|
}
|
|
|
|
// Convenience method to read the register and avoid repetition.
|
|
func (d *Device) ReadRegister(reg uint8, buf []byte) error {
|
|
return legacy.ReadRegister(d.bus, uint8(d.Address), reg, buf)
|
|
}
|
|
|
|
// Convenience method to write the register and avoid repetition.
|
|
func (d *Device) WriteRegister(reg uint8, v uint16) error {
|
|
data := []byte{byte(v)}
|
|
err := legacy.WriteRegister(d.bus, uint8(d.Address), reg, data)
|
|
return err
|
|
}
|