Skip to content

Instantly share code, notes, and snippets.

@mux2000
Created August 11, 2016 09:27
Show Gist options
  • Save mux2000/5a0db5be900ca8b7f2d528fb37968b38 to your computer and use it in GitHub Desktop.
Save mux2000/5a0db5be900ca8b7f2d528fb37968b38 to your computer and use it in GitHub Desktop.
bmp180.c
/*
* 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