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)
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/>.
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))
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)
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
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
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
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
));
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
);
254 bool lps22dfDetect(baroDev_t
*baro
)
258 extDevice_t
*dev
= &baro
->dev
;
259 bool defaultAddressApplied
= false;
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;
279 busDeviceRegister(dev
);
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;
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
;
308 static void lps22dfStartUT(baroDev_t
*baro
)
314 static bool lps22dfReadUT(baroDev_t
*baro
)
321 static bool lps22dfGetUT(baroDev_t
*baro
)
328 static void lps22dfStartUP(baroDev_t
*baro
)
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
)) {
341 // Read data from sensor
342 busReadRegisterBufferStart(&baro
->dev
, LPS22DF_PRESSURE_OUT_XL
, sensor_data
, LPS22DF_DATA_FRAME_SIZE
);
347 static bool lps22dfGetUP(baroDev_t
*baro
)
349 if (busBusy(&baro
->dev
, NULL
)) {
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);
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
)
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
)
378 t
= lps22dfCompensateTemperature(lps22df_ut
);
379 p
= lps22dfCompensatePressure(lps22df_up
);
382 *pressure
= (int32_t)(p
/ 256);