/** ****************************************************************************** * * @file pios_i2c.c * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. * Parts by Thorsten Klose (tk@midibox.org) (tk@midibox.org) * @brief I2C Enable/Disable routines * @see The GNU Public License (GPL) Version 3 * @defgroup PIOS_I2C I2C Functions * @{ * *****************************************************************************/ /* * 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_DONT_USE_I2C) /* Options */ //#define USE_DEBUG_PINS /* Global Variables */ volatile uint32_t PIOS_I2C_UnexpectedEvent; /* Local types */ typedef union { struct { unsigned ALL:8; }; struct { unsigned BUSY:1; unsigned STOP_REQUESTED:1; unsigned ABORT_IF_FIRST_BYTE_0:1; unsigned WRITE_WITHOUT_STOP:1; }; } TransferStateTypeDef; typedef struct { I2C_TypeDef *base; uint8_t i2c_address; uint8_t *tx_buffer_ptr; uint8_t *rx_buffer_ptr; volatile uint16_t buffer_len; volatile uint16_t buffer_ix; volatile TransferStateTypeDef transfer_state; volatile int32_t transfer_error; volatile int32_t last_transfer_error; volatile uint8_t i2c_semaphore; } I2CRecTypeDef; /* Local Prototypes */ static void PIOS_I2C_InitPeripheral(void); static void EV_IRQHandler(I2CRecTypeDef *i2cx); static void ER_IRQHandler(I2CRecTypeDef *i2cx); /* Local Variables */ static I2CRecTypeDef I2CRec; /* Macros */ #ifdef USE_DEBUG_PINS #define DEBUG_PIN_ISR 0 #define DEBUG_PIN_BUSY 1 #define DebugPinHigh(x) PIOS_DEBUG_PinHigh(x) #define DebugPinLow(x) PIOS_DEBUG_PinLow(x) #else #define DebugPinHigh(x) #define DebugPinLow(x) #endif /** * Initializes IIC driver * \param[in] mode currently only mode 0 supported * \return < 0 if initialisation failed */ int32_t PIOS_I2C_Init(void) { /* Configure IIC pins in open drain mode */ GPIO_InitTypeDef GPIO_InitStructure; GPIO_StructInit(&GPIO_InitStructure); GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_InitStructure.GPIO_Pin = PIOS_I2C_SCL_PIN; GPIO_Init(PIOS_I2C_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = PIOS_I2C_SDA_PIN; GPIO_Init(PIOS_I2C_GPIO_PORT, &GPIO_InitStructure); PIOS_I2C_InitPeripheral(); /* Now accessible for other tasks */ I2CRec.i2c_semaphore = 0; I2CRec.last_transfer_error = 0; /* Configure and enable I2C2 interrupts */ NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = I2C2_EV_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = PIOS_I2C_IRQ_EV_PRIORITY; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = I2C2_ER_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = PIOS_I2C_IRQ_ER_PRIORITY; NVIC_Init(&NVIC_InitStructure); /* No error */ return 0; } /** * Internal function to (re-)initialize the I2C peripheral */ static void PIOS_I2C_InitPeripheral(void) { I2C_InitTypeDef I2C_InitStructure; I2CRecTypeDef *i2cx = &I2CRec; /* Prepare I2C init-struct */ I2C_StructInit(&I2C_InitStructure); I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_OwnAddress1 = 0; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; /* Define base address */ i2cx->base = I2C2; /* enable peripheral clock of I2C */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE); /* Set I2C clock bus clock params */ /* Note that the STM32 driver handles value <= 100kHz differently! (duty cycle always 1:1) */ /* Important: bus frequencies > 400kHz don't work stable */ I2C_InitStructure.I2C_DutyCycle = PIOS_I2C_DUTY_CYCLE; I2C_InitStructure.I2C_ClockSpeed = PIOS_I2C_BUS_FREQ; /* Trigger software reset via I2C_DeInit */ I2C_DeInit(i2cx->base); /* Clear transfer state and error value */ i2cx->transfer_state.ALL = 0; PIOS_DEBUG_PinLow(0); i2cx->transfer_error = 0; /* Configure I2C peripheral */ I2C_Init(i2cx->base, &I2C_InitStructure); } /** * Semaphore handling: requests the IIC interface * \param[in] semaphore_type is either IIC_Blocking or IIC_Non_Blocking * \return Non_Blocking: returns -1 to request a retry * \return 0 if IIC interface free */ int32_t PIOS_I2C_LockDevice(I2CSemaphoreTypeDef semaphore_type) { volatile I2CRecTypeDef *i2cx = &I2CRec; int32_t status = -1; do { PIOS_IRQ_Disable(); if(!i2cx->i2c_semaphore) { i2cx->i2c_semaphore = 1; status = 0; } PIOS_IRQ_Enable(); } while(semaphore_type == I2C_Blocking && status != 0); /* Clear transfer errors of last transmission */ i2cx->last_transfer_error = 0; i2cx->transfer_error = 0; return status; } /** * Semaphore handling: releases the IIC interface for other tasks * \return < 0 on errors */ int32_t PIOS_I2C_UnlockDevice(void) { I2CRec.i2c_semaphore = 0; /* No error */ return 0; } /** * Returns the last transfer error
* Will be updated by PIOS_I2C_TransferCheck(), so that the error status * doesn't get lost (the check function will return 0 when called again)
* Will be cleared when a new transfer has been started successfully * \return last error status */ int32_t PIOS_IIC_LastErrorGet(void) { return I2CRec.last_transfer_error; } /** * Internal function called at the start of a transfer */ static void TransferStart(I2CRecTypeDef *i2cx) { DebugPinHigh(DEBUG_PIN_BUSY); i2cx->transfer_state.BUSY = 1; } /** * Internal function called at the end of a transfer */ static void TransferEnd(I2CRecTypeDef *i2cx) { DebugPinLow(DEBUG_PIN_BUSY); i2cx->transfer_state.BUSY = 0; } /** * Checks if transfer is finished * \return 0 if no ongoing transfer * \return 1 if ongoing transfer * \return < 0 if error during transfer * \note Note that the semaphore will be released automatically after an error * (PIOS_I2C_TransferBegin() has to be called again) */ int32_t PIOS_I2C_TransferCheck(void) { I2CRecTypeDef *i2cx = &I2CRec; /* Ongoing transfer? */ if(i2cx->transfer_state.BUSY) { return 1; } /* Error during transfer? */ /* (must be done *after* BUSY check to avoid race conditon!) */ if(i2cx->transfer_error) { /* Store error status for PIOS_IIC_LastErrorGet() function */ i2cx->last_transfer_error = i2cx->transfer_error; /* Clear current error status */ i2cx->transfer_error = 0; /* Release semaphore for easier programming at user level */ i2cx->i2c_semaphore = 0; /* And exit */ return i2cx->last_transfer_error; } /* No transfer */ return 0; } /** * Waits until transfer is finished * \return 0 if no ongoing transfer * \return < 0 if error during transfer * \note Note that the semaphore will be released automatically after an error * (PIOS_I2C_TransferBegin() has to be called again) */ int32_t PIOS_I2C_TransferWait(void) { I2CRecTypeDef *i2cx = &I2CRec; uint32_t repeat_ctr = PIOS_I2C_TIMEOUT_VALUE; uint16_t last_buffer_ix = i2cx->buffer_ix; while(--repeat_ctr > 0) { /* Check if buffer index has changed - if so, reload repeat counter */ if(i2cx->buffer_ix != last_buffer_ix) { repeat_ctr = PIOS_I2C_TIMEOUT_VALUE; last_buffer_ix = i2cx->buffer_ix; } /* Get transfer state */ int32_t check_state = PIOS_I2C_TransferCheck(); /* Exit if transfer finished or error detected */ if(check_state <= 0) { if(check_state < 0) { /* Release semaphore for easier programming at user level */ i2cx->i2c_semaphore = 0; } return check_state; } } /* Timeout error - something is stalling... */ /* Send stop condition */ I2C_GenerateSTOP(i2cx->base, ENABLE); /* Re-initialize peripheral */ PIOS_I2C_InitPeripheral(); /* Release semaphore (!) */ i2cx->i2c_semaphore = 0; return (i2cx->last_transfer_error = I2C_ERROR_TIMEOUT); } /** * Starts a new transfer. If this function is called during an ongoing * transfer, we wait until it has been finished and setup the new transfer * \param[in] transfer type:
*