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 #include "dev/i2c-arch.h"
44 /*---------------------------------------------------------------------------*/
45 #include "board-conf.h"
46 #include "bmp-280-sensor.h"
47 /*---------------------------------------------------------------------------*/
48 #include <Board.h>
49 
50 #include <ti/drivers/I2C.h>
51 /*---------------------------------------------------------------------------*/
52 #include <stdbool.h>
53 #include <stdint.h>
54 #include <string.h>
55 /*---------------------------------------------------------------------------*/
56 #define DEBUG 0
57 #if DEBUG
58 #define PRINTF(...) printf(__VA_ARGS__)
59 #else
60 #define PRINTF(...)
61 #endif
62 /*---------------------------------------------------------------------------*/
63 /*
64  * Disable the entire file if sensors are disabled, as it could potentially
65  * create compile errors with missing defines from either the Board file or
66  * configuration defines.
67  */
68 #if BOARD_SENSORS_ENABLE
69 /*---------------------------------------------------------------------------*/
70 #ifndef Board_BMP280_ADDR
71 #error "Board file doesn't define I2C address Board_BMP280_ADDR"
72 #endif
73 /* Sensor I2C address */
74 #define BMP280_I2C_ADDRESS Board_BMP280_ADDR
75 /*---------------------------------------------------------------------------*/
76 /* Registers */
77 #define ADDR_CALIB 0x88
78 #define ADDR_PROD_ID 0xD0
79 #define ADDR_RESET 0xE0
80 #define ADDR_STATUS 0xF3
81 #define ADDR_CTRL_MEAS 0xF4
82 #define ADDR_CONFIG 0xF5
83 #define ADDR_PRESS_MSB 0xF7
84 #define ADDR_PRESS_LSB 0xF8
85 #define ADDR_PRESS_XLSB 0xF9
86 #define ADDR_TEMP_MSB 0xFA
87 #define ADDR_TEMP_LSB 0xFB
88 #define ADDR_TEMP_XLSB 0xFC
89 /*---------------------------------------------------------------------------*/
90 /* Reset values */
91 #define VAL_PROD_ID 0x58
92 #define VAL_RESET 0x00
93 #define VAL_STATUS 0x00
94 #define VAL_CTRL_MEAS 0x00
95 #define VAL_CONFIG 0x00
96 #define VAL_PRESS_MSB 0x80
97 #define VAL_PRESS_LSB 0x00
98 #define VAL_TEMP_MSB 0x80
99 #define VAL_TEMP_LSB 0x00
100 /*---------------------------------------------------------------------------*/
101 /* Test values */
102 #define VAL_RESET_EXECUTE 0xB6
103 #define VAL_CTRL_MEAS_TEST 0x55
104 /*---------------------------------------------------------------------------*/
105 /* Misc. */
106 #define SENSOR_DATA_BUF_SIZE 6
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 /**
166  * \brief Initalise the sensor.
167  * \return Boolean Value descibing whether initialization were
168  * successful or not.
169  * \retval true Successful initialization
170  * \retval false Error during initialization
171  */
172 static bool
173 init(void)
174 {
175  bool rv_write_read, rv_write;
176 
177  i2c_handle = i2c_arch_acquire(Board_I2C0);
178 
179  if(!i2c_handle) {
180  sensor_status = SENSOR_STATUS_DISABLED;
181  return false;
182  }
183 
184  uint8_t reset_data[] = { ADDR_RESET, VAL_RESET_EXECUTE };
185 
186  uint8_t calib_reg = ADDR_CALIB;
187  /* Read and store calibration data */
188  rv_write_read = i2c_arch_write_read(i2c_handle, BMP280_I2C_ADDRESS,
189  &calib_reg, sizeof(calib_reg),
190  &calib_data, sizeof(calib_data));
191  /* then reset the sensor */
192  rv_write = i2c_arch_write(i2c_handle, BMP280_I2C_ADDRESS, reset_data,
193  sizeof(reset_data));
194 
195  i2c_arch_release(i2c_handle);
196 
197  return rv_write_read && rv_write;
198 }
199 /*---------------------------------------------------------------------------*/
200 /**
201  * \brief Enable/disable measurements.
202  * \param enable Enable if true; else, disable.
203  * \return Boolean Value descibing whether initialization were
204  * successful or not.
205  * \retval true Successful initialization
206  * \retval false Error during initialization
207  */
208 static bool
209 enable_sensor(bool enable)
210 {
211  bool rv;
212 
213  uint8_t val = (enable)
214  ? PM_FORCED | OSRSP(1) | OSRST(1)
215  : PM_OFF;
216 
217  uint8_t ctrl_meas_data[] = { ADDR_CTRL_MEAS, val };
218 
219  i2c_handle = i2c_arch_acquire(Board_I2C0);
220 
221  if(!i2c_handle) {
222  sensor_status = SENSOR_STATUS_DISABLED;
223  return false;
224  }
225 
226  rv = i2c_arch_write(i2c_handle, BMP280_I2C_ADDRESS, &ctrl_meas_data,
227  sizeof(ctrl_meas_data));
228 
229  i2c_arch_release(i2c_handle);
230  return rv;
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  bool rv;
247  uint8_t press_msb_reg = ADDR_PRESS_MSB;
248 
249  i2c_handle = i2c_arch_acquire(Board_I2C0);
250 
251  if(!i2c_handle) {
252  sensor_status = SENSOR_STATUS_DISABLED;
253  return false;
254  }
255 
256  rv = i2c_arch_write_read(i2c_handle, BMP280_I2C_ADDRESS, &press_msb_reg,
257  sizeof(press_msb_reg), data, count);
258 
259  i2c_arch_release(i2c_handle);
260 
261  return rv;
262 }
263 /*---------------------------------------------------------------------------*/
264 /**
265  * \brief Convert raw data to values in degrees C (temp) and Pascal
266  * (pressure).
267  * \param data Pointer to a buffer that holds raw sensor data.
268  * \param temp Pointer to a variable where the converted temperature will
269  * be written.
270  * \param press Pointer to a variable where the converted pressure will be
271  * written.
272  */
273 static void
274 convert(uint8_t *data, int32_t *temp, uint32_t *press)
275 {
276  BMP_280_Calibration *p = &calib_data;
277 
278  /* Pressure */
279  const int32_t upress = (int32_t)(
280  (((uint32_t)data[0]) << 12) |
281  (((uint32_t)data[1]) << 4) |
282  (((uint32_t)data[2]) >> 4)
283  );
284  /* Temperature */
285  const int32_t utemp = (int32_t)(
286  (((uint32_t)data[3]) << 12) |
287  (((uint32_t)data[4]) << 4) |
288  (((uint32_t)data[5]) >> 4)
289  );
290 
291  /* Compensate temperature */
292  int32_t v_x1_u32r = (((utemp >> 3) - ((int32_t)p->dig_t1 << 1)) * (int32_t)p->dig_t2) >> 11;
293  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;
294 
295  const uint32_t t_fine = v_x1_u32r + v_x2_u32r;
296  const int32_t temperature = (t_fine * 5 + 128) >> 8;
297  *temp = temperature;
298 
299  /* Compensate pressure */
300  v_x1_u32r = ((int32_t)t_fine >> 1) - (int32_t)64000;
301  v_x2_u32r = (((v_x1_u32r >> 2) * (v_x1_u32r >> 2)) >> 11) * (int32_t)p->dig_p6;
302  v_x2_u32r = ((v_x1_u32r * (int32_t)p->dig_p5) << 1) + v_x2_u32r;
303  v_x2_u32r = (v_x2_u32r >> 2) + ((int32_t)p->dig_p4 << 16);
304  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;
305  v_x1_u32r = ((32768 + v_x1_u32r) * (int32_t)p->dig_p1) >> 15;
306 
307  if(v_x1_u32r == 0) {
308  /* Avoid exception caused by division by zero */
309  *press = 0;
310  return;
311  }
312 
313  uint32_t pressure = (((uint32_t)((int32_t)1048576 - upress)) - (v_x2_u32r >> 12)) * 3125;
314  if((int32_t)pressure < 0) {
315  pressure = (pressure << 1) / (uint32_t)v_x1_u32r;
316  } else {
317  pressure = (pressure / (uint32_t)v_x1_u32r) * 2;
318  }
319 
320  v_x1_u32r = (((int32_t)(((pressure >> 3) * (pressure >> 3)) >> 13)) * (int32_t)p->dig_p9) >> 12;
321  v_x2_u32r = ((int32_t)(pressure >> 2) * (int32_t)p->dig_p8) >> 13;
322  pressure = (uint32_t)(((v_x1_u32r + v_x2_u32r + p->dig_p7) >> 4) + (int32_t)pressure);
323 
324  *press = pressure;
325 }
326 /*---------------------------------------------------------------------------*/
327 /**
328  * \brief Returns a reading from the sensor.
329  * \param type Parameter of type BMP_280_SENSOR_TYPE, choosing between either
330  * measuring temperature or pressure.
331  * \return Sensor data of either Temperature (centi degrees C) or
332  * Pressure (Pascal).
333  */
334 static int
335 value(int type)
336 {
337  int32_t temp = 0;
338  uint32_t pres = 0;
339 
340  /* A buffer for the raw reading from the sensor */
341  uint8_t sensor_value[SENSOR_DATA_BUF_SIZE];
342 
343  if(sensor_status != SENSOR_STATUS_READY) {
344  PRINTF("Sensor disabled or starting up (%d)\n", sensor_status);
345  return BMP_280_READING_ERROR;
346  }
347 
348  switch(type) {
349  case BMP_280_SENSOR_TYPE_TEMP:
350  case BMP_280_SENSOR_TYPE_PRESS:
351  memset(sensor_value, 0, SENSOR_DATA_BUF_SIZE);
352  if(!read_data(sensor_value, SENSOR_DATA_BUF_SIZE)) {
353  return BMP_280_READING_ERROR;
354  }
355 
356  PRINTF("val: %02x%02x%02x %02x%02x%02x\n",
357  sensor_value[0], sensor_value[1], sensor_value[2],
358  sensor_value[3], sensor_value[4], sensor_value[5]);
359 
360  convert(sensor_value, &temp, &pres);
361 
362  if(type == BMP_280_SENSOR_TYPE_TEMP) {
363  return (int)temp;
364  } else if(type == BMP_280_SENSOR_TYPE_PRESS) {
365  return (int)pres;
366  } else {
367  return 0;
368  }
369 
370  default:
371  PRINTF("Invalid BMP 208 Sensor Type\n");
372  return BMP_280_READING_ERROR;
373  }
374 }
375 /*---------------------------------------------------------------------------*/
376 /**
377  * \brief Configuration function for the BMP280 sensor.
378  * \param type Activate, enable or disable the sensor. See below
379  * \param enable Disable sensor if 0; else, enable sensor otherwise.
380  * When type == SENSORS_HW_INIT we turn on the hardware.
381  * When type == SENSORS_ACTIVE and enable==1 we enable the sensor.
382  * When type == SENSORS_ACTIVE and enable==0 we disable the sensor.
383  */
384 static int
385 configure(int type, int enable)
386 {
387  switch(type) {
388  case SENSORS_HW_INIT:
389  if(init()) {
390  enable_sensor(false);
391  sensor_status = SENSOR_STATUS_INITIALISED;
392  } else {
393  sensor_status = SENSOR_STATUS_DISABLED;
394  }
395  break;
396 
397  case SENSORS_ACTIVE:
398  /* Must be initialised first */
399  if(sensor_status == SENSOR_STATUS_DISABLED) {
400  break;
401  }
402  if(enable) {
403  enable_sensor(true);
404  ctimer_set(&startup_timer, SENSOR_STARTUP_DELAY, notify_ready, NULL);
405  sensor_status = SENSOR_STATUS_NOT_READY;
406  } else {
407  ctimer_stop(&startup_timer);
408  enable_sensor(false);
409  sensor_status = SENSOR_STATUS_INITIALISED;
410  }
411  break;
412 
413  default:
414  break;
415  }
416  return sensor_status;
417 }
418 /*---------------------------------------------------------------------------*/
419 /**
420  * \brief Returns the status of the sensor.
421  * \param type SENSORS_ACTIVE or SENSORS_READY.
422  * \return Current status of the sensor.
423  */
424 static int
425 status(int type)
426 {
427  switch(type) {
428  case SENSORS_ACTIVE:
429  case SENSORS_READY:
430  return sensor_status;
431  default:
432  return SENSOR_STATUS_DISABLED;
433  }
434 }
435 /*---------------------------------------------------------------------------*/
436 SENSORS_SENSOR(bmp_280_sensor, "BMP280", value, configure, status);
437 /*---------------------------------------------------------------------------*/
438 #endif /* BOARD_SENSORS_ENABLE */
439 /*---------------------------------------------------------------------------*/
440 /** @} */
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 volatile uint64_t count
Num.
Definition: clock.c:50
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.
Implementation of the I2C HAL driver for CC13xx/CC26xx.
bool i2c_arch_write_read(I2C_Handle i2c_handle, uint_least8_t slave_addr, void *wbuf, size_t wcount, void *rbuf, size_t rcount)
Setup and peform an I2C transaction.
Definition: i2c-arch.c:53
static bool i2c_arch_write(I2C_Handle i2c_handle, uint_least8_t slave_addr, void *wbuf, size_t wcount)
Perform a write-only I2C transaction.
Definition: i2c-arch.h:128
Header file for the callback timer
static int configure(int type, int enable)
Configuration function for the BMP280 sensor.
void i2c_arch_release(I2C_Handle i2c_handle)
Release the I2C Peripheral for other modules to use.
Definition: i2c-arch.c:74
I2C_Handle i2c_arch_acquire(uint_least8_t index)
Open and lock the I2C Peripheral for use.
Definition: i2c-arch.c:84
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.