px-fwlib 0.10.0
Cross-platform embedded library and documentation for 8/16/32-bit microcontrollers generated with Doxygen 1.9.2
px_bmp280.c
1/* =============================================================================
2 ____ ___ ____ ___ _ _ ___ __ __ ___ __ __ TM
3 | _ \ |_ _| / ___| / _ \ | \ | | / _ \ | \/ | |_ _| \ \/ /
4 | |_) | | | | | | | | | | \| | | | | | | |\/| | | | \ /
5 | __/ | | | |___ | |_| | | |\ | | |_| | | | | | | | / \
6 |_| |___| \____| \___/ |_| \_| \___/ |_| |_| |___| /_/\_\
7
8 Copyright (c) 2018 Pieter Conradie <https://piconomix.com>
9
10 License: MIT
11 https://github.com/piconomix/px-fwlib/blob/master/LICENSE.md
12
13 Title: Bosch BMP280 Barometric pressure sensor driver
14 Author(s): Pieter Conradie
15 Creation Date: 2018-07-12
16
17============================================================================= */
18
19/* _____STANDARD INCLUDES____________________________________________________ */
20
21/* _____PROJECT INCLUDES_____________________________________________________ */
22#include "px_bmp280.h"
23#include "px_i2c.h"
24#include "px_board.h"
25#include "px_log.h"
26
27#ifdef PX_BMP280_DBG
28#warning "Debugging of BMP280 enabled. Using constants to verify formula"
29#endif
30
31/* _____LOCAL DEFINITIONS____________________________________________________ */
32PX_LOG_NAME("bmp280");
33
34/// @name Memory Map (table 18, p. 24)
35/// @{
36#define PX_BMP280_REG_CALIB00 0x88
37#define PX_BMP280_REG_CALIB25 0xa1
38#define PX_BMP280_REG_ID 0xd0
39#define PX_BMP280_REG_RESET 0xe0
40#define PX_BMP280_REG_STATUS 0xf3
41#define PX_BMP280_REG_CTRL_MEAS 0xf4
42#define PX_BMP280_REG_CONFIG 0xf5
43#define PX_BMP280_REG_PRESS_MSB 0xf7
44#define PX_BMP280_REG_PRESS_LSB 0xf8
45#define PX_BMP280_REG_PRESS_XLSB 0xf9
46#define PX_BMP280_REG_TEMP_MSB 0xfa
47#define PX_BMP280_REG_TEMP_LSB 0xfb
48#define PX_BMP280_REG_TEMP_XLSB 0xfc
49/// @}
50
51// 4.3.1 Register 0xD0 "id"
52#define PX_BMP280_REG_ID_VAL 0x58
53
54// 4.3.2 Register 0xE0 "reset"
55#define PX_BMP280_REG_RESET_VAL 0xb6
56
57// 4.3.3 Register 0xF3 "status"
58#define PX_BMP280_REG_STATUS_MEAS_POS 3
59#define PX_BMP280_REG_STATUS_MEAS_MSK (1 << 3)
60#define PX_BMP280_REG_STATUS_IM_UPDATE_POS 0
61#define PX_BMP280_REG_STATUS_IM_UPDATE_MSK (1 << 0)
62
63// 4.3.4 Register 0xF4 "ctrl_meas"
64#define PX_BMP280_REG_CTRL_MEAS_OSRS_T_POS 5
65#define PX_BMP280_REG_CTRL_MEAS_OSRS_T_MSK (7 << 5)
66#define PX_BMP280_REG_CTRL_MEAS_OSRS_P_POS 2
67#define PX_BMP280_REG_CTRL_MEAS_OSRS_P_MSK (7 << 2)
68#define PX_BMP280_REG_CTRL_MEAS_MODE_POS 0
69#define PX_BMP280_REG_CTRL_MEAS_MODE_MSK (3 << 0)
70#define PX_BMP280_REG_CTRL_MEAS_OVERS_SKIPPED 0
71#define PX_BMP280_REG_CTRL_MEAS_OVERS_1 1
72#define PX_BMP280_REG_CTRL_MEAS_OVERS_2 2
73#define PX_BMP280_REG_CTRL_MEAS_OVERS_4 3
74#define PX_BMP280_REG_CTRL_MEAS_OVERS_8 4
75#define PX_BMP280_REG_CTRL_MEAS_OVERS_16 5
76#define PX_BMP280_REG_CTRL_MEAS_MODE_SLEEP 0
77#define PX_BMP280_REG_CTRL_MEAS_MODE_FORCED 1
78#define PX_BMP280_REG_CTRL_MEAS_MODE_NORMAL 3
79
80// 4.3.5 Register 0xF5 "config"
81#define PX_BMP280_REG_CONFIG_T_SB_POS 5
82#define PX_BMP280_REG_CONFIG_T_SB_MSK (7 << 5)
83#define PX_BMP280_REG_CONFIG_FILTER_POS 2
84#define PX_BMP280_REG_CONFIG_FILTER_MSK (7 << 2)
85#define PX_BMP280_REG_CONFIG_SPI3W_EN_POS 0
86#define PX_BMP280_REG_CONFIG_SPI3W_EN_MSK (1 << 0)
87
88// 4.3.6 Register 0xF7..0xF9 "pressure" (_msb, _lsb, _xlsb)
89#define PX_BMP280_PRESS_XLSB_POS 4
90#define PX_BMP280_PRESS_XLSB_MSK (15 << 4)
91
92// 4.3.7 Register 0xFA..0xFC "temperature" (_msb, _lsb, _xlsb)
93#define PX_BMP280_TEMP_XLSB_POS 4
94#define PX_BMP280_TEMP_XLSB_MSK (15 << 4)
95
96/// BMP280 calibration coefficients
97typedef struct
98{
99 uint16_t t1;
100 int16_t t2;
101 int16_t t3;
102 uint16_t p1;
103 int16_t p2;
104 int16_t p3;
105 int16_t p4;
106 int16_t p5;
107 int16_t p6;
108 int16_t p7;
109 int16_t p8;
110 int16_t p9;
112
113/// Number of calibration coefficients
114#define PX_BMP280_NR_OF_CALS 12
115
116/* _____MACROS_______________________________________________________________ */
117
118/* _____GLOBAL VARIABLES_____________________________________________________ */
119
120/* _____LOCAL VARIABLES______________________________________________________ */
121static px_i2c_handle_t * px_bmp280_i2c_handle;
122
123static union
124{
125 px_bmp280_cal_t dig;
126 uint16_t data[PX_BMP280_NR_OF_CALS];
127} px_bmp280_cal;
128
129/* _____LOCAL FUNCTION DECLARATIONS__________________________________________ */
130
131/* _____LOCAL FUNCTIONS______________________________________________________ */
132static bool px_bmp280_reg_wr(uint8_t adr, uint8_t data)
133{
134 uint8_t i2c_data[2];
135
136 i2c_data[0] = adr;
137 i2c_data[1] = data;
138
139 if(!px_i2c_wr(px_bmp280_i2c_handle, i2c_data, 2, PX_I2C_FLAG_START_AND_STOP))
140 {
141 // Error
142 PX_LOG_E("Unable to write register value");
143 return false;
144 }
145 // Success
146 return true;
147}
148
149static bool px_bmp280_reg_rd(uint8_t adr, uint8_t * data)
150{
151 uint8_t i2c_data[1];
152
153 i2c_data[0] = adr;
154
155 // Write address
156 if(!px_i2c_wr(px_bmp280_i2c_handle, i2c_data, 1, PX_I2C_FLAG_START_AND_END))
157 {
158 // Error
159 PX_LOG_E("Unable to write register address");
160 return false;
161 }
162 // Read data
163 if(!px_i2c_rd(px_bmp280_i2c_handle, data, 1, PX_I2C_FLAG_REP_START_AND_STOP))
164 {
165 // Error
166 PX_LOG_E("Unable to read register value");
167 return false;
168 }
169 // Success
170 return true;
171}
172
173static bool px_bmp280_reg_rd_data(uint8_t adr, void * data, uint8_t nr_of_bytes)
174{
175 uint8_t i2c_data[1];
176
177 i2c_data[0] = adr;
178
179 // Write address
180 if(!px_i2c_wr(px_bmp280_i2c_handle, i2c_data, 1, PX_I2C_FLAG_START_AND_END))
181 {
182 // Error
183 PX_LOG_E("Unable to write register address");
184 return false;
185 }
186 // Read data
187 if(!px_i2c_rd(px_bmp280_i2c_handle, data, nr_of_bytes, PX_I2C_FLAG_REP_START_AND_STOP))
188 {
189 // Error
190 PX_LOG_E("Unable to read register data");
191 return false;
192 }
193 // Success
194 return true;
195}
196
197static bool px_bmp280_cal_rd(void)
198{
199 uint8_t i;
200 uint8_t adr;
201 uint8_t i2c_data[2];
202 uint16_t val;
203
204 // Start at first calibration coefficient
205 adr = PX_BMP280_REG_CALIB00;
206
207 for(i = 0; i < PX_BMP280_NR_OF_CALS; i++)
208 {
209 // Read 16-bit value (Least Significant Byte first)
210 if(!px_bmp280_reg_rd_data(adr, i2c_data, 2))
211 {
212 // Error
213 PX_LOG_E("Unable to read calibration value");
214 return false;
215 }
216 PX_LOG_D("Adr %02X: %02X %02X", adr, i2c_data[0], i2c_data[1]);
217 // Assemble value
218 val = PX_U16_CONCAT_U8(i2c_data[1], i2c_data[0]);
219 // Store value
220 px_bmp280_cal.data[i] = val;
221 // Next calibration coefficient
222 adr += 2;
223 }
224
225#ifdef PX_BMP280_DBG
226 // Set calibration values to excel table on page 23 to verify formula
227 px_bmp280_cal.dig.t1 = 27504;
228 px_bmp280_cal.dig.t2 = 26435;
229 px_bmp280_cal.dig.t3 = -1000;
230 px_bmp280_cal.dig.p1 = 36477;
231 px_bmp280_cal.dig.p2 = -10685;
232 px_bmp280_cal.dig.p3 = 3024;
233 px_bmp280_cal.dig.p4 = 2855;
234 px_bmp280_cal.dig.p5 = 140;
235 px_bmp280_cal.dig.p6 = -7;
236 px_bmp280_cal.dig.p7 = 15500;
237 px_bmp280_cal.dig.p8 = -14600;
238 px_bmp280_cal.dig.p9 = 6000;
239#endif
240
242 {
243 // Report calibration values
244 PX_LOG_D("BMP280 Cal:");
245 PX_LOG_TRACE("T1 = %d\n", px_bmp280_cal.dig.t1);
246 PX_LOG_TRACE("T2 = %d\n", px_bmp280_cal.dig.t2);
247 PX_LOG_TRACE("T3 = %u\n", px_bmp280_cal.dig.t3);
248 PX_LOG_TRACE("P1 = %d\n", px_bmp280_cal.dig.p1);
249 PX_LOG_TRACE("P2 = %d\n", px_bmp280_cal.dig.p2);
250 PX_LOG_TRACE("P3 = %d\n", px_bmp280_cal.dig.p3);
251 PX_LOG_TRACE("P4 = %d\n", px_bmp280_cal.dig.p4);
252 PX_LOG_TRACE("P5 = %d\n", px_bmp280_cal.dig.p5);
253 PX_LOG_TRACE("P6 = %d\n", px_bmp280_cal.dig.p6);
254 PX_LOG_TRACE("P7 = %d\n", px_bmp280_cal.dig.p7);
255 PX_LOG_TRACE("P8 = %d\n", px_bmp280_cal.dig.p8);
256 PX_LOG_TRACE("P9 = %d\n", px_bmp280_cal.dig.p9);
257 }
258
259 return true;
260}
261
262static bool px_bmp280_temp_raw_rd(int32_t * data)
263{
264 uint8_t i2c_data[3];
265
266 if(!px_bmp280_reg_rd_data(PX_BMP280_REG_TEMP_MSB, i2c_data, 3))
267 {
268 // Error
269 PX_LOG_E("Unable to read temp");
270 return false;
271 }
272 PX_LOG_D("Temp data: %02X %02X %02X", i2c_data[0], i2c_data[1], i2c_data[2]);
273
274 // Assemble value
275 *data = (int32_t)( (((uint32_t)i2c_data[0]) << 12)
276 | (((uint32_t)i2c_data[1]) << 4)
277 | (((uint32_t)i2c_data[2]) >> 4) );
278
279#ifdef PX_BMP280_DBG
280 *data = 519888;
281#endif
282
283 PX_LOG_D("Raw Temp = %ld", *data);
284
285 return true;
286}
287
288static bool px_bmp280_press_raw_rd(int32_t * data)
289{
290 uint8_t i2c_data[3];
291
292 if(!px_bmp280_reg_rd_data(PX_BMP280_REG_PRESS_MSB, i2c_data, 3))
293 {
294 // Error
295 PX_LOG_E("Unable to read temp");
296 return false;
297 }
298 PX_LOG_D("Press data: %02X %02X %02X", i2c_data[0], i2c_data[1], i2c_data[2]);
299
300 // Assemble value
301 *data = (int32_t)( (((uint32_t)i2c_data[0]) << 12)
302 | (((uint32_t)i2c_data[1]) << 4)
303 | (((uint32_t)i2c_data[2]) >> 4) );
304
305#ifdef PX_BMP280_DBG
306 *data = 415148;
307#endif
308
309 PX_LOG_D("Raw Press = %ld", *data);
310
311 return true;
312}
313
314/**
315 * Calculate compensated temperature in 0.01 deg C steps, e.g.
316 * "5123" equals 51.23 deg C
317 *
318 * @param ut[in] Uncompensated Temperature
319 * @param t_fine[out] Fine temperature used to calculate compensated pressure
320 *
321 * @return int32_t Compensated Temperature
322 */
323static int32_t px_bmp280_comp_temp(int32_t ut, int32_t * t_fine)
324{
325 int32_t var1;
326 int32_t var2;
327 int32_t temp;
328
329 var1 = ( (((ut >> 3) - ((int32_t)px_bmp280_cal.dig.t1 << 1)))
330 * ((int32_t)px_bmp280_cal.dig.t2)) >> 11;
331 var2 = ( (( ((ut >> 4) - ((int32_t)px_bmp280_cal.dig.t1))
332 * ((ut >> 4) - ((int32_t)px_bmp280_cal.dig.t1))) >> 12)
333 * ((int32_t)px_bmp280_cal.dig.t3)) >> 14;
334
335 *t_fine = var1 + var2;
336 temp = (*t_fine * 5 + 128) >> 8;
337
338 PX_LOG_D("Compensated Temp = %ld", temp);
339
340 return temp;
341}
342
343/**
344 * Calculate compensated pressure in Pa as unsigned 32 bit integer, e.g.
345 * "96386" equals 96386 Pa = 963.86 hPa
346 *
347 * @param up[in] Uncompensated Pressure
348 * @param t_fine[in] Fine temperature returned from compensated temperature
349 * calculation
350 *
351 * @return uint32_t Comnpensated Pressure
352 */
353static uint32_t px_bmp280_comp_press(int32_t up, int32_t t_fine)
354{
355 int32_t var1;
356 int32_t var2;
357 uint32_t press;
358
359 var1 = (((int32_t)t_fine) >> 1) - (int32_t)64000;
360 var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * ((int32_t)px_bmp280_cal.dig.p6);
361 var2 = var2 + ((var1 * ((int32_t)px_bmp280_cal.dig.p5)) << 1);
362 var2 = (var2 >> 2) + (((int32_t)px_bmp280_cal.dig.p4) << 16);
363 var1 = ( ((px_bmp280_cal.dig.p3 * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3)
364 + ((((int32_t)px_bmp280_cal.dig.p2) * var1) >> 1)) >> 18;
365 var1 = (((32768 + var1)) * ((int32_t)px_bmp280_cal.dig.p1)) >> 15;
366 press = (((uint32_t)(((int32_t)1048576) - up) - (var2 >> 12))) * 3125;
367
368 // Avoid divide by zero
369 if(var1 == 0)
370 {
371 return 0;
372 }
373
374 // Avoid overflow
375 if (press < 0x80000000)
376 {
377 press = (press << 1) / ((uint32_t)var1);
378 }
379 else
380 {
381 press = (press / (uint32_t)var1) * 2;
382 }
383
384 var1 = ( ((int32_t)px_bmp280_cal.dig.p9)
385 * ((int32_t)(((press >> 3) * (press >> 3)) >> 13)) ) >> 12;
386 var2 = (((int32_t)(press >> 2)) * ((int32_t)px_bmp280_cal.dig.p8)) >> 13;
387 press = (uint32_t)((int32_t)press + ((var1 + var2 + px_bmp280_cal.dig.p7) >> 4));
388
389 PX_LOG_D("Compensated Pressure = %ld", press);
390
391 return press;
392}
393
394/* _____GLOBAL FUNCTIONS_____________________________________________________ */
395bool px_bmp280_init(px_i2c_handle_t * handle)
396{
397 uint8_t data;
398
399 // Save I2C slave handle
400 px_bmp280_i2c_handle = handle;
401 // Verify Chip ID
402 if(!px_bmp280_reg_rd(PX_BMP280_REG_ID, &data))
403 {
404 // Error
405 PX_LOG_E("Unable to read Chip ID");
406 return false;
407 }
408 if(data != PX_BMP280_REG_ID_VAL)
409 {
410 // Error
411 PX_LOG_E("Chip ID does not match. Received %02X, but expected %02X",
412 data, PX_BMP280_REG_ID_VAL);
413 return false;
414 }
415 // Wait until NVM calibration data is copied to registers
416 do
417 {
418 if(!px_bmp280_reg_rd(PX_BMP280_REG_STATUS, &data))
419 {
420 // Error
421 PX_LOG_E("Unable to read status");
422 return false;
423 }
424 }
425 while(data & PX_BMP280_REG_STATUS_IM_UPDATE_MSK);
426 // Read calibration
427 if(!px_bmp280_cal_rd())
428 {
429 // Error
430 return false;
431 }
432
433 // Success
434 return true;
435}
436
437bool px_bmp280_rd(int32_t * temp, int32_t * press)
438{
439 uint8_t data;
440 int32_t t_fine;
441
442 // Read calibration
443 if(!px_bmp280_cal_rd())
444 {
445 // Error
446 return false;
447 }
448 // Start new "forced" measurement (pressure x 8, temperature x 1 oversampled)
449 data = (PX_BMP280_REG_CTRL_MEAS_OVERS_1 << PX_BMP280_REG_CTRL_MEAS_OSRS_T_POS)
450 + (PX_BMP280_REG_CTRL_MEAS_OVERS_8 << PX_BMP280_REG_CTRL_MEAS_OSRS_P_POS)
451 + (PX_BMP280_REG_CTRL_MEAS_MODE_FORCED << PX_BMP280_REG_CTRL_MEAS_MODE_POS);
452 if(!px_bmp280_reg_wr(PX_BMP280_REG_CTRL_MEAS, data))
453 {
454 // Error
455 PX_LOG_E("Unable to start measurement");
456 return false;
457 }
458 // Wait until conversion is finished
459 do
460 {
461 if(!px_bmp280_reg_rd(PX_BMP280_REG_STATUS, &data))
462 {
463 // Error
464 PX_LOG_E("Unable to read status");
465 return false;
466 }
467 }
468 while(data & PX_BMP280_REG_STATUS_MEAS_MSK);
469 // Read raw temperature value
470 if(!px_bmp280_temp_raw_rd(temp))
471 {
472 return false;
473 }
474 // Read raw pressure value
475 if(!px_bmp280_press_raw_rd(press))
476 {
477 return false;
478 }
479 // Calculated compensated temperature value
480 *temp = px_bmp280_comp_temp(*temp, &t_fine);
481 // Calculated compensated pressure value
482 *press = px_bmp280_comp_press(*press, t_fine);
483
484 // Success
485 return true;
486}
#define PX_U16_CONCAT_U8(hi8, lo8)
Concatenate 2 x 8 bits to form 16-bit value.
Definition: px_defs.h:268
#define PX_LOG_D(format,...)
Macro to display a formatted DEBUG message.
Definition: px_log.h:410
#define PX_LOG_TRACE(format,...)
Macro to output a user format string if PX_LOG=1.
Definition: px_log.h:458
#define PX_LOG_NAME(name)
Macro to declare a log name string once for each file to reduce code size.
Definition: px_log.h:349
#define PX_LOG_LEVEL_IS_D()
Debug level enabled?
Definition: px_log.h:371
#define PX_LOG_E(format,...)
Macro to display a formatted ERROR message.
Definition: px_log.h:377
#define PX_I2C_FLAG_START_AND_STOP
Begin and finish an I2C transaction with a START and STOP condition.
Definition: px_i2c.h:93
bool px_i2c_rd(px_i2c_handle_t *handle, void *data, size_t nr_of_bytes, uint8_t flags)
Perform an I2C read transaction with an I2C slave.
Definition: px_i2c.c:430
#define PX_I2C_FLAG_REP_START_AND_STOP
Begin and finish an I2C transaction with a REP START and STOP condition.
Definition: px_i2c.h:97
#define PX_I2C_FLAG_START_AND_END
Begin and finish an I2C transaction with a START and END condition.
Definition: px_i2c.h:95
bool px_i2c_wr(px_i2c_handle_t *handle, const void *data, size_t nr_of_bytes, uint8_t flags)
Perform an I2C write transaction with an I2C slave.
Definition: px_i2c.c:240
Define I2C handle for a slave.
Definition: px_i2c.h:104
BMP280 calibration coefficients.
Definition: px_bmp280.c:98