diff --git a/flight/Makefile b/flight/Makefile index bb0345e2a..8f6e2e781 100644 --- a/flight/Makefile +++ b/flight/Makefile @@ -95,6 +95,7 @@ SRC += $(PIOSSTM32F10X)/pios_usart.c SRC += $(PIOSSTM32F10X)/pios_irq.c SRC += $(PIOSSTM32F10X)/pios_adc.c SRC += $(PIOSSTM32F10X)/pios_servo.c +SRC += $(PIOSSTM32F10X)/pios_i2c.c ## PIOS Hardware (Common) SRC += $(PIOSCOMMON)/pios_settings.c diff --git a/flight/PiOS/STM32F10x/pios_i2c.c b/flight/PiOS/STM32F10x/pios_i2c.c new file mode 100644 index 000000000..54b3b6555 --- /dev/null +++ b/flight/PiOS/STM32F10x/pios_i2c.c @@ -0,0 +1,574 @@ +/** + ****************************************************************************** + * + * @file pios_i2c.c + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2009. + * @brief I2C Enable/Disable routines + * @see The GNU Public License (GPL) Version 3 + * @defgroup PIOS_I2C I2C Communication 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" + +/* 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; + +/** +* 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 = I2C_SCL_PIN; + GPIO_Init(I2C_GPIO_PORT, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN; + GPIO_Init(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 = IRQ_I2C_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 = IRQ_I2C_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 = I2C_DUTY_CYCLE; + I2C_InitStructure.I2C_ClockSpeed = I2C_BUS_FREQUENCY; + + /* Trigger software reset via I2C_DeInit */ + I2C_DeInit(i2cx->base); + + /* Clear transfer state and error value */ + i2cx->transfer_state.ALL = 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_TransferBegin(I2CSemaphoreTypeDef semaphore_type) +{ + 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_TransferFinished(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 MIOS32_IIC_LastErrorGet(void) +{ + return I2CRec.last_transfer_error; +} + + +/** +* 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 MIOS32_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 = 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 = 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:
+*