2 * This file is part of Betaflight.
4 * Betaflight is free software. You can redistribute this software
5 * and/or modify this software under the terms of the GNU General
6 * Public License as published by the Free Software Foundation,
7 * either version 3 of the License, or (at your option) any later
10 * Betaflight is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this software.
19 * If not, see <http://www.gnu.org/licenses/>.
27 #include "build/build_config.h"
28 #include "build/debug.h"
30 #include "barometer.h"
32 #include "drivers/bus.h"
33 #include "drivers/bus_i2c.h"
34 #include "drivers/bus_i2c_busdev.h"
35 #include "drivers/bus_spi.h"
36 #include "drivers/io.h"
37 #include "drivers/time.h"
39 #include "barometer_lps22df.h"
41 // 10 MHz max SPI frequency
42 #define LPS22DF_MAX_SPI_CLK_HZ 10000000
44 #if defined(USE_BARO) && (defined(USE_BARO_LPS22DF) || defined(USE_BARO_SPI_LPS22DF))
48 * https://www.st.com/resource/en/datasheet/lps22df.pdf
51 // Macros to encode/decode multi-bit values
52 #define LSM6DSV_ENCODE_BITS(val, mask, shift) ((val << shift) & mask)
53 #define LSM6DSV_DECODE_BITS(val, mask, shift) ((val & mask) >> shift)
57 // Interrupt mode for pressure acquisition configuration (R/W)
58 #define LPS22DF_INTERRUPT_CFG 0x0B
59 #define LPS22DF_INTERRUPT_CFG_AUTOREFP 0x80
60 #define LPS22DF_INTERRUPT_CFG_RESET_ARP 0x40
61 #define LPS22DF_INTERRUPT_CFG_AUTOZERO 0x20
62 #define LPS22DF_INTERRUPT_CFG_RESET_AZ 0x10
63 #define LPS22DF_INTERRUPT_CFG_LIR 0x04
64 #define LPS22DF_INTERRUPT_CFG_PLE 0x02
65 #define LPS22DF_INTERRUPT_CFG_PHE 0x01
67 // Threshold value for pressure interrupt event (least significant bits) (R/W)
68 #define LPS22DF_THS_P_L 0x0C
69 #define LPS22DF_THS_P_H 0x0D
71 // Interface control register (R/W)
72 #define LPS22DF_IF_CTRL 0x0E
73 #define LPS22DF_IF_CTRL_INT_EN_I3C 0x80
74 #define LPS22DF_IF_CTRL_I2C_I3C_DIS 0x40
75 #define LPS22DF_IF_CTRL_SIM 0x20
76 #define LPS22DF_IF_CTRL_SDA_PU_EN 0x10
77 #define LPS22DF_IF_CTRL_SDO_PU_EN 0x08
78 #define LPS22DF_IF_CTRL_INT_PD_DIS 0x04
79 #define LPS22DF_IF_CTRL_CS_PU_DIS 0x02
81 // Device Who am I (R)
82 #define LPS22DF_WHO_AM_I 0x0F
83 #define LPS22DF_CHIP_ID 0xB4
85 // Control register 1 (R/W)
86 #define LPS22DF_CTRL_REG1 0x10
87 #define LPS22DF_CTRL_REG1_ODR_MASK 0x78
88 #define LPS22DF_CTRL_REG1_ODR_SHIFT 3
89 #define LPS22DF_CTRL_REG1_ODR_ONE_SHOT 0
90 #define LPS22DF_CTRL_REG1_ODR_1HZ 1
91 #define LPS22DF_CTRL_REG1_ODR_4HZ 2
92 #define LPS22DF_CTRL_REG1_ODR_10HZ 3
93 #define LPS22DF_CTRL_REG1_ODR_25HZ 4
94 #define LPS22DF_CTRL_REG1_ODR_50HZ 5
95 #define LPS22DF_CTRL_REG1_ODR_75HZ 6
96 #define LPS22DF_CTRL_REG1_ODR_100HZ 7
97 #define LPS22DF_CTRL_REG1_ODR_200HZ 8
98 #define LPS22DF_CTRL_REG1_AVG_MASK 0x03
99 #define LPS22DF_CTRL_REG1_AVG_SHIFT 0
100 #define LPS22DF_CTRL_REG1_AVG_4 0
101 #define LPS22DF_CTRL_REG1_AVG_8 1
102 #define LPS22DF_CTRL_REG1_AVG_16 2
103 #define LPS22DF_CTRL_REG1_AVG_32 3
104 #define LPS22DF_CTRL_REG1_AVG_64 4
105 #define LPS22DF_CTRL_REG1_AVG_128 5
106 #define LPS22DF_CTRL_REG1_AVG_512 7
108 // Control register 2 (R/W)
109 #define LPS22DF_CTRL_REG2 0x11
110 #define LPS22DF_CTRL_REG2_BOOT 0x80
111 #define LPS22DF_CTRL_REG2_LFPF_CFG 0x20
112 #define LPS22DF_CTRL_REG2_EN_LPFP 0x10
113 #define LPS22DF_CTRL_REG2_BDU 0x08
114 #define LPS22DF_CTRL_REG2_SWRESET 0x04
115 #define LPS22DF_CTRL_REG2_ONESHOT 0x01
117 // Control register 3 (R/W)
118 #define LPS22DF_CTRL_REG3 0x12
119 #define LPS22DF_CTRL_REG3_INT_HL 0x08
120 #define LPS22DF_CTRL_REG3_PP_OD 0x02
121 #define LPS22DF_CTRL_REG3_IF_ADD_INC 0x01
123 // Control register 4 (R/W)
124 #define LPS22DF_CTRL_REG4 0x13
125 #define LPS22DF_CTRL_REG4_DRDY_PLS 0x40
126 #define LPS22DF_CTRL_REG4_DRDY 0x20
127 #define LPS22DF_CTRL_REG4_INT_EN 0x10
128 #define LPS22DF_CTRL_REG4_INT_F_FULL 0x04
129 #define LPS22DF_CTRL_REG4_INT_F_WTM 0x02
130 #define LPS22DF_CTRL_REG4_INT_F_OVR 0x01
132 //FIFO control register (R/W)
133 #define LPS22DF_FIFO_CTRL 0x14
134 #define LPS22DF_FIFO_CTRL_STOP_ON_WTM 0x08
135 #define LPS22DF_FIFO_CTRL_TRIG_MODES 0x04
136 #define LPS22DF_FIFO_CTRL_F_MODE_MASK 0x03
137 #define LPS22DF_FIFO_CTRL_F_MODE_SHIFT 0
138 #define LPS22DF_FIFO_CTRL_F_MODE_BYPASS 0
139 #define LPS22DF_FIFO_CTRL_F_MODE_FIFO_MODE 1
140 #define LPS22DF_FIFO_CTRL_F_MODE_CONT 2
141 #define LPS22DF_FIFO_CTRL_F_MODE_CONT_TO_FIFO 3
143 // FIFO threshold setting register (R/W)
144 #define LPS22DF_FIFO_WTM 0x15
146 // Reference pressure LSB data (R)
147 #define LPS22DF_REF_P_L 0x16
148 #define LPS22DF_REF_P_H 0x17
152 // Control register (R/W)
153 #define LPS22DF_I3C_IF_CTRL_ADD 0x19
154 #define LPS22DF_I3C_IF_CTRL_ADD_RESVD 0x80
155 #define LPS22DF_I3C_IF_CTRL_ADD_ASF_ON 0x20
156 #define LPS22DF_I3C_IF_CTRL_ADD_I3C_BUS_AVB_SEL_MASK 0x03
157 #define LPS22DF_I3C_IF_CTRL_ADD_I3C_BUS_AVB_SEL_SHIFT 0
158 #define LPS22DF_I3C_IF_CTRL_ADD_I3C_BUS_AVB_SEL_50US 0
159 #define LPS22DF_I3C_IF_CTRL_ADD_I3C_BUS_AVB_SEL_2US 1
160 #define LPS22DF_I3C_IF_CTRL_ADD_I3C_BUS_AVB_SEL_1MS 2
161 #define LPS22DF_I3C_IF_CTRL_ADD_I3C_BUS_AVB_SEL_25MS 3
163 // Pressure offset (R/W)
164 #define LPS22DF_RPDS_L 0x1A
165 #define LPS22DF_RPDS_H 0x1B
169 // Interrupt source (R) register for differential pressure. A read at this address clears the INT_SOURCE register itself.
170 #define LPS22DF_INT_SOURCE 0x24
171 #define LPS22DF_INT_SOURCE_BOOT_ON 0x80
172 #define LPS22DF_INT_SOURCE_IA 0x04
173 #define LPS22DF_INT_SOURCE_PL 0x02
174 #define LPS22DF_INT_SOURCE_PH 0x01
176 // FIFO status register (R)
177 #define LPS22DF_FIFO_STATUS1 0x25
179 // FIFO status register (R)
180 #define LPS22DF_FIFO_STATUS2 0x26
181 #define LPS22DF_FIFO_STATUS2_WTM_IA 0x80
182 #define LPS22DF_FIFO_STATUS2_OVR_IA 0x40
183 #define LPS22DF_FIFO_STATUS2_FULL_IA 0x20
185 // Status register (R)
186 #define LPS22DF_STATUS 0x27
187 #define LPS22DF_STATUS_T_OR 0x20
188 #define LPS22DF_STATUS_P_OR 0x10
189 #define LPS22DF_STATUS_T_DA 0x02
190 #define LPS22DF_STATUS_P_DA 0x01
192 // Pressure output value (R)
193 #define LPS22DF_PRESSURE_OUT_XL 0x28
194 #define LPS22DF_PRESSURE_OUT_L 0x29
195 #define LPS22DF_PRESSURE_OUT_H 0x2A
197 // Temperature output value (R)
198 #define LPS22DF_TEMP_OUT_L 0x2B
199 #define LPS22DF_TEMP_OUT_H 0x2C
203 // FIFO pressure output (R)
204 #define LPS22DF_FIFO_DATA_OUT_PRESS_XL 0x78
205 #define LPS22DF_FIFO_DATA_OUT_PRESS_L 0x79
206 #define LPS22DF_FIFO_DATA_OUT_PRESS_H 0x7A
208 #define LPS22DF_I2C_ADDR 0x5D
210 static uint8_t lps22df_chip_id
= 0;
212 // uncompensated pressure and temperature
213 static int32_t lps22df_up
= 0;
214 static int32_t lps22df_ut
= 0;
216 // 3 bytes of pressure followed by two bytes of temperature
217 #define LPS22DF_DATA_FRAME_SIZE (LPS22DF_TEMP_OUT_H - LPS22DF_PRESSURE_OUT_XL + 1)
219 static DMA_DATA_ZERO_INIT
uint8_t sensor_data
[LPS22DF_DATA_FRAME_SIZE
];
221 static bool lps22dfStartUT(baroDev_t
*baro
);
222 static bool lps22dfReadUT(baroDev_t
*baro
);
223 static bool lps22dfGetUT(baroDev_t
*baro
);
224 static bool lps22dfStartUP(baroDev_t
*baro
);
225 static bool lps22dfReadUP(baroDev_t
*baro
);
226 static bool lps22dfGetUP(baroDev_t
*baro
);
228 STATIC_UNIT_TESTED
void lps22dfCalculate(int32_t *pressure
, int32_t *temperature
);
230 static void lps22dfBusInit(const extDevice_t
*dev
)
232 #ifdef USE_BARO_SPI_LPS22DF
233 if (dev
->bus
->busType
== BUS_TYPE_SPI
) {
234 IOHi(dev
->busType_u
.spi
.csnPin
); // Disable
235 IOInit(dev
->busType_u
.spi
.csnPin
, OWNER_BARO_CS
, 0);
236 IOConfigGPIO(dev
->busType_u
.spi
.csnPin
, IOCFG_OUT_PP
);
237 spiSetClkDivisor(dev
, spiCalculateDivider(LPS22DF_MAX_SPI_CLK_HZ
));
244 static void lps22dfBusDeinit(const extDevice_t
*dev
)
246 #ifdef USE_BARO_SPI_LPS22DF
247 if (dev
->bus
->busType
== BUS_TYPE_SPI
) {
248 ioPreinitByIO(dev
->busType_u
.spi
.csnPin
, IOCFG_IPU
, PREINIT_PIN_STATE_HIGH
);
255 bool lps22dfDetect(baroDev_t
*baro
)
259 extDevice_t
*dev
= &baro
->dev
;
260 bool defaultAddressApplied
= false;
264 if ((dev
->bus
->busType
== BUS_TYPE_I2C
) && (dev
->busType_u
.i2c
.address
== 0)) {
265 // Default address for LPS22DF
266 dev
->busType_u
.i2c
.address
= LPS22DF_I2C_ADDR
;
267 defaultAddressApplied
= true;
270 busReadRegisterBuffer(dev
, LPS22DF_WHO_AM_I
, &lps22df_chip_id
, 1); /* read Chip Id */
272 if ((lps22df_chip_id
!= LPS22DF_CHIP_ID
)) {
273 lps22dfBusDeinit(dev
);
274 if (defaultAddressApplied
) {
275 dev
->busType_u
.i2c
.address
= 0;
280 busDeviceRegister(dev
);
283 busWriteRegister(dev
, LPS22DF_CTRL_REG2
, LPS22DF_CTRL_REG2_SWRESET
);
285 // Enable one-shot ODR and averaging at 16
286 busWriteRegister(dev
, LPS22DF_CTRL_REG1
, LSM6DSV_ENCODE_BITS(LPS22DF_CTRL_REG1_ODR_ONE_SHOT
,
287 LPS22DF_CTRL_REG1_ODR_MASK
,
288 LPS22DF_CTRL_REG1_ODR_SHIFT
) |
289 LSM6DSV_ENCODE_BITS(LPS22DF_CTRL_REG1_AVG_16
,
290 LPS22DF_CTRL_REG1_AVG_MASK
,
291 LPS22DF_CTRL_REG1_AVG_SHIFT
));
293 // these are dummy as temperature is measured as part of pressure
294 baro
->combined_read
= true;
296 baro
->start_ut
= lps22dfStartUT
;
297 baro
->get_ut
= lps22dfGetUT
;
298 baro
->read_ut
= lps22dfReadUT
;
299 // only _up part is executed, and gets both temperature and pressure
300 baro
->start_up
= lps22dfStartUP
;
301 baro
->get_up
= lps22dfGetUP
;
302 baro
->read_up
= lps22dfReadUP
;
303 baro
->up_delay
= 10000; // 10ms
304 baro
->calculate
= lps22dfCalculate
;
309 static bool lps22dfStartUT(baroDev_t
*baro
)
317 static bool lps22dfReadUT(baroDev_t
*baro
)
324 static bool lps22dfGetUT(baroDev_t
*baro
)
331 static bool lps22dfStartUP(baroDev_t
*baro
)
334 // Trigger one-shot enable block data update to ensure LSB/MSB are coherent
335 return busWriteRegister(&baro
->dev
, LPS22DF_CTRL_REG2
, LPS22DF_CTRL_REG2_ONESHOT
| LPS22DF_CTRL_REG2_BDU
);
338 static bool lps22dfReadUP(baroDev_t
*baro
)
340 if (busBusy(&baro
->dev
, NULL
)) {
344 // Read data from sensor
345 return busReadRegisterBufferStart(&baro
->dev
, LPS22DF_PRESSURE_OUT_XL
, sensor_data
, LPS22DF_DATA_FRAME_SIZE
);
348 static bool lps22dfGetUP(baroDev_t
*baro
)
350 if (busBusy(&baro
->dev
, NULL
)) {
354 lps22df_up
= (int32_t)(sensor_data
[0] | sensor_data
[1] << 8 | sensor_data
[2] << 16);
355 lps22df_ut
= (int32_t)(sensor_data
[3] | sensor_data
[4] << 8);
360 // Returns temperature in DegC, resolution is 0.01 DegC. Output value of "5123" equals 51.23 DegC
361 // t_fine carries fine temperature as global value
362 static int32_t lps22dfCompensateTemperature(int32_t adc_T
)
367 // Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
368 // Output value of "24674867" represents 24674867/256 = 96386.2 Pa = 963.862 hPa
369 static uint32_t lps22dfCompensatePressure(int32_t adc_P
)
371 return (uint32_t)(adc_P
* 100.0f
/ 16);
374 STATIC_UNIT_TESTED
void lps22dfCalculate(int32_t *pressure
, int32_t *temperature
)
379 t
= lps22dfCompensateTemperature(lps22df_ut
);
380 p
= lps22dfCompensatePressure(lps22df_up
);
383 *pressure
= (int32_t)(p
/ 256);