Merge pull request #165 from ourairquality/bmp180-rework

bmp180: make a lower level interface available and support oversampling.
This commit is contained in:
sheinz 2016-08-06 00:39:43 +03:00 committed by GitHub
commit 964a2f850a
3 changed files with 230 additions and 194 deletions

View file

@ -73,7 +73,7 @@ void bmp180_task(void *pvParameters)
bmp180_trigger_measurement(com_queue); bmp180_trigger_measurement(com_queue);
break; break;
case MY_EVT_BMP180: case MY_EVT_BMP180:
printf("%s: Received BMP180 Event temp:=%d.%d°C press=%d.%02dhPa\n", __FUNCTION__, \ printf("%s: Received BMP180 Event temp:=%d.%dC press=%d.%02dhPa\n", __FUNCTION__, \
(int32_t)ev.bmp180_data.temperature, abs((int32_t)(ev.bmp180_data.temperature*10)%10), \ (int32_t)ev.bmp180_data.temperature, abs((int32_t)(ev.bmp180_data.temperature*10)%10), \
ev.bmp180_data.pressure/100, ev.bmp180_data.pressure%100 ); ev.bmp180_data.pressure/100, ev.bmp180_data.pressure%100 );
break; break;

View file

@ -27,12 +27,7 @@
// Values for BMP180_CONTROL_REG // Values for BMP180_CONTROL_REG
// //
#define BMP180_MEASURE_TEMP 0x2E #define BMP180_MEASURE_TEMP 0x2E
#define BMP180_MEASURE_PRESS_OSS0 0x34 #define BMP180_MEASURE_PRESS 0x34
#define BMP180_MEASURE_PRESS_OSS1 0x74
#define BMP180_MEASURE_PRESS_OSS2 0xB4
#define BMP180_MEASURE_PRESS_OSS3 0xF4
#define BMP180_DEFAULT_CONV_TIME 5000
// //
// CHIP ID stored in BMP180_VERSION_REG // CHIP ID stored in BMP180_VERSION_REG
@ -44,6 +39,176 @@
// //
#define BMP180_RESET_VALUE 0xB6 #define BMP180_RESET_VALUE 0xB6
static bool bmp180_readRegister16(uint8_t reg, int16_t *r)
{
uint8_t d[] = { 0, 0 };
if (!i2c_slave_read(BMP180_DEVICE_ADDRESS, reg, d, 2))
return false;
*r = ((int16_t)d[0] << 8) | (d[1]);
return true;
}
static bool bmp180_start_Messurement(uint8_t cmd)
{
uint8_t d[] = { BMP180_CONTROL_REG, cmd };
return i2c_slave_write(BMP180_DEVICE_ADDRESS, d, 2);
}
static bool bmp180_get_uncompensated_temperature(int32_t *ut)
{
// Write Start Code into reg 0xF4.
if (!bmp180_start_Messurement(BMP180_MEASURE_TEMP))
return false;
// Wait 5ms, datasheet states 4.5ms
sdk_os_delay_us(5000);
int16_t v;
if (!bmp180_readRegister16(BMP180_OUT_MSB_REG, &v))
return false;
*ut = v;
return true;
}
static bool bmp180_get_uncompensated_pressure(uint8_t oss, uint32_t *up)
{
uint16_t us;
// Limit oss and set the measurement wait time. The datasheet
// states 4.5, 7.5, 13.5, 25.5ms for oss 0 to 3.
switch (oss) {
case 0: us = 5000; break;
case 1: us = 8000; break;
case 2: us = 14000; break;
default: oss = 3; us = 26000; break;
}
// Write Start Code into reg 0xF4
if (!bmp180_start_Messurement(BMP180_MEASURE_PRESS | (oss << 6)))
return false;
sdk_os_delay_us(us);
uint8_t d[] = { 0, 0, 0 };
if (!i2c_slave_read(BMP180_DEVICE_ADDRESS, BMP180_OUT_MSB_REG, d, 3))
return false;
uint32_t r = ((uint32_t)d[0] << 16) | ((uint32_t)d[1] << 8) | d[2];
r >>= 8 - oss;
*up = r;
return true;
}
// Returns true of success else false.
bool bmp180_fillInternalConstants(bmp180_constants_t *c)
{
if (!bmp180_readRegister16(BMP180_CALIBRATION_REG+0, &c->AC1) ||
!bmp180_readRegister16(BMP180_CALIBRATION_REG+2, &c->AC2) ||
!bmp180_readRegister16(BMP180_CALIBRATION_REG+4, &c->AC3) ||
!bmp180_readRegister16(BMP180_CALIBRATION_REG+6, (int16_t *)&c->AC4) ||
!bmp180_readRegister16(BMP180_CALIBRATION_REG+8, (int16_t *)&c->AC5) ||
!bmp180_readRegister16(BMP180_CALIBRATION_REG+10, (int16_t *)&c->AC6) ||
!bmp180_readRegister16(BMP180_CALIBRATION_REG+12, &c->B1) ||
!bmp180_readRegister16(BMP180_CALIBRATION_REG+14, &c->B2) ||
!bmp180_readRegister16(BMP180_CALIBRATION_REG+16, &c->MB) ||
!bmp180_readRegister16(BMP180_CALIBRATION_REG+18, &c->MC) ||
!bmp180_readRegister16(BMP180_CALIBRATION_REG+20, &c->MD)) {
return false;
}
#ifdef BMP180_DEBUG
printf("%s: AC1:=%d AC2:=%d AC3:=%d AC4:=%u AC5:=%u AC6:=%u \n", __FUNCTION__, c->AC1, c->AC2, c->AC3, c->AC4, c->AC5, c->AC6);
printf("%s: B1:=%d B2:=%d\n", __FUNCTION__, c->B1, c->B2);
printf("%s: MB:=%d MC:=%d MD:=%d\n", __FUNCTION__, c->MB, c->MC, c->MD);
#endif
// Error if any read as 0x0000 or 0xffff.
return !(c->AC1 == 0x0000 || c->AC2 == 0x0000 || c->AC3 == 0x0000 ||
c->AC4 == 0x0000 || c->AC5 == 0x0000 || c->AC6 == 0x0000 ||
c->B1 == 0x0000 || c->B2 == 0x0000 ||
c->MB == 0x0000 || c->MC == 0x0000 || c->MD == 0x0000 ||
c->AC1 == 0xffff || c->AC2 == 0xffff || c->AC3 == 0xffff ||
c->AC4 == 0xffff || c->AC5 == 0xffff || c->AC6 == 0xffff ||
c->B1 == 0xffff || c->B2 == 0xffff ||
c->MB == 0xffff || c->MC == 0xffff || c->MD == 0xffff);
}
bool bmp180_is_available()
{
uint8_t id;
return i2c_slave_read(BMP180_DEVICE_ADDRESS, BMP180_VERSION_REG, &id, 1) &&
id == BMP180_CHIP_ID;
}
bool bmp180_measure(bmp180_constants_t *c, int32_t *temperature,
uint32_t *pressure, uint8_t oss)
{
int32_t T, P;
if (!temperature && !pressure)
return false;
// Temperature is always needed, allso required for pressure only.
//
// Calculation taken from BMP180 Datasheet
int32_t UT, X1, X2, B5;
if (!bmp180_get_uncompensated_temperature(&UT))
return false;
X1 = ((UT - (int32_t)c->AC6) * (int32_t)c->AC5) >> 15;
X2 = ((int32_t)c->MC << 11) / (X1 + (int32_t)c->MD);
B5 = X1 + X2;
T = (B5 + 8) >> 4;
if (temperature)
*temperature = T;
#ifdef BMP180_DEBUG
printf("%s: T:= %ld.%d\n", __FUNCTION__, T/10, abs(T%10));
#endif
if (pressure) {
int32_t X3, B3, B6;
uint32_t B4, B7, UP;
if (!bmp180_get_uncompensated_pressure(oss, &UP))
return false;
// Calculation taken from BMP180 Datasheet
B6 = B5 - 4000;
X1 = ((int32_t)c->B2 * ((B6 * B6) >> 12)) >> 11;
X2 = ((int32_t)c->AC2 * B6) >> 11;
X3 = X1 + X2;
B3 = ((((int32_t)c->AC1 * 4 + X3) << oss) + 2) >> 2;
X1 = ((int32_t)c->AC3 * B6) >> 13;
X2 = ((int32_t)c->B1 * ((B6 * B6) >> 12)) >> 16;
X3 = ((X1 + X2) + 2) >> 2;
B4 = ((uint32_t)c->AC4 * (uint32_t)(X3 + 32768)) >> 15;
B7 = ((uint32_t)UP - B3) * (uint32_t)(50000UL >> oss);
if (B7 < 0x80000000UL) {
P = (B7 * 2) / B4;
} else {
P = (B7 / B4) * 2;
}
X1 = (P >> 8) * (P >> 8);
X1 = (X1 * 3038) >> 16;
X2 = (-7357 * P) >> 16;
P = P + ((X1 + X2 + (int32_t)3791) >> 4);
if (pressure)
*pressure = P;
#ifdef BMP180_DEBUG
printf("%s: P:= %ld\n", __FUNCTION__, P);
#endif
}
return true;
}
// BMP180_Event_Command // BMP180_Event_Command
typedef struct typedef struct
@ -56,25 +221,9 @@ typedef struct
static xQueueHandle bmp180_rx_queue = NULL; static xQueueHandle bmp180_rx_queue = NULL;
static xTaskHandle bmp180_task_handle = NULL; static xTaskHandle bmp180_task_handle = NULL;
// Calibration constants
static int16_t AC1;
static int16_t AC2;
static int16_t AC3;
static uint16_t AC4;
static uint16_t AC5;
static uint16_t AC6;
static int16_t B1;
static int16_t B2;
static int16_t MB;
static int16_t MC;
static int16_t MD;
// //
// Forward declarations // Forward declarations
// //
static void bmp180_meassure(const bmp180_command_t* command);
static bool bmp180_informUser_Impl(const xQueueHandle* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure); static bool bmp180_informUser_Impl(const xQueueHandle* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure);
// Set default implementation .. User gets result as bmp180_result_t event // Set default implementation .. User gets result as bmp180_result_t event
@ -85,109 +234,53 @@ static void bmp180_driver_task(void *pvParameters)
{ {
// Data to be received from user // Data to be received from user
bmp180_command_t current_command; bmp180_command_t current_command;
bmp180_constants_t bmp180_constants;
#ifdef BMP180_DEBUG #ifdef BMP180_DEBUG
// Wait for commands from the outside // Wait for commands from the outside
printf("%s: Started Task\n", __FUNCTION__); printf("%s: Started Task\n", __FUNCTION__);
#endif #endif
while(1) // Initialize all internal constants.
{ if (!bmp180_fillInternalConstants(&bmp180_constants)) {
printf("%s: reading internal constants failed\n", __FUNCTION__);
vTaskDelete(NULL);
}
while(1) {
// Wait for user to insert commands // Wait for user to insert commands
if (xQueueReceive(bmp180_rx_queue, &current_command, portMAX_DELAY) == pdTRUE) if (xQueueReceive(bmp180_rx_queue, &current_command, portMAX_DELAY) == pdTRUE) {
{
#ifdef BMP180_DEBUG #ifdef BMP180_DEBUG
printf("%s: Received user command %d 0x%p\n", __FUNCTION__, current_command.cmd, current_command.resultQueue); printf("%s: Received user command %d 0x%p\n", __FUNCTION__, current_command.cmd, current_command.resultQueue);
#endif #endif
// use user provided queue // use user provided queue
if (current_command.resultQueue != NULL) if (current_command.resultQueue != NULL) {
{
// Work on it ... // Work on it ...
bmp180_meassure(&current_command); int32_t T = 0;
uint32_t P = 0;
if (bmp180_measure(&bmp180_constants, &T, (current_command.cmd & BMP180_PRESSURE) ? &P : NULL, 3)) {
// Inform the user ...
if (!bmp180_informUser(current_command.resultQueue,
current_command.cmd,
((bmp180_temp_t)T)/10.0,
(bmp180_press_t)P)) {
// Failed to send info to user
printf("%s: Unable to inform user bmp180_informUser returned \"false\"\n", __FUNCTION__);
} }
} }
} }
}
static uint8_t bmp180_readRegister8(uint8_t reg)
{
uint8_t r = 0;
if (!i2c_slave_read(BMP180_DEVICE_ADDRESS, reg, &r, 1))
{
r = 0;
} }
return r;
}
static int16_t bmp180_readRegister16(uint8_t reg)
{
uint8_t d[] = { 0, 0 };
int16_t r = 0;
if (i2c_slave_read(BMP180_DEVICE_ADDRESS, reg, d, 2))
{
r = ((int16_t)d[0]<<8) | (d[1]);
} }
return r;
}
static void bmp180_start_Messurement(uint8_t cmd)
{
uint8_t d[] = { BMP180_CONTROL_REG, cmd };
i2c_slave_write(BMP180_DEVICE_ADDRESS, d, 2);
}
static int16_t bmp180_getUncompensatedMessurement(uint8_t cmd)
{
// Write Start Code into reg 0xF4 (Currently without oversampling ...)
bmp180_start_Messurement((cmd==BMP180_TEMPERATURE)?BMP180_MEASURE_TEMP:BMP180_MEASURE_PRESS_OSS0);
// Wait 5ms Datasheet states 4.5ms
sdk_os_delay_us(BMP180_DEFAULT_CONV_TIME);
return (int16_t)bmp180_readRegister16(BMP180_OUT_MSB_REG);
}
static void bmp180_fillInternalConstants(void)
{
AC1 = bmp180_readRegister16(BMP180_CALIBRATION_REG+0);
AC2 = bmp180_readRegister16(BMP180_CALIBRATION_REG+2);
AC3 = bmp180_readRegister16(BMP180_CALIBRATION_REG+4);
AC4 = bmp180_readRegister16(BMP180_CALIBRATION_REG+6);
AC5 = bmp180_readRegister16(BMP180_CALIBRATION_REG+8);
AC6 = bmp180_readRegister16(BMP180_CALIBRATION_REG+10);
B1 = bmp180_readRegister16(BMP180_CALIBRATION_REG+12);
B2 = bmp180_readRegister16(BMP180_CALIBRATION_REG+14);
MB = bmp180_readRegister16(BMP180_CALIBRATION_REG+16);
MC = bmp180_readRegister16(BMP180_CALIBRATION_REG+18);
MD = bmp180_readRegister16(BMP180_CALIBRATION_REG+20);
#ifdef BMP180_DEBUG
printf("%s: AC1:=%d AC2:=%d AC3:=%d AC4:=%u AC5:=%u AC6:=%u \n", __FUNCTION__, AC1, AC2, AC3, AC4, AC5, AC6);
printf("%s: B1:=%d B2:=%d\n", __FUNCTION__, B1, B2);
printf("%s: MB:=%d MC:=%d MD:=%d\n", __FUNCTION__, MB, MC, MD);
#endif
} }
static bool bmp180_create_communication_queues() static bool bmp180_create_communication_queues()
{ {
// Just create them once // Just create them once
if (bmp180_rx_queue==NULL) if (bmp180_rx_queue == NULL)
{
bmp180_rx_queue = xQueueCreate(BMP180_RX_QUEUE_SIZE, sizeof(bmp180_result_t)); bmp180_rx_queue = xQueueCreate(BMP180_RX_QUEUE_SIZE, sizeof(bmp180_result_t));
}
return (bmp180_rx_queue!=NULL); return bmp180_rx_queue != NULL;
}
static bool bmp180_is_avaialble()
{
return (bmp180_readRegister8(BMP180_VERSION_REG)==BMP180_CHIP_ID);
} }
static bool bmp180_createTask() static bool bmp180_createTask()
@ -195,87 +288,10 @@ static bool bmp180_createTask()
// We already have a task // We already have a task
portBASE_TYPE x = pdPASS; portBASE_TYPE x = pdPASS;
if (bmp180_task_handle==NULL) if (bmp180_task_handle == NULL) {
{
x = xTaskCreate(bmp180_driver_task, (signed char *)"bmp180_driver_task", 256, NULL, BMP180_TASK_PRIORITY, &bmp180_task_handle); x = xTaskCreate(bmp180_driver_task, (signed char *)"bmp180_driver_task", 256, NULL, BMP180_TASK_PRIORITY, &bmp180_task_handle);
} }
return (x==pdPASS); return x == pdPASS;
}
static void bmp180_meassure(const bmp180_command_t* command)
{
int32_t T, P;
// Init result to 0
T = P = 0;
if (command->resultQueue != NULL)
{
int32_t UT, X1, X2, B5;
//
// Temperature is always needed ... Also required for pressure only
//
// Calculation taken from BMP180 Datasheet
UT = (int32_t)bmp180_getUncompensatedMessurement(BMP180_TEMPERATURE);
X1 = (UT - (int32_t)AC6) * ((int32_t)AC5) >> 15;
X2 = ((int32_t)MC << 11) / (X1 + (int32_t)MD);
B5 = X1 + X2;
T = (B5 + 8) >> 4;
#ifdef BMP180_DEBUG
printf("%s: T:= %ld.%d\n", __FUNCTION__, T/10, abs(T%10));
#endif
// Do we also need pressure?
if (command->cmd & BMP180_PRESSURE)
{
int32_t X3, B3, B6;
uint32_t B4, B7, UP;
UP = ((uint32_t)bmp180_getUncompensatedMessurement(BMP180_PRESSURE) & 0xFFFF);
// Calculation taken from BMP180 Datasheet
B6 = B5 - 4000;
X1 = ((int32_t)B2 * ((B6 * B6) >> 12)) >> 11;
X2 = ((int32_t)AC2 * B6) >> 11;
X3 = X1 + X2;
B3 = (((int32_t)AC1 * 4 + X3) + 2) >> 2;
X1 = ((int32_t)AC3 * B6) >> 13;
X2 = ((int32_t)B1 * ((B6 * B6) >> 12)) >> 16;
X3 = ((X1 + X2) + 2) >> 2;
B4 = ((uint32_t)AC4 * (uint32_t)(X3 + 32768)) >> 15;
B7 = (UP - B3) * (uint32_t)(50000UL);
if (B7 < 0x80000000)
{
P = (B7 * 2) / B4;
}
else
{
P = (B7 / B4) * 2;
}
X1 = (P >> 8) * (P >> 8);
X1 = (X1 * 3038) >> 16;
X2 = (-7357 * P) >> 16;
P = P + ((X1 + X2 + (int32_t)3791) >> 4);
#ifdef BMP180_DEBUG
printf("%s: P:= %ld\n", __FUNCTION__, P);
#endif
}
// Inform the user ...
if (!bmp180_informUser(command->resultQueue, command->cmd, ((bmp180_temp_t)T)/10.0, (bmp180_press_t)P))
{
// Failed to send info to user
printf("%s: Unable to inform user bmp180_informUser returned \"false\"\n", __FUNCTION__);
}
}
} }
// Default user inform implementation // Default user inform implementation
@ -296,20 +312,13 @@ bool bmp180_init(uint8_t scl, uint8_t sda)
// 1. Create required queues // 1. Create required queues
bool result = false; bool result = false;
if (bmp180_create_communication_queues()) if (bmp180_create_communication_queues()) {
{
// 2. Init i2c driver // 2. Init i2c driver
i2c_init(scl, sda); i2c_init(scl, sda);
// 3. Check for bmp180 ... // 3. Check for bmp180 ...
if (bmp180_is_avaialble()) if (bmp180_is_available()) {
{ // 4. Start driver task
// 4. Init all internal constants ... if (bmp180_createTask()) {
bmp180_fillInternalConstants();
// 5. Start driver task
if (bmp180_createTask())
{
// We are finished // We are finished
result = true; result = true;
} }

View file

@ -52,4 +52,31 @@ void bmp180_trigger_pressure_measurement(const xQueueHandle* resultQueue);
// Give the user the chance to create it's own handler // Give the user the chance to create it's own handler
extern bool (*bmp180_informUser)(const xQueueHandle* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure); extern bool (*bmp180_informUser)(const xQueueHandle* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure);
// Calibration constants
typedef struct
{
int16_t AC1;
int16_t AC2;
int16_t AC3;
uint16_t AC4;
uint16_t AC5;
uint16_t AC6;
int16_t B1;
int16_t B2;
int16_t MB;
int16_t MC;
int16_t MD;
} bmp180_constants_t;
// Returns true if the bmp180 is detected.
bool bmp180_is_available();
// Reads all the internal constants, returning true on success.
bool bmp180_fillInternalConstants(bmp180_constants_t *c);
// Reads an optional temperature and pressure. The over sampling
// setting, oss, may be 0 to 3. Returns true on success.
bool bmp180_measure(bmp180_constants_t *c, int32_t *temperature,
uint32_t *pressure, uint8_t oss);
#endif /* DRIVER_BMP180_H_ */ #endif /* DRIVER_BMP180_H_ */