OP-1516 fixed boundf mistake
[librepilot.git] / flight / pios / common / pios_bma180.c
blobc1140a4291f841cd305391ba172a82a85300e4e4
1 /**
2 ******************************************************************************
3 * @addtogroup PIOS PIOS Core hardware abstraction layer
4 * @{
5 * @addtogroup PIOS_BMA180 BMA180 Functions
6 * @brief Deals with the hardware interface to the BMA180 3-axis accelerometer
7 * @{
9 * @file pios_bma180.h
10 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
11 * @brief PiOS BMA180 digital accelerometer driver.
12 * - Driver for the BMA180 digital accelerometer on the SPI bus.
13 * @see The GNU Public License (GPL) Version 3
15 *****************************************************************************/
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 3 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25 * for more details.
27 * You should have received a copy of the GNU General Public License along
28 * with this program; if not, write to the Free Software Foundation, Inc.,
29 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include "pios.h"
34 #ifdef PIOS_INCLUDE_BMA180
36 #include "fifo_buffer.h"
38 enum pios_bma180_dev_magic {
39 PIOS_BMA180_DEV_MAGIC = 0xcbb93755,
42 #define PIOS_BMA180_MAX_DOWNSAMPLE 10
43 struct bma180_dev {
44 uint32_t spi_id;
45 uint32_t slave_num;
46 int16_t buffer[PIOS_BMA180_MAX_DOWNSAMPLE * sizeof(struct pios_bma180_data)];
47 t_fifo_buffer fifo;
48 const struct pios_bma180_cfg *cfg;
49 enum bma180_bandwidth bandwidth;
50 enum bma180_range range;
51 enum pios_bma180_dev_magic magic;
54 // ! Global structure for this device device
55 static struct bma180_dev *dev;
57 // ! Private functions
58 static struct bma180_dev *PIOS_BMA180_alloc(void);
59 static int32_t PIOS_BMA180_Validate(struct bma180_dev *dev);
60 static int32_t PIOS_BMA180_GetReg(uint8_t reg);
61 static int32_t PIOS_BMA180_SetReg(uint8_t reg, uint8_t data);
62 static int32_t PIOS_BMA180_SelectBW(enum bma180_bandwidth bw);
63 static int32_t PIOS_BMA180_SetRange(enum bma180_range range);
64 static int32_t PIOS_BMA180_Config();
65 static int32_t PIOS_BMA180_EnableIrq();
67 #define GRAV 9.81f
69 /**
70 * @brief Allocate a new device
72 static struct bma180_dev *PIOS_BMA180_alloc(void)
74 struct bma180_dev *bma180_dev;
76 bma180_dev = (struct bma180_dev *)pios_malloc(sizeof(*bma180_dev));
77 if (!bma180_dev) {
78 return NULL;
81 fifoBuf_init(&bma180_dev->fifo, (uint8_t *)bma180_dev->buffer, sizeof(bma180_dev->buffer));
83 bma180_dev->magic = PIOS_BMA180_DEV_MAGIC;
84 return bma180_dev;
87 /**
88 * @brief Validate the handle to the spi device
89 * @returns 0 for valid device or -1 otherwise
91 static int32_t PIOS_BMA180_Validate(struct bma180_dev *vdev)
93 if (vdev == NULL) {
94 return -1;
96 if (vdev->magic != PIOS_BMA180_DEV_MAGIC) {
97 return -2;
99 if (vdev->spi_id == 0) {
100 return -3;
102 return 0;
106 * @brief Initialize with good default settings
107 * @returns 0 for success, -1 for failure
109 int32_t PIOS_BMA180_Init(uint32_t spi_id, uint32_t slave_num, const struct pios_bma180_cfg *cfg)
111 dev = PIOS_BMA180_alloc();
112 if (dev == NULL) {
113 return -1;
116 dev->spi_id = spi_id;
117 dev->slave_num = slave_num;
118 dev->cfg = cfg;
120 if (PIOS_BMA180_Config() < 0) {
121 return -1;
123 PIOS_DELAY_WaituS(50);
125 PIOS_EXTI_Init(dev->cfg->exti_cfg);
127 while (PIOS_BMA180_EnableIrq() != 0) {
131 return 0;
135 * @brief Claim the SPI bus for the accel communications and select this chip
136 * @return 0 if successful, -1 if unable to claim bus
138 int32_t PIOS_BMA180_ClaimBus()
140 if (PIOS_BMA180_Validate(dev) != 0) {
141 return -1;
144 if (PIOS_SPI_ClaimBus(dev->spi_id) != 0) {
145 return -1;
148 PIOS_SPI_RC_PinSet(dev->spi_id, dev->slave_num, 0);
150 return 0;
154 * @brief Claim the SPI bus for the accel communications and select this chip. Call from an ISR.
155 * @param woken[in,out] If non-NULL, will be set to true if woken was false and a higher priority
156 * task has is now eligible to run, else unchanged
157 * @return 0 if successful, -1 if unable to claim bus
159 int32_t PIOS_BMA180_ClaimBusISR(bool *woken)
161 if (PIOS_BMA180_Validate(dev) != 0) {
162 return -1;
165 if (PIOS_SPI_ClaimBusISR(dev->spi_id, woken) != 0) {
166 return -1;
169 PIOS_SPI_RC_PinSet(dev->spi_id, dev->slave_num, 0);
170 return 0;
174 * @brief Release the SPI bus for the accel communications and end the transaction
175 * @return 0 if successful
177 int32_t PIOS_BMA180_ReleaseBus()
179 if (PIOS_BMA180_Validate(dev) != 0) {
180 return -1;
182 PIOS_SPI_RC_PinSet(dev->spi_id, dev->slave_num, 1);
184 return PIOS_SPI_ReleaseBus(dev->spi_id);
188 * @brief Release the SPI bus for the accel communications and end the transaction. Call from an ISR
189 * @param woken[in,out] If non-NULL, will be set to true if woken was false and a higher priority
190 * task has is now eligible to run, else unchanged
191 * @return 0 if successful
193 int32_t PIOS_BMA180_ReleaseBusISR(bool *woken)
195 if (PIOS_BMA180_Validate(dev) != 0) {
196 return -1;
199 PIOS_SPI_RC_PinSet(dev->spi_id, dev->slave_num, 1);
201 return PIOS_SPI_ReleaseBusISR(dev->spi_id, woken);
205 * @brief Read a register from BMA180
206 * @returns The register value or -1 if failure to get bus
207 * @param reg[in] Register address to be read
209 int32_t PIOS_BMA180_GetReg(uint8_t reg)
211 if (PIOS_BMA180_Validate(dev) != 0) {
212 return -1;
215 uint8_t data;
217 if (PIOS_BMA180_ClaimBus() != 0) {
218 return -1;
221 PIOS_SPI_TransferByte(dev->spi_id, (0x80 | reg)); // request byte
222 data = PIOS_SPI_TransferByte(dev->spi_id, 0); // receive response
224 PIOS_BMA180_ReleaseBus();
225 return data;
229 * @brief Write a BMA180 register. EEPROM must be unlocked before calling this function.
230 * @return none
231 * @param reg[in] address of register to be written
232 * @param data[in] data that is to be written to register
234 int32_t PIOS_BMA180_SetReg(uint8_t reg, uint8_t data)
236 if (PIOS_BMA180_ClaimBus() != 0) {
237 return -1;
240 PIOS_SPI_TransferByte(dev->spi_id, 0x7f & reg);
241 PIOS_SPI_TransferByte(dev->spi_id, data);
243 PIOS_BMA180_ReleaseBus();
245 return 0;
249 static int32_t PIOS_BMA180_EnableEeprom()
251 // Enable EEPROM writing
252 int32_t byte = PIOS_BMA180_GetReg(BMA_CTRREG0);
254 if (byte < 0) {
255 return -1;
257 byte |= 0x10; // Set bit 4
258 if (PIOS_BMA180_SetReg(BMA_CTRREG0, (uint8_t)byte) < 0) { // Have to set ee_w to
259 return -1;
261 return 0;
264 static int32_t PIOS_BMA180_DisableEeprom()
266 // Enable EEPROM writing
267 int32_t byte = PIOS_BMA180_GetReg(BMA_CTRREG0);
269 if (byte < 0) {
270 return -1;
272 byte |= 0x10; // Set bit 4
273 if (PIOS_BMA180_SetReg(BMA_CTRREG0, (uint8_t)byte) < 0) { // Have to set ee_w to
274 return -1;
276 return 0;
280 * @brief Set the default register settings
281 * @return 0 if successful, -1 if not
283 static int32_t PIOS_BMA180_Config()
286 0x35 = 0x81 //smp-skip = 1 for less interrupts
287 0x33 = 0x81 //shadow-dis = 1, update MSB and LSB synchronously
288 0x27 = 0x01 //dis-i2c
289 0x21 = 0x02 //new_data_int = 1
292 PIOS_DELAY_WaituS(20);
294 if (PIOS_BMA180_Validate(dev) != 0) {
295 return -1;
298 while (PIOS_BMA180_EnableEeprom() != 0) {
301 while (PIOS_BMA180_SetReg(BMA_RESET, BMA_RESET_CODE) != 0) {
304 while (PIOS_BMA180_SetReg(BMA_OFFSET_LSB1, 0x81) != 0) {
307 while (PIOS_BMA180_SetReg(BMA_GAIN_Y, 0x81) != 0) {
310 while (PIOS_BMA180_SetReg(BMA_CTRREG3, 0xFF) != 0) {
313 while (PIOS_BMA180_SelectBW(dev->cfg->bandwidth) != 0) {
316 while (PIOS_BMA180_SetRange(dev->cfg->range) != 0) {
319 while (PIOS_BMA180_DisableEeprom() != 0) {
323 return 0;
327 * @brief Select the bandwidth the digital filter pass allows.
328 * @return 0 if successful, -1 if not
329 * @param rate[in] Bandwidth setting to be used
331 * EEPROM must be write-enabled before calling this function.
333 static int32_t PIOS_BMA180_SelectBW(enum bma180_bandwidth bw)
335 if (PIOS_BMA180_Validate(dev) != 0) {
336 return -1;
339 dev->bandwidth = bw;
341 uint8_t reg;
342 reg = PIOS_BMA180_GetReg(BMA_BW_ADDR);
343 reg = (reg & ~BMA_BW_MASK) | ((bw << BMA_BW_SHIFT) & BMA_BW_MASK);
344 return PIOS_BMA180_SetReg(BMA_BW_ADDR, reg);
348 * @brief Select the full scale acceleration range.
349 * @return 0 if successful, -1 if not
350 * @param rate[in] Range setting to be used
353 static int32_t PIOS_BMA180_SetRange(enum bma180_range new_range)
355 if (PIOS_BMA180_Validate(dev) != 0) {
356 return -1;
359 uint8_t reg;
361 dev->range = new_range;
362 reg = PIOS_BMA180_GetReg(BMA_RANGE_ADDR);
363 reg = (reg & ~BMA_RANGE_MASK) | ((dev->range << BMA_RANGE_SHIFT) & BMA_RANGE_MASK);
364 return PIOS_BMA180_SetReg(BMA_RANGE_ADDR, reg);
367 static int32_t PIOS_BMA180_EnableIrq()
369 if (PIOS_BMA180_EnableEeprom() < 0) {
370 return -1;
373 if (PIOS_BMA180_SetReg(BMA_CTRREG3, BMA_NEW_DAT_INT) < 0) {
374 return -1;
377 if (PIOS_BMA180_DisableEeprom() < 0) {
378 return -1;
381 return 0;
385 * @brief Read a single set of values from the x y z channels
386 * @param[out] data Int16 array of (x,y,z) sensor values
387 * @returns 0 if successful
388 * @retval -1 unable to claim bus
389 * @retval -2 unable to transfer data
391 int32_t PIOS_BMA180_ReadAccels(struct pios_bma180_data *data)
393 // To save memory use same buffer for in and out but offset by
394 // a byte
395 uint8_t buf[7] = { BMA_X_LSB_ADDR | 0x80, 0, 0, 0, 0, 0 };
396 uint8_t rec[7] = { 0, 0, 0, 0, 0, 0 };
398 if (PIOS_BMA180_ClaimBus() != 0) {
399 return -1;
401 if (PIOS_SPI_TransferBlock(dev->spi_id, &buf[0], &rec[0], 7, NULL) != 0) {
402 return -2;
404 PIOS_BMA180_ReleaseBus();
406 // | MSB | LSB | 0 | new_data |
407 data->x = ((rec[2] << 8) | rec[1]);
408 data->y = ((rec[4] << 8) | rec[3]);
409 data->z = ((rec[6] << 8) | rec[5]);
410 data->x /= 4;
411 data->y /= 4;
412 data->z /= 4;
414 return 0; // return number of remaining entries
418 * @brief Returns the scale the BMA180 chip is using
419 * @return Scale (m / s^2) / LSB
421 float PIOS_BMA180_GetScale()
423 if (PIOS_BMA180_Validate(dev) != 0) {
424 return -1;
427 switch (dev->cfg->range) {
428 case BMA_RANGE_1G:
429 return GRAV / 8192.0f;
431 case BMA_RANGE_1_5G:
432 return GRAV / 5460.0f;
434 case BMA_RANGE_2G:
435 return GRAV / 4096.0f;
437 case BMA_RANGE_3G:
438 return GRAV / 2730.0f;
440 case BMA_RANGE_4G:
441 return GRAV / 2048.0f;
443 case BMA_RANGE_8G:
444 return GRAV / 1024.0f;
446 case BMA_RANGE_16G:
447 return GRAV / 512.0f;
449 return 0;
453 * @brief Get data from fifo
454 * @param [out] buffer pointer to a @ref pios_bma180_data structure to receive data
455 * @return 0 for success, -1 for failure (no data available)
457 int32_t PIOS_BMA180_ReadFifo(struct pios_bma180_data *buffer)
459 if (PIOS_BMA180_Validate(dev) != 0) {
460 return -1;
463 if (fifoBuf_getUsed(&dev->fifo) < sizeof(*buffer)) {
464 return -1;
467 fifoBuf_getData(&dev->fifo, (uint8_t *)buffer, sizeof(*buffer));
469 return 0;
474 * @brief Test SPI and chip functionality by reading chip ID register
475 * @return 0 if success, -1 if failure.
478 int32_t PIOS_BMA180_Test()
480 // Read chip ID then version ID
481 uint8_t buf[3] = { 0x80 | BMA_CHIPID_ADDR, 0, 0 };
482 uint8_t rec[3] = { 0, 0, 0 };
483 int32_t retval;
485 if (PIOS_BMA180_ClaimBus() != 0) {
486 return -1;
488 retval = PIOS_SPI_TransferBlock(dev->spi_id, &buf[0], &rec[0], sizeof(buf), NULL);
489 PIOS_BMA180_ReleaseBus();
491 if (retval != 0) {
492 return -2;
495 struct pios_bma180_data data;
496 if (PIOS_BMA180_ReadAccels(&data) != 0) {
497 return -3;
500 if (rec[1] != 0x3) {
501 return -4;
504 if (rec[2] < 0x12) {
505 return -5;
508 return 0;
512 * @brief EXTI IRQ Handler. Read data from the BMA180 FIFO and push onto a local fifo.
513 * @return a boolean to the EXTI IRQ Handler wrapper indicating if a
514 * higher priority task is now eligible to run
516 int32_t bma180_irqs = 0;
517 bool PIOS_BMA180_IRQHandler(void)
519 bool woken = false;
520 static const uint8_t pios_bma180_req_buf[7] = { BMA_X_LSB_ADDR | 0x80, 0, 0, 0, 0, 0 };
521 uint8_t pios_bma180_dmabuf[8];
523 bma180_irqs++;
525 // If we can't get the bus then just move on for efficiency
526 if (PIOS_BMA180_ClaimBusISR(&woken) != 0) {
527 return woken; // Something else is using bus, miss this data
530 PIOS_SPI_TransferBlock(dev->spi_id, pios_bma180_req_buf, (uint8_t *)pios_bma180_dmabuf,
531 sizeof(pios_bma180_dmabuf), NULL);
533 // TODO: Make this conversion depend on configuration scale
534 struct pios_bma180_data data;
536 // Don't release bus till data has copied
537 PIOS_BMA180_ReleaseBusISR(&woken);
539 // Must not return before releasing bus
540 if (fifoBuf_getFree(&dev->fifo) < sizeof(data)) {
541 return woken;
544 // Bottom two bits indicate new data and are constant zeros. Don't right
545 // shift because it drops sign bit
546 data.x = ((pios_bma180_dmabuf[2] << 8) | pios_bma180_dmabuf[1]);
547 data.y = ((pios_bma180_dmabuf[4] << 8) | pios_bma180_dmabuf[3]);
548 data.z = ((pios_bma180_dmabuf[6] << 8) | pios_bma180_dmabuf[5]);
549 data.x /= 4;
550 data.y /= 4;
551 data.z /= 4;
552 data.temperature = pios_bma180_dmabuf[7];
554 fifoBuf_putData(&dev->fifo, (uint8_t *)&data, sizeof(data));
556 return woken;
559 #endif /* PIOS_INCLUDE_BMA180 */
562 * @}
563 * @}