mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2024-11-30 08:24:11 +01:00
326 lines
8.6 KiB
C
326 lines
8.6 KiB
C
/**
|
|
******************************************************************************
|
|
* @addtogroup PIOS PIOS Core hardware abstraction layer
|
|
* @{
|
|
* @addtogroup PIOS_BMP085 BMP085 Functions
|
|
* @brief Hardware functions to deal with the altitude pressure sensor
|
|
* @{
|
|
*
|
|
* @file pios_bmp085.c
|
|
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
|
|
* @brief BMP085 Pressure Sensor Routines
|
|
* @see The GNU Public License (GPL) Version 3
|
|
*
|
|
******************************************************************************/
|
|
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/* Project Includes */
|
|
// TODO: Clean this up. Getting around old constant.
|
|
#define PIOS_BMP085_OVERSAMPLING oversampling
|
|
|
|
#include "pios.h"
|
|
|
|
#if defined(PIOS_INCLUDE_BMP085)
|
|
#if !defined(PIOS_INCLUDE_EXTI)
|
|
#error PIOS_EXTI Must be included in the project!
|
|
#endif /* PIOS_INCLUDE_EXTI */
|
|
|
|
/* Glocal Variables */
|
|
ConversionTypeTypeDef CurrentRead;
|
|
#if defined(PIOS_INCLUDE_FREERTOS)
|
|
xSemaphoreHandle PIOS_BMP085_EOC;
|
|
#else
|
|
int32_t PIOS_BMP085_EOC;
|
|
#endif
|
|
|
|
/* Local Variables */
|
|
static BMP085CalibDataTypeDef CalibData;
|
|
|
|
/* Straight from the datasheet */
|
|
static int32_t X1, X2, X3, B3, B5, B6, P;
|
|
static uint32_t B4, B7;
|
|
static volatile uint16_t RawTemperature;
|
|
static volatile uint32_t RawPressure;
|
|
static volatile uint32_t Pressure;
|
|
static volatile uint16_t Temperature;
|
|
|
|
static int32_t PIOS_BMP085_Read(uint8_t address, uint8_t * buffer, uint8_t len);
|
|
static int32_t PIOS_BMP085_Write(uint8_t address, uint8_t buffer);
|
|
|
|
// Move into proper driver structure with cfg stored
|
|
static uint32_t oversampling;
|
|
static const struct pios_bmp085_cfg * dev_cfg;
|
|
|
|
/**
|
|
* Initialise the BMP085 sensor
|
|
*/
|
|
void PIOS_BMP085_Init(const struct pios_bmp085_cfg * cfg)
|
|
{
|
|
PIOS_BMP085_EOC = 0;
|
|
|
|
oversampling = cfg->oversampling;
|
|
dev_cfg = cfg; // Store cfg before enabling interrupt
|
|
|
|
/* Configure EOC pin as input floating */
|
|
GPIO_Init(cfg->drdy.gpio, &cfg->drdy.init);
|
|
|
|
/* Configure the End Of Conversion (EOC) interrupt */
|
|
SYSCFG_EXTILineConfig(cfg->eoc_exti.port_source, cfg->eoc_exti.pin_source);
|
|
EXTI_Init(&cfg->eoc_exti.init);
|
|
|
|
/* Enable and set EOC EXTI Interrupt to the lowest priority */
|
|
NVIC_Init(&cfg->eoc_irq.init);
|
|
|
|
/* Configure anothing GPIO pin pin as output to set address */
|
|
GPIO_Init(cfg->xclr.gpio, &cfg->xclr.init);
|
|
GPIO_SetBits(cfg->xclr.gpio,cfg->xclr.init.GPIO_Pin);
|
|
|
|
/* Read all 22 bytes of calibration data in one transfer, this is a very optimized way of doing things */
|
|
uint8_t Data[BMP085_CALIB_LEN];
|
|
bool good_cal = false;
|
|
while(!good_cal) {
|
|
good_cal = (PIOS_BMP085_Read(BMP085_CALIB_ADDR, Data, BMP085_CALIB_LEN) == 0);
|
|
// Check none of the calibration is zero to ensure calibration is good
|
|
for(uint8_t i = 0; i < BMP085_CALIB_LEN; i += 2)
|
|
good_cal &= (Data[i] != 0) || (Data[i+1] != 0);
|
|
}
|
|
|
|
|
|
/* Parameters AC1-AC6 */
|
|
CalibData.AC1 = (Data[0] << 8) | Data[1];
|
|
CalibData.AC2 = (Data[2] << 8) | Data[3];
|
|
CalibData.AC3 = (Data[4] << 8) | Data[5];
|
|
CalibData.AC4 = (Data[6] << 8) | Data[7];
|
|
CalibData.AC5 = (Data[8] << 8) | Data[9];
|
|
CalibData.AC6 = (Data[10] << 8) | Data[11];
|
|
|
|
/* Parameters B1, B2 */
|
|
CalibData.B1 = (Data[12] << 8) | Data[13];
|
|
CalibData.B2 = (Data[14] << 8) | Data[15];
|
|
|
|
/* Parameters MB, MC, MD */
|
|
CalibData.MB = (Data[16] << 8) | Data[17];
|
|
CalibData.MC = (Data[18] << 8) | Data[19];
|
|
CalibData.MD = (Data[20] << 8) | Data[21];
|
|
}
|
|
|
|
/**
|
|
* Start the ADC conversion
|
|
* \param[in] PresOrTemp BMP085_PRES_ADDR or BMP085_TEMP_ADDR
|
|
* \return 0 for success, -1 for failure (conversion completed and not read)
|
|
*/
|
|
int32_t PIOS_BMP085_StartADC(ConversionTypeTypeDef Type)
|
|
{
|
|
if(PIOS_BMP085_EOC)
|
|
return -1;
|
|
|
|
/* Start the conversion */
|
|
if (Type == TemperatureConv) {
|
|
while (PIOS_BMP085_Write(BMP085_CTRL_ADDR, BMP085_TEMP_ADDR) != 0)
|
|
continue;
|
|
} else if (Type == PressureConv) {
|
|
while (PIOS_BMP085_Write(BMP085_CTRL_ADDR, BMP085_PRES_ADDR) != 0)
|
|
continue;
|
|
}
|
|
|
|
CurrentRead = Type;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Read the ADC conversion value (once ADC conversion has completed)
|
|
* \param[in] PresOrTemp BMP085_PRES_ADDR or BMP085_TEMP_ADDR
|
|
* \return 0 if successfully read the ADC, -1 if failed
|
|
*/
|
|
int32_t PIOS_BMP085_ReadADC(void)
|
|
{
|
|
uint8_t Data[3];
|
|
Data[0] = 0;
|
|
Data[1] = 0;
|
|
Data[2] = 0;
|
|
|
|
if(!PIOS_BMP085_EOC)
|
|
return -1;
|
|
|
|
PIOS_BMP085_EOC = 0;
|
|
|
|
/* Read and store the 16bit result */
|
|
if (CurrentRead == TemperatureConv) {
|
|
/* Read the temperature conversion */
|
|
if (PIOS_BMP085_Read(BMP085_ADC_MSB, Data, 2) != 0)
|
|
return -1;
|
|
|
|
RawTemperature = ((Data[0] << 8) | Data[1]);
|
|
|
|
X1 = (RawTemperature - CalibData.AC6) * CalibData.AC5 >> 15;
|
|
X2 = ((int32_t) CalibData.MC << 11) / (X1 + CalibData.MD);
|
|
B5 = X1 + X2;
|
|
Temperature = (B5 + 8) >> 4;
|
|
} else {
|
|
/* Read the pressure conversion */
|
|
if (PIOS_BMP085_Read(BMP085_ADC_MSB, Data, 3) != 0)
|
|
return -1;
|
|
RawPressure = ((Data[0] << 16) | (Data[1] << 8) | Data[2]) >> (8 - oversampling);
|
|
|
|
B6 = B5 - 4000;
|
|
X1 = (CalibData.B2 * (B6 * B6 >> 12)) >> 11;
|
|
X2 = CalibData.AC2 * B6 >> 11;
|
|
X3 = X1 + X2;
|
|
B3 = ((((int32_t) CalibData.AC1 * 4 + X3) << oversampling) + 2) >> 2;
|
|
X1 = CalibData.AC3 * B6 >> 13;
|
|
X2 = (CalibData.B1 * (B6 * B6 >> 12)) >> 16;
|
|
X3 = ((X1 + X2) + 2) >> 2;
|
|
B4 = (CalibData.AC4 * (uint32_t) (X3 + 32768)) >> 15;
|
|
B7 = ((uint32_t) RawPressure - B3) * (50000 >> oversampling);
|
|
P = B7 < 0x80000000 ? (B7 * 2) / B4 : (B7 / B4) * 2;
|
|
|
|
X1 = (P >> 8) * (P >> 8);
|
|
X1 = (X1 * 3038) >> 16;
|
|
X2 = (-7357 * P) >> 16;
|
|
Pressure = P + ((X1 + X2 + 3791) >> 4);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int16_t PIOS_BMP085_GetTemperature(void)
|
|
{
|
|
return Temperature;
|
|
}
|
|
|
|
int32_t PIOS_BMP085_GetPressure(void)
|
|
{
|
|
return Pressure;
|
|
}
|
|
|
|
/**
|
|
* Reads one or more bytes into a buffer
|
|
* \param[in] address BMP085 register address (depends on size)
|
|
* \param[out] buffer destination buffer
|
|
* \param[in] len number of bytes which should be read
|
|
* \return 0 if operation was successful
|
|
* \return -1 if error during I2C transfer
|
|
*/
|
|
int32_t PIOS_BMP085_Read(uint8_t address, uint8_t * buffer, uint8_t len)
|
|
{
|
|
uint8_t addr_buffer[] = {
|
|
address,
|
|
};
|
|
|
|
const struct pios_i2c_txn txn_list[] = {
|
|
{
|
|
.info = __func__,
|
|
.addr = BMP085_I2C_ADDR,
|
|
.rw = PIOS_I2C_TXN_WRITE,
|
|
.len = sizeof(addr_buffer),
|
|
.buf = addr_buffer,
|
|
}
|
|
,
|
|
{
|
|
.info = __func__,
|
|
.addr = BMP085_I2C_ADDR,
|
|
.rw = PIOS_I2C_TXN_READ,
|
|
.len = len,
|
|
.buf = buffer,
|
|
}
|
|
};
|
|
|
|
return PIOS_I2C_Transfer(PIOS_I2C_MAIN_ADAPTER, txn_list, NELEMENTS(txn_list)) ? 0 : -1;
|
|
}
|
|
|
|
/**
|
|
* Writes one or more bytes to the BMP085
|
|
* \param[in] address Register address
|
|
* \param[in] buffer source buffer
|
|
* \return 0 if operation was successful
|
|
* \return -1 if error during I2C transfer
|
|
*/
|
|
int32_t PIOS_BMP085_Write(uint8_t address, uint8_t buffer)
|
|
{
|
|
uint8_t data[] = {
|
|
address,
|
|
buffer,
|
|
};
|
|
|
|
const struct pios_i2c_txn txn_list[] = {
|
|
{
|
|
.info = __func__,
|
|
.addr = BMP085_I2C_ADDR,
|
|
.rw = PIOS_I2C_TXN_WRITE,
|
|
.len = sizeof(data),
|
|
.buf = data,
|
|
}
|
|
,
|
|
};
|
|
|
|
return PIOS_I2C_Transfer(PIOS_I2C_MAIN_ADAPTER, txn_list, NELEMENTS(txn_list)) ? 0 : -1;
|
|
}
|
|
|
|
/**
|
|
* @brief Run self-test operation.
|
|
* \return 0 if self-test succeed, -1 if failed
|
|
*/
|
|
int32_t PIOS_BMP085_Test()
|
|
{
|
|
// TODO: Is there a better way to test this than just checking that pressure/temperature has changed?
|
|
int32_t cur_value = 0;
|
|
|
|
if(PIOS_BMP085_Read(BMP085_ADC_MSB, (uint8_t *) &cur_value, 1) != 0)
|
|
return -1;
|
|
return 0;
|
|
|
|
cur_value = Temperature;
|
|
PIOS_BMP085_StartADC(TemperatureConv);
|
|
PIOS_DELAY_WaitmS(5);
|
|
PIOS_BMP085_ReadADC();
|
|
if (cur_value == Temperature)
|
|
return -1;
|
|
|
|
cur_value=Pressure;
|
|
PIOS_BMP085_StartADC(PressureConv);
|
|
PIOS_DELAY_WaitmS(26);
|
|
PIOS_BMP085_ReadADC();
|
|
if (cur_value == Pressure)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Handle external line 2 interrupt requests
|
|
*/
|
|
uint32_t bmp_eocs = 0;
|
|
void EXTI2_IRQHandler(void)
|
|
{
|
|
if (EXTI_GetITStatus(dev_cfg->eoc_exti.init.EXTI_Line) != RESET) {
|
|
/* Read the ADC Value */
|
|
PIOS_BMP085_EOC=1;
|
|
|
|
bmp_eocs++;
|
|
|
|
/* Clear the EXTI line pending bit */
|
|
EXTI_ClearITPendingBit(dev_cfg->eoc_exti.init.EXTI_Line);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/**
|
|
* @}
|
|
* @}
|
|
*/
|