41d4427fba
This just separates them in the one file leaving it as possible future change to actually remove the higher level api or move it to an example.
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 xQueueHandle* resultQueue;
|
||
} bmp180_command_t;
|
||
|
||
// Just works due to the fact that xQueueHandle is a "void *"
|
||
static xQueueHandle bmp180_rx_queue = NULL;
|
||
static xTaskHandle bmp180_task_handle = NULL;
|
||
|
||
//
|
||
// Forward declarations
|
||
//
|
||
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
|
||
bool (*bmp180_informUser)(const xQueueHandle* 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, (signed char *)"bmp180_driver_task", 256, NULL, BMP180_TASK_PRIORITY, &bmp180_task_handle);
|
||
}
|
||
return x == pdPASS;
|
||
}
|
||
|
||
// Default user inform implementation
|
||
static bool bmp180_informUser_Impl(const xQueueHandle* 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 xQueueHandle* 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 xQueueHandle* resultQueue)
|
||
{
|
||
bmp180_command_t c;
|
||
|
||
c.cmd = BMP180_PRESSURE;
|
||
c.resultQueue = resultQueue;
|
||
|
||
xQueueSend(bmp180_rx_queue, &c, 0);
|
||
}
|
||
|
||
void bmp180_trigger_temperature_measurement(const xQueueHandle* resultQueue)
|
||
{
|
||
bmp180_command_t c;
|
||
|
||
c.cmd = BMP180_TEMPERATURE;
|
||
c.resultQueue = resultQueue;
|
||
|
||
xQueueSend(bmp180_rx_queue, &c, 0);
|
||
}
|