#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); }