Created
August 11, 2016 09:27
-
-
Save mux2000/5a0db5be900ca8b7f2d528fb37968b38 to your computer and use it in GitHub Desktop.
bmp180.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Coding excercise by Izhar Mahalalel | |
* | |
* Example i2c driver for BMP-180 sensor by Bosch | |
* | |
* Author: Izhar Mahalalel <mux2000@gmail.com> | |
*/ | |
#include <stdio.h> | |
#include <unistd.h> | |
/*====================*/ | |
/* HELPER DEFINITIONS */ | |
/*====================*/ | |
#define LOG_W printf | |
#define usleep(x) ASM(" nop") | |
typedef unsigned long uint64_t; | |
typedef signed long int64_t; | |
typedef unsigned int uint32_t; | |
typedef signed int int32_t; | |
typedef unsigned short uint16_t; | |
typedef signed short int16_t; | |
typedef unsigned char uint8_t; | |
typedef signed char int8_t; | |
#define ENODEV 19 | |
#define EIO 5 | |
struct sensor_ops { | |
double (*read_temperature)(); | |
double (*read_pressure)(); | |
void (*configure)(void *); | |
}; | |
/*====================*/ | |
/* SUPPLIED PROTOTYPES */ | |
// I2C burst read (reads from 7 bit I2C device 'address' 'size' bytes starting with register 'reg' into 'data') | |
void i2c_read(uint8_t address, uint8_t reg, uint8_t *data, size_t size); | |
// I2C burst write (writes to 7 but I2C device 'address' 'size' bytes, starting with register 'reg') | |
void i2c_write(uint8_t address, uint8_t reg, uint8_t *data, size_t size); | |
#define BMP180_CAL_DATA_SIZE 11 | |
#define BMP180_CAL_EEPROM_ADDR 0xAA | |
uint16_t bmp180_cal_data[] = { 0x0198, 0xFFB8, 0xC7D1, 0x7FE5, 0x7FF5, 0x5A71, 0x182E, 0x0004, 0x8000, 0xDDF9, 0xF4CC }; | |
#define BMP180_CTL_REG_ADDR 0xF4 | |
#define BMP180_READ_TEMP_CMD 0x2E | |
#define BMP180_READ_PRES_CMD 0x34 | |
#define BMP180_DATA_ADDR_MSB 0xF6 | |
#define BMP180_DATA_ADDR_LSB 0xF7 | |
#define BMP180_DATA_ADDR_XLSB 0xF8 | |
#define BMP180_CHIP_ID_ADDR 0x00 | |
#define BMP180_CHIP_ID 0x55 | |
#define BMP180_CHIP_ADDR 0x77 | |
enum { | |
AC1 = 1, | |
AC2, | |
AC3, | |
AC4, | |
AC5, | |
AC6, | |
B1, | |
B2, | |
MB, | |
MC, | |
MD, | |
} bmp180_cal_names; | |
enum bmp180_accuracy_mode { | |
BMP180_ACC_ULTRA_LOW_POWER = 1, | |
BMP180_ACC_STANDARD, | |
BMP180_ACC_HIGH_RES, | |
BMP180_ACC_ULTRA_HIGH_RES, | |
}; | |
// Sample wait times for pressure measurement depend on precision setting | |
const uint16_t bmp180_temp_sample_wait_time_usec = 4500; | |
const uint16_t bmp180_pressure_sample_wait_times_usec[4] = { 4500, 7500, 13500, 25500 }; | |
struct bmp180_results { | |
double temp; | |
double pressure; | |
int32_t b5; | |
}; | |
struct bmp180_config { | |
enum bmp180_accuracy_mode acc_mode; | |
} bmp180_cfg_g; | |
struct bmp180_plat_data { | |
struct bmp180_config *bmp180_cfg; | |
struct sensor_ops *ops; | |
}; | |
static double bmp180_read_temperature(); | |
static double bmp180_read_pressure(); | |
static void bmp180_configure(void *); | |
struct sensor_ops bmp180_ops = { | |
.read_temperature = bmp180_read_temperature, | |
.read_pressure = bmp180_read_pressure, | |
.configure = bmp180_configure, | |
}; | |
static void bmp180_configure(void *cfg) | |
{ | |
uint8_t cmd; | |
bmp180_cfg_g.acc_mode = ((struct bmp180_config *)cfg)->acc_mode; | |
switch(bmp180_cfg_g.acc_mode) | |
{ | |
case BMP180_ACC_ULTRA_LOW_POWER: | |
cmd = 0x34; | |
break; | |
case BMP180_ACC_STANDARD: | |
cmd = 0x74; | |
break; | |
case BMP180_ACC_HIGH_RES: | |
cmd = 0xB4; | |
break; | |
case BMP180_ACC_ULTRA_HIGH_RES: | |
cmd = 0xF4; | |
break; | |
default: | |
LOG_W("bmp180: invalid configuration setting"); | |
return; | |
} | |
i2c_write(BMP180_CHIP_ADDR, BMP180_CTL_REG_ADDR, &cmd, 1); | |
} | |
static int bmp180_read_cal_data(uint16_t *cal_data) | |
{ | |
uint8_t msb, lsb; | |
uint16_t rdata; | |
int i; | |
for (i = 0; i < BMP180_CAL_DATA_SIZE; i++) | |
{ | |
msb = lsb = 0; | |
i2c_read(BMP180_CHIP_ADDR, BMP180_CAL_EEPROM_ADDR + (2 * i), &msb, 1); | |
i2c_read(BMP180_CHIP_ADDR, BMP180_CAL_EEPROM_ADDR + (2 * i) + 1, &lsb, 1); | |
rdata = msb << 8 + lsb; | |
if ((rdata == 0) || (rdata == 0xFFFF)) | |
return -EIO; | |
if (rdata != cal_data[i]) | |
{ | |
LOG_W("bmp180: Mismatch on calibration data read from device\n"); | |
cal_data[i] = rdata; | |
} | |
} | |
return 0; | |
} | |
static uint16_t bmp180_read_uncompensated_temp_data() | |
{ | |
uint8_t msb, lsb, cmd; | |
uint16_t rdata; | |
cmd = BMP180_READ_TEMP_CMD; | |
i2c_write(BMP180_CHIP_ADDR, BMP180_CTL_REG_ADDR, &cmd, 1); | |
usleep (bmp180_temp_sample_wait_time_usec); | |
i2c_read(BMP180_CHIP_ADDR, BMP180_DATA_ADDR_MSB, &msb, 1); | |
i2c_read(BMP180_CHIP_ADDR, BMP180_DATA_ADDR_LSB, &lsb, 1); | |
rdata = msb << 8 + lsb; | |
return rdata; | |
} | |
static uint16_t bmp180_read_uncompensated_pressure_data() | |
{ | |
uint8_t msb, lsb, xlsb, cmd, oss; | |
uint32_t rdata; | |
switch (bmp180_cfg_g.acc_mode) | |
{ | |
case BMP180_ACC_ULTRA_LOW_POWER: | |
oss = 0; | |
break; | |
case BMP180_ACC_STANDARD: | |
oss = 1; | |
break; | |
case BMP180_ACC_HIGH_RES: | |
oss = 2; | |
break; | |
case BMP180_ACC_ULTRA_HIGH_RES: | |
oss = 3; | |
break; | |
default: | |
LOG_W("bmp180: Error reading pressure: invalid precision mode setting\n"); | |
rdata = 0; | |
break; | |
} | |
cmd = BMP180_READ_PRES_CMD; | |
i2c_write(BMP180_CHIP_ADDR, BMP180_CTL_REG_ADDR, &cmd, 1); | |
usleep(bmp180_pressure_sample_wait_times_usec[oss]); | |
i2c_read(BMP180_CHIP_ADDR, BMP180_DATA_ADDR_MSB, &msb, 1); | |
i2c_read(BMP180_CHIP_ADDR, BMP180_DATA_ADDR_LSB, &lsb, 1); | |
i2c_read(BMP180_CHIP_ADDR, BMP180_DATA_ADDR_XLSB, &xlsb, 1); | |
rdata = (msb << 16) + (lsb << 8) + xlsb; | |
return (uint16_t)(rdata << (8 - oss)); | |
} | |
static double bmp180_calc_temp(uint16_t UT, int32_t *b5) | |
{ | |
int32_t x1, x2, ut = (int32_t)(int16_t)UT; | |
int32_t ac6 = (int32_t)(int16_t)bmp180_cal_data[AC6], | |
ac5 = (int32_t)(int16_t)bmp180_cal_data[AC5], | |
mc = (int32_t)(int16_t)bmp180_cal_data[MC], | |
md = (int32_t)(int16_t)bmp180_cal_data[MD]; | |
x1 = ( (ut - ac6) * ac5 ) >> 15; | |
x2 = (mc << 11) / (x1 + md); | |
*b5 = x1 + x2; | |
return ((double)(*b5))/16 + 0.5; | |
} | |
static double bmp180_calc_pressure(uint16_t up, int32_t b5) | |
{ | |
int32_t b3, b4, b6, p, b7; | |
int32_t ac2 = (int32_t)(int16_t)bmp180_cal_data[AC2], | |
b2 = (int32_t)(int16_t)bmp180_cal_data[B2], | |
ac3 = (int32_t)(int16_t)bmp180_cal_data[AC3], | |
b1 = (int32_t)(int16_t)bmp180_cal_data[B1]; | |
int64_t x1, x2, x3, tmp, | |
ac1 = (int64_t)(int16_t)bmp180_cal_data[AC1], | |
ac4 = (int64_t)(int16_t)bmp180_cal_data[AC4]; | |
uint8_t oss; | |
switch (bmp180_cfg_g.acc_mode) | |
{ | |
case BMP180_ACC_ULTRA_LOW_POWER: | |
oss = 0; | |
break; | |
case BMP180_ACC_STANDARD: | |
oss = 1; | |
break; | |
case BMP180_ACC_HIGH_RES: | |
oss = 2; | |
break; | |
case BMP180_ACC_ULTRA_HIGH_RES: | |
oss = 3; | |
break; | |
default: | |
LOG_W("bmp180: Invalid precision mode setting\n"); | |
return 0; | |
break; | |
} | |
b6 = b5 - 4000; | |
x1 = (b2 * ((b6 * b6) >> 12) ) >> 11; | |
x2 = (ac2 * b6) >> 11; | |
x3 = (int64_t)x1 + (int64_t)x2; | |
tmp = (ac1 * 4) + x3; | |
b3 = (int32_t)((tmp << oss + 2) << 2); | |
x1 = (ac3 * b6) >> 13; | |
x2 = (b1 * ((b6 * b6) >> 12)) >> 16; | |
x3 = (x1 + x2 + 2) >> 2; | |
b4 = (int32_t)(ac4 * ( (x3 + 32768) >> 15 )); | |
b7 = ((int32_t)up - b3) * (50000 >> oss); | |
if (b7 < 0x80000000) | |
p = (b7 << 1) / b4; | |
else | |
p = (b7 / b4) << 1; | |
x1 = (p >> 8) * (p >> 8); | |
x1 = ((x1 * 3038) >> 16); | |
x2 = (((uint64_t)p * (-7357)) >> 16); | |
return (double)( (int64_t)p + (x1 + x2 + 3791) >> 4 ); | |
} | |
static void bmp180_read_data(struct bmp180_results *res) | |
{ | |
uint16_t utemp = bmp180_read_uncompensated_temp_data(); | |
uint16_t upress = bmp180_read_uncompensated_pressure_data(); | |
res->temp = bmp180_calc_temp(utemp, &res->b5); | |
res->pressure = bmp180_calc_pressure(upress, res->b5); | |
} | |
static double bmp180_read_temperature() | |
{ | |
struct bmp180_results res; | |
bmp180_read_data(&res); | |
return res.temp; | |
} | |
static double bmp180_read_pressure() | |
{ | |
struct bmp180_results res; | |
bmp180_read_data(&res); | |
return res.pressure; | |
} | |
static int bmp180_probe(struct bmp180_plat_data *plat_data) | |
{ | |
int ret = 0; | |
uint8_t chip_id; | |
if (!plat_data) | |
return -ENODEV; | |
i2c_read(BMP180_CHIP_ADDR, BMP180_CHIP_ID_ADDR, &chip_id, 1); | |
if (chip_id != BMP180_CHIP_ID) | |
return -EIO; | |
bmp180_configure(plat_data->bmp180_cfg); | |
ret = bmp180_read_cal_data(bmp180_cal_data); | |
if (ret) | |
return ret; | |
plat_data->ops = &bmp180_ops; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment