1 /* Driver for the BMP085 Preassure and Temperature Sensor */
4 #include <minix/drivers.h>
6 #include <minix/i2cdriver.h>
7 #include <minix/chardriver.h>
10 /* Control Register for triggering a measurement */
13 /* temperature sensor - it only has one 'mode' - conversion time 4.5 ms */
14 #define CMD_TRIG_T 0x2e
15 #define UDELAY_T (4500)
17 /* pressure sensor - ultra low power mode - conversion time 4.5 ms */
18 #define CMD_TRIG_P_ULP 0x34
20 #define UDELAY_ULP (4500)
22 /* pressure sensor - standard mode - conversion time 7.5 ms */
23 #define CMD_TRIG_P_STD 0x74
25 #define UDELAY_STD (7500)
27 /* pressure sensor - high resolution mode - conversion time 13.5 ms */
28 #define CMD_TRIG_P_HR 0xb4
30 #define UDELAY_HR (13500)
32 /* pressure sensor - ultra high resolution mode - conversion time 25.5 ms */
33 #define CMD_TRIG_P_UHR 0xf4
35 #define UDELAY_UHR (25500)
37 /* Values for the different modes of operation */
45 /* Table of available modes and their parameters. */
46 static struct pressure_cmd pressure_cmds
[4] = {
47 {CMD_TRIG_P_ULP
, MODE_ULP
, UDELAY_ULP
},
48 {CMD_TRIG_P_STD
, MODE_STD
, UDELAY_STD
},
49 {CMD_TRIG_P_HR
, MODE_HR
, UDELAY_HR
},
50 {CMD_TRIG_P_UHR
, MODE_UHR
, UDELAY_UHR
}
53 /* Default to standard mode.
54 * There isn't code to configure the resolution at runtime, but it should
55 * easy to implement by setting p_cmd to the right element of pressure_cmds.
57 static struct pressure_cmd
*p_cmd
= &pressure_cmds
[MODE_STD
];
59 /* Chip Identification */
60 #define CHIPID_REG 0xd0
61 #define BMP085_CHIPID 0x55
64 * There is also a version register at 0xd1, but documentation seems to be
65 * lacking. The sample code says high 4 bytes are AL version and low 4 are ML.
68 /* Calibration coefficients
70 * These are unique to each chip and must be read when starting the driver.
71 * Validate them by checking that none are 0x0000 nor 0xffff. Types and
72 * names are from the datasheet.
89 /* Register locations for calibration coefficients */
90 #define AC1_MSB_REG 0xaa
91 #define AC1_LSB_REG 0xab
92 #define AC2_MSB_REG 0xac
93 #define AC2_LSB_REG 0xad
94 #define AC3_MSB_REG 0xae
95 #define AC3_LSB_REG 0xaf
96 #define AC4_MSB_REG 0xb0
97 #define AC4_LSB_REG 0xb1
98 #define AC5_MSB_REG 0xb2
99 #define AC5_LSB_REG 0xb3
100 #define AC6_MSB_REG 0xb4
101 #define AC6_LSB_REG 0xb5
102 #define B1_MSB_REG 0xb6
103 #define B1_LSB_REG 0xb7
104 #define B2_MSB_REG 0xb8
105 #define B2_LSB_REG 0xb9
106 #define MB_MSB_REG 0xba
107 #define MB_LSB_REG 0xbb
108 #define MC_MSB_REG 0xbc
109 #define MC_LSB_REG 0xbd
110 #define MD_MSB_REG 0xbe
111 #define MD_LSB_REG 0xbf
113 #define CAL_COEF_FIRST AC1_MSB_REG
114 #define CAL_COEF_LAST MD_LSB_REG
116 #define CAL_COEF_IS_VALID(x) (x != 0x0000 && (uint16_t)x != 0xffff)
118 #define SENSOR_VAL_MSB_REG 0xf6
119 #define SENSOR_VAL_LSB_REG 0xf7
120 #define SENSOR_VAL_XLSB_REG 0xf8
122 /* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */
123 static struct log log
= {
125 .log_level
= LEVEL_INFO
,
126 .log_func
= default_log
129 /* Only one valid slave address. It isn't configurable. */
130 static i2c_addr_t valid_addrs
[5] = {
134 /* Buffer to store output string returned when reading from device file. */
135 #define BUFFER_LEN 64
136 char buffer
[BUFFER_LEN
+ 1];
138 /* the bus that this device is on (counting starting at 1) */
141 /* slave address of the device */
142 static i2c_addr_t address
;
144 /* endpoint for the driver for the bus itself. */
145 static endpoint_t bus_endpoint
;
147 /* main device functions */
148 static int bmp085_init(void);
149 static int version_check(void);
150 static int read_cal_coef(void);
151 static int measure(int32_t * temperature
, int32_t * pressure
);
153 /* libchardriver callbacks */
154 static ssize_t
bmp085_read(devminor_t minor
, u64_t position
, endpoint_t endpt
,
155 cp_grant_id_t grant
, size_t size
, int flags
, cdev_id_t id
);
156 static void bmp085_other(message
* m
, int ipc_status
);
158 /* Entry points to this driver from libchardriver. */
159 static struct chardriver bmp085_tab
= {
160 .cdr_read
= bmp085_read
,
161 .cdr_other
= bmp085_other
165 * Initialize the driver. Checks the CHIPID against a known value and
166 * reads the calibration coefficients.
168 * The chip does have a soft reset register (0xe0), but there
169 * doesn't appear to be any documentation or example usage for it.
196 r
= i2creg_read8(bus_endpoint
, address
, CHIPID_REG
, &chipid
);
198 log_warn(&log
, "Couldn't read CHIPID\n");
202 if (chipid
!= BMP085_CHIPID
) {
203 log_warn(&log
, "Bad CHIPID\n");
207 log_debug(&log
, "CHIPID OK\n");
213 * Read the calibration data from the chip. Each individual chip has a unique
214 * set of calibration parameters that get used to compute the true temperature
222 /* Populate the calibration struct with values */
223 r
= i2creg_read16(bus_endpoint
, address
, AC1_MSB_REG
, &cal
.ac1
);
227 log_debug(&log
, "cal.ac1 = %d\n", cal
.ac1
);
229 r
= i2creg_read16(bus_endpoint
, address
, AC2_MSB_REG
, &cal
.ac2
);
233 log_debug(&log
, "cal.ac2 = %d\n", cal
.ac2
);
235 r
= i2creg_read16(bus_endpoint
, address
, AC3_MSB_REG
, &cal
.ac3
);
239 log_debug(&log
, "cal.ac3 = %d\n", cal
.ac3
);
241 r
= i2creg_read16(bus_endpoint
, address
, AC4_MSB_REG
, &cal
.ac4
);
245 log_debug(&log
, "cal.ac4 = %u\n", cal
.ac4
);
247 r
= i2creg_read16(bus_endpoint
, address
, AC5_MSB_REG
, &cal
.ac5
);
251 log_debug(&log
, "cal.ac5 = %u\n", cal
.ac5
);
253 r
= i2creg_read16(bus_endpoint
, address
, AC6_MSB_REG
, &cal
.ac6
);
257 log_debug(&log
, "cal.ac6 = %u\n", cal
.ac6
);
259 r
= i2creg_read16(bus_endpoint
, address
, B1_MSB_REG
, &cal
.b1
);
263 log_debug(&log
, "cal.b1 = %d\n", cal
.b1
);
265 r
= i2creg_read16(bus_endpoint
, address
, B2_MSB_REG
, &cal
.b2
);
269 log_debug(&log
, "cal.b2 = %d\n", cal
.b2
);
271 r
= i2creg_read16(bus_endpoint
, address
, MB_MSB_REG
, &cal
.mb
);
275 log_debug(&log
, "cal.mb = %d\n", cal
.mb
);
277 r
= i2creg_read16(bus_endpoint
, address
, MC_MSB_REG
, &cal
.mc
);
281 log_debug(&log
, "cal.mc = %d\n", cal
.mc
);
283 r
= i2creg_read16(bus_endpoint
, address
, MD_MSB_REG
, &cal
.md
);
287 log_debug(&log
, "cal.md = %d\n", cal
.md
);
289 /* Validate. Data sheet says values should not be 0x0000 nor 0xffff */
290 if (!CAL_COEF_IS_VALID(cal
.ac1
) ||
291 !CAL_COEF_IS_VALID(cal
.ac2
) ||
292 !CAL_COEF_IS_VALID(cal
.ac3
) ||
293 !CAL_COEF_IS_VALID(cal
.ac4
) ||
294 !CAL_COEF_IS_VALID(cal
.ac5
) ||
295 !CAL_COEF_IS_VALID(cal
.ac6
) ||
296 !CAL_COEF_IS_VALID(cal
.b1
) ||
297 !CAL_COEF_IS_VALID(cal
.b2
) ||
298 !CAL_COEF_IS_VALID(cal
.mb
) ||
299 !CAL_COEF_IS_VALID(cal
.mc
) || !CAL_COEF_IS_VALID(cal
.md
)) {
301 log_warn(&log
, "Invalid calibration data found on chip.\n");
305 log_debug(&log
, "Read Cal Data OK\n");
311 * Measure the uncompensated temperature and uncompensated pressure from the
312 * chip and apply the formulas to determine the true temperature and pressure.
313 * Note, the data sheet is light on the details when it comes to defining the
314 * meaning of each variable, so this function has a lot of cryptic names in it.
317 measure(int32_t * temperature
, int32_t * pressure
)
321 /* Types are given in the datasheet. Their long translates to 32-bits */
323 int16_t ut
; /* uncompensated temperature */
324 int32_t up
; /* uncompensated pressure */
333 int32_t t
; /* true temperature (in 0.1C) */
334 int32_t p
; /* true pressure (in Pa) */
336 log_debug(&log
, "Triggering Temp Reading...\n");
338 /* trigger temperature reading */
339 r
= i2creg_write8(bus_endpoint
, address
, CTRL_REG
, CMD_TRIG_T
);
341 log_warn(&log
, "Failed to trigger temperature reading.\n");
345 /* wait for sampling to be completed. */
346 micro_delay(UDELAY_T
);
348 /* read the uncompensated temperature */
349 r
= i2creg_read16(bus_endpoint
, address
, SENSOR_VAL_MSB_REG
, &ut
);
351 log_warn(&log
, "Failed to read temperature.\n");
355 log_debug(&log
, "ut = %d\n", ut
);
357 log_debug(&log
, "Triggering Pressure Reading...\n");
359 /* trigger pressure reading */
360 r
= i2creg_write8(bus_endpoint
, address
, CTRL_REG
, p_cmd
->cmd
);
362 log_warn(&log
, "Failed to trigger pressure reading.\n");
366 /* wait for sampling to be completed. */
367 micro_delay(p_cmd
->udelay
);
369 /* read the uncompensated pressure */
370 r
= i2creg_read24(bus_endpoint
, address
, SENSOR_VAL_MSB_REG
, &up
);
372 log_warn(&log
, "Failed to read pressure.\n");
376 /* shift by 8 - oversampling setting */
377 up
= (up
>> (8 - p_cmd
->mode
));
379 log_debug(&log
, "up = %d\n", up
);
381 /* convert uncompensated temperature to true temperature */
382 x1
= ((ut
- cal
.ac6
) * cal
.ac5
) / (1 << 15);
383 x2
= (cal
.mc
* (1 << 11)) / (x1
+ cal
.md
);
385 t
= (b5
+ 8) / (1 << 4);
387 /* save the result */
390 log_debug(&log
, "t = %d\n", t
);
392 /* Convert uncompensated pressure to true pressure.
393 * This is really how the data sheet suggests doing it.
394 * There is no alternative approach suggested. Other open
395 * source drivers I've found use this method.
398 x1
= ((cal
.b2
* ((b6
* b6
) >> 12)) >> 11);
399 x2
= ((cal
.ac2
* b6
) >> 11);
401 b3
= (((((cal
.ac1
* 4) + x3
) << p_cmd
->mode
) + 2) >> 2);
402 x1
= ((cal
.ac3
* b6
) >> 13);
403 x2
= ((cal
.b1
* ((b6
* b6
) >> 12)) >> 16);
404 x3
= (((x1
+ x2
) + 2) >> 2);
405 b4
= ((cal
.ac4
* ((uint32_t) (x3
+ 32768))) >> 15);
406 b7
= ((uint32_t) up
- b3
) * (50000 >> p_cmd
->mode
);
407 p
= (b7
< 0x80000000) ? (b7
* 2) / b4
: (b7
/ b4
) * 2;
408 x1
= (p
>> 8) * (p
>> 8);
409 x1
= ((x1
* 3038) >> 16);
410 x2
= ((-7357 * p
) >> 16);
411 p
= p
+ ((x1
+ x2
+ 3791) >> 4);
415 log_debug(&log
, "p = %d\n", p
);
421 bmp085_read(devminor_t
UNUSED(minor
), u64_t position
, endpoint_t endpt
,
422 cp_grant_id_t grant
, size_t size
, int UNUSED(flags
), cdev_id_t
UNUSED(id
))
426 uint32_t temperature
, pressure
;
428 r
= measure(&temperature
, &pressure
);
433 memset(buffer
, '\0', BUFFER_LEN
+ 1);
434 snprintf(buffer
, BUFFER_LEN
, "%-16s: %d.%01d\n%-16s: %d\n",
435 "TEMPERATURE", temperature
/ 10, temperature
% 10, "PRESSURE",
438 log_trace(&log
, "%s", buffer
);
440 dev_size
= (u64_t
)strlen(buffer
);
441 if (position
>= dev_size
) return 0;
442 if (position
+ size
> dev_size
)
443 size
= (size_t)(dev_size
- position
);
445 r
= sys_safecopyto(endpt
, grant
, 0,
446 (vir_bytes
)(buffer
+ (size_t)position
), size
);
448 return (r
!= OK
) ? r
: size
;
452 bmp085_other(message
* m
, int ipc_status
)
456 if (is_ipc_notify(ipc_status
)) {
457 if (m
->m_source
== DS_PROC_NR
) {
459 "bus driver changed state, update endpoint\n");
460 i2cdriver_handle_bus_update(&bus_endpoint
, bus
,
466 log_warn(&log
, "Invalid message type (0x%x)\n", m
->m_type
);
470 sef_cb_lu_state_save(int UNUSED(result
), int UNUSED(flags
))
472 ds_publish_u32("bus", bus
, DSF_OVERWRITE
);
473 ds_publish_u32("address", address
, DSF_OVERWRITE
);
478 lu_state_restore(void)
480 /* Restore the state. */
483 ds_retrieve_u32("bus", &value
);
484 ds_delete_u32("bus");
487 ds_retrieve_u32("address", &value
);
488 ds_delete_u32("address");
489 address
= (int) value
;
495 sef_cb_init(int type
, sef_init_info_t
* UNUSED(info
))
499 if (type
== SEF_INIT_LU
) {
500 /* Restore the state. */
504 /* look-up the endpoint for the bus driver */
505 bus_endpoint
= i2cdriver_bus_endpoint(bus
);
506 if (bus_endpoint
== 0) {
507 log_warn(&log
, "Couldn't find bus driver.\n");
511 /* claim the device */
512 r
= i2cdriver_reserve_device(bus_endpoint
, address
);
514 log_warn(&log
, "Couldn't reserve device 0x%x (r=%d)\n",
521 log_warn(&log
, "Couldn't initialize device\n");
525 if (type
!= SEF_INIT_LU
) {
527 /* sign up for updates about the i2c bus going down/up */
528 r
= i2cdriver_subscribe_bus_updates(bus
);
530 log_warn(&log
, "Couldn't subscribe to bus updates\n");
534 i2cdriver_announce(bus
);
535 log_debug(&log
, "announced\n");
542 sef_local_startup(void)
545 * Register init callbacks. Use the same function for all event types
547 sef_setcb_init_fresh(sef_cb_init
);
548 sef_setcb_init_lu(sef_cb_init
);
549 sef_setcb_init_restart(sef_cb_init
);
552 * Register live update callbacks.
554 sef_setcb_lu_state_save(sef_cb_lu_state_save
);
556 /* Let SEF perform startup. */
561 main(int argc
, char *argv
[])
565 env_setargs(argc
, argv
);
567 r
= i2cdriver_env_parse(&bus
, &address
, valid_addrs
);
569 log_warn(&log
, "Expecting -args 'bus=X address=0x77'\n");
570 log_warn(&log
, "Example -args 'bus=1 address=0x77'\n");
574 "Invalid slave address for device, expecting 0x77\n");
580 chardriver_task(&bmp085_tab
);