Skyward boardcore
Loading...
Searching...
No Matches
MPU9250.cpp
Go to the documentation of this file.
1/* Copyright (c) 2021 Skyward Experimental Rocketry
2 * Author: Alberto Nidasio
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include "MPU9250.h"
24
26#include <interfaces/endianness.h>
27#include <utils/Constants.h>
28
29namespace Boardcore
30{
31
32MPU9250::MPU9250(SPIBusInterface& bus, miosix::GpioPin cs, SPIBusConfig config,
33 unsigned short samplingRate, GyroFSR gyroFsr,
34 AccelFSR accelFsr)
35 : spiSlave(bus, cs, config), samplingRate(samplingRate), gyroFsr(gyroFsr),
36 accelFsr(accelFsr)
37{
38}
39
41{
42 SPIBusConfig spiConfig{};
44 spiConfig.mode = SPI::Mode::MODE_3;
45 return spiConfig;
46}
47
49{
50 // Check if already initialized
51 if (initialized)
52 {
53 LOG_ERR(logger, "Already initialized");
54
56
57 return false;
58 }
59
60 // Reset the device
61 resetDevice();
62 miosix::Thread::sleep(100);
63
64 // Wake up the chip
65 selectAutoClock();
66
67 // Check WHO AM I
68 if (!checkWhoAmI())
69 {
70 LOG_ERR(logger, "Invalid WHO AM I");
71
73
74 return false;
75 }
76
77 // Setup I2C master interface to communicate with the AK8963
78 enableMpuI2CMasterInterface();
79 setMpuI2CMasterInterfaceClock(I2C_MST_IF_CLK_400);
80
81 // Init the AK8963
82 if (!initAk())
83 return false;
84
85 // Set full scale resolution for gyroscope and accelerometer (they are
86 // enabled by default)
87 setGyroFsr(gyroFsr);
88 setAccelFsr(accelFsr);
89
90 // Set the sample rate
91 setSampleRate(samplingRate);
92
93 LOG_DEBUG(logger, "Magnetometer sensitivity adjustment: {}, {}, {}",
94 magSensAdjCoeff[0], magSensAdjCoeff[1], magSensAdjCoeff[2]);
95
96 initialized = true;
97 return true;
98}
99
101{
102 MPU9250RawData rawData;
103 MPU9250Data data;
104
105 {
106 SPITransaction transaction(spiSlave);
107 transaction.readRegisters(REG_ACCEL_XOUT_H, (uint8_t*)rawData.bytes,
108 sizeof(MPU9250RawData));
109 }
110
111 // Save timestamps
112 uint64_t timestamp = TimestampTimer::getTimestamp();
113 data.accelerationTimestamp = timestamp;
114 data.temperatureTimestamp = timestamp;
115 data.angularSpeedTimestamp = timestamp;
116 data.magneticFieldTimestamp = timestamp;
117
118 // Save data
119 data.accelerationX =
120 normalizeAcceleration(swapBytes16(rawData.bits.accelX));
121 data.accelerationY =
122 normalizeAcceleration(swapBytes16(rawData.bits.accelY));
123 data.accelerationZ =
124 normalizeAcceleration(swapBytes16(rawData.bits.accelZ));
125 data.temperature = normalizeTemperature(swapBytes16(rawData.bits.temp));
126 data.angularSpeedX = normalizeGyroscope(swapBytes16(rawData.bits.gyroX));
127 data.angularSpeedY = normalizeGyroscope(swapBytes16(rawData.bits.gyroY));
128 data.angularSpeedZ = normalizeGyroscope(swapBytes16(rawData.bits.gyroZ));
129 data.magneticFieldX =
130 normalizeMagnetometer(rawData.bits.magX, magSensAdjCoeff[0]);
131 data.magneticFieldY =
132 normalizeMagnetometer(rawData.bits.magY, magSensAdjCoeff[1]);
133 data.magneticFieldZ =
134 normalizeMagnetometer(rawData.bits.magZ, magSensAdjCoeff[2]);
135
136 return data;
137}
138
139void MPU9250::resetDevice()
140{
141 SPITransaction transaction(spiSlave);
142
143 transaction.writeRegister(REG_PWR_MGMT_1, REG_PWR_MGMT_1_BIT_H_RESET);
144}
145
146void MPU9250::selectAutoClock()
147{
148 SPITransaction transaction(spiSlave);
149
150 // Wake up by clearing sleep bit and set clock auto select
151 writeSPIWithDelay(transaction, REG_PWR_MGMT_1, REG_PWR_MGMT_1_CLKSEL_AUTO);
152}
153
154void MPU9250::setGyroFsr(GyroFSR fs)
155{
156 SPITransaction transaction(spiSlave);
157
158 writeSPIWithDelay(transaction, REG_GYRO_CONFIG, fs);
159}
160
161void MPU9250::setAccelFsr(AccelFSR fs)
162{
163 SPITransaction transaction(spiSlave);
164
165 writeSPIWithDelay(transaction, REG_ACCEL_CONFIG, fs);
166}
167
168void MPU9250::setSampleRate(unsigned short rate)
169{
170 if (rate < 4)
171 rate = 4;
172 else if (rate > 1000)
173 rate = 1000;
174
175 uint8_t data = 1000 / rate - 1;
176
177 SPITransaction transaction(spiSlave);
178
179 writeSPIWithDelay(transaction, REG_SMPLRT_DIV, data);
180
181 // Set the digital low pass filter in order to use the sample rate divider
182 writeSPIWithDelay(transaction, REG_CONFIG, REG_CONFIG_DLPF_CFG_1);
183
184 // We do not need to write Fchoise_b bits becouse they reset to 0 and they
185 // are the inverse of Fchoise, see page 13 of MPU9250 register map
186}
187
188void MPU9250::enableMpuI2CMasterInterface()
189{
190 SPITransaction transaction(spiSlave);
191
192 writeSPIWithDelay(transaction, REG_USER_CTRL, REG_USER_CTRL_I2C_MST_EN);
193}
194
195void MPU9250::setMpuI2CMasterInterfaceClock(I2CMasterInterfaceClock clk)
196{
197 SPITransaction transaction(spiSlave);
198
199 writeSPIWithDelay(transaction, REG_I2C_MST_CTRL, clk);
200}
201
202void MPU9250::setI2CMasterSlaveRead(uint8_t addr, uint8_t reg, uint8_t nBytes,
203 uint8_t slave)
204{
205 uint8_t regSlvAddr, regSlvReg, regSlvCtrl;
206
207 switch (slave)
208 {
209 case 0:
210 regSlvAddr = REG_I2C_SLV0_ADDR;
211 regSlvReg = REG_I2C_SLV0_REG;
212 regSlvCtrl = REG_I2C_SLV0_CTRL;
213 break;
214 case 1:
215 regSlvAddr = REG_I2C_SLV1_ADDR;
216 regSlvReg = REG_I2C_SLV1_REG;
217 regSlvCtrl = REG_I2C_SLV1_CTRL;
218 break;
219 case 2:
220 regSlvAddr = REG_I2C_SLV2_ADDR;
221 regSlvReg = REG_I2C_SLV2_REG;
222 regSlvCtrl = REG_I2C_SLV2_CTRL;
223 break;
224 case 3:
225 regSlvAddr = REG_I2C_SLV3_ADDR;
226 regSlvReg = REG_I2C_SLV3_REG;
227 regSlvCtrl = REG_I2C_SLV3_CTRL;
228 break;
229
230 default:
231 LOG_ERR(logger,
232 "invalid slave parameter in function "
233 "setI2CMasterSlaveRead");
234 return;
235 }
236
237 SPITransaction transaction(spiSlave);
238
239 writeSPIWithDelay(transaction, regSlvAddr, addr | 0x80); // Set read bit
240 writeSPIWithDelay(transaction, regSlvReg, reg);
241
242 // Set bytes number and enable the data transfer on each sample
243 writeSPIWithDelay(transaction, regSlvCtrl, REG_I2C_SLV_CTRL_EN | nBytes);
244}
245
246void MPU9250::setI2CMasterSlaveWrite(uint8_t addr, uint8_t reg, uint8_t data,
247 uint8_t slave)
248{
249 uint8_t regSlvAddr, regSlvReg, regSlvCtrl, regSlvDo;
250
251 switch (slave)
252 {
253 case 0:
254 regSlvAddr = REG_I2C_SLV0_ADDR;
255 regSlvReg = REG_I2C_SLV0_REG;
256 regSlvCtrl = REG_I2C_SLV0_CTRL;
257 regSlvDo = REG_I2C_SLV0_DO;
258 break;
259 case 1:
260 regSlvAddr = REG_I2C_SLV1_ADDR;
261 regSlvReg = REG_I2C_SLV1_REG;
262 regSlvCtrl = REG_I2C_SLV1_CTRL;
263 regSlvDo = REG_I2C_SLV1_DO;
264 break;
265 case 2:
266 regSlvAddr = REG_I2C_SLV2_ADDR;
267 regSlvReg = REG_I2C_SLV2_REG;
268 regSlvCtrl = REG_I2C_SLV2_CTRL;
269 regSlvDo = REG_I2C_SLV2_DO;
270 break;
271 case 3:
272 regSlvAddr = REG_I2C_SLV3_ADDR;
273 regSlvReg = REG_I2C_SLV3_REG;
274 regSlvCtrl = REG_I2C_SLV3_CTRL;
275 regSlvDo = REG_I2C_SLV3_DO;
276 break;
277
278 default:
279 LOG_ERR(logger,
280 "invalid slave parameter in function "
281 "setI2CMasterSlaveRead");
282 return;
283 }
284
285 SPITransaction transaction(spiSlave);
286
287 writeSPIWithDelay(transaction, regSlvAddr, addr);
288 writeSPIWithDelay(transaction, regSlvReg, reg);
289 writeSPIWithDelay(transaction, regSlvDo, data);
290
291 // Enable the data transfer on each sample
292 writeSPIWithDelay(transaction, regSlvCtrl, REG_I2C_SLV_CTRL_EN | 0x1);
293}
294
295void MPU9250::disableI2CMasterSlave(uint8_t slave)
296{
297 uint8_t regSlvCtrl;
298
299 switch (slave)
300 {
301 case 0:
302 regSlvCtrl = REG_I2C_SLV0_CTRL;
303 break;
304 case 1:
305 regSlvCtrl = REG_I2C_SLV1_CTRL;
306 break;
307 case 2:
308 regSlvCtrl = REG_I2C_SLV2_CTRL;
309 break;
310 case 3:
311 regSlvCtrl = REG_I2C_SLV3_CTRL;
312 break;
313 // In the control register of slave 4 there is also the
314 // configuration for the slaves sampling rate. If slave 4 is needed
315 // the salmpling rate divider should not be changed case 4:
316 // regSlvCtrl = REG_I2C_SLV4_CTRL;
317 // break;
318
319 default:
320 LOG_ERR(logger,
321 "invalid slave parameter in function "
322 "setI2CMasterSlaveRead");
323 return;
324 }
325
326 SPITransaction transaction(spiSlave);
327
328 // Enable the data transfer on each sample
329 writeSPIWithDelay(transaction, regSlvCtrl, 0);
330}
331
332uint8_t MPU9250::readFromAk(uint8_t reg)
333{
334 // Set the device address and register
335 setI2CMasterSlaveRead(AK8963_ADDR, reg);
336
337 // Wait for the sample
338 miosix::Thread::sleep(1);
339
340 SPITransaction transaction(spiSlave);
341
342 return transaction.readRegister(REG_EXT_SENS_DATA_00);
343}
344
345void MPU9250::writeToAk(uint8_t reg, uint8_t data)
346{
347 // Set the device address, register and data byte
348 setI2CMasterSlaveWrite(AK8963_ADDR, reg, data);
349 miosix::Thread::sleep(1);
350}
351
352bool MPU9250::initAk()
353{
354 // Set the sample rate to 1KHz in order to communicate faster, but safely,
355 // with the AK (if we use 8KHz the communication does not work properly)
356 setSampleRate(1000);
357
358 // Power down the magnetometer
360
361 // Wait a bit more, the first slave communication seams to be delayed by 1
362 // sample
363 miosix::Thread::sleep(1);
364
365 // Reset
367
368 // Check AK8963 WHO AM I
369 if (!checkAkWhoAmI())
370 {
371 LOG_ERR(logger, "Invalid AK8963 WHO AM I");
372
374
375 return false;
376 }
377
378 // Enter rom access mode
381
382 // Read magnetometer sensitivity adjustment data (page 53 of register map)
383 magSensAdjCoeff[0] =
384 (((readFromAk(AK8963_REG_ASAX) - 128) + .5) / 128 + 1) * 4912.0f /
385 32760.0f;
386 magSensAdjCoeff[1] =
387 (((readFromAk(AK8963_REG_ASAY) - 128) + .5) / 128 + 1) * 4912.0f /
388 32760.0f;
389 magSensAdjCoeff[2] =
390 (((readFromAk(AK8963_REG_ASAZ) - 128) + .5) / 128 + 1) * 4912.0f /
391 32760.0f;
392
393 // Set continuos measurement mode
396
397 // Set I2C master slave communication on each sample
398 setI2CMasterSlaveRead(AK8963_ADDR, AK8963_REG_ST1, 8);
399
400 return true;
401}
402
403bool MPU9250::checkWhoAmI()
404{
405 SPITransaction transaction(spiSlave);
406
407 uint8_t whoAmIValue = transaction.readRegister(REG_WHO_AM_I);
408
409 return whoAmIValue == REG_WHO_AM_I_VAL;
410}
411
412bool MPU9250::checkAkWhoAmI()
413{
414 uint8_t whoAmIValue = readFromAk(AK8963_REG_WHO_AM_I);
415
416 return whoAmIValue == AK8963_REG_WHO_AM_I_VAL;
417}
418
419void MPU9250::writeSPIWithDelay(SPITransaction& transaction, uint8_t reg,
420 uint8_t data)
421{
422 transaction.writeRegister(reg, data);
423 miosix::delayUs(1);
424 /*transaction.readRegister(reg);
425 miosix::delayUs(1);*/
426}
427
428float MPU9250::normalizeAcceleration(int16_t rawValue)
429{
430 return static_cast<float>(rawValue) / 32768.0f *
431 ACCELERATION_FS_MAP[accelFsr >> 3] * Constants::g;
432}
433
434// Page 33 of register map document
435float MPU9250::normalizeTemperature(int16_t rawValue)
436{
437 return static_cast<float>(rawValue) / 512.0f + 21.0f;
438}
439
440float MPU9250::normalizeGyroscope(int16_t rawValue)
441{
442 return static_cast<float>(rawValue) / 32768.0f *
443 GYROSCOPE_FS_MAP[gyroFsr >> 3] * Constants::DEGREES_TO_RADIANS;
444}
445
446float MPU9250::normalizeMagnetometer(int16_t rawValue, float adjustmentCoeff)
447{
448 // Page 50 and 53 of register map document
449 return static_cast<float>(rawValue) * adjustmentCoeff;
450}
451
452} // namespace Boardcore
#define LOG_ERR(logger,...)
#define LOG_DEBUG(logger,...)
SensorErrors lastError
Definition Sensor.h:54
const float GYROSCOPE_FS_MAP[4]
Definition MPU9250.h:187
static constexpr uint8_t REG_WHO_AM_I_VAL
Definition MPU9250.h:172
static constexpr uint8_t REG_I2C_SLV_CTRL_EN
Definition MPU9250.h:169
static constexpr uint8_t AK8963_REG_CNTL1_FUSE_ROM_ACCESS_MODE
Definition MPU9250.h:182
static constexpr uint8_t AK8963_REG_WHO_AM_I_VAL
Definition MPU9250.h:176
MPU9250(SPIBusInterface &bus, miosix::GpioPin cs, SPIBusConfig config=getDefaultSPIConfig(), unsigned short samplingRate=100, GyroFSR gyroFsr=GYRO_FSR_250DPS, AccelFSR accelFsr=ACCEL_FSR_2G)
Instantiates the driver.
Definition MPU9250.cpp:32
static constexpr uint8_t REG_USER_CTRL_I2C_MST_EN
Definition MPU9250.h:168
static constexpr uint8_t REG_PWR_MGMT_1_BIT_H_RESET
Definition MPU9250.h:170
static constexpr uint8_t REG_CONFIG_DLPF_CFG_1
Definition MPU9250.h:167
static constexpr uint8_t AK8963_REG_CNTL1_CONT_MES_MODE_2
Definition MPU9250.h:180
bool init() override
Initialize the device.
Definition MPU9250.cpp:48
static constexpr uint8_t AK8963_REG_CNTL1_POWER_DOWN_MODE
Definition MPU9250.h:177
static constexpr uint8_t REG_PWR_MGMT_1_CLKSEL_AUTO
Definition MPU9250.h:171
const float ACCELERATION_FS_MAP[4]
Definition MPU9250.h:186
static constexpr uint8_t AK8963_ADDR
Definition MPU9250.h:175
MPU9250Data sampleImpl() override
Read a data sample from the sensor. In case of errors, the method should return the last available co...
Definition MPU9250.cpp:100
static SPIBusConfig getDefaultSPIConfig()
Constructs the default config for SPI Bus.
Definition MPU9250.cpp:40
static constexpr uint8_t AK8963_REG_CNTL2_BIT_SRST
Definition MPU9250.h:184
Interface for low level access of a SPI bus as a master.
Provides high-level access to the SPI Bus for a single transaction.
void readRegisters(uint8_t reg, uint8_t *data, size_t size)
Reads multiple bytes starting from the specified register.
uint64_t getTimestamp()
Returns the current timer value in microseconds.
This file includes all the types the logdecoder script will decode.
@ INVALID_WHOAMI
Definition SensorData.h:40
SPI Bus configuration for a specific slave.
SPI::ClockDivider clockDivider
< Peripheral clock division