tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/qmi8658c/qmi8658c.go (about) 1 // Package qmi8658c provides a driver for the QMI8658C accelerometer and gyroscope 2 // made by QST Solutions. 3 // 4 // Datasheet: 5 // https://www.qstcorp.com/upload/pdf/202202/%EF%BC%88%E5%B7%B2%E4%BC%A0%EF%BC%89QMI8658C%20datasheet%20rev%200.9.pdf 6 package qmi8656c 7 8 import ( 9 "tinygo.org/x/drivers" 10 "tinygo.org/x/drivers/internal/legacy" 11 ) 12 13 // Device wraps the I2C connection to the QMIC8658 sensor 14 type Device struct { 15 bus drivers.I2C 16 Address uint16 17 AccLsbDiv uint16 18 GyroLsbDiv uint16 19 } 20 21 type Config struct { 22 // SPI Config 23 SPIMode byte // One of SPI_X_WIRE 24 SPIEndian byte // One of SPI_XXX_ENDIAN 25 SPIAutoInc byte // One of SPI_NOT_AUTO_INC or SPI_AUTO_INC 26 // Accelerometer 27 AccEnable byte // One of ACC_ENABLE or ACC_DISABLE 28 AccScale byte // One of ACC_XG 29 AccRate byte // One of ACC_XX_YYHZ 30 AccLowPass byte // One of ACC_LOW_PASS_X 31 // Gyro 32 GyroEnable byte // One of GYRO_X_ENABLE or GYRO_DISABLE 33 GyroScale byte // One of GYRO_XDPS 34 GyroRate byte // One of GYRO_X_YHZ 35 GyroLowPass byte // One of GYRO_LOW_PASS_X 36 } 37 38 // Create a new device with the I2C passed, correct address and nil values for 39 // AccLsbDiv and GyroLsbDiv, which will be corrected based on the config. 40 func New(bus drivers.I2C) Device { 41 return Device{ 42 bus, 43 Address, 44 1, 45 1, 46 } 47 } 48 49 // Check if the device is connected by calling WHO_AM_I and checking the 50 // default identifier. 51 func (d *Device) Connected() bool { 52 data := []byte{0} 53 d.ReadRegister(WHO_AM_I, data) 54 return data[0] == IDENTIFIER 55 } 56 57 // Create a basic default configuration that works with the "WaveShare RP2040 58 // Round LCD 1.28in". 59 func DefaultConfig() (cfg Config) { 60 return Config{ 61 SPIMode: SPI_4_WIRE, 62 SPIEndian: SPI_BIG_ENDIAN, 63 SPIAutoInc: SPI_AUTO_INC, 64 AccEnable: ACC_ENABLE, 65 AccScale: ACC_8G, 66 AccRate: ACC_NORMAL_1000HZ, 67 AccLowPass: ACC_LOW_PASS_2_62, 68 GyroEnable: GYRO_FULL_ENABLE, 69 GyroScale: GYRO_512DPS, 70 GyroRate: GYRO_1000HZ, 71 GyroLowPass: GYRO_LOW_PASS_2_62, 72 } 73 } 74 75 // Check if the user has defined a desired configuration, if not uses the 76 // DefaultConfig, then defines the AccLsbDiv and GyroLsbDiv based on the 77 // configurations and, finally, send the commands and configure the IMU. 78 func (d *Device) Configure(cfg Config) { 79 if cfg == (Config{}) { 80 cfg = DefaultConfig() 81 } 82 var val uint16 83 // Setting accelerometer LSB 84 switch cfg.AccScale { 85 case ACC_2G: 86 d.AccLsbDiv = 1 << 14 87 case ACC_4G: 88 d.AccLsbDiv = 1 << 13 89 case ACC_8G: 90 d.AccLsbDiv = 1 << 12 91 case ACC_16G: 92 d.AccLsbDiv = 1 << 11 93 default: 94 d.AccLsbDiv = 1 << 12 95 } 96 // Setting gyro LSB 97 switch cfg.GyroScale { 98 case GYRO_16DPS: 99 d.GyroLsbDiv = 2048 100 case GYRO_32DPS: 101 d.GyroLsbDiv = 1024 102 case GYRO_64DPS: 103 d.GyroLsbDiv = 512 104 case GYRO_128DPS: 105 d.GyroLsbDiv = 256 106 case GYRO_256DPS: 107 d.GyroLsbDiv = 128 108 case GYRO_512DPS: 109 d.GyroLsbDiv = 64 110 case GYRO_1024DPS: 111 d.GyroLsbDiv = 32 112 case GYRO_2048DPS: 113 d.GyroLsbDiv = 16 114 default: 115 d.GyroLsbDiv = 64 116 } 117 // SPI Modes 118 val = uint16((cfg.SPIMode | cfg.SPIEndian | cfg.SPIAutoInc)) 119 d.WriteRegister(CTRL1, val) 120 // Accelerometer config 121 val = uint16(cfg.AccScale | cfg.AccRate) 122 d.WriteRegister(CTRL2, val) 123 // Gyro config 124 val = uint16(cfg.GyroScale | cfg.GyroRate) 125 d.WriteRegister(CTRL3, val) 126 // Sensor DSP config 127 val = uint16(cfg.GyroLowPass | cfg.AccLowPass) 128 d.WriteRegister(CTRL5, val) 129 // Sensors config 130 val = uint16(cfg.GyroEnable | cfg.AccEnable) 131 d.WriteRegister(CTRL7, val) 132 } 133 134 // Read the acceleration from the sensor, the values returned are in mg 135 // (milli gravity), which means that 1000 = 1g. 136 func (d *Device) ReadAcceleration() (x int32, y int32, z int32) { 137 data := make([]byte, 6) 138 raw := make([]int32, 3) 139 d.ReadRegister(ACC_XOUT_L, data) 140 for i := range raw { 141 raw[i] = int32(uint16(data[(2*i+1)])<<8 | uint16(data[i])) 142 if raw[i] >= 32767 { 143 raw[i] = raw[i] - 65535 144 } 145 } 146 x = -raw[0] * 1000 / int32(d.AccLsbDiv) 147 y = -raw[1] * 1000 / int32(d.AccLsbDiv) 148 z = -raw[2] * 1000 / int32(d.AccLsbDiv) 149 return x, y, z 150 } 151 152 // Read the rotation from the sensor, the values returned are in mdeg/sec 153 // (milli degress/second), which means that a full rotation is 360000. 154 func (d *Device) ReadRotation() (x int32, y int32, z int32) { 155 data := make([]byte, 6) 156 raw := make([]int32, 3) 157 d.ReadRegister(GYRO_XOUT_L, data) 158 for i := range raw { 159 raw[i] = int32(uint16(data[(2*i+1)])<<8 | uint16(data[i])) 160 if raw[i] >= 32767 { 161 raw[i] = raw[i] - 65535 162 } 163 } 164 x = raw[0] * 1000 / int32(d.GyroLsbDiv) 165 y = raw[1] * 1000 / int32(d.GyroLsbDiv) 166 z = raw[2] * 1000 / int32(d.GyroLsbDiv) 167 return x, y, z 168 } 169 170 // Read the temperature from the sensor, the values returned are in 171 // millidegrees Celsius. 172 func (d *Device) ReadTemperature() (int32, error) { 173 data := make([]byte, 2) 174 err := d.ReadRegister(TEMP_OUT_L, data) 175 if err != nil { 176 return 0, err 177 } 178 raw := uint16(data[1])<<8 | uint16(data[0]) 179 t := int32(raw) * 1000 / 256 180 return t, err 181 } 182 183 // Convenience method to read the register and avoid repetition. 184 func (d *Device) ReadRegister(reg uint8, buf []byte) error { 185 return legacy.ReadRegister(d.bus, uint8(d.Address), reg, buf) 186 } 187 188 // Convenience method to write the register and avoid repetition. 189 func (d *Device) WriteRegister(reg uint8, v uint16) error { 190 data := []byte{byte(v)} 191 err := legacy.WriteRegister(d.bus, uint8(d.Address), reg, data) 192 return err 193 }