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 MEAS_DATA_SIZE 6
106 #define CALIB_DATA_SIZE 24
107 /*---------------------------------------------------------------------------*/
108 #define RES_OFF 0
109 #define RES_ULTRA_LOW_POWER 1
110 #define RES_LOW_POWER 2
111 #define RES_STANDARD 3
112 #define RES_HIGH 5
113 #define RES_ULTRA_HIGH 6
114 /*---------------------------------------------------------------------------*/
115 /* Bit fields in CTRL_MEAS register */
116 #define PM_OFF 0
117 #define PM_FORCED 1
118 #define PM_NORMAL 3
119 /*---------------------------------------------------------------------------*/
120 #define OSRST(v) ((v) << 5)
121 #define OSRSP(v) ((v) << 2)
122 /*---------------------------------------------------------------------------*/
123 typedef struct {
124  uint16_t dig_t1;
125  int16_t dig_t2;
126  int16_t dig_t3;
127  uint16_t dig_p1;
128  int16_t dig_p2;
129  int16_t dig_p3;
130  int16_t dig_p4;
131  int16_t dig_p5;
132  int16_t dig_p6;
133  int16_t dig_p7;
134  int16_t dig_p8;
135  int16_t dig_p9;
136 } BMP_280_Calibration;
137 /*---------------------------------------------------------------------------*/
138 static BMP_280_Calibration calib_data;
139 /*---------------------------------------------------------------------------*/
140 static I2C_Handle i2c_handle;
141 /*---------------------------------------------------------------------------*/
142 typedef enum {
143  SENSOR_STATUS_DISABLED,
144  SENSOR_STATUS_INITIALISED,
145  SENSOR_STATUS_NOT_READY,
146  SENSOR_STATUS_READY
147 } SENSOR_STATUS;
148 
149 static volatile SENSOR_STATUS sensor_status = SENSOR_STATUS_DISABLED;
150 /*---------------------------------------------------------------------------*/
151 /* Wait SENSOR_STARTUP_DELAY clock ticks for the sensor to be ready - ~80ms */
152 #define SENSOR_STARTUP_DELAY 3
153 
154 static struct ctimer startup_timer;
155 /*---------------------------------------------------------------------------*/
156 static void
157 notify_ready(void *unused)
158 {
159  (void)unused;
160 
161  sensor_status = SENSOR_STATUS_READY;
162  sensors_changed(&bmp_280_sensor);
163 }
164 /*---------------------------------------------------------------------------*/
165 static bool
166 i2c_write_read(void *writeBuf, size_t writeCount, void *readBuf, size_t readCount)
167 {
168  I2C_Transaction i2cTransaction = {
169  .writeBuf = writeBuf,
170  .writeCount = writeCount,
171  .readBuf = readBuf,
172  .readCount = readCount,
173  .slaveAddress = BMP280_I2C_ADDRESS,
174  };
175 
176  return I2C_transfer(i2c_handle, &i2cTransaction);
177 }
178 #define i2c_write(writeBuf, writeCount) i2c_write_read(writeBuf, writeCount, NULL, 0)
179 #define i2c_read(readBuf, readCount) i2c_write_read(NULL, 0, readBuf, readCount)
180 /*---------------------------------------------------------------------------*/
181 /**
182  * \brief Initalise the sensor.
183  * \return Boolean Value descibing whether initialization were
184  * successful or not.
185  * \retval true Successful initialization
186  * \retval false Error during initialization
187  */
188 static bool
189 init(void)
190 {
191  if(i2c_handle) {
192  return true;
193  }
194 
195  I2C_Params i2cParams;
196  I2C_Params_init(&i2cParams);
197 
198  i2cParams.transferMode = I2C_MODE_BLOCKING;
199  i2cParams.bitRate = I2C_400kHz;
200 
201  i2c_handle = I2C_open(Board_I2C0, &i2cParams);
202  if(i2c_handle == NULL) {
203  return false;
204  }
205 
206  uint8_t reset_data[] = { ADDR_RESET, VAL_RESET_EXECUTE };
207 
208  uint8_t calib_reg = ADDR_CALIB;
209  /* Read and store calibration data */
210  return i2c_write_read(&calib_reg, sizeof(calib_reg), &calib_data, sizeof(calib_data))
211  /* then reset the sensor */
212  && i2c_write(reset_data, sizeof(reset_data));
213 }
214 /*---------------------------------------------------------------------------*/
215 /**
216  * \brief Enable/disable measurements.
217  * \param enable Enable if true; else, disable.
218  * \return Boolean Value descibing whether initialization were
219  * successful or not.
220  * \retval true Successful initialization
221  * \retval false Error during initialization
222  */
223 static bool
224 enable_sensor(bool enable)
225 {
226  uint8_t val = (enable)
227  ? PM_FORCED | OSRSP(1) | OSRST(1)
228  : PM_OFF;
229 
230  uint8_t ctrl_meas_data[] = { ADDR_CTRL_MEAS, val };
231  return i2c_write(&ctrl_meas_data, sizeof(ctrl_meas_data));
232 }
233 /*---------------------------------------------------------------------------*/
234 /**
235  * \brief Read temperature and pressure data.
236  * \param data Pointer to a buffer where temperature and pressure will be
237  * written.
238  * \param count Number of byes to read.
239  * \return Boolean Value descibing whether initialization were
240  * successful or not.
241  * \retval true Successful initialization
242  * \retval false Error during initialization
243  */
244 static bool
245 read_data(uint8_t *data, size_t count)
246 {
247  uint8_t press_msb_reg = ADDR_PRESS_MSB;
248  return i2c_write_read(&press_msb_reg, sizeof(press_msb_reg), data, count);
249 }
250 /*---------------------------------------------------------------------------*/
251 /**
252  * \brief Convert raw data to values in degrees C (temp) and Pascal
253  * (pressure).
254  * \param data Pointer to a buffer that holds raw sensor data.
255  * \param temp Pointer to a variable where the converted temperature will
256  * be written.
257  * \param press Pointer to a variable where the converted pressure will be
258  * written.
259  */
260 static void
261 convert(uint8_t *data, int32_t *temp, uint32_t *press)
262 {
263  BMP_280_Calibration *p = &calib_data;
264 
265  /* Pressure */
266  const int32_t upress = (int32_t)(
267  (((uint32_t)data[0]) << 12) |
268  (((uint32_t)data[1]) << 4) |
269  (((uint32_t)data[2]) >> 4)
270  );
271  /* Temperature */
272  const int32_t utemp = (int32_t)(
273  (((uint32_t)data[3]) << 12) |
274  (((uint32_t)data[4]) << 4) |
275  (((uint32_t)data[5]) >> 4)
276  );
277 
278  /* Compensate temperature */
279  int32_t v_x1_u32r = (((utemp >> 3) - ((int32_t)p->dig_t1 << 1)) * (int32_t)p->dig_t2) >> 11;
280  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;
281 
282  const uint32_t t_fine = v_x1_u32r + v_x2_u32r;
283  const int32_t temperature = (t_fine * 5 + 128) >> 8;
284  *temp = temperature;
285 
286  /* Compensate pressure */
287  v_x1_u32r = ((int32_t)t_fine >> 1) - (int32_t)64000;
288  v_x2_u32r = (((v_x1_u32r >> 2) * (v_x1_u32r >> 2)) >> 11) * (int32_t)p->dig_p6;
289  v_x2_u32r = ((v_x1_u32r * (int32_t)p->dig_p5) << 1) + v_x2_u32r;
290  v_x2_u32r = (v_x2_u32r >> 2) + ((int32_t)p->dig_p4 << 16);
291  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;
292  v_x1_u32r = ((32768 + v_x1_u32r) * (int32_t)p->dig_p1) >> 15;
293 
294  if(v_x1_u32r == 0) {
295  /* Avoid exception caused by division by zero */
296  *press = 0;
297  return;
298  }
299 
300  uint32_t pressure = (((uint32_t)((int32_t)1048576 - upress)) - (v_x2_u32r >> 12)) * 3125;
301  if((int32_t)pressure < 0) {
302  pressure = (pressure << 1) / (uint32_t)v_x1_u32r;
303  } else {
304  pressure = (pressure / (uint32_t)v_x1_u32r) * 2;
305  }
306 
307  v_x1_u32r = (((int32_t)(((pressure >> 3) * (pressure >> 3)) >> 13)) * (int32_t)p->dig_p9) >> 12;
308  v_x2_u32r = ((int32_t)(pressure >> 2) * (int32_t)p->dig_p8) >> 13;
309  pressure = (uint32_t)(((v_x1_u32r + v_x2_u32r + p->dig_p7) >> 4) + (int32_t)pressure);
310 
311  *press = pressure;
312 }
313 /*---------------------------------------------------------------------------*/
314 /**
315  * \brief Returns a reading from the sensor.
316  * \param type Parameter of type BMP_280_SENSOR_TYPE, choosing between either
317  * measuring temperature or pressure.
318  * \return Sensor data of either Temperature (centi degrees C) or
319  * Pressure (Pascal).
320  */
321 static int
322 value(int type)
323 {
324  int32_t temp = 0;
325  uint32_t pres = 0;
326 
327  if(sensor_status != SENSOR_STATUS_READY) {
328  PRINTF("Sensor disabled or starting up (%d)\n", sensor_status);
329  return BMP_280_READING_ERROR;
330  }
331 
332  /* A buffer for the raw reading from the sensor */
333  uint8_t sensor_value[MEAS_DATA_SIZE];
334 
335  switch(type) {
336  case BMP_280_SENSOR_TYPE_TEMP:
337  case BMP_280_SENSOR_TYPE_PRESS:
338  memset(sensor_value, 0, MEAS_DATA_SIZE);
339  if(!read_data(sensor_value, MEAS_DATA_SIZE)) {
340  return BMP_280_READING_ERROR;
341  }
342 
343  PRINTF("val: %02x%02x%02x %02x%02x%02x\n",
344  sensor_value[0], sensor_value[1], sensor_value[2],
345  sensor_value[3], sensor_value[4], sensor_value[5]);
346 
347  convert(sensor_value, &temp, &pres);
348 
349  if(type == BMP_280_SENSOR_TYPE_TEMP) {
350  return (int)temp;
351  } else if(type == BMP_280_SENSOR_TYPE_PRESS) {
352  return (int)pres;
353  } else {
354  return 0;
355  }
356 
357  default:
358  PRINTF("Invalid BMP 208 Sensor Type\n");
359  return BMP_280_READING_ERROR;
360  }
361 }
362 /*---------------------------------------------------------------------------*/
363 /**
364  * \brief Configuration function for the BMP280 sensor.
365  * \param type Activate, enable or disable the sensor. See below
366  * \param enable Disable sensor if 0; else, enable sensor otherwise.
367  * When type == SENSORS_HW_INIT we turn on the hardware.
368  * When type == SENSORS_ACTIVE and enable==1 we enable the sensor.
369  * When type == SENSORS_ACTIVE and enable==0 we disable the sensor.
370  */
371 static int
372 configure(int type, int enable)
373 {
374  switch(type) {
375  case SENSORS_HW_INIT:
376  if(init()) {
377  enable_sensor(false);
378  sensor_status = SENSOR_STATUS_INITIALISED;
379  } else {
380  sensor_status = SENSOR_STATUS_DISABLED;
381  }
382  break;
383 
384  case SENSORS_ACTIVE:
385  /* Must be initialised first */
386  if(sensor_status == SENSOR_STATUS_DISABLED) {
387  break;
388  }
389  if(enable) {
390  enable_sensor(true);
391  ctimer_set(&startup_timer, SENSOR_STARTUP_DELAY, notify_ready, NULL);
392  sensor_status = SENSOR_STATUS_NOT_READY;
393  } else {
394  ctimer_stop(&startup_timer);
395  enable_sensor(false);
396  sensor_status = SENSOR_STATUS_INITIALISED;
397  }
398  break;
399 
400  default:
401  break;
402  }
403  return sensor_status;
404 }
405 /*---------------------------------------------------------------------------*/
406 /**
407  * \brief Returns the status of the sensor.
408  * \param type SENSORS_ACTIVE or SENSORS_READY.
409  * \return Current status of the sensor.
410  */
411 static int
412 status(int type)
413 {
414  switch(type) {
415  case SENSORS_ACTIVE:
416  case SENSORS_READY:
417  return sensor_status;
418  default:
419  return SENSOR_STATUS_DISABLED;
420  }
421 }
422 /*---------------------------------------------------------------------------*/
423 SENSORS_SENSOR(bmp_280_sensor, "BMP280", value, configure, status);
424 /*---------------------------------------------------------------------------*/
425 #endif /* BOARD_SENSORS_ENABLE */
426 /*---------------------------------------------------------------------------*/
427 /** @} */
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.
const struct sensors_sensor bmp_280_sensor
Exports a global symbol to be used by the sensor API.
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.