Merged in f5soh/librepilot/laurent/LP-92_Feed_forward_remove (pull request #33)
[librepilot.git] / flight / pios / common / pios_bmp085.c
blob96aa23d7230ecaa247b952d736778c786e1db694
1 /**
2 ******************************************************************************
3 * @addtogroup PIOS PIOS Core hardware abstraction layer
4 * @{
5 * @addtogroup PIOS_BMP085 BMP085 Functions
6 * @brief Hardware functions to deal with the altitude pressure sensor
7 * @{
9 * @file pios_bmp085.c
10 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
11 * @brief BMP085 Pressure Sensor Routines
12 * @see The GNU Public License (GPL) Version 3
14 ******************************************************************************/
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 3 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
23 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24 * for more details.
26 * You should have received a copy of the GNU General Public License along
27 * with this program; if not, write to the Free Software Foundation, Inc.,
28 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 #include "pios.h"
32 #include <pios_bmp085.h>
33 #ifdef PIOS_INCLUDE_BMP085
35 #ifndef PIOS_INCLUDE_EXTI
36 #error PIOS_EXTI must be included in the project
37 #endif /* PIOS_INCLUDE_EXTI */
39 /* Glocal Variables */
40 ConversionTypeTypeDef CurrentRead;
42 /* Local Variables */
43 static BMP085CalibDataTypeDef CalibData;
45 /* Straight from the datasheet */
46 static int32_t X1, X2, X3, B3, B5, B6, P;
47 static uint32_t B4, B7;
48 static volatile uint16_t RawTemperature;
49 static volatile uint32_t RawPressure;
50 static volatile uint32_t Pressure;
51 static volatile uint16_t Temperature;
53 #ifdef PIOS_BMP085_HAS_GPIOS
55 #if defined(PIOS_INCLUDE_FREERTOS)
56 xSemaphoreHandle PIOS_BMP085_EOC;
57 #else
58 int32_t PIOS_BMP085_EOC;
59 #endif
61 void PIOS_BMP085_EndOfConversion(void)
63 #if defined(PIOS_INCLUDE_FREERTOS)
64 portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
65 #endif
67 /* Read the ADC Value */
68 #if defined(PIOS_INCLUDE_FREERTOS)
69 xSemaphoreGiveFromISR(PIOS_BMP085_EOC, &xHigherPriorityTaskWoken);
70 #else
71 PIOS_BMP085_EOC = 1;
72 #endif
74 #if defined(PIOS_INCLUDE_FREERTOS)
75 /* Yield From ISR if needed */
76 portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
77 #endif
80 static const struct pios_exti_cfg pios_exti_bmp085_cfg __exti_config = {
81 .vector = PIOS_BMP085_EndOfConversion,
82 .line = PIOS_BMP085_EOC_EXTI_LINE,
83 .pin = {
84 .gpio = PIOS_BMP085_EOC_GPIO_PORT,
85 .init = {
86 .GPIO_Pin = PIOS_BMP085_EOC_GPIO_PIN,
87 .GPIO_Mode = GPIO_Mode_IN_FLOATING,
90 .irq = {
91 .init = {
92 .NVIC_IRQChannel = PIOS_BMP085_EOC_IRQn,
93 .NVIC_IRQChannelPreemptionPriority = PIOS_BMP085_EOC_PRIO,
94 .NVIC_IRQChannelSubPriority = 0,
95 .NVIC_IRQChannelCmd = ENABLE,
98 .exti = {
99 .init = {
100 .EXTI_Line = PIOS_BMP085_EOC_EXTI_LINE,
101 .EXTI_Mode = EXTI_Mode_Interrupt,
102 .EXTI_Trigger = EXTI_Trigger_Rising,
103 .EXTI_LineCmd = ENABLE,
108 #endif /* PIOS_BMP085_HAS_GPIOS */
110 * Initialise the BMP085 sensor
112 void PIOS_BMP085_Init(void)
114 #ifdef PIOS_BMP085_HAS_GPIOS
116 #if defined(PIOS_INCLUDE_FREERTOS)
117 /* Semaphore used by ISR to signal End-Of-Conversion */
118 vSemaphoreCreateBinary(PIOS_BMP085_EOC);
119 /* Must start off empty so that first transfer waits for EOC */
120 xSemaphoreTake(PIOS_BMP085_EOC, portMAX_DELAY);
121 #else
122 PIOS_BMP085_EOC = 0;
123 #endif
125 /* Enable EOC GPIO clock */
126 RCC_APB2PeriphClockCmd(PIOS_BMP085_EOC_CLK | RCC_APB2Periph_AFIO, ENABLE);
128 if (PIOS_EXTI_Init(&pios_exti_bmp085_cfg)) {
129 PIOS_Assert(0);
132 /* Configure XCLR pin as push/pull alternate funtion output */
133 GPIO_InitTypeDef GPIO_InitStructure;
134 GPIO_InitStructure.GPIO_Pin = PIOS_BMP085_XCLR_GPIO_PIN;
135 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
136 GPIO_Init(PIOS_BMP085_XCLR_GPIO_PORT, &GPIO_InitStructure);
138 #endif /* PIOS_BMP085_HAS_GPIOS */
140 /* Read all 22 bytes of calibration data in one transfer, this is a very optimized way of doing things */
141 uint8_t Data[BMP085_CALIB_LEN];
142 while (PIOS_BMP085_Read(BMP085_CALIB_ADDR, Data, BMP085_CALIB_LEN) != 0) {
143 continue;
146 /* Parameters AC1-AC6 */
147 CalibData.AC1 = (Data[0] << 8) | Data[1];
148 CalibData.AC2 = (Data[2] << 8) | Data[3];
149 CalibData.AC3 = (Data[4] << 8) | Data[5];
150 CalibData.AC4 = (Data[6] << 8) | Data[7];
151 CalibData.AC5 = (Data[8] << 8) | Data[9];
152 CalibData.AC6 = (Data[10] << 8) | Data[11];
154 /* Parameters B1, B2 */
155 CalibData.B1 = (Data[12] << 8) | Data[13];
156 CalibData.B2 = (Data[14] << 8) | Data[15];
158 /* Parameters MB, MC, MD */
159 CalibData.MB = (Data[16] << 8) | Data[17];
160 CalibData.MC = (Data[18] << 8) | Data[19];
161 CalibData.MD = (Data[20] << 8) | Data[21];
165 * Start the ADC conversion
166 * \param[in] PresOrTemp BMP085_PRES_ADDR or BMP085_TEMP_ADDR
167 * \return Raw ADC value
169 void PIOS_BMP085_StartADC(ConversionTypeTypeDef Type)
171 /* Start the conversion */
172 if (Type == TemperatureConv) {
173 while (PIOS_BMP085_Write(BMP085_CTRL_ADDR, BMP085_TEMP_ADDR) != 0) {
174 continue;
176 } else if (Type == PressureConv) {
177 while (PIOS_BMP085_Write(BMP085_CTRL_ADDR, BMP085_PRES_ADDR) != 0) {
178 continue;
182 CurrentRead = Type;
186 * Read the ADC conversion value (once ADC conversion has completed)
187 * \param[in] PresOrTemp BMP085_PRES_ADDR or BMP085_TEMP_ADDR
188 * \return Raw ADC value
190 void PIOS_BMP085_ReadADC(void)
192 uint8_t Data[3];
194 Data[0] = 0;
195 Data[1] = 0;
196 Data[2] = 0;
198 /* Read and store the 16bit result */
199 if (CurrentRead == TemperatureConv) {
200 /* Read the temperature conversion */
201 while (PIOS_BMP085_Read(BMP085_ADC_MSB, Data, 2) != 0) {
202 continue;
204 RawTemperature = ((Data[0] << 8) | Data[1]);
206 X1 = (RawTemperature - CalibData.AC6) * CalibData.AC5 >> 15;
207 X2 = ((int32_t)CalibData.MC << 11) / (X1 + CalibData.MD);
208 B5 = X1 + X2;
209 Temperature = (B5 + 8) >> 4;
210 } else {
211 /* Read the pressure conversion */
212 while (PIOS_BMP085_Read(BMP085_ADC_MSB, Data, 3) != 0) {
213 continue;
215 RawPressure = ((Data[0] << 16) | (Data[1] << 8) | Data[2]) >> (8 - BMP085_OVERSAMPLING);
217 B6 = B5 - 4000;
218 X1 = (CalibData.B2 * (B6 * B6 >> 12)) >> 11;
219 X2 = CalibData.AC2 * B6 >> 11;
220 X3 = X1 + X2;
221 B3 = ((((int32_t)CalibData.AC1 * 4 + X3) << BMP085_OVERSAMPLING) + 2) >> 2;
222 X1 = CalibData.AC3 * B6 >> 13;
223 X2 = (CalibData.B1 * (B6 * B6 >> 12)) >> 16;
224 X3 = ((X1 + X2) + 2) >> 2;
225 B4 = (CalibData.AC4 * (uint32_t)(X3 + 32768)) >> 15;
226 B7 = ((uint32_t)RawPressure - B3) * (50000 >> BMP085_OVERSAMPLING);
227 P = B7 < 0x80000000 ? (B7 * 2) / B4 : (B7 / B4) * 2;
229 X1 = (P >> 8) * (P >> 8);
230 X1 = (X1 * 3038) >> 16;
231 X2 = (-7357 * P) >> 16;
232 Pressure = P + ((X1 + X2 + 3791) >> 4);
236 int16_t PIOS_BMP085_GetTemperature(void)
238 return Temperature;
241 int32_t PIOS_BMP085_GetPressure(void)
243 return Pressure;
247 * Reads one or more bytes into a buffer
248 * \param[in] address BMP085 register address (depends on size)
249 * \param[out] buffer destination buffer
250 * \param[in] len number of bytes which should be read
251 * \return 0 if operation was successful
252 * \return -1 if error during I2C transfer
253 * \return -2 if BMP085 blocked by another task (retry it!)
254 * \return -4 if invalid length
256 bool PIOS_BMP085_Read(uint8_t address, uint8_t *buffer, uint8_t len)
258 uint8_t addr_buffer[] = {
259 address,
262 const struct pios_i2c_txn txn_list[] = {
264 .info = __func__,
265 .addr = BMP085_I2C_ADDR,
266 .rw = PIOS_I2C_TXN_WRITE,
267 .len = sizeof(addr_buffer),
268 .buf = addr_buffer,
272 .info = __func__,
273 .addr = BMP085_I2C_ADDR,
274 .rw = PIOS_I2C_TXN_READ,
275 .len = len,
276 .buf = buffer,
280 return PIOS_I2C_Transfer(PIOS_I2C_BMP085_ADAPTER, txn_list, NELEMENTS(txn_list));
284 * Writes one or more bytes to the BMP085
285 * \param[in] address Register address
286 * \param[in] buffer source buffer
287 * \return 0 if operation was successful
288 * \return -1 if error during I2C transfer
289 * \return -2 if BMP085 blocked by another task (retry it!)
291 bool PIOS_BMP085_Write(uint8_t address, uint8_t buffer)
293 uint8_t data[] = {
294 address,
295 buffer,
298 const struct pios_i2c_txn txn_list[] = {
300 .info = __func__,
301 .addr = BMP085_I2C_ADDR,
302 .rw = PIOS_I2C_TXN_WRITE,
303 .len = sizeof(data),
304 .buf = data,
309 return PIOS_I2C_Transfer(PIOS_I2C_BMP085_ADAPTER, txn_list, NELEMENTS(txn_list));
313 * @brief Run self-test operation.
314 * \return 0 if self-test failed
315 * \return any non-0 number if test passed
317 int32_t PIOS_BMP085_Test()
319 // TODO: Is there a better way to test this than just checking that pressure/temperature has changed?
320 uint32_t passed = 1;
321 uint32_t cur_value = 0;
323 cur_value = Temperature;
324 PIOS_BMP085_StartADC(TemperatureConv);
325 PIOS_DELAY_WaitmS(5);
326 PIOS_BMP085_ReadADC();
327 if (cur_value == Temperature) {
328 passed = 0;
331 cur_value = Pressure;
332 PIOS_BMP085_StartADC(PressureConv);
333 PIOS_DELAY_WaitmS(26);
334 PIOS_BMP085_ReadADC();
335 if (cur_value == Pressure) {
336 passed = 0;
339 return passed;
342 #endif /* PIOS_INCLUDE_BMP085 */