360 lines
10 KiB
C
360 lines
10 KiB
C
#include "bmp180.h"
|
||
|
||
#include "FreeRTOS.h"
|
||
#include "queue.h"
|
||
#include "task.h"
|
||
|
||
#include "espressif/esp_common.h"
|
||
#include "espressif/sdk_private.h"
|
||
|
||
#include "i2c/i2c.h"
|
||
|
||
#define BMP180_RX_QUEUE_SIZE 10
|
||
#define BMP180_TASK_PRIORITY 9
|
||
|
||
#define BMP180_DEVICE_ADDRESS 0x77
|
||
|
||
#define BMP180_VERSION_REG 0xD0
|
||
#define BMP180_CONTROL_REG 0xF4
|
||
#define BMP180_RESET_REG 0xE0
|
||
#define BMP180_OUT_MSB_REG 0xF6
|
||
#define BMP180_OUT_LSB_REG 0xF7
|
||
#define BMP180_OUT_XLSB_REG 0xF8
|
||
|
||
#define BMP180_CALIBRATION_REG 0xAA
|
||
|
||
//
|
||
// Values for BMP180_CONTROL_REG
|
||
//
|
||
#define BMP180_MEASURE_TEMP 0x2E
|
||
#define BMP180_MEASURE_PRESS 0x34
|
||
|
||
//
|
||
// CHIP ID stored in BMP180_VERSION_REG
|
||
//
|
||
#define BMP180_CHIP_ID 0x55
|
||
|
||
//
|
||
// Reset value for BMP180_RESET_REG
|
||
//
|
||
#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
|
||
typedef struct
|
||
{
|
||
uint8_t cmd;
|
||
const QueueHandle_t* resultQueue;
|
||
} bmp180_command_t;
|
||
|
||
// Just works due to the fact that QueueHandle_t is a "void *"
|
||
static QueueHandle_t bmp180_rx_queue = NULL;
|
||
static TaskHandle_t bmp180_task_handle = NULL;
|
||
|
||
//
|
||
// Forward declarations
|
||
//
|
||
static bool bmp180_informUser_Impl(const QueueHandle_t* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure);
|
||
|
||
// Set default implementation .. User gets result as bmp180_result_t event
|
||
bool (*bmp180_informUser)(const QueueHandle_t* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure) = bmp180_informUser_Impl;
|
||
|
||
// I2C Driver Task
|
||
static void bmp180_driver_task(void *pvParameters)
|
||
{
|
||
// Data to be received from user
|
||
bmp180_command_t current_command;
|
||
bmp180_constants_t bmp180_constants;
|
||
|
||
#ifdef BMP180_DEBUG
|
||
// Wait for commands from the outside
|
||
printf("%s: Started Task\n", __FUNCTION__);
|
||
#endif
|
||
|
||
// 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
|
||
if (xQueueReceive(bmp180_rx_queue, ¤t_command, portMAX_DELAY) == pdTRUE) {
|
||
#ifdef BMP180_DEBUG
|
||
printf("%s: Received user command %d 0x%p\n", __FUNCTION__, current_command.cmd, current_command.resultQueue);
|
||
#endif
|
||
// use user provided queue
|
||
if (current_command.resultQueue != NULL) {
|
||
// Work on it ...
|
||
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 bool bmp180_create_communication_queues()
|
||
{
|
||
// Just create them once
|
||
if (bmp180_rx_queue == NULL)
|
||
bmp180_rx_queue = xQueueCreate(BMP180_RX_QUEUE_SIZE, sizeof(bmp180_result_t));
|
||
|
||
return bmp180_rx_queue != NULL;
|
||
}
|
||
|
||
static bool bmp180_createTask()
|
||
{
|
||
// We already have a task
|
||
portBASE_TYPE x = pdPASS;
|
||
|
||
if (bmp180_task_handle == NULL) {
|
||
x = xTaskCreate(bmp180_driver_task, "bmp180_driver_task", 256, NULL, BMP180_TASK_PRIORITY, &bmp180_task_handle);
|
||
}
|
||
return x == pdPASS;
|
||
}
|
||
|
||
// Default user inform implementation
|
||
static bool bmp180_informUser_Impl(const QueueHandle_t* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure)
|
||
{
|
||
bmp180_result_t result;
|
||
|
||
result.cmd = cmd;
|
||
result.temperature = temperature;
|
||
result.pressure = pressure;
|
||
|
||
return (xQueueSend(*resultQueue, &result, 0) == pdTRUE);
|
||
}
|
||
|
||
// Just init all needed queues
|
||
bool bmp180_init(uint8_t scl, uint8_t sda)
|
||
{
|
||
// 1. Create required queues
|
||
bool result = false;
|
||
|
||
if (bmp180_create_communication_queues()) {
|
||
// 2. Init i2c driver
|
||
i2c_init(scl, sda);
|
||
// 3. Check for bmp180 ...
|
||
if (bmp180_is_available()) {
|
||
// 4. Start driver task
|
||
if (bmp180_createTask()) {
|
||
// We are finished
|
||
result = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
void bmp180_trigger_measurement(const QueueHandle_t* resultQueue)
|
||
{
|
||
bmp180_command_t c;
|
||
|
||
c.cmd = BMP180_PRESSURE + BMP180_TEMPERATURE;
|
||
c.resultQueue = resultQueue;
|
||
|
||
xQueueSend(bmp180_rx_queue, &c, 0);
|
||
}
|
||
|
||
|
||
void bmp180_trigger_pressure_measurement(const QueueHandle_t* resultQueue)
|
||
{
|
||
bmp180_command_t c;
|
||
|
||
c.cmd = BMP180_PRESSURE;
|
||
c.resultQueue = resultQueue;
|
||
|
||
xQueueSend(bmp180_rx_queue, &c, 0);
|
||
}
|
||
|
||
void bmp180_trigger_temperature_measurement(const QueueHandle_t* resultQueue)
|
||
{
|
||
bmp180_command_t c;
|
||
|
||
c.cmd = BMP180_TEMPERATURE;
|
||
c.resultQueue = resultQueue;
|
||
|
||
xQueueSend(bmp180_rx_queue, &c, 0);
|
||
}
|