Skyward boardcore
Loading...
Searching...
No Matches
LIS3MDL.cpp
Go to the documentation of this file.
1/* Copyright (c) 2020 Skyward Experimental Rocketry
2 * Author: Riccardo Musso
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 "LIS3MDL.h"
24
26
27namespace Boardcore
28{
29
30LIS3MDL::LIS3MDL(SPIBusInterface& bus, miosix::GpioPin pin,
31 SPIBusConfig spiConfig, Config config)
32 : slave(bus, pin, spiConfig), configuration(config)
33{
36}
37
39{
40 if (isInitialized)
41 {
42 LOG_ERR(logger, "Attempted to initialized sensor twice but failed");
44 return false;
45 }
46
47 {
48 SPITransaction spi(slave);
49 uint8_t res = spi.readRegister(WHO_AM_I);
50
51 if (res != WHO_AM_I_VALUE)
52 {
53 LOG_ERR(logger,
54 "WHO_AM_I value differs from expectation: read 0x{:x} "
55 "but expected 0x{:x}",
56 res, WHO_AM_I_VALUE);
58 return false;
59 }
60 }
61
62 isInitialized = true;
63 return applyConfig(configuration);
64}
65
67{
68 if (!isInitialized)
69 {
70 LOG_ERR(logger, "Invoked selfTest() but sensor was uninitialized");
72 return false;
73 }
74
75 constexpr int NUM_SAMPLES = 5;
76 constexpr int SLEEP_TIME = 50;
77
78 // Absolute value of extra tolerance
79 constexpr float t = 0.1f;
80
81 // Range which delta must be between, one for axis and expressed as {min,
82 // max}. The unit is gauss.
83 constexpr float deltaRange[3][2] = {{1.f, 3.f}, {1.f, 3.f}, {0.1f, 1.f}};
84
85 float avgX = 0.f, avgY = 0.f, avgZ = 0.f;
86
87 {
88 SPITransaction spi(slave);
89 spi.writeRegister(CTRL_REG2, FS_12_GAUSS);
90 }
91 updateUnit(FS_12_GAUSS);
92
93 for (int i = 0; i < NUM_SAMPLES; ++i)
94 {
95 miosix::Thread::sleep(SLEEP_TIME);
96
97 LIS3MDLData lastData = sampleImpl();
98 avgX += lastData.magneticFieldX;
99 avgY += lastData.magneticFieldY;
100 avgZ += lastData.magneticFieldZ;
101 }
102
103 avgX /= NUM_SAMPLES;
104 avgY /= NUM_SAMPLES;
105 avgZ /= NUM_SAMPLES;
106
107 // Setting up the sensor settings for proper usage of the self test mode.
108 {
109 SPITransaction spi(slave);
110
111 spi.writeRegister(CTRL_REG1, ODR_20_HZ | ENABLE_SELF_TEST |
112 (OM_ULTRA_HIGH_POWER << 4));
113 spi.writeRegister(CTRL_REG2, FS_12_GAUSS);
114 spi.writeRegister(CTRL_REG4, OM_ULTRA_HIGH_POWER << 2);
115 }
116
117 // Deltas: absolute difference between the values measured before and after
118 float deltas[3];
119
120 miosix::Thread::sleep(SLEEP_TIME);
121
122 LIS3MDLData lastData = sampleImpl();
123 deltas[0] = std::abs(lastData.magneticFieldX - avgX);
124 deltas[1] = std::abs(lastData.magneticFieldY - avgY);
125 deltas[2] = std::abs(lastData.magneticFieldZ - avgZ);
126
127 bool passed = true;
128 for (int j = 0; j < 3; ++j)
129 {
130 if (deltas[j] < (deltaRange[j][0] - t) ||
131 deltas[j] > (deltaRange[j][1] + t))
132 {
133 passed = false;
134 }
135 }
136
137 // Reset configuration, then return
138 applyConfig(configuration);
139
140 if (!passed)
141 {
143 return false;
144 }
145
146 return applyConfig(configuration);
147}
148
150{
151 SPITransaction spi(slave);
152 uint8_t reg = 0, err = 0;
153
154 configuration = config;
155
156 // CTRL_REG1
157 if (config.temperatureDivider != 0)
158 reg = ENABLE_TEMPERATURE;
159 reg |= config.odr;
160
161 // odr <= 80Hz
162 if (!(config.odr & FAST_ODR_BIT))
163 reg |= config.xyMode << 4;
164 spi.writeRegister(CTRL_REG1, reg);
165 err |= spi.readRegister(CTRL_REG1) != reg;
166
167 // CTRL_REG2
168 reg = config.scale;
169 spi.writeRegister(CTRL_REG2, reg);
170 err |= spi.readRegister(CTRL_REG2) != reg;
171
172 // CTRL_REG3
173 reg = CONTINUOS_CONVERSION;
174 spi.writeRegister(CTRL_REG3, reg);
175 err |= spi.readRegister(CTRL_REG3) != reg;
176
177 // CTRL_REG4
178 reg = config.zMode << 2;
179 spi.writeRegister(CTRL_REG4, reg);
180 err |= spi.readRegister(CTRL_REG4) != reg;
181
182 // CTRL_REG5
183 if (config.doBlockDataUpdate)
184 reg = ENABLE_BDU;
185 else
186 reg = 0;
187
188 spi.writeRegister(CTRL_REG5, reg);
189 err |= spi.readRegister(CTRL_REG5) != reg;
190
191 // Set mUnit according to scale
192 updateUnit(config.scale);
193
194 if (err)
195 {
196 LOG_ERR(logger, "Spi error");
198 return false;
199 }
200
201 return true;
202}
203
205{
206 if (!isInitialized)
207 {
208 LOG_ERR(logger, "Invoked sampleImpl() but sensor was uninitialized");
210 return lastSample;
211 }
212
213 SPITransaction spi(slave);
214 LIS3MDLData newData;
215 tempCounter++;
216 if (configuration.temperatureDivider != 0 &&
217 tempCounter % configuration.temperatureDivider == 0)
218 {
219 int16_t outTemp = spi.readRegister16(TEMP_OUT_L | INCREMENT_REG_FLAG);
220 newData.temperatureTimestamp = TimestampTimer::getTimestamp();
221 newData.temperature = DEG_PER_LSB * outTemp;
222 newData.temperature += REFERENCE_TEMPERATURE;
223 }
224 else
225 {
226 newData.temperature = lastSample.temperature;
227 }
228
229 int16_t values[3];
230 spi.readRegisters(OUT_X_L | INCREMENT_REG_FLAG,
231 reinterpret_cast<uint8_t*>(values), sizeof(values));
232 newData.magneticFieldTimestamp = TimestampTimer::getTimestamp();
233 newData.magneticFieldX = currentUnit * values[0];
234 newData.magneticFieldY = currentUnit * values[1];
235 newData.magneticFieldZ = currentUnit * values[2];
236
237 return newData;
238}
239
240void LIS3MDL::updateUnit(FullScale fs)
241{
242 switch (fs)
243 {
244 case FS_4_GAUSS:
245 currentUnit = GAUSS_PER_LSB_FS_4;
246 break;
247
248 case FS_8_GAUSS:
249 currentUnit = GAUSS_PER_LSB_FS_8;
250 break;
251
252 case FS_12_GAUSS:
253 currentUnit = GAUSS_PER_LSB_FS_12;
254 break;
255
256 case FS_16_GAUSS:
257 currentUnit = GAUSS_PER_LSB_FS_16;
258 break;
259 }
260};
261
262} // namespace Boardcore
#define LOG_ERR(logger,...)
SensorErrors lastError
Definition Sensor.h:54
bool selfTest() override
Check if the sensor is working.
Definition LIS3MDL.cpp:66
LIS3MDL(SPIBusInterface &bus, miosix::GpioPin pin, SPIBusConfig spiConfig={}, Config config={})
Definition LIS3MDL.cpp:30
LIS3MDLData sampleImpl() override
Read a data sample from the sensor. In case of errors, the method should return the last available co...
Definition LIS3MDL.cpp:204
bool init() override
Initialize the sensor.
Definition LIS3MDL.cpp:38
@ FS_16_GAUSS
+/- 16 gauss
Definition LIS3MDL.h:75
@ FS_12_GAUSS
+/- 12 gauss
Definition LIS3MDL.h:74
@ FS_4_GAUSS
+/- 4 gauss
Definition LIS3MDL.h:72
@ FS_8_GAUSS
+/- 8 gauss
Definition LIS3MDL.h:73
bool applyConfig(Config config)
Overrides the sensor settings.
Definition LIS3MDL.cpp:149
Interface for low level access of a SPI bus as a master.
Provides high-level access to the SPI Bus for a single transaction.
uint8_t readRegister(uint8_t reg)
Reads an 8 bit register.
void readRegisters(uint8_t reg, uint8_t *data, size_t size)
Reads multiple bytes starting from the specified register.
void writeRegister(uint8_t reg, uint8_t data)
Writes an 8 bit register.
uint16_t readRegister16(uint8_t reg)
Reads a 16 bit register.
uint64_t getTimestamp()
Returns the current timer value in microseconds.
This file includes all the types the logdecoder script will decode.
@ SELF_TEST_FAIL
Definition SensorData.h:44
@ INVALID_WHOAMI
Definition SensorData.h:40
Sensor configuration.
Definition LIS3MDL.h:100
bool doBlockDataUpdate
BDU setting.
Definition LIS3MDL.h:122
unsigned temperatureDivider
Divide the temperature sampling rate.
Definition LIS3MDL.h:114
SPI Bus configuration for a specific slave.
SPI::Order byteOrder
MSByte or LSByte first.
SPI::Mode mode
MSBit or LSBit first.