STMicro LPS22DF baro support (#13054)
[betaflight.git] / src / main / drivers / barometer / barometer_lps22df.c
blobbb0f3ca9f06e3e20098f58cbb5daaf2f94e5aec2
1 /*
2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
8 * any later version.
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
21 #include <stdbool.h>
22 #include <stdint.h>
24 #include "platform.h"
26 #include "build/build_config.h"
27 #include "build/debug.h"
29 #include "barometer.h"
31 #include "drivers/bus.h"
32 #include "drivers/bus_i2c.h"
33 #include "drivers/bus_i2c_busdev.h"
34 #include "drivers/bus_spi.h"
35 #include "drivers/io.h"
36 #include "drivers/time.h"
38 #include "barometer_lps22df.h"
40 // 10 MHz max SPI frequency
41 #define LPS22DF_MAX_SPI_CLK_HZ 10000000
43 #if defined(USE_BARO) && (defined(USE_BARO_LPS22DF) || defined(USE_BARO_SPI_LPS22DF))
45 /* See datasheet
47 * https://www.st.com/resource/en/datasheet/lps22df.pdf
50 // Macros to encode/decode multi-bit values
51 #define LSM6DSV_ENCODE_BITS(val, mask, shift) ((val << shift) & mask)
52 #define LSM6DSV_DECODE_BITS(val, mask, shift) ((val & mask) >> shift)
54 // RESERVED - 00-0A
56 // Interrupt mode for pressure acquisition configuration (R/W)
57 #define LPS22DF_INTERRUPT_CFG 0x0B
58 #define LPS22DF_INTERRUPT_CFG_AUTOREFP 0x80
59 #define LPS22DF_INTERRUPT_CFG_RESET_ARP 0x40
60 #define LPS22DF_INTERRUPT_CFG_AUTOZERO 0x20
61 #define LPS22DF_INTERRUPT_CFG_RESET_AZ 0x10
62 #define LPS22DF_INTERRUPT_CFG_LIR 0x04
63 #define LPS22DF_INTERRUPT_CFG_PLE 0x02
64 #define LPS22DF_INTERRUPT_CFG_PHE 0x01
66 // Threshold value for pressure interrupt event (least significant bits) (R/W)
67 #define LPS22DF_THS_P_L 0x0C
68 #define LPS22DF_THS_P_H 0x0D
70 // Interface control register (R/W)
71 #define LPS22DF_IF_CTRL 0x0E
72 #define LPS22DF_IF_CTRL_INT_EN_I3C 0x80
73 #define LPS22DF_IF_CTRL_I2C_I3C_DIS 0x40
74 #define LPS22DF_IF_CTRL_SIM 0x20
75 #define LPS22DF_IF_CTRL_SDA_PU_EN 0x10
76 #define LPS22DF_IF_CTRL_SDO_PU_EN 0x08
77 #define LPS22DF_IF_CTRL_INT_PD_DIS 0x04
78 #define LPS22DF_IF_CTRL_CS_PU_DIS 0x02
80 // Device Who am I (R)
81 #define LPS22DF_WHO_AM_I 0x0F
82 #define LPS22DF_CHIP_ID 0xB4
84 // Control register 1 (R/W)
85 #define LPS22DF_CTRL_REG1 0x10
86 #define LPS22DF_CTRL_REG1_ODR_MASK 0x78
87 #define LPS22DF_CTRL_REG1_ODR_SHIFT 3
88 #define LPS22DF_CTRL_REG1_ODR_ONE_SHOT 0
89 #define LPS22DF_CTRL_REG1_ODR_1HZ 1
90 #define LPS22DF_CTRL_REG1_ODR_4HZ 2
91 #define LPS22DF_CTRL_REG1_ODR_10HZ 3
92 #define LPS22DF_CTRL_REG1_ODR_25HZ 4
93 #define LPS22DF_CTRL_REG1_ODR_50HZ 5
94 #define LPS22DF_CTRL_REG1_ODR_75HZ 6
95 #define LPS22DF_CTRL_REG1_ODR_100HZ 7
96 #define LPS22DF_CTRL_REG1_ODR_200HZ 8
97 #define LPS22DF_CTRL_REG1_AVG_MASK 0x03
98 #define LPS22DF_CTRL_REG1_AVG_SHIFT 0
99 #define LPS22DF_CTRL_REG1_AVG_4 0
100 #define LPS22DF_CTRL_REG1_AVG_8 1
101 #define LPS22DF_CTRL_REG1_AVG_16 2
102 #define LPS22DF_CTRL_REG1_AVG_32 3
103 #define LPS22DF_CTRL_REG1_AVG_64 4
104 #define LPS22DF_CTRL_REG1_AVG_128 5
105 #define LPS22DF_CTRL_REG1_AVG_512 7
107 // Control register 2 (R/W)
108 #define LPS22DF_CTRL_REG2 0x11
109 #define LPS22DF_CTRL_REG2_BOOT 0x80
110 #define LPS22DF_CTRL_REG2_LFPF_CFG 0x20
111 #define LPS22DF_CTRL_REG2_EN_LPFP 0x10
112 #define LPS22DF_CTRL_REG2_BDU 0x08
113 #define LPS22DF_CTRL_REG2_SWRESET 0x04
114 #define LPS22DF_CTRL_REG2_ONESHOT 0x01
116 // Control register 3 (R/W)
117 #define LPS22DF_CTRL_REG3 0x12
118 #define LPS22DF_CTRL_REG3_INT_HL 0x08
119 #define LPS22DF_CTRL_REG3_PP_OD 0x02
120 #define LPS22DF_CTRL_REG3_IF_ADD_INC 0x01
122 // Control register 4 (R/W)
123 #define LPS22DF_CTRL_REG4 0x13
124 #define LPS22DF_CTRL_REG4_DRDY_PLS 0x40
125 #define LPS22DF_CTRL_REG4_DRDY 0x20
126 #define LPS22DF_CTRL_REG4_INT_EN 0x10
127 #define LPS22DF_CTRL_REG4_INT_F_FULL 0x04
128 #define LPS22DF_CTRL_REG4_INT_F_WTM 0x02
129 #define LPS22DF_CTRL_REG4_INT_F_OVR 0x01
131 //FIFO control register (R/W)
132 #define LPS22DF_FIFO_CTRL 0x14
133 #define LPS22DF_FIFO_CTRL_STOP_ON_WTM 0x08
134 #define LPS22DF_FIFO_CTRL_TRIG_MODES 0x04
135 #define LPS22DF_FIFO_CTRL_F_MODE_MASK 0x03
136 #define LPS22DF_FIFO_CTRL_F_MODE_SHIFT 0
137 #define LPS22DF_FIFO_CTRL_F_MODE_BYPASS 0
138 #define LPS22DF_FIFO_CTRL_F_MODE_FIFO_MODE 1
139 #define LPS22DF_FIFO_CTRL_F_MODE_CONT 2
140 #define LPS22DF_FIFO_CTRL_F_MODE_CONT_TO_FIFO 3
142 // FIFO threshold setting register (R/W)
143 #define LPS22DF_FIFO_WTM 0x15
145 // Reference pressure LSB data (R)
146 #define LPS22DF_REF_P_L 0x16
147 #define LPS22DF_REF_P_H 0x17
149 // RESERVED - 18
151 // Control register (R/W)
152 #define LPS22DF_I3C_IF_CTRL_ADD 0x19
153 #define LPS22DF_I3C_IF_CTRL_ADD_RESVD 0x80
154 #define LPS22DF_I3C_IF_CTRL_ADD_ASF_ON 0x20
155 #define LPS22DF_I3C_IF_CTRL_ADD_I3C_BUS_AVB_SEL_MASK 0x03
156 #define LPS22DF_I3C_IF_CTRL_ADD_I3C_BUS_AVB_SEL_SHIFT 0
157 #define LPS22DF_I3C_IF_CTRL_ADD_I3C_BUS_AVB_SEL_50US 0
158 #define LPS22DF_I3C_IF_CTRL_ADD_I3C_BUS_AVB_SEL_2US 1
159 #define LPS22DF_I3C_IF_CTRL_ADD_I3C_BUS_AVB_SEL_1MS 2
160 #define LPS22DF_I3C_IF_CTRL_ADD_I3C_BUS_AVB_SEL_25MS 3
162 // Pressure offset (R/W)
163 #define LPS22DF_RPDS_L 0x1A
164 #define LPS22DF_RPDS_H 0x1B
166 // RESERVED - 1C-23
168 // Interrupt source (R) register for differential pressure. A read at this address clears the INT_SOURCE register itself.
169 #define LPS22DF_INT_SOURCE 0x24
170 #define LPS22DF_INT_SOURCE_BOOT_ON 0x80
171 #define LPS22DF_INT_SOURCE_IA 0x04
172 #define LPS22DF_INT_SOURCE_PL 0x02
173 #define LPS22DF_INT_SOURCE_PH 0x01
175 // FIFO status register (R)
176 #define LPS22DF_FIFO_STATUS1 0x25
178 // FIFO status register (R)
179 #define LPS22DF_FIFO_STATUS2 0x26
180 #define LPS22DF_FIFO_STATUS2_WTM_IA 0x80
181 #define LPS22DF_FIFO_STATUS2_OVR_IA 0x40
182 #define LPS22DF_FIFO_STATUS2_FULL_IA 0x20
184 // Status register (R)
185 #define LPS22DF_STATUS 0x27
186 #define LPS22DF_STATUS_T_OR 0x20
187 #define LPS22DF_STATUS_P_OR 0x10
188 #define LPS22DF_STATUS_T_DA 0x02
189 #define LPS22DF_STATUS_P_DA 0x01
191 // Pressure output value (R)
192 #define LPS22DF_PRESSURE_OUT_XL 0x28
193 #define LPS22DF_PRESSURE_OUT_L 0x29
194 #define LPS22DF_PRESSURE_OUT_H 0x2A
196 // Temperature output value (R)
197 #define LPS22DF_TEMP_OUT_L 0x2B
198 #define LPS22DF_TEMP_OUT_H 0x2C
200 // RESERVED - 2D-77
202 // FIFO pressure output (R)
203 #define LPS22DF_FIFO_DATA_OUT_PRESS_XL 0x78
204 #define LPS22DF_FIFO_DATA_OUT_PRESS_L 0x79
205 #define LPS22DF_FIFO_DATA_OUT_PRESS_H 0x7A
207 #define LPS22DF_I2C_ADDR 0x5D
209 static uint8_t lps22df_chip_id = 0;
211 // uncompensated pressure and temperature
212 static int32_t lps22df_up = 0;
213 static int32_t lps22df_ut = 0;
215 // 3 bytes of pressure followed by two bytes of temperature
216 #define LPS22DF_DATA_FRAME_SIZE (LPS22DF_TEMP_OUT_H - LPS22DF_PRESSURE_OUT_XL + 1)
218 static DMA_DATA_ZERO_INIT uint8_t sensor_data[LPS22DF_DATA_FRAME_SIZE];
220 static void lps22dfStartUT(baroDev_t *baro);
221 static bool lps22dfReadUT(baroDev_t *baro);
222 static bool lps22dfGetUT(baroDev_t *baro);
223 static void lps22dfStartUP(baroDev_t *baro);
224 static bool lps22dfReadUP(baroDev_t *baro);
225 static bool lps22dfGetUP(baroDev_t *baro);
227 STATIC_UNIT_TESTED void lps22dfCalculate(int32_t *pressure, int32_t *temperature);
229 void lps22dfBusInit(const extDevice_t *dev)
231 #ifdef USE_BARO_SPI_LPS22DF
232 if (dev->bus->busType == BUS_TYPE_SPI) {
233 IOHi(dev->busType_u.spi.csnPin); // Disable
234 IOInit(dev->busType_u.spi.csnPin, OWNER_BARO_CS, 0);
235 IOConfigGPIO(dev->busType_u.spi.csnPin, IOCFG_OUT_PP);
236 spiSetClkDivisor(dev, spiCalculateDivider(LPS22DF_MAX_SPI_CLK_HZ));
238 #else
239 UNUSED(dev);
240 #endif
243 void lps22dfBusDeinit(const extDevice_t *dev)
245 #ifdef USE_BARO_SPI_LPS22DF
246 if (dev->bus->busType == BUS_TYPE_SPI) {
247 spiPreinitByIO(dev->busType_u.spi.csnPin);
249 #else
250 UNUSED(dev);
251 #endif
254 bool lps22dfDetect(baroDev_t *baro)
256 delay(20);
258 extDevice_t *dev = &baro->dev;
259 bool defaultAddressApplied = false;
261 lps22dfBusInit(dev);
263 if ((dev->bus->busType == BUS_TYPE_I2C) && (dev->busType_u.i2c.address == 0)) {
264 // Default address for LPS22DF
265 dev->busType_u.i2c.address = LPS22DF_I2C_ADDR;
266 defaultAddressApplied = true;
269 busReadRegisterBuffer(dev, LPS22DF_WHO_AM_I, &lps22df_chip_id, 1); /* read Chip Id */
271 if ((lps22df_chip_id != LPS22DF_CHIP_ID)) {
272 lps22dfBusDeinit(dev);
273 if (defaultAddressApplied) {
274 dev->busType_u.i2c.address = 0;
276 return false;
279 busDeviceRegister(dev);
281 // Reset the device
282 busWriteRegister(dev, LPS22DF_CTRL_REG2, LPS22DF_CTRL_REG2_SWRESET);
284 // Enable one-shot ODR and averaging at 16
285 busWriteRegister(dev, LPS22DF_CTRL_REG1, LSM6DSV_ENCODE_BITS(LPS22DF_CTRL_REG1_ODR_ONE_SHOT,
286 LPS22DF_CTRL_REG1_ODR_MASK,
287 LPS22DF_CTRL_REG1_ODR_SHIFT) |
288 LSM6DSV_ENCODE_BITS(LPS22DF_CTRL_REG1_AVG_16,
289 LPS22DF_CTRL_REG1_AVG_MASK,
290 LPS22DF_CTRL_REG1_AVG_SHIFT));
292 // these are dummy as temperature is measured as part of pressure
293 baro->combined_read = true;
294 baro->ut_delay = 0;
295 baro->start_ut = lps22dfStartUT;
296 baro->get_ut = lps22dfGetUT;
297 baro->read_ut = lps22dfReadUT;
298 // only _up part is executed, and gets both temperature and pressure
299 baro->start_up = lps22dfStartUP;
300 baro->get_up = lps22dfGetUP;
301 baro->read_up = lps22dfReadUP;
302 baro->up_delay = 10000; // 10ms
303 baro->calculate = lps22dfCalculate;
305 return true;
308 static void lps22dfStartUT(baroDev_t *baro)
310 UNUSED(baro);
311 // dummy
314 static bool lps22dfReadUT(baroDev_t *baro)
316 UNUSED(baro);
317 // dummy
318 return true;
321 static bool lps22dfGetUT(baroDev_t *baro)
323 UNUSED(baro);
324 // dummy
325 return true;
328 static void lps22dfStartUP(baroDev_t *baro)
330 // start measurement
331 // Trigger one-shot enable block data update to ensure LSB/MSB are coherent
332 busWriteRegister(&baro->dev, LPS22DF_CTRL_REG2, LPS22DF_CTRL_REG2_ONESHOT | LPS22DF_CTRL_REG2_BDU);
335 static bool lps22dfReadUP(baroDev_t *baro)
337 if (busBusy(&baro->dev, NULL)) {
338 return false;
341 // Read data from sensor
342 busReadRegisterBufferStart(&baro->dev, LPS22DF_PRESSURE_OUT_XL, sensor_data, LPS22DF_DATA_FRAME_SIZE);
344 return true;
347 static bool lps22dfGetUP(baroDev_t *baro)
349 if (busBusy(&baro->dev, NULL)) {
350 return false;
353 lps22df_up = (int32_t)(sensor_data[0] | sensor_data[1] << 8 | sensor_data[2] << 16);
354 lps22df_ut = (int32_t)(sensor_data[3] | sensor_data[4] << 8);
356 return true;
359 // Returns temperature in DegC, resolution is 0.01 DegC. Output value of "5123" equals 51.23 DegC
360 // t_fine carries fine temperature as global value
361 static int32_t lps22dfCompensateTemperature(int32_t adc_T)
363 return adc_T;
366 // Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
367 // Output value of "24674867" represents 24674867/256 = 96386.2 Pa = 963.862 hPa
368 static uint32_t lps22dfCompensatePressure(int32_t adc_P)
370 return (uint32_t)(adc_P * 100.0f / 16);
373 STATIC_UNIT_TESTED void lps22dfCalculate(int32_t *pressure, int32_t *temperature)
375 // calculate
376 int32_t t;
377 uint32_t p;
378 t = lps22dfCompensateTemperature(lps22df_ut);
379 p = lps22dfCompensatePressure(lps22df_up);
381 if (pressure)
382 *pressure = (int32_t)(p / 256);
383 if (temperature)
384 *temperature = t;
387 #endif