/** ****************************************************************************** * @addtogroup PIOS PIOS Core hardware abstraction layer * @{ * @addtogroup PIOS_IMU3000 IMU3000 Functions * @brief Deals with the hardware interface to the 3-axis gyro * @{ * * @file pios_IMU3000.c * @author David "Buzz" Carlson (buzz@chebuzz.com) * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2011. * @brief IMU3000 3-axis gyor functions from INS * @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 */ #include "pios.h" #if defined(PIOS_INCLUDE_IMU3000) /* Global Variables */ /* Local Types */ typedef struct { uint8_t Fifo_store; /* FIFO storage of different readings (See datasheet page 31 for more details) */ uint8_t Smpl_rate_div; /* Sample rate divider to use (See datasheet page 32 for more details) */ uint8_t DigLPF_Scale; /* Digital low-pass filter and full-range scale (See datasheet page 33 for more details) */ uint8_t Interrupt_cfg; /* Interrupt configuration (See datasheet page 35 for more details) */ uint8_t User_ctl; /* User control settings (See datasheet page 41 for more details) */ uint8_t Pwr_mgmt_clk; /* Power management and clock selection (See datasheet page 32 for more details) */ } PIOS_IMU3000_ConfigTypeDef; /* Local Variables */ static void PIOS_IMU3000_Config(PIOS_IMU3000_ConfigTypeDef * IMU3000_Config_Struct); static int32_t PIOS_IMU3000_Read(uint8_t address, uint8_t * buffer, uint8_t len); static int32_t PIOS_IMU3000_Write(uint8_t address, uint8_t buffer); int32_t imu3000_id = 0; #define PIOS_IMU3000_MAX_DOWNSAMPLE 10 static int16_t pios_imu3000_buffer[PIOS_IMU3000_MAX_DOWNSAMPLE * 4]; static t_fifo_buffer pios_imu3000_fifo; volatile bool imu3000_first_read = true; volatile bool imu3000_configured = false; volatile bool imu3000_cb_ready = true; /** * @brief Initialize the IMU3000 3-axis gyro sensor. * @return none */ void PIOS_IMU3000_Init(const struct pios_imu3000_cfg * cfg) { fifoBuf_init(&pios_imu3000_fifo, (uint8_t *) pios_imu3000_buffer, sizeof(pios_imu3000_buffer)); /* 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 the IMU3000 Sensor */ PIOS_IMU3000_ConfigTypeDef IMU3000_InitStructure; IMU3000_InitStructure.Fifo_store = PIOS_IMU3000_FIFO_GYRO_X_OUT | PIOS_IMU3000_FIFO_GYRO_Y_OUT | PIOS_IMU3000_FIFO_GYRO_Z_OUT | PIOS_IMU3000_FIFO_FOOTER; IMU3000_InitStructure.Smpl_rate_div = 3; // Clock at 8 khz, downsampled by 8 for 1khz IMU3000_InitStructure.DigLPF_Scale = PIOS_IMU3000_LOWPASS_256_HZ | PIOS_IMU3000_SCALE_500_DEG; IMU3000_InitStructure.Interrupt_cfg = PIOS_IMU3000_INT_DATA_RDY | PIOS_IMU3000_INT_CLR_ANYRD; IMU3000_InitStructure.User_ctl = PIOS_IMU3000_USERCTL_FIFO_EN; IMU3000_InitStructure.Pwr_mgmt_clk = PIOS_IMU3000_PWRMGMT_PLL_X_CLK; PIOS_IMU3000_Config(&IMU3000_InitStructure); } /** * @brief Initialize the IMU3000 3-axis gyro sensor * \return none * \param[in] PIOS_IMU3000_ConfigTypeDef struct to be used to configure sensor. * */ static void PIOS_IMU3000_Config(PIOS_IMU3000_ConfigTypeDef * IMU3000_Config_Struct) { imu3000_first_read = true; imu3000_cb_ready = true; // Reset chip and fifo while (PIOS_IMU3000_Write(PIOS_IMU3000_USER_CTRL_REG, 0x01 | 0x02) != 0); PIOS_DELAY_WaituS(20); while (PIOS_IMU3000_Write(PIOS_IMU3000_USER_CTRL_REG, 0x00) != 0); // FIFO storage while (PIOS_IMU3000_Write(PIOS_IMU3000_FIFO_EN_REG, IMU3000_Config_Struct->Fifo_store) != 0); // Sample rate divider while (PIOS_IMU3000_Write(PIOS_IMU3000_SMPLRT_DIV_REG, IMU3000_Config_Struct->Smpl_rate_div) != 0) ; // Digital low-pass filter and scale while (PIOS_IMU3000_Write(PIOS_IMU3000_DLPF_CFG_REG, IMU3000_Config_Struct->DigLPF_Scale) != 0) ; // Interrupt configuration while (PIOS_IMU3000_Write(PIOS_IMU3000_USER_CTRL_REG, IMU3000_Config_Struct->User_ctl) != 0) ; // Interrupt configuration while (PIOS_IMU3000_Write(PIOS_IMU3000_PWR_MGMT_REG, IMU3000_Config_Struct->Pwr_mgmt_clk) != 0) ; // Interrupt configuration while (PIOS_IMU3000_Write(PIOS_IMU3000_INT_CFG_REG, IMU3000_Config_Struct->Interrupt_cfg) != 0) ; imu3000_configured = true; } /** * @brief Read current X, Z, Y values (in that order) * \param[out] int16_t array of size 3 to store X, Z, and Y magnetometer readings * \returns The number of samples remaining in the fifo */ int32_t PIOS_IMU3000_ReadGyros(int16_t * data) { uint8_t buf[6]; if(PIOS_IMU3000_Read(PIOS_IMU3000_GYRO_X_OUT_MSB, (uint8_t *) buf, sizeof(buf)) < 0) return -1; data[0] = buf[0] << 8 | buf[1]; data[1] = buf[2] << 8 | buf[3]; data[2] = buf[4] << 8 | buf[5]; return 0; } /** * @brief Read the identification bytes from the IMU3000 sensor * \return ID read from IMU3000 or -1 if failure */ int32_t PIOS_IMU3000_ReadID() { uint8_t id; if(PIOS_IMU3000_Read(0x00, (uint8_t *) &imu3000_id, 1) != 0) return -1; id = imu3000_id; return id; } /** * \brief Reads the data from the IMU3000 FIFO * \param[out] buffer destination buffer * \param[in] len maximum number of bytes which should be read * \note This returns the data as X, Y, Z the temperature * \return number of bytes transferred if operation was successful * \return -1 if error during I2C transfer */ #define IMU3000_DATA_SIZE 6 int32_t PIOS_IMU3000_ReadFifo(int16_t * buffer) { if(fifoBuf_getUsed(&pios_imu3000_fifo) < IMU3000_DATA_SIZE) return -1; fifoBuf_getData(&pios_imu3000_fifo, (uint8_t *) buffer, IMU3000_DATA_SIZE); return 0; } /** * @brief Reads one or more bytes from IMU3000 into a buffer * \param[in] address IMU3000 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 * \return -4 if invalid length */ static int32_t PIOS_IMU3000_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 = PIOS_IMU3000_I2C_ADDR, .rw = PIOS_I2C_TXN_WRITE, .len = sizeof(addr_buffer), .buf = addr_buffer, } , { .info = __func__, .addr = PIOS_IMU3000_I2C_ADDR, .rw = PIOS_I2C_TXN_READ, .len = len, .buf = buffer, } }; return PIOS_I2C_Transfer(PIOS_I2C_GYRO_ADAPTER, txn_list, NELEMENTS(txn_list)) ? 0 : -1; } // Must allocate on stack to be persistent static uint8_t cb_addr_buffer[] = { 0, }; static struct pios_i2c_txn cb_txn_list[] = { { .addr = PIOS_IMU3000_I2C_ADDR, .rw = PIOS_I2C_TXN_WRITE, .len = sizeof(cb_addr_buffer), .buf = cb_addr_buffer, } , { .addr = PIOS_IMU3000_I2C_ADDR, .rw = PIOS_I2C_TXN_READ, .len = 0, .buf = 0, } }; static int32_t PIOS_IMU3000_Read_Callback(uint8_t address, uint8_t * buffer, uint8_t len, void *callback) { cb_addr_buffer[0] = address; cb_txn_list[0].info = __func__, cb_txn_list[1].info = __func__; cb_txn_list[1].len = len; cb_txn_list[1].buf = buffer; PIOS_I2C_Transfer_Callback(PIOS_I2C_GYRO_ADAPTER, cb_txn_list, NELEMENTS(cb_txn_list), callback); return 0; } /** * @brief Writes one or more bytes to the IMU3000 * \param[in] address Register address * \param[in] buffer source buffer * \return 0 if operation was successful * \return -1 if error during I2C transfer */ static int32_t PIOS_IMU3000_Write(uint8_t address, uint8_t buffer) { uint8_t data[] = { address, buffer, }; const struct pios_i2c_txn txn_list[] = { { .info = __func__, .addr = PIOS_IMU3000_I2C_ADDR, .rw = PIOS_I2C_TXN_WRITE, .len = sizeof(data), .buf = data, } , }; return PIOS_I2C_Transfer(PIOS_I2C_GYRO_ADAPTER, txn_list, NELEMENTS(txn_list)) ? 0 : -1; } /** * @brief Run self-test operation. * \return 0 if test succeeded * \return non-zero value if test succeeded */ uint8_t PIOS_IMU3000_Test(void) { /* Verify that ID matches (IMU3000 ID is 0x69) */ int32_t id = 0; id = PIOS_IMU3000_ReadID(); if(id < 0) return -1; if(id != PIOS_IMU3000_I2C_ADDR) return -2; return 0; } uint32_t cb_count = 0; uint8_t imu3000_read_buffer[IMU3000_DATA_SIZE + 2]; // Right now using ,Y,Z,fifo_footer int16_t imu3000_data_buffer[IMU3000_DATA_SIZE / sizeof(int16_t)]; uint32_t imu_read = 0; static void imu3000_callback() { imu3000_cb_ready = true; cb_count++; if(imu3000_first_read) { imu3000_data_buffer[0] = imu3000_read_buffer[0] << 8 | imu3000_read_buffer[1]; imu3000_data_buffer[1] = imu3000_read_buffer[2] << 8 | imu3000_read_buffer[3]; imu3000_data_buffer[2] = imu3000_read_buffer[4] << 8 | imu3000_read_buffer[5]; imu3000_first_read = false; } else { // First two bytes are left over fifo from last call imu3000_data_buffer[0] = imu3000_read_buffer[2] << 8 | imu3000_read_buffer[3]; imu3000_data_buffer[1] = imu3000_read_buffer[4] << 8 | imu3000_read_buffer[5]; imu3000_data_buffer[2] = imu3000_read_buffer[6] << 8 | imu3000_read_buffer[7]; } fifoBuf_putData(&pios_imu3000_fifo, (uint8_t *) imu3000_data_buffer, sizeof(imu3000_data_buffer)); imu_read += sizeof(imu3000_data_buffer); } /** * @brief IRQ Handler */ uint32_t imu3000_irq = 0; uint16_t fifo_level; uint8_t fifo_level_data[2]; uint32_t imu3000_readtime; void PIOS_IMU3000_IRQHandler(void) { imu3000_irq++; if(!imu3000_configured) return; PIOS_Assert(imu3000_cb_ready); // If at least one read doesnt succeed then the irq not reset and we will stall while(PIOS_IMU3000_Read(PIOS_IMU3000_FIFO_CNT_MSB, (uint8_t *) &fifo_level_data, sizeof(fifo_level_data)) != 0) PIOS_DELAY_WaituS(10); fifo_level = (fifo_level_data[0] << 8) + fifo_level_data[1]; PIOS_DELAY_WaituS(10); if(imu3000_first_read) { // Stupid system for IMU3000. If first read from buffer then we will read 4 sensors without fifo // footer. After this we will read out a fifo footer if(fifo_level < sizeof(imu3000_read_buffer)) return; imu3000_cb_ready = false; // Leave footer in buffer PIOS_IMU3000_Read_Callback(PIOS_IMU3000_FIFO_REG, imu3000_read_buffer, sizeof(imu3000_read_buffer) - 2, imu3000_callback); } else { // Stupid system for IMU3000. Ensure something is left in buffer if(fifo_level < (sizeof(imu3000_read_buffer) + 2)) return; uint32_t timeval = PIOS_DELAY_GetRaw(); imu3000_cb_ready = false; // Leave footer in buffer PIOS_IMU3000_Read_Callback(PIOS_IMU3000_FIFO_REG, imu3000_read_buffer, sizeof(imu3000_read_buffer), imu3000_callback); imu3000_readtime = PIOS_DELAY_DiffuS(timeval); } } /** * The physical IRQ handler * Soon this will be generic in pios_exti and the BMA180 will register * against it. Right now this is crap! */ void EXTI1_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line1) != RESET) { PIOS_IMU3000_IRQHandler(); EXTI_ClearITPendingBit(EXTI_Line1); } } #endif /** * @} * @} */