Contiki-NG
bmp-280-sensor.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018, Texas Instruments Incorporated - http://www.ti.com/
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holder nor the names of its
14  * contributors may be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28  * OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 /**
31  * \addtogroup sensortag-bmp-sensor
32  * @{
33  *
34  * \file
35  * Driver for the Sensortag BMP280 Altimeter / Pressure Sensor
36  * \author
37  * Edvard Pettersen <e.pettersen@ti.com>
38  */
39 /*---------------------------------------------------------------------------*/
40 #include "contiki.h"
41 #include "lib/sensors.h"
42 #include "sys/ctimer.h"
43 /*---------------------------------------------------------------------------*/
44 #include "board-conf.h"
45 #include "bmp-280-sensor.h"
46 /*---------------------------------------------------------------------------*/
47 #include <Board.h>
48 
49 #include <ti/drivers/I2C.h>
50 /*---------------------------------------------------------------------------*/
51 #include <stdbool.h>
52 #include <stdint.h>
53 #include <string.h>
54 /*---------------------------------------------------------------------------*/
55 #define DEBUG 0
56 #if DEBUG
57 #define PRINTF(...) printf(__VA_ARGS__)
58 #else
59 #define PRINTF(...)
60 #endif
61 /*---------------------------------------------------------------------------*/
62 /*
63  * Disable the entire file if sensors are disabled, as it could potentially
64  * create compile errors with missing defines from either the Board file or
65  * configuration defines.
66  */
67 #if BOARD_SENSORS_ENABLE
68 /*---------------------------------------------------------------------------*/
69 #ifndef Board_BMP280_ADDR
70 #error "Board file doesn't define I2C address Board_BMP280_ADDR"
71 #endif
72 /* Sensor I2C address */
73 #define BMP280_I2C_ADDRESS Board_BMP280_ADDR
74 /*---------------------------------------------------------------------------*/
75 /* Registers */
76 #define ADDR_CALIB 0x88
77 #define ADDR_PROD_ID 0xD0
78 #define ADDR_RESET 0xE0
79 #define ADDR_STATUS 0xF3
80 #define ADDR_CTRL_MEAS 0xF4
81 #define ADDR_CONFIG 0xF5
82 #define ADDR_PRESS_MSB 0xF7
83 #define ADDR_PRESS_LSB 0xF8
84 #define ADDR_PRESS_XLSB 0xF9
85 #define ADDR_TEMP_MSB 0xFA
86 #define ADDR_TEMP_LSB 0xFB
87 #define ADDR_TEMP_XLSB 0xFC
88 /*---------------------------------------------------------------------------*/
89 /* Reset values */
90 #define VAL_PROD_ID 0x58
91 #define VAL_RESET 0x00
92 #define VAL_STATUS 0x00
93 #define VAL_CTRL_MEAS 0x00
94 #define VAL_CONFIG 0x00
95 #define VAL_PRESS_MSB 0x80
96 #define VAL_PRESS_LSB 0x00
97 #define VAL_TEMP_MSB 0x80
98 #define VAL_TEMP_LSB 0x00
99 /*---------------------------------------------------------------------------*/
100 /* Test values */
101 #define VAL_RESET_EXECUTE 0xB6
102 #define VAL_CTRL_MEAS_TEST 0x55
103 /*---------------------------------------------------------------------------*/
104 /* Misc. */
105 #define SENSOR_DATA_BUF_SIZE 6
106 /*---------------------------------------------------------------------------*/
107 #define RES_OFF 0
108 #define RES_ULTRA_LOW_POWER 1
109 #define RES_LOW_POWER 2
110 #define RES_STANDARD 3
111 #define RES_HIGH 5
112 #define RES_ULTRA_HIGH 6
113 /*---------------------------------------------------------------------------*/
114 /* Bit fields in CTRL_MEAS register */
115 #define PM_OFF 0
116 #define PM_FORCED 1
117 #define PM_NORMAL 3
118 /*---------------------------------------------------------------------------*/
119 #define OSRST(v) ((v) << 5)
120 #define OSRSP(v) ((v) << 2)
121 /*---------------------------------------------------------------------------*/
122 typedef struct {
123  uint16_t dig_t1;
124  int16_t dig_t2;
125  int16_t dig_t3;
126  uint16_t dig_p1;
127  int16_t dig_p2;
128  int16_t dig_p3;
129  int16_t dig_p4;
130  int16_t dig_p5;
131  int16_t dig_p6;
132  int16_t dig_p7;
133  int16_t dig_p8;
134  int16_t dig_p9;
135 } BMP_280_Calibration;
136 /*---------------------------------------------------------------------------*/
137 static BMP_280_Calibration calib_data;
138 /*---------------------------------------------------------------------------*/
139 static I2C_Handle i2c_handle;
140 /*---------------------------------------------------------------------------*/
141 typedef enum {
142  SENSOR_STATUS_DISABLED,
143  SENSOR_STATUS_INITIALISED,
144  SENSOR_STATUS_NOT_READY,
145  SENSOR_STATUS_READY
146 } SENSOR_STATUS;
147 
148 static volatile SENSOR_STATUS sensor_status = SENSOR_STATUS_DISABLED;
149 /*---------------------------------------------------------------------------*/
150 /* Wait SENSOR_STARTUP_DELAY clock ticks for the sensor to be ready - ~80ms */
151 #define SENSOR_STARTUP_DELAY 3
152 
153 static struct ctimer startup_timer;
154 /*---------------------------------------------------------------------------*/
155 static void
156 notify_ready(void *unused)
157 {
158  (void)unused;
159 
160  sensor_status = SENSOR_STATUS_READY;
161  sensors_changed(&bmp_280_sensor);
162 }
163 /*---------------------------------------------------------------------------*/
164 static bool
165 i2c_write_read(void *writeBuf, size_t writeCount, void *readBuf, size_t readCount)
166 {
167  I2C_Transaction i2cTransaction = {
168  .writeBuf = writeBuf,
169  .writeCount = writeCount,
170  .readBuf = readBuf,
171  .readCount = readCount,
172  .slaveAddress = BMP280_I2C_ADDRESS,
173  };
174 
175  return I2C_transfer(i2c_handle, &i2cTransaction);
176 }
177 #define i2c_write(writeBuf, writeCount) i2c_write_read(writeBuf, writeCount, NULL, 0)
178 #define i2c_read(readBuf, readCount) i2c_write_read(NULL, 0, readBuf, readCount)
179 /*---------------------------------------------------------------------------*/
180 /**
181  * \brief Initalise the sensor.
182  * \return Boolean Value descibing whether initialization were
183  * successful or not.
184  * \retval true Successful initialization
185  * \retval false Error during initialization
186  */
187 static bool
188 init(void)
189 {
190  if(i2c_handle) {
191  return true;
192  }
193 
194  I2C_Params i2cParams;
195  I2C_Params_init(&i2cParams);
196 
197  i2cParams.transferMode = I2C_MODE_BLOCKING;
198  i2cParams.bitRate = I2C_400kHz;
199 
200  i2c_handle = I2C_open(Board_I2C0, &i2cParams);
201  if(i2c_handle == NULL) {
202  return false;
203  }
204 
205  uint8_t reset_data[] = { ADDR_RESET, VAL_RESET_EXECUTE };
206 
207  uint8_t calib_reg = ADDR_CALIB;
208  /* Read and store calibration data */
209  return i2c_write_read(&calib_reg, sizeof(calib_reg), &calib_data, sizeof(calib_data))
210  /* then reset the sensor */
211  && i2c_write(reset_data, sizeof(reset_data));
212 }
213 /*---------------------------------------------------------------------------*/
214 /**
215  * \brief Enable/disable measurements.
216  * \param enable Enable if true; else, disable.
217  * \return Boolean Value descibing whether initialization were
218  * successful or not.
219  * \retval true Successful initialization
220  * \retval false Error during initialization
221  */
222 static bool
223 enable_sensor(bool enable)
224 {
225  uint8_t val = (enable)
226  ? PM_FORCED | OSRSP(1) | OSRST(1)
227  : PM_OFF;
228 
229  uint8_t ctrl_meas_data[] = { ADDR_CTRL_MEAS, val };
230  return i2c_write(&ctrl_meas_data, sizeof(ctrl_meas_data));
231 }
232 /*---------------------------------------------------------------------------*/
233 /**
234  * \brief Read temperature and pressure data.
235  * \param data Pointer to a buffer where temperature and pressure will be
236  * written.
237  * \param count Number of byes to read.
238  * \return Boolean Value descibing whether initialization were
239  * successful or not.
240  * \retval true Successful initialization
241  * \retval false Error during initialization
242  */
243 static bool
244 read_data(uint8_t *data, size_t count)
245 {
246  uint8_t press_msb_reg = ADDR_PRESS_MSB;
247  return i2c_write_read(&press_msb_reg, sizeof(press_msb_reg), data, count);
248 }
249 /*---------------------------------------------------------------------------*/
250 /**
251  * \brief Convert raw data to values in degrees C (temp) and Pascal
252  * (pressure).
253  * \param data Pointer to a buffer that holds raw sensor data.
254  * \param temp Pointer to a variable where the converted temperature will
255  * be written.
256  * \param press Pointer to a variable where the converted pressure will be
257  * written.
258  */
259 static void
260 convert(uint8_t *data, int32_t *temp, uint32_t *press)
261 {
262  BMP_280_Calibration *p = &calib_data;
263 
264  /* Pressure */
265  const int32_t upress = (int32_t)(
266  (((uint32_t)data[0]) << 12) |
267  (((uint32_t)data[1]) << 4) |
268  (((uint32_t)data[2]) >> 4)
269  );
270  /* Temperature */
271  const int32_t utemp = (int32_t)(
272  (((uint32_t)data[3]) << 12) |
273  (((uint32_t)data[4]) << 4) |
274  (((uint32_t)data[5]) >> 4)
275  );
276 
277  /* Compensate temperature */
278  int32_t v_x1_u32r = (((utemp >> 3) - ((int32_t)p->dig_t1 << 1)) * (int32_t)p->dig_t2) >> 11;
279  int32_t v_x2_u32r = (((((utemp >> 4) - (int32_t)p->dig_t1) * ((utemp >> 4) - (int32_t)p->dig_t1)) >> 12) * (int32_t)p->dig_t3) >> 14;
280 
281  const uint32_t t_fine = v_x1_u32r + v_x2_u32r;
282  const int32_t temperature = (t_fine * 5 + 128) >> 8;
283  *temp = temperature;
284 
285  /* Compensate pressure */
286  v_x1_u32r = ((int32_t)t_fine >> 1) - (int32_t)64000;
287  v_x2_u32r = (((v_x1_u32r >> 2) * (v_x1_u32r >> 2)) >> 11) * (int32_t)p->dig_p6;
288  v_x2_u32r = ((v_x1_u32r * (int32_t)p->dig_p5) << 1) + v_x2_u32r;
289  v_x2_u32r = (v_x2_u32r >> 2) + ((int32_t)p->dig_p4 << 16);
290  v_x1_u32r = ((((((v_x1_u32r >> 2) * (v_x1_u32r >> 2)) >> 13) * p->dig_p3) >> 3) + (((int32_t)p->dig_p2 * v_x1_u32r) >> 1)) >> 18;
291  v_x1_u32r = ((32768 + v_x1_u32r) * (int32_t)p->dig_p1) >> 15;
292 
293  if(v_x1_u32r == 0) {
294  /* Avoid exception caused by division by zero */
295  *press = 0;
296  return;
297  }
298 
299  uint32_t pressure = (((uint32_t)((int32_t)1048576 - upress)) - (v_x2_u32r >> 12)) * 3125;
300  if((int32_t)pressure < 0) {
301  pressure = (pressure << 1) / (uint32_t)v_x1_u32r;
302  } else {
303  pressure = (pressure / (uint32_t)v_x1_u32r) * 2;
304  }
305 
306  v_x1_u32r = (((int32_t)(((pressure >> 3) * (pressure >> 3)) >> 13)) * (int32_t)p->dig_p9) >> 12;
307  v_x2_u32r = ((int32_t)(pressure >> 2) * (int32_t)p->dig_p8) >> 13;
308  pressure = (uint32_t)(((v_x1_u32r + v_x2_u32r + p->dig_p7) >> 4) + (int32_t)pressure);
309 
310  *press = pressure;
311 }
312 /*---------------------------------------------------------------------------*/
313 /**
314  * \brief Returns a reading from the sensor.
315  * \param type Parameter of type BMP_280_SENSOR_TYPE, choosing between either
316  * measuring temperature or pressure.
317  * \return Sensor data of either Temperature (centi degrees C) or
318  * Pressure (Pascal).
319  */
320 static int
321 value(int type)
322 {
323  int32_t temp = 0;
324  uint32_t pres = 0;
325 
326  /* A buffer for the raw reading from the sensor */
327  uint8_t sensor_value[SENSOR_DATA_BUF_SIZE];
328 
329  if(sensor_status != SENSOR_STATUS_READY) {
330  PRINTF("Sensor disabled or starting up (%d)\n", sensor_status);
331  return BMP_280_READING_ERROR;
332  }
333 
334  switch(type) {
335  case BMP_280_SENSOR_TYPE_TEMP:
336  case BMP_280_SENSOR_TYPE_PRESS:
337  memset(sensor_value, 0, SENSOR_DATA_BUF_SIZE);
338  if(!read_data(sensor_value, SENSOR_DATA_BUF_SIZE)) {
339  return BMP_280_READING_ERROR;
340  }
341 
342  PRINTF("val: %02x%02x%02x %02x%02x%02x\n",
343  sensor_value[0], sensor_value[1], sensor_value[2],
344  sensor_value[3], sensor_value[4], sensor_value[5]);
345 
346  convert(sensor_value, &temp, &pres);
347 
348  if(type == BMP_280_SENSOR_TYPE_TEMP) {
349  return (int)temp;
350  } else if(type == BMP_280_SENSOR_TYPE_PRESS) {
351  return (int)pres;
352  } else {
353  return 0;
354  }
355 
356  default:
357  PRINTF("Invalid BMP 208 Sensor Type\n");
358  return BMP_280_READING_ERROR;
359  }
360 }
361 /*---------------------------------------------------------------------------*/
362 /**
363  * \brief Configuration function for the BMP280 sensor.
364  * \param type Activate, enable or disable the sensor. See below
365  * \param enable Disable sensor if 0; else, enable sensor otherwise.
366  * When type == SENSORS_HW_INIT we turn on the hardware.
367  * When type == SENSORS_ACTIVE and enable==1 we enable the sensor.
368  * When type == SENSORS_ACTIVE and enable==0 we disable the sensor.
369  */
370 static int
371 configure(int type, int enable)
372 {
373  switch(type) {
374  case SENSORS_HW_INIT:
375  if(init()) {
376  enable_sensor(false);
377  sensor_status = SENSOR_STATUS_INITIALISED;
378  } else {
379  sensor_status = SENSOR_STATUS_DISABLED;
380  }
381  break;
382 
383  case SENSORS_ACTIVE:
384  /* Must be initialised first */
385  if(sensor_status == SENSOR_STATUS_DISABLED) {
386  break;
387  }
388  if(enable) {
389  enable_sensor(true);
390  ctimer_set(&startup_timer, SENSOR_STARTUP_DELAY, notify_ready, NULL);
391  sensor_status = SENSOR_STATUS_NOT_READY;
392  } else {
393  ctimer_stop(&startup_timer);
394  enable_sensor(false);
395  sensor_status = SENSOR_STATUS_INITIALISED;
396  }
397  break;
398 
399  default:
400  break;
401  }
402  return sensor_status;
403 }
404 /*---------------------------------------------------------------------------*/
405 /**
406  * \brief Returns the status of the sensor.
407  * \param type SENSORS_ACTIVE or SENSORS_READY.
408  * \return Current status of the sensor.
409  */
410 static int
411 status(int type)
412 {
413  switch(type) {
414  case SENSORS_ACTIVE:
415  case SENSORS_READY:
416  return sensor_status;
417  default:
418  return SENSOR_STATUS_DISABLED;
419  }
420 }
421 /*---------------------------------------------------------------------------*/
422 SENSORS_SENSOR(bmp_280_sensor, "BMP280", value, configure, status);
423 /*---------------------------------------------------------------------------*/
424 #endif /* BOARD_SENSORS_ENABLE */
425 /*---------------------------------------------------------------------------*/
426 /** @} */
void ctimer_stop(struct ctimer *c)
Stop a pending callback timer.
Definition: ctimer.c:149
static int status(int type)
Returns the status of the sensor.
static void convert(uint8_t *data, int32_t *temp, uint32_t *press)
Convert raw data to values in degrees C (temp) and Pascal (pressure).
static bool enable_sensor(bool enable)
Enable/disable measurements.
static bool init(void)
Initalise the sensor.
static int value(int type)
Returns a reading from the sensor.
static bool read_data(uint8_t *data, size_t count)
Read temperature and pressure data.
Header file for the callback timer
static int configure(int type, int enable)
Configuration function for the BMP280 sensor.
void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
Definition: ctimer.c:99
static void notify_ready(void *unused)
Callback when sensor is ready to read data from.
Header file with definitions related to the sensors on the Sensortags.
const struct sensors_sensor bmp_280_sensor
Exports a global symbol to be used by the sensor API.