diff --git a/conf/autopilot/lisa_test_progs.makefile b/conf/autopilot/lisa_test_progs.makefile index b6f29d928c..694707ab39 100644 --- a/conf/autopilot/lisa_test_progs.makefile +++ b/conf/autopilot/lisa_test_progs.makefile @@ -182,7 +182,7 @@ test_baro.CFLAGS += -I$(SRC_LISA) -I$(SRC_BOARD) test_baro.srcs += $(SRC_BOARD)/test_baro.c test_baro.srcs += $(SRC_BOARD)/baro_board.c test_baro.CFLAGS += -DUSE_I2C2 -test_baro.srcs += mcu_periph/i2c.c $(SRC_ARCH)/mcu_periph/i2c_arch.rewritten.c +test_baro.srcs += mcu_periph/i2c.c $(SRC_ARCH)/mcu_periph/i2c_arch.c # diff --git a/conf/autopilot/subsystems/shared/i2c_select.makefile b/conf/autopilot/subsystems/shared/i2c_select.makefile index 67badbf340..ab9ba6cd56 100644 --- a/conf/autopilot/subsystems/shared/i2c_select.makefile +++ b/conf/autopilot/subsystems/shared/i2c_select.makefile @@ -4,8 +4,7 @@ $(TARGET).srcs += mcu_periph/i2c.c ifeq ($(ARCH), stm32) -#$(TARGET).srcs += $(SRC_ARCH)/mcu_periph/i2c_arch.c -$(TARGET).srcs += $(SRC_ARCH)/mcu_periph/i2c_arch.rewritten.c +$(TARGET).srcs += $(SRC_ARCH)/mcu_periph/i2c_arch.c else $(TARGET).srcs += $(SRC_ARCH)/mcu_periph/i2c_arch.c endif diff --git a/sw/airborne/arch/stm32/mcu_periph/i2c_arch.c b/sw/airborne/arch/stm32/mcu_periph/i2c_arch.c index 8d69b3f9da..2da4cd09de 100644 --- a/sw/airborne/arch/stm32/mcu_periph/i2c_arch.c +++ b/sw/airborne/arch/stm32/mcu_periph/i2c_arch.c @@ -1,27 +1,69 @@ #include "mcu_periph/i2c.h" -#include -#include -#include -#include +#include +#include +#include +#include + +//#define I2C_DEBUG_LED + +/////////// DEBUGGING ////////////// +// TODO: remove this -static void start_transaction(struct i2c_periph* p); -static inline void end_of_transaction(struct i2c_periph *p); -static inline void i2c_hard_reset(struct i2c_periph *p); -static inline void i2c_reset_init(struct i2c_periph *p); +#ifdef I2C_DEBUG_LED -#define I2C_BUSY 0x20 +static inline void LED1_ON(void) +{ + GPIO_WriteBit(GPIOB, GPIO_Pin_6 , Bit_SET ); +} -#ifdef DEBUG_I2C -#define SPURIOUS_INTERRUPT(_periph, _status, _event) { while(1); } -#define OUT_OF_SYNC_STATE_MACHINE(_periph, _status, _event) { while(1); } -#else -//#define SPURIOUS_INTERRUPT(_periph, _status, _event) { periph->errors->unexpected_event_cnt++; abort_and_reset(_periph);} -#define SPURIOUS_INTERRUPT(_periph, _status, _event) { if (_status == I2CAddrWrSent) abort_and_reset(_periph);} -#define OUT_OF_SYNC_STATE_MACHINE(_periph, _status, _event) { abort_and_reset(_periph);} +static inline void LED1_OFF(void) +{ + GPIO_WriteBit(GPIOB, GPIO_Pin_6 , !Bit_SET ); +} + +static inline void LED2_ON(void) +{ + GPIO_WriteBit(GPIOB, GPIO_Pin_7 , Bit_SET ); +} + +static inline void LED2_OFF(void) +{ + GPIO_WriteBit(GPIOB, GPIO_Pin_7 , !Bit_SET ); +} + +static inline void LED_INIT(void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + LED1_OFF(); + LED2_OFF(); +} + + +static inline void LED_ERROR(uint8_t base, uint8_t nr) +{ + LED2_ON(); + for (int i=0;i<(base+nr);i++) + { + LED1_ON(); + LED1_OFF(); + } + LED2_OFF(); +} #endif +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +/* Not used in libopencm3 implementation. Keeping commented for reference. */ +#if 0 #ifdef USE_I2C1 static I2C_InitTypeDef I2C1_InitStruct = { .I2C_Mode = I2C_Mode_I2C, @@ -29,7 +71,7 @@ static I2C_InitTypeDef I2C1_InitStruct = { .I2C_OwnAddress1 = 0x00, .I2C_Ack = I2C_Ack_Enable, .I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit, - .I2C_ClockSpeed = 200000 + .I2C_ClockSpeed = 40000 }; #endif @@ -40,577 +82,1387 @@ static I2C_InitTypeDef I2C2_InitStruct = { .I2C_OwnAddress1 = 0x00, .I2C_Ack = I2C_Ack_Enable, .I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit, +// .I2C_ClockSpeed = 37500 // Like on LPC .I2C_ClockSpeed = 300000 }; #endif +#endif -static inline void i2c_delay(void) +#define I2C1_CLOCK_SPEED 40000 +#define I2C2_CLOCK_SPEED 30000 + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +// Bypassing the libSTM I2C functions to have more control over the reading of registers +// e.g. SR1 and SR2 should not always be read together as it might unwantedly clear ADDR flags etc. + +// Referring to STM32 manual: +// -Doc ID 13902 Rev 11 + +/* This macros are defined in libopencm3 already. Keeping commented for reference. */ + +#if 0 +// Status Register 1 + +#define I2C_SR1_BIT_SB (1<<0) // Start Condition Met +#define I2C_SR1_BIT_ADDR (1<<1) // Address Sent +#define I2C_SR1_BIT_BTF (1<<2) // SCL held low +#define I2C_SR1_BIT_RXNE (1<<6) // Data Read Available +#define I2C_SR1_BIT_TXE (1<<7) // TX buffer space available + +#define I2C_SR1_BIT_ERR_BUS (1<<8) // Misplaced Start/Stop (usually interference) +#define I2C_SR1_BIT_ERR_ARLO (1<<9) // Arbitration Lost (in multimaster) or SDA short-to-ground (in single master) +#define I2C_SR1_BIT_ERR_AF (1<<10) // Ack Failure (too fast/too soon/no sensor/wiring break/...) +#define I2C_SR1_BIT_ERR_OVR (1<<11) // Overrun [data loss] (in slave) or SCL short-to-ground (in single master) + +#define I2C_SR1_BITS_ERR ((1<<8)|(1<<9)|(1<<10)|(1<<11)|(1<<12)|(1<<14)|(1<<15)) + +// Status Register 2 + +#define I2C_SR2_BIT_TRA (1<<2) // Transmitting +#define I2C_SR2_BIT_BUSY (1<<1) // Busy +#define I2C_SR2_BIT_MSL (1<<0) // Master Selected + +// Control Register 1 + +#define I2C_CR1_BIT_PE (1<<0) // Peripheral Enable +#define I2C_CR1_BIT_START (1<<8) // Generate a Start +#define I2C_CR1_BIT_STOP (1<<9) // Generate a Stop +#define I2C_CR1_BIT_ACK (1<<10) // ACK / NACK +#define I2C_CR1_BIT_POS (1<<11) // Ack will control not the next but secondnext received byte +#define I2C_CR1_BIT_SWRST (1<<15) // Clear Busy Condition when no stop was detected + +// Control Register 2 + +#define I2C_CR2_BIT_ITERREN (1<<8) // Error Interrupt +#define I2C_CR2_BIT_ITEVTEN (1<<9) // Event Interrupt +#define I2C_CR2_BIT_ITBUFEN (1<<10) // Buffer Interrupt + +#endif + +// Error bit mask +// XXX: consider moving this define into libopencm3 +#define I2C_SR1_ERR_MASK (I2C_SR1_SMBALERT | \ + I2C_SR1_TIMEOUT | \ + I2C_SR1_PECERR | \ + I2C_SR1_OVR | \ + I2C_SR1_AF | \ + I2C_SR1_ARLO | \ + I2C_SR1_BERR) + +// Bit Control + +#define BIT_X_IS_SET_IN_REG(X,REG) (((REG) & (X)) == (X)) + +// disable and enable irq functions are not implemented in libopencm3 defining them here +// XXX: consider moving this definitions into libopencm3 +static inline void __disable_irq(void) { asm volatile ("cpsid i"); } +static inline void __enable_irq(void) { asm volatile ("cpsie i"); } + +// Critical Zones + +#define __I2C_REG_CRITICAL_ZONE_START __disable_irq(); +#define __I2C_REG_CRITICAL_ZONE_STOP __enable_irq(); + + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +// TODO: remove debug +#ifdef I2C_DEBUG_LED + +static inline void LED_SHOW_ACTIVE_BITS(I2C_TypeDef *regs) { - for (__IO int j = 0; j < 50; j++); -} + uint16_t CR1 = regs->CR1; + uint16_t SR1 = regs->SR1; + uint16_t SR2 = regs->SR2; + // Note: reading SR1 and then SR2 will clear ADDR bits -static inline void i2c_apply_config(struct i2c_periph *p) + LED1_ON(); + + // 1 Start + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + LED2_ON(); + else + LED2_OFF(); + LED2_OFF(); + + // 2 Addr + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_ADDR, SR1 ) ) + LED2_ON(); + else + LED2_OFF(); + LED2_OFF(); + + // 3 BTF + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_BTF, SR1 ) ) + LED2_ON(); + else + LED2_OFF(); + LED2_OFF(); + + // 4 ERROR + if (( SR1 & I2C_SR1_BITS_ERR ) != 0x0000) + LED2_ON(); + else + LED2_OFF(); + LED2_OFF(); + + // Anything? + if (( SR1 + SR2) != 0x0000) + LED2_ON(); + else + LED2_OFF(); + LED2_OFF(); + + LED1_OFF(); + + + LED1_ON(); + + // 1 Start + if (BIT_X_IS_SET_IN_REG( I2C_CR1_BIT_START, CR1 ) ) + LED2_ON(); + else + LED2_OFF(); + LED2_OFF(); + + // 2 Stop + if (BIT_X_IS_SET_IN_REG( I2C_CR1_BIT_STOP, CR1 ) ) + LED2_ON(); + else + LED2_OFF(); + LED2_OFF(); + + // 3 Busy + if (BIT_X_IS_SET_IN_REG( I2C_SR2_BIT_BUSY, SR2 ) ) + LED2_ON(); + else + LED2_OFF(); + LED2_OFF(); + + // 4 Tra + if (BIT_X_IS_SET_IN_REG( I2C_SR2_BIT_TRA, SR2 ) ) + LED2_ON(); + else + LED2_OFF(); + LED2_OFF(); + + // 5 Master + if (BIT_X_IS_SET_IN_REG( I2C_SR2_BIT_MSL, SR2 ) ) + LED2_ON(); + else + LED2_OFF(); + LED2_OFF(); + LED1_OFF(); + +//#define I2C_DEBUG_LED_CONTROL +#ifdef I2C_DEBUG_LED_CONTROL + + + LED1_ON(); + + // 1 Anything CR? + if (( CR1) != 0x0000) + LED2_ON(); + else + LED2_OFF(); + LED2_OFF(); + + // 2 PE + if (BIT_X_IS_SET_IN_REG( I2C_CR1_BIT_PE, CR1 ) ) + LED2_ON(); + else + LED2_OFF(); + LED2_OFF(); + + // 3 SWRESET + if (BIT_X_IS_SET_IN_REG( I2C_CR1_BIT_SWRST, CR1 ) ) + LED2_ON(); + else + LED2_OFF(); + LED2_OFF(); + + LED1_OFF(); +#endif + +} +#endif + +static inline void PPRZ_I2C_SEND_STOP(u32 i2c) { - I2C_Init(p->reg_addr, p->init_struct); + // Man: p722: Stop generation after the current byte transfer or after the current Start condition is sent. + I2C_CR1(i2c) |= I2C_CR1_STOP; + +#ifdef I2C_DEBUG_LED + LED2_ON(); + LED1_ON(); + LED1_OFF(); + LED2_OFF(); +#endif } -static inline void end_of_transaction(struct i2c_periph *p) +// (RE)START + +static inline void PPRZ_I2C_SEND_START(struct i2c_periph *periph) { - p->trans_extract_idx++; - if (p->trans_extract_idx >= I2C_TRANSACTION_QUEUE_LEN) - p->trans_extract_idx = 0; - /* if we have no more transaction to process, stop here */ - if (p->trans_extract_idx == p->trans_insert_idx) - p->status = I2CIdle; - /* if not, start next transaction */ - else - start_transaction(p); + u32 i2c = (u32) periph->reg_addr; + + // Reset the buffer pointer to the first byte + periph->idx_buf = 0; + +#ifdef I2C_DEBUG_LED + LED_SHOW_ACTIVE_BITS(regs); + + LED2_ON(); + LED1_ON(); + LED1_OFF(); + LED1_ON(); + LED1_OFF(); + LED1_ON(); + LED1_OFF(); + LED2_OFF(); + +#endif + + // Enable Error IRQ, Event IRQ but disable Buffer IRQ + I2C_CR2(i2c) |= I2C_CR2_ITERREN; + I2C_CR2(i2c) |= I2C_CR2_ITEVTEN; + I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; + + // Issue a new start + I2C_CR1(i2c) = (I2C_CR1_START | I2C_CR1_PE); + periph->status = I2CStartRequested; + } -static inline void abort_and_reset(struct i2c_periph *p) { - struct i2c_transaction* trans = p->trans[p->trans_extract_idx]; - trans->status = I2CTransFailed; - I2C_ITConfig(p->reg_addr, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE); - i2c_hard_reset(p); - I2C_ITConfig(p->reg_addr, I2C_IT_ERR, ENABLE); - end_of_transaction(p); -} +// STOP -#ifdef USE_I2C2 -static inline void on_status_start_requested(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event); -static inline void on_status_addr_wr_sent(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event); -static inline void on_status_sending_byte(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event); -static inline void on_status_stop_requested(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event); -static inline void on_status_addr_rd_sent(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event); -static inline void on_status_reading_byte(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event); -static inline void on_status_reading_last_byte(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event); -static inline void on_status_restart_requested(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event); +/////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// SUBTRANSACTION SEQUENCES +// -We arrive here every time a ISR is called with no error -/* - * Start Requested - * - */ -static inline void on_status_start_requested(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event) { - if (event & I2C_FLAG_SB) { - if(trans->type == I2CTransRx) { - I2C_Send7bitAddress(periph->reg_addr, trans->slave_addr, I2C_Direction_Receiver); - periph->status = I2CAddrRdSent; - } - else { - I2C_Send7bitAddress(periph->reg_addr, trans->slave_addr, I2C_Direction_Transmitter); - periph->status = I2CAddrWrSent; - } +enum STMI2CSubTransactionStatus { + STMI2C_SubTra_Busy, + STMI2C_SubTra_Ready_StopRequested, + STMI2C_SubTra_Ready, + STMI2C_SubTra_Error +}; + +// Doc ID 13902 Rev 11 p 710/1072 +// Transfer Sequence Diagram for Master Transmitter +static inline enum STMI2CSubTransactionStatus stmi2c_send(u32 i2c, struct i2c_periph *periph, struct i2c_transaction *trans) +{ + uint16_t SR1 = I2C_SR1(i2c); + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_SB, SR1 ) ) + { + // Disable buffer interrupt + I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; + // Send Slave address and wait for ADDR interrupt + I2C_DR(i2c) = trans->slave_addr; + // Document the current Status + periph->status = I2CAddrWrSent; } - // else - // SPURIOUS_INTERRUPT(periph, I2CStartRequested, event); - // FIXME: this one seems to get called all the time with mkk controllers -} + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_ADDR, SR1) ) + { + // Now read SR2 to clear the ADDR status Bit + uint16_t SR2 __attribute__ ((unused)) = I2C_SR2(i2c); -/* - * Addr WR sent - * - */ -static inline void on_status_addr_wr_sent(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event) { - if ((event & I2C_FLAG_ADDR) && (event & I2C_FLAG_TRA)) { - I2C_SendData(periph->reg_addr, trans->buf[0]); - if (trans->len_w > 1) { - I2C_SendData(periph->reg_addr, trans->buf[1]); + // Maybe check we are transmitting (did not loose arbitration for instance) + // if (! BIT_X_IS_SET_IN_REG(I2C_SR2_TRA, SR2)) { } + // update: this should be caught by the ARLO error: so we will not arrive here + + // Send First max 2 bytes + I2C_DR(i2c) = trans->buf[0]; + if (trans->len_w > 1) + { + I2C_DR(i2c) = trans->buf[1]; periph->idx_buf = 2; - I2C_ITConfig(periph->reg_addr, I2C_IT_BUF, ENABLE); - periph->status = I2CSendingByte; } - else { + else + { periph->idx_buf = 1; - if (trans->type == I2CTransTx) { - I2C_GenerateSTOP(periph->reg_addr, ENABLE); - periph->status = I2CStopRequested; - } - else { - I2C_GenerateSTART(periph->reg_addr, ENABLE); - periph->status = I2CRestartRequested; - } + } + + // Enable buffer-space available interrupt + // only if there is more to send: wait for TXE, no more to send: wait for BTF + if ( periph->idx_buf < trans->len_w) + I2C_CR2(i2c) |= I2C_CR2_ITBUFEN; + + // Document the current Status + periph->status = I2CSendingByte; + } + // The buffer is not full anymore AND we were not waiting for BTF + else if ((BIT_X_IS_SET_IN_REG(I2C_SR1_TxE, SR1) ) && (BIT_X_IS_SET_IN_REG(I2C_CR2_ITBUFEN, I2C_CR2(i2c))) ) + { + // Send the next byte + I2C_DR(i2c) = trans->buf[periph->idx_buf]; + periph->idx_buf++; + + // All bytes Sent? Then wait for BTF instead + if ( periph->idx_buf >= trans->len_w) + { + // Not interested anymore to know the buffer has space left + I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; + // Next interrupt will be BTF (or error) } } - else { - SPURIOUS_INTERRUPT(periph, I2CAddrWrSent, event); - // FIXME: this was where the code would break with mkk controllers on april 10 2011 - // now have SPURIOUS_INTERRUPT call abort_and_reset - } -} - -/* - * Sending Byte - * - */ -static inline void on_status_sending_byte(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event) { - I2C_TypeDef *regs = (I2C_TypeDef *) periph->reg_addr; - if (event & I2C_FLAG_TXE) { - if (periph->idx_buf < trans->len_w) { - I2C_SendData(periph->reg_addr, trans->buf[periph->idx_buf]); - periph->idx_buf++; - } - else { - I2C_ITConfig(periph->reg_addr, I2C_IT_BUF, DISABLE); - if (trans->type == I2CTransTx) { - I2C_GenerateSTOP(periph->reg_addr, ENABLE); - /* Make sure that the STOP bit is cleared by Hardware */ - static __IO uint8_t counter = 0; - while ((regs->CR1 & 0x200) == 0x200) { - counter++; - if (counter > 100) break; - } - periph->status = I2CStopRequested; + // BTF: means last byte was sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BTF, SR1) ) + { + if (trans->type == I2CTransTx) + { + // Tell the driver we are ready + trans->status = I2CTransSuccess; } - else { - I2C_GenerateSTART(periph->reg_addr, ENABLE); - periph->status = I2CRestartRequested; - } - } + // Otherwise we still need to do the receiving part + + return STMI2C_SubTra_Ready; } - else - SPURIOUS_INTERRUPT(periph, I2CSendingByte, event); + else // Event Logic Error + { + return STMI2C_SubTra_Error; + } + + return STMI2C_SubTra_Busy; } -/* - * Stop Requested - * - */ -static inline void on_status_stop_requested(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event) { - /* bummer.... */ - if (event & I2C_FLAG_RXNE) { - uint8_t read_byte = I2C_ReceiveData(periph->reg_addr); - if (periph->idx_buf < trans->len_r) { - trans->buf[periph->idx_buf] = read_byte; - } - } - I2C_ITConfig(periph->reg_addr, I2C_IT_EVT|I2C_IT_BUF, DISABLE); // should only need to disable evt, buf already disabled - trans->status = I2CTransSuccess; - end_of_transaction(periph); -} +// Doc ID 13902 Rev 11 p 714/1072 +// Transfer Sequence Diagram for Master Receiver for N=1 +static inline enum STMI2CSubTransactionStatus stmi2c_read1(u32 i2c, struct i2c_periph *periph, struct i2c_transaction *trans) +{ + uint16_t SR1 = I2C_SR1(i2c); -/* - * Addr RD sent - * - */ -static inline void on_status_addr_rd_sent(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event) { - I2C_TypeDef *regs = (I2C_TypeDef *) periph->reg_addr; + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_SB, SR1 ) ) + { + I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; + I2C_DR(i2c) = trans->slave_addr | 0x01; - if ((event & I2C_FLAG_ADDR) && !(event & I2C_FLAG_TRA)) { - periph->idx_buf = 0; - if(trans->len_r == 1) { // If we're going to read only one byte - I2C_AcknowledgeConfig(periph->reg_addr, DISABLE); // make sure it's gonna be nacked - I2C_GenerateSTOP(periph->reg_addr, ENABLE); // and followed by a stop - /* Make sure that the STOP bit is cleared by Hardware */ - static __IO uint8_t counter = 0; - while ((regs->CR1 & 0x200) == 0x200) { - counter++; - if (counter > 100) break; - } - periph->status = I2CReadingLastByte; // and remember we did - } - else { - I2C_AcknowledgeConfig(periph->reg_addr, ENABLE); // if it's more than one byte, ack it - I2C_ITConfig(periph->reg_addr, I2C_IT_BUF, ENABLE); - periph->status = I2CReadingByte; // and remember we did - } - } - else - SPURIOUS_INTERRUPT(periph, I2CAddrRdSent, event); -} - - -/* - * Reading byte - * - */ -static inline void on_status_reading_byte(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event) { - I2C_TypeDef *regs = (I2C_TypeDef *) periph->reg_addr; - if (event & I2C_FLAG_RXNE) { - uint8_t read_byte = I2C_ReceiveData(periph->reg_addr); - if (periph->idx_buf < trans->len_r) { - trans->buf[periph->idx_buf] = read_byte; - periph->idx_buf++; - if (periph->idx_buf >= trans->len_r-1) { // We're reading our last byte - I2C_AcknowledgeConfig(periph->reg_addr, DISABLE); // give them a nack once it's done - I2C_GenerateSTOP(periph->reg_addr, ENABLE); // and follow with a stop - /* Make sure that the STOP bit is cleared by Hardware */ - static __IO uint8_t counter = 0; - while ((regs->CR1 & 0x200) == 0x200) { - counter++; - if (counter > 100) break; - } - periph->status = I2CStopRequested; // remember we already trigered the stop - } - } // else { something very wrong has happened } - } - else - SPURIOUS_INTERRUPT(periph, I2CReadingByte, event); -} - -/* - * Reading last byte - * - */ -static inline void on_status_reading_last_byte(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event) { - if (event & I2C_FLAG_BTF) { - uint8_t read_byte = I2C_ReceiveData(periph->reg_addr); - trans->buf[periph->idx_buf] = read_byte; - I2C_GenerateSTOP(periph->reg_addr, ENABLE); - periph->status = I2CStopRequested; - } - else if (event & I2C_FLAG_RXNE) { // should really be BTF ? - uint8_t read_byte = I2C_ReceiveData(periph->reg_addr); - trans->buf[periph->idx_buf] = read_byte; - periph->status = I2CStopRequested; - } - else - SPURIOUS_INTERRUPT(periph, I2CReadingLastByte, event); -} - -/* - * Restart requested - * - */ -static inline void on_status_restart_requested(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event) { - if (event & I2C_FLAG_SB) { - I2C_Send7bitAddress(periph->reg_addr, trans->slave_addr, I2C_Direction_Receiver); + // Document the current Status periph->status = I2CAddrRdSent; } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_ADDR, SR1) ) + { + // First Clear the ACK bit: after the next byte we do not want new bytes + I2C_CR1(i2c) &= ~ I2C_CR1_POS; + I2C_CR1(i2c) &= ~ I2C_CR1_ACK; + + // --- next to steps MUST be executed together to avoid missing the stop + __I2C_REG_CRITICAL_ZONE_START; + + // Only after setting ACK, read SR2 to clear the ADDR (next byte will start arriving) + uint16_t SR2 __attribute__ ((unused)) = I2C_SR2(i2c); + + // Schedule a Stop + PPRZ_I2C_SEND_STOP(i2c); + + __I2C_REG_CRITICAL_ZONE_STOP; + // --- end of critical zone ----------- + + // Enable the RXNE: it will trigger as soon as the 1 byte is received to get the result + I2C_CR2(i2c) |= I2C_CR2_ITBUFEN; + + // Document the current Status + periph->status = I2CReadingLastByte; + } + // As soon as there is 1 byte ready to read, we have our byte + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_RxNE, SR1) ) + { + I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; + trans->buf[0] = I2C_DR(i2c); + + // We got all the results (stop condition might still be in progress but this is the last interrupt) + trans->status = I2CTransSuccess; + + // Document the current Status: + // -the stop was actually already requested in the previous step + periph->status = I2CStopRequested; + + return STMI2C_SubTra_Ready_StopRequested; + } + else // Event Logic Error + { + return STMI2C_SubTra_Error; + } + + return STMI2C_SubTra_Busy; +} + +// Doc ID 13902 Rev 11 p 713/1072 +// Transfer Sequence Diagram for Master Receiver for N=2 +static inline enum STMI2CSubTransactionStatus stmi2c_read2(u32 i2c, struct i2c_periph *periph, struct i2c_transaction *trans) +{ + uint16_t SR1 = I2C_SR1(i2c); + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_SB, SR1 ) ) + { + // according to the datasheet: instantly shedule a NAK on the second received byte: + I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; + I2C_CR1(i2c) |= I2C_CR1_ACK; + I2C_CR1(i2c) |= I2C_CR1_POS; + I2C_DR(i2c) = trans->slave_addr | 0x01; + + // Document the current Status + periph->status = I2CAddrRdSent; + } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_ADDR, SR1) ) + { + // --- make absolutely sure this command is not delayed too much after the previous: + // --- the NAK bits must be set before the first byte arrived: allow other interrupts here + __I2C_REG_CRITICAL_ZONE_START; + + // if transfer of DR was finished already then we will get too many bytes + // BEFORE clearing ACK, read SR2 to clear the ADDR (next byte will start arriving) + // clearing ACK after the byte transfer has already started will NACK the next (2nd) + uint16_t SR2 __attribute__ ((unused)) = I2C_SR2(i2c); + + // NOT First Clear the ACK bit but only AFTER clearing ADDR + I2C_CR1(i2c) &= ~ I2C_CR1_ACK; + + // Disable the RXNE and wait for BTF + I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; + + __I2C_REG_CRITICAL_ZONE_STOP; + // --- end of critical zone ----------- + + // We do not set the RxE but wait for both bytes to arrive using BTF + + // Document the current Status + periph->status = I2CReadingByte; + } + // Receive buffer if full, master is halted: BTF + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BTF, SR1) ) + { + // Stop condition MUST be set BEFORE reading the DR + // otherwise since there is new buffer space a new byte will be read + PPRZ_I2C_SEND_STOP(i2c); + + // Document the current Status + periph->status = I2CStopRequested; + + trans->buf[0] = I2C_DR(i2c); + trans->buf[1] = I2C_DR(i2c); + + // We got all the results + trans->status = I2CTransSuccess; + + return STMI2C_SubTra_Ready_StopRequested; + } + else // Event Logic Error + { + return STMI2C_SubTra_Error; + } + + return STMI2C_SubTra_Busy; +} + +// Doc ID 13902 Rev 11 p 712/1072 +// Transfer Sequence Diagram for Master Receiver for N>2 +static inline enum STMI2CSubTransactionStatus stmi2c_readmany(u32 i2c, struct i2c_periph *periph, struct i2c_transaction *trans) +{ + uint16_t SR1 = I2C_SR1(i2c); + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_SB, SR1 ) ) + { + I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; + // The first data byte will be acked in read many so the slave knows it should send more + I2C_CR1(i2c) &= ~ I2C_CR1_POS; + I2C_CR1(i2c) |= I2C_CR1_ACK; + // Clear the SB flag + I2C_DR(i2c) = trans->slave_addr | 0x01; + + // Document the current Status + periph->status = I2CAddrRdSent; + } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_ADDR, SR1) ) + { + periph->idx_buf = 0; + + // Enable RXNE: receive an interrupt any time a byte is available + // only enable if MORE than 3 bytes need to be read + if (periph->idx_buf < (trans->len_r - 3)) + { + I2C_CR2(i2c) |= I2C_CR2_ITBUFEN; + } + + // ACK is still on to get more DATA + // Read SR2 to clear the ADDR (next byte will start arriving) + uint16_t SR2 __attribute__ ((unused)) = I2C_SR2(i2c); + + // Document the current Status + periph->status = I2CReadingByte; + } + // one or more bytes are available AND we were interested in Buffer interrupts + else if ( (BIT_X_IS_SET_IN_REG(I2C_SR1_RxNE, SR1) ) && (BIT_X_IS_SET_IN_REG(I2C_CR2_ITBUFEN, I2C_CR2(i2c))) ) + { + // read byte until 3 bytes remain to be read (e.g. len_r = 6, -> idx=3 means idx 3,4,5 = 3 remain to be read + if (periph->idx_buf < (trans->len_r - 3)) + { + trans->buf[periph->idx_buf] = I2C_DR(i2c); + periph->idx_buf ++; + } + // from : 3bytes -> last byte: do nothing + // + // finally: this was the last byte + else if (periph->idx_buf >= (trans->len_r - 1)) + { + I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; + + // Last Value + trans->buf[periph->idx_buf] = I2C_DR(i2c); + periph->idx_buf ++; + + // We got all the results + trans->status = I2CTransSuccess; + + return STMI2C_SubTra_Ready_StopRequested; + } + + // Check for end of transaction: start waiting for BTF instead of RXNE + if (periph->idx_buf < (trans->len_r - 3)) + { + I2C_CR2(i2c) |= I2C_CR2_ITBUFEN; + } + else // idx >= len-3: there are 3 bytes to be read + { + // We want to halt I2C to have sufficient time to clear ACK, so: + // Stop listening to RXNE as it will be triggered infinitely since we did not empty the buffer + // on the next (second in buffer) received byte BTF will be set (buffer full and I2C halted) + I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; + } + } + // Buffer is full while this was not a RXNE interrupt + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BTF, SR1) ) + { + // Now the shift register and data register contain data(n-2) and data(n-1) + // And I2C is halted so we have time + + // --- Make absolutely sure the next 2 I2C actions are performed with no delay + __I2C_REG_CRITICAL_ZONE_START; + + // First we clear the ACK while the SCL is held low by BTF + I2C_CR1(i2c) &= ~ I2C_CR1_ACK; + + // Now that ACK is cleared we read one byte: instantly the last byte is being clocked in... + trans->buf[periph->idx_buf] = I2C_DR(i2c); + periph->idx_buf ++; + + // Now the last byte is being clocked. Stop in MUST be set BEFORE the transfer of the last byte is complete + PPRZ_I2C_SEND_STOP(i2c); + + __I2C_REG_CRITICAL_ZONE_STOP; + // --- end of critical zone ----------- + + // Document the current Status + periph->status = I2CStopRequested; + + // read the byte2 we had in the buffer (BTF means 2 bytes available) + trans->buf[periph->idx_buf] = I2C_DR(i2c); + periph->idx_buf ++; + + // Ask for an interrupt to read the last byte (which is normally still busy now) + // The last byte will be received with RXNE + I2C_CR2(i2c) |= I2C_CR2_ITBUFEN; + } + else // Event Logic Error + { + return STMI2C_SubTra_Error; + } + + return STMI2C_SubTra_Busy; +} + +//////////////////////////////////////////////// +// Restore bus conditions to normal after errors + +static inline void i2c_error(struct i2c_periph *periph) +{ + uint8_t err_nr = 0; + periph->errors->er_irq_cnt; + if ((I2C_SR1((u32)periph->reg_addr) & I2C_SR1_AF) != 0) { /* Acknowledge failure */ + periph->errors->ack_fail_cnt++; + I2C_SR1((u32)periph->reg_addr) &= ~I2C_SR1_AF; + err_nr = 1; + } + if ((I2C_SR1((u32)periph->reg_addr) & I2C_SR1_BERR) != 0) { /* Misplaced Start or Stop condition */ + periph->errors->miss_start_stop_cnt++; + I2C_SR1((u32)periph->reg_addr) &= ~I2C_SR1_BERR; + err_nr = 2; + } + if ((I2C_SR1((u32)periph->reg_addr) & I2C_SR1_ARLO) != 0) { /* Arbitration lost */ + periph->errors->arb_lost_cnt++; + I2C_SR1((u32)periph->reg_addr) &= ~I2C_SR1_ARLO; + err_nr = 3; + } + if ((I2C_SR1((u32)periph->reg_addr) & I2C_SR1_OVR) != 0) { /* Overrun/Underrun */ + periph->errors->over_under_cnt++; + I2C_SR1((u32)periph->reg_addr) &= ~I2C_SR1_OVR; + err_nr = 4; + } + if ((I2C_SR1((u32)periph->reg_addr) & I2C_SR1_PECERR) != 0) { /* PEC Error in reception */ + periph->errors->pec_recep_cnt++; + I2C_SR1((u32)periph->reg_addr) &= ~I2C_SR1_PECERR; + err_nr = 5; + } + if ((I2C_SR1((u32)periph->reg_addr) & I2C_SR1_TIMEOUT) != 0) { /* Timeout or Tlow error */ + periph->errors->timeout_tlow_cnt++; + I2C_SR1((u32)periph->reg_addr) &= ~I2C_SR1_TIMEOUT; + err_nr = 6; + } + if ((I2C_SR1((u32)periph->reg_addr) & I2C_SR1_SMBALERT) != 0) { /* SMBus alert */ + periph->errors->smbus_alert_cnt++; + I2C_SR1((u32)periph->reg_addr) &= ~I2C_SR1_SMBALERT; + err_nr = 7; + } + +#ifdef I2C_DEBUG_LED + LED_ERROR(20, err_nr); +#endif + + return; } - -static inline void i2c_event(struct i2c_periph *p, uint32_t event) +static inline void stmi2c_clear_pending_interrupts(u32 i2c) { - struct i2c_transaction* trans = p->trans[p->trans_extract_idx]; - switch (p->status) { - case I2CStartRequested: - on_status_start_requested(p, trans, event); - break; - case I2CAddrWrSent: - on_status_addr_wr_sent(p, trans, event); - break; - case I2CSendingByte: - on_status_sending_byte(p, trans, event); - break; - case I2CStopRequested: - on_status_stop_requested(p, trans, event); - break; - case I2CAddrRdSent: - on_status_addr_rd_sent(p, trans, event); - break; - case I2CReadingByte: - on_status_reading_byte(p, trans, event); - break; - case I2CReadingLastByte: - on_status_reading_last_byte(p, trans, event); - break; - case I2CRestartRequested: - on_status_restart_requested(p, trans, event); - break; - default: - OUT_OF_SYNC_STATE_MACHINE(p, p->status, event); - break; - } -} + uint16_t SR1 = I2C_SR1(i2c); -static inline void i2c_error(struct i2c_periph *p) -{ - p->errors->er_irq_cnt; - if (I2C_GetITStatus(p->reg_addr, I2C_IT_AF)) { /* Acknowledge failure */ - p->errors->ack_fail_cnt++; - I2C_ClearITPendingBit(p->reg_addr, I2C_IT_AF); + // Certainly do not wait for buffer interrupts: + // ------------------------------------------- + I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; // Disable TXE, RXNE + + // Error interrupts are handled separately: + // --------------------------------------- + + // Clear Event interrupt conditions: + // -------------------------------- + + // Start Condition Was Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_SB, SR1 ) ) + { + // SB: cleared by software when reading SR1 and writing to DR + I2C_DR(i2c) = 0x00; } - if (I2C_GetITStatus(p->reg_addr, I2C_IT_BERR)) { /* Misplaced Start or Stop condition */ - p->errors->miss_start_stop_cnt++; - I2C_ClearITPendingBit(p->reg_addr, I2C_IT_BERR); + // Address Was Sent + if (BIT_X_IS_SET_IN_REG(I2C_SR1_ADDR, SR1) ) + { + // ADDR: Cleared by software when reading SR1 and then SR2 + uint16_t SR2 __attribute__ ((unused)) = I2C_SR2(i2c); } - if (I2C_GetITStatus(p->reg_addr, I2C_IT_ARLO)) { /* Arbitration lost */ - p->errors->arb_lost_cnt++; - I2C_ClearITPendingBit(p->reg_addr, I2C_IT_ARLO); - // I2C_AcknowledgeConfig(I2C2, DISABLE); - // uint8_t dummy __attribute__ ((unused)) = I2C_ReceiveData(I2C2); - // I2C_GenerateSTOP(I2C2, ENABLE); - } - if (I2C_GetITStatus(p->reg_addr, I2C_IT_OVR)) { /* Overrun/Underrun */ - p->errors->over_under_cnt++; - I2C_ClearITPendingBit(p->reg_addr, I2C_IT_OVR); - } - if (I2C_GetITStatus(p->reg_addr, I2C_IT_PECERR)) { /* PEC Error in reception */ - p->errors->pec_recep_cnt++; - I2C_ClearITPendingBit(p->reg_addr, I2C_IT_PECERR); - } - if (I2C_GetITStatus(p->reg_addr, I2C_IT_TIMEOUT)) { /* Timeout or Tlow error */ - p->errors->timeout_tlow_cnt++; - I2C_ClearITPendingBit(p->reg_addr, I2C_IT_TIMEOUT); - } - if (I2C_GetITStatus(p->reg_addr, I2C_IT_SMBALERT)) { /* SMBus alert */ - p->errors->smbus_alert_cnt++; - I2C_ClearITPendingBit(p->reg_addr, I2C_IT_SMBALERT); + // Byte Transfer Finished + if (BIT_X_IS_SET_IN_REG(I2C_SR1_BTF, SR1) ) + { + // SB: cleared by software when reading SR1 and reading/writing to DR + uint8_t dummy __attribute__ ((unused)) = I2C_DR(i2c); + I2C_DR(i2c) = 0x00; } - abort_and_reset(p); } -static inline void i2c_hard_reset(struct i2c_periph *p) +//////////////////////////////////////////////// +// Restore bus conditions to normal after errors + +static inline void i2c_irq(struct i2c_periph *periph) { - I2C_TypeDef *regs = (I2C_TypeDef *) p->reg_addr; - I2C_DeInit(p->reg_addr); + /* + There are 7 possible event reasons to get here + all errors - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_InitStructure.GPIO_Pin = p->scl_pin | p->sda_pin; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; - GPIO_SetBits(GPIOB, p->scl_pin | p->sda_pin); - GPIO_Init(GPIOB, &GPIO_InitStructure); + If IT_EV_FEN + ------------------------- - while(GPIO_ReadInputDataBit(GPIOB, p->sda_pin) == Bit_RESET) { - // Raise SCL, wait until SCL is high (in case of clock stretching) - GPIO_SetBits(GPIOB, p->scl_pin); - while (GPIO_ReadInputDataBit(GPIOB, p->scl_pin) == Bit_RESET); - i2c_delay(); + We are always interested in all IT_EV_FEV: all are required. - // Lower SCL, wait - GPIO_ResetBits(GPIOB, p->scl_pin); - i2c_delay(); + 1) SB // Start Condition Success in Master mode + 2) ADDR // Address sent received Acknoledge + [ADDR10] // -- 10bit address stuff: not used + [STOPF] // -- only for slaves: master has no stop interrupt: not used + 3) BTF // I2C has stopped working (it is waiting for new data, all buffers are tx_empty/rx_full) - // Raise SCL, wait - GPIO_SetBits(GPIOB, p->scl_pin); - i2c_delay(); + // Beware: using the buffered I2C has some interesting properties: + -in master receive mode: BTF only occurs after the 2nd received byte: after the first byte is received it is + in RD but the I2C can still receive a second byte. Only when the 2nd byte is received while the RxNE is 1 + then a BTF occurs (I2C can not continue receiving bytes or they will get lost). During BTF I2C is halted (SCL held low) + -in master transmit mode: when writing a byte to WD, you instantly get a new TxE interrupt while the first is not + transmitted yet. The byte was pushed to the I2C shift register and the buffer is ready for more. You can already + fill new data in the buffer while the first is still being transmitted for max performance transmission. + + // Beware: besides data buffering you can/must plan several consecutive actions. You can send 2 bytes to the buffer, ask for a stop and + a new start in one go. + + -thanks to / because of this buffering and event sheduling there is not 1 interrupt per start / byte / stop + This also means you must think more in advance and a transaction could be popped from the transaction stack even before it's + stop condition is actually generated. + + // Beware: the order in which Status (and other register) is read determines how flags are cleared. + You should NOT simply read SR1 & SR2 every time + + If IT_EV_FEN AND IT_EV_BUF + -------------------------- + + Buffer event are not always wanted and are typically switched on during longer data transfers. Make sure to turn off in time. + + 4) RxNE + 5) TxE + + -------------------------------------------------------------------------------------------------- + + The STM waits indefinately (holding SCL low) for user interaction: + a) after a master-start (waiting for address) + b) after an address (waiting for data) + not during data sending when using buffered + c) after the last byte is transmitted (waiting for either stop or restart) + not during data receiving when using buffered + not after the last byte is received + + -The STM I2C stalls indefinately when a stop condition was attempted that + did not succeed. The BUSY flag remains on. + -There is no STOP interrupt. + + Caution Reading the status: + - Caution: this clears several flags and can start transmissions etc... + - Certain flags like STOP / (N)ACK need to be guaranteed to be set before + the transmission of the byte is finished. At higher clock rates that can be + quite fast: so we allow no other interrupt to be triggered in between + reading the status and setting all needed flags + + */ + + // Here we go ... + + // Apparently we got an I2C interrupt: EVT BUF or ERR + +#ifdef I2C_DEBUG_LED + // Notify ISR is triggered + LED1_ON(); + LED1_OFF(); +#endif + + // Save Some Direct Access to the I2C Registers ... + u32 i2c = (u32) periph->reg_addr; + + ///////////////////////////// + // Check if we were ready ... + if (periph->trans_extract_idx == periph->trans_insert_idx) + { + // Nothing Left To Do + +#ifdef I2C_DEBUG_LED + LED2_ON(); + LED1_ON(); + LED2_OFF(); + LED1_OFF(); + + // no transaction and also an error? + LED_SHOW_ACTIVE_BITS(regs); +#endif + + // If we still get an interrupt but there are no more things to do + // (which can happen if an event was sheduled just before a bus error occurs) + // (or can happen if both error and event interrupts were called together [the 2nd will then get this error]) + + // since there is nothing more to do: its easy: just stop: clear all interrupt generating bits + + // Count The Errors + i2c_error(periph); + + // Clear Running Events + stmi2c_clear_pending_interrupts(i2c); + + // Mark this as a special error + periph->errors->last_unexpected_event++; + + // Document the current Status + periph->status = I2CIdle; + + // There are no transactions anymore: return + // further-on in this routine we need a transaction pointer: so we are not allowed to continue + return; } - // Generate a start condition followed by a stop condition - GPIO_SetBits(GPIOB, p->scl_pin); - i2c_delay(); - GPIO_ResetBits(GPIOB, p->sda_pin); - i2c_delay(); - GPIO_ResetBits(GPIOB, p->sda_pin); - i2c_delay(); + // get the I2C transaction we were working on ... - // Raise both SCL and SDA and wait for SCL high (in case of clock stretching) - GPIO_SetBits(GPIOB, p->scl_pin | p->sda_pin); - while (GPIO_ReadInputDataBit(GPIOB, p->scl_pin) == Bit_RESET); + enum STMI2CSubTransactionStatus ret = 0; + struct i2c_transaction* trans = periph->trans[periph->trans_extract_idx]; - // Wait for SDA to be high - while (GPIO_ReadInputDataBit(GPIOB, p->sda_pin) != Bit_SET); + /////////////////////////// + // If there was an error: + if (( I2C_SR1(i2c) & I2C_SR1_ERR_MASK ) != 0x0000) + { - // SCL and SDA should be high at this point, bus should be free - // Return the GPIO pins to the alternate function - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; - GPIO_Init(GPIOB, &GPIO_InitStructure); +#ifdef I2C_DEBUG_LED + LED1_ON(); + LED2_ON(); + LED1_OFF(); + LED2_OFF(); - I2C_DeInit(p->reg_addr); + LED_SHOW_ACTIVE_BITS(regs); +#endif - i2c_apply_config(p); + // Notify everyone about the error ... - if (regs->SR2 & I2C_BUSY) { + // Set result in transaction + trans->status = I2CTransFailed; + + // Document the current Status + periph->status = I2CFailed; + + // Make sure a TxRx does not Restart + trans->type = I2CTransRx; + + // Count The Errors + i2c_error(periph); + + // Clear Running Events + stmi2c_clear_pending_interrupts(i2c); + + // Now continue as if everything was normal from now on + ret = STMI2C_SubTra_Ready; + + } + + /////////////////////////// + // Normal Event: + else + { + + /////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // SUB-TRANSACTION HANDLER + + if (trans->type == I2CTransRx) // TxRx are converted to Rx after the Tx Part + { + switch (trans->len_r) + { + case 1: + ret = stmi2c_read1(i2c,periph,trans); + break; + case 2: + ret = stmi2c_read2(i2c,periph,trans); + break; + default: + ret = stmi2c_readmany(i2c,periph,trans); + break; + } + } + else // TxRx or Tx + { + ret = stmi2c_send(i2c,periph,trans); + } + } + + ///////////////////////////////// + // Sub-transaction has finished + if (ret != STMI2C_SubTra_Busy) + { + // Ready or SubTraError + // -ready: with or without stop already asked + + // In case of unexpected event condition during subtransaction handling: + if (ret == STMI2C_SubTra_Error) + { + // Tell everyone about the subtransaction error: + // this is the previously called SPURRIOUS INTERRUPT + periph->status = I2CFailed; + trans->type = I2CTransRx; // Avoid possible restart + trans->status = I2CTransFailed; // Notify Ready + periph->errors->unexpected_event_cnt++; + + // Error +#ifdef I2C_DEBUG_LED + LED2_ON(); + LED1_ON(); + LED2_OFF(); + LED1_OFF(); + + LED_SHOW_ACTIVE_BITS(regs); +#endif + + // Clear Running Events + stmi2c_clear_pending_interrupts(i2c); + } + + // RxTx -> Restart and do Rx part + if (trans->type == I2CTransTxRx) + { + trans->type = I2CTransRx; + periph->status = I2CStartRequested; + I2C_CR1(i2c) |= I2C_CR1_START; + + // Silent any BTF that would occur before SB + I2C_DR(i2c) = 0x00; + } + // If a restart is not needed: Rx part or Tx-only + else + { + // Ready, no stop condition set yet + if (ret == STMI2C_SubTra_Ready) + { + + // Program a stop + PPRZ_I2C_SEND_STOP(i2c); + + // Silent any BTF that would occur before STOP is executed + I2C_DR(i2c) = 0x00; + } + + // Jump to the next transaction + periph->trans_extract_idx++; + if (periph->trans_extract_idx >= I2C_TRANSACTION_QUEUE_LEN) + periph->trans_extract_idx = 0; + + // Tell everyone we are ready + periph->status = I2CIdle; + + + // if we have no more transaction to process, stop here + if (periph->trans_extract_idx == periph->trans_insert_idx) + { + +#ifdef I2C_DEBUG_LED + LED2_ON(); + LED1_ON(); + LED1_OFF(); + LED1_ON(); + LED1_OFF(); + LED2_OFF(); +#endif + } + // if not, start next transaction + else + { + // Restart transaction doing the Rx part now +// --- moved to idle function + PPRZ_I2C_SEND_START(periph); +// ------ + } + } + } + + return; +} + + +/* + // Make sure the bus is free before resetting (p722) + if (regs->SR2 & (I2C_FLAG_BUSY >> 16)) { // Reset the I2C block - I2C_SoftwareResetCmd(p->reg_addr, ENABLE); - I2C_SoftwareResetCmd(p->reg_addr, DISABLE); + I2C_SoftwareResetCmd(periph->reg_addr, ENABLE); + I2C_SoftwareResetCmd(periph->reg_addr, DISABLE); } -} +*/ -static inline void i2c_reset_init(struct i2c_periph *p) -{ - // Reset bus and configure GPIO pins - i2c_hard_reset(p); - - // enable peripheral - I2C_Cmd(p->reg_addr, ENABLE); - - // enable error interrupts - I2C_ITConfig(p->reg_addr, I2C_IT_ERR, ENABLE); -} -#endif /* USE_I2C2 */ #ifdef USE_I2C1 struct i2c_errors i2c1_errors; - -#include "my_debug_servo.h" +volatile uint32_t i2c1_watchdog_counter; void i2c1_hw_init(void) { - i2c1.reg_addr = I2C1; - i2c1.init_struct = &I2C1_InitStruct; - i2c1.scl_pin = GPIO_Pin_6; - i2c1.sda_pin = GPIO_Pin_7; + i2c1.reg_addr = (void *)I2C1; + i2c1.init_struct = NULL; + i2c1.scl_pin = GPIO_I2C1_SCL; + i2c1.sda_pin = GPIO_I2C1_SDA; i2c1.errors = &i2c1_errors; + i2c1_watchdog_counter = 0; /* zeros error counter */ ZEROS_ERR_COUNTER(i2c1_errors); + // Extra +#ifdef I2C_DEBUG_LED + LED_INIT(); +#else + /* reset peripheral to default state ( sometimes not achieved on reset :( ) */ - I2C_DeInit(I2C1); + //i2c_reset(I2C1); - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); - NVIC_InitTypeDef NVIC_InitStructure; + /* Configure priority grouping 0 bits for pre-emption priority and 4 bits for sub-priority. */ + scb_set_priority_grouping(SCB_AIRCR_PRIGROUP_NOGROUP_SUB16); - /* Configure and enable I2C1 event interrupt -------------------------------*/ - NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); + /* Configure and enable I2C1 event interrupt --------------------------------*/ + nvic_set_priority(NVIC_I2C1_EV_IRQ, 0); + nvic_enable_irq(NVIC_I2C1_EV_IRQ); - /* Configure and enable I2C1 err interrupt -------------------------------*/ - NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); + /* Configure and enable I2C1 err interrupt ----------------------------------*/ + nvic_set_priority(NVIC_I2C1_ER_IRQ, 1); + nvic_enable_irq(NVIC_I2C1_ER_IRQ); - /* Enable peripheral clocks --------------------------------------------------*/ + /* Enable peripheral clocks -------------------------------------------------*/ /* Enable I2C1 clock */ - RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); + rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_I2C1EN); /* Enable GPIOB clock */ - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); + rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPBEN); - /* Configure I2C1 pins: SCL and SDA ------------------------------------------*/ - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_StructInit(&GPIO_InitStructure); - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; - GPIO_Init(GPIOB, &GPIO_InitStructure); + gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ, + GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, + i2c1.scl_pin | i2c1.sda_pin); - /* I2C configuration ----------------------------------------------------------*/ + i2c_reset(I2C1); + // enable peripheral + i2c_peripheral_enable(I2C1); - /* Reset and initialize I2C HW */ - i2c_reset_init(&i2c1); + /* + * XXX: there is a function to do that already in libopencm3 but I am not + * sure if it is correct, using direct register instead (esden) + */ + //i2c_set_own_7bit_slave_address(I2C1, 0); + I2C_OAR1(I2C1) = 0 | 0x4000; + // enable error interrupts + I2C_CR2(I2C1) |= I2C_CR2_ITERREN; + + i2c_setbitrate(&i2c2, I2C2_CLOCK_SPEED); +#endif } - -void i2c1_ev_irq_handler(void) { - - uint32_t event = I2C_GetLastEvent(I2C1); - i2c_event(&i2c1, event); - +void i2c1_ev_isr(void) { + u32 i2c = (u32) i2c1.reg_addr; + I2C_CR2(i2c) &= ~I2C_CR2_ITERREN; + i2c_irq(&i2c1); + i2c1_watchdog_counter = 0; + I2C_CR2(i2c) |= I2C_CR2_ITERREN; } - -void i2c1_er_irq_handler(void) { - i2c_error(&i2c1); +void i2c1_er_isr(void) { + u32 i2c = (u32) i2c1.reg_addr; + I2C_CR2(i2c) &= ~I2C_CR2_ITEVTEN; + i2c_irq(&i2c1); + i2c1_watchdog_counter = 0; + I2C_CR2(i2c) |= I2C_CR2_ITEVTEN; } #endif /* USE_I2C1 */ - - - - #ifdef USE_I2C2 -// dec hex -// 196609 30001 BUSY MSL | SB -// 458882 70082 TRA BUSY MSL | TXE ADDR -// 458884 70084 TRA BUSY MSL | TXE BTF -// 196609 30001 BUSY MSL | SB -// 196610 30002 BUSY MSL | ADDR -// - - struct i2c_errors i2c2_errors; - -#include "my_debug_servo.h" +volatile uint32_t i2c2_watchdog_counter; void i2c2_hw_init(void) { - i2c2.reg_addr = I2C2; - i2c2.init_struct = &I2C2_InitStruct; - i2c2.scl_pin = GPIO_Pin_10; - i2c2.sda_pin = GPIO_Pin_11; + i2c2.reg_addr = (void *)I2C2; + i2c2.init_struct = NULL; + i2c2.scl_pin = GPIO_I2C2_SCL; + i2c2.sda_pin = GPIO_I2C2_SDA; i2c2.errors = &i2c2_errors; + i2c2_watchdog_counter = 0; /* zeros error counter */ ZEROS_ERR_COUNTER(i2c2_errors); /* reset peripheral to default state ( sometimes not achieved on reset :( ) */ - I2C_DeInit(I2C2); + //i2c_reset(I2C2); - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); - NVIC_InitTypeDef NVIC_InitStructure; + /* Configure priority grouping 0 bits for pre-emption priority and 4 bits for sub-priority. */ + scb_set_priority_grouping(SCB_AIRCR_PRIGROUP_NOGROUP_SUB16); /* Configure and enable I2C2 event interrupt --------------------------------*/ - NVIC_InitStructure.NVIC_IRQChannel = I2C2_EV_IRQn; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); + nvic_set_priority(NVIC_I2C2_EV_IRQ, 0); + nvic_enable_irq(NVIC_I2C2_EV_IRQ); /* Configure and enable I2C2 err interrupt ----------------------------------*/ - NVIC_InitStructure.NVIC_IRQChannel = I2C2_ER_IRQn; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); + nvic_set_priority(NVIC_I2C2_ER_IRQ, 1); + nvic_enable_irq(NVIC_I2C2_ER_IRQ); /* Enable peripheral clocks -------------------------------------------------*/ /* Enable I2C2 clock */ - RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE); + rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_I2C2EN); /* Enable GPIOB clock */ - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); + rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPBEN); - // Reset and initialize I2C HW - i2c_reset_init(&i2c2); + gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ, + GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, + i2c2.scl_pin | i2c2.sda_pin); + i2c_reset(I2C2); + + // enable peripheral + i2c_peripheral_enable(I2C2); + + /* + * XXX: there is a function to do that already in libopencm3 but I am not + * sure if it is correct, using direct register instead (esden) + */ + //i2c_set_own_7bit_slave_address(I2C2, 0); + I2C_OAR1(I2C2) = 0 | 0x4000; + + // enable error interrupts + I2C_CR2(I2C1) |= I2C_CR2_ITERREN; + + i2c_setbitrate(&i2c2, I2C2_CLOCK_SPEED); } - - - -void i2c2_ev_irq_handler(void) { - uint32_t event = I2C_GetLastEvent(I2C2); - i2c_event(&i2c2, event); +void i2c2_ev_isr(void) { + u32 i2c = (u32) i2c2.reg_addr; + I2C_CR2(i2c) &= ~I2C_CR2_ITERREN; + i2c_irq(&i2c2); + i2c2_watchdog_counter = 0; + I2C_CR2(i2c) |= I2C_CR2_ITERREN; } -void i2c2_er_irq_handler(void) { - i2c_error(&i2c2); - +void i2c2_er_isr(void) { + u32 i2c = (u32) i2c2.reg_addr; + I2C_CR2(i2c) &= ~I2C_CR2_ITEVTEN; + i2c_irq(&i2c2); + i2c2_watchdog_counter = 0; + I2C_CR2(i2c) |= I2C_CR2_ITEVTEN; } #endif /* USE_I2C2 */ +////////////////////////////////////////////////// +// Set Bitrate to Match your application: +// -short wires, low capacitance bus: IMU: high speed +// -long wires with a lot of capacitance: motor controller: put speed as low as possible - -bool_t i2c_idle(struct i2c_periph* p) +void i2c_setbitrate(struct i2c_periph *periph, int bitrate) { - return !I2C_GetFlagStatus(p->reg_addr, I2C_FLAG_BUSY); + // If NOT Busy + if (i2c_idle(periph)) + { + volatile int devider; + volatile int risetime; + + u32 i2c = (u32) periph->reg_addr; + +/***************************************************** + Bitrate: + + -CR2 + CCR + TRISE registers + -only change when PE=0 + + e.g. + + 10kHz: 36MHz + Standard 0x708 + 0x25 + 70kHz: 36MHz + Standard 0x101 + + 400kHz: 36MHz + Fast 0x1E + 0xb + + // 1) Program peripheral input clock CR2: to get correct timings + // 2) Configure clock control registers + // 3) Configure rise time register +******************************************************/ + + if (bitrate < 3000) + bitrate = 3000; + + // 36MHz, fast scl: 2counts low 1 count high -> / 3: + devider = 18000 / (bitrate/1000); + + // never allow faster than 600kbps + if (devider < 20) + devider = 20; + + // no overflow either + if (devider >=4095) + devider = 4095; + + // risetime can be up to 1/6th of the period + risetime = 1000000 / (bitrate/1000) / 6 / 28; + + if (risetime < 10) + risetime = 10; + + // more will overflow the register: for more you should lower the FREQ + if (risetime >=31) + risetime = 31; + + // we do not expect an interrupt as the interface should have been idle, but just in case... + __disable_irq(); // this code is in user space: + + // CCR can only be written when PE is disabled + // p731 note 5 + I2C_CR1(i2c) &= ~ I2C_CR1_PE; + + // 1) + I2C_CR2(i2c) = 0x0324; + // 2) + //I2C_CCR(i2c) = 0x8000 + devider; + I2C_CCR(i2c) = 0x0000 + devider; + // 3) + I2C_TRISE(i2c) = risetime; + + // Re-Enable + I2C_CR1(i2c) |= I2C_CR1_PE; + + __enable_irq(); + +#ifdef I2C_DEBUG_LED + __disable_irq(); // this code is in user space: + + LED2_ON(); + LED1_ON(); + LED2_OFF(); + LED1_OFF(); + LED2_ON(); + LED1_ON(); + LED2_OFF(); + LED1_OFF(); + + __enable_irq(); +#endif + + } } -bool_t i2c_submit(struct i2c_periph* p, struct i2c_transaction* t) { + +// TODO: TODO: TODO: +// Watchdog timer +void i2c_event(void) +{ +#ifdef USE_I2C1 + i2c1_watchdog_counter++; +#endif + +#ifdef USE_I2C2 + i2c2_watchdog_counter++; + + if (i2c2_watchdog_counter > 10000) + { + i2c2.errors->timeout_tlow_cnt++; + i2c2_watchdog_counter = 0; + } + + +#ifdef I2C_DEBUG_LED + if (i2c2_watchdog_counter == 0) + { + __disable_irq(); + + LED2_ON(); + LED1_ON(); + LED1_OFF(); + LED1_ON(); + LED1_OFF(); + LED1_ON(); + LED1_OFF(); + LED1_ON(); + LED1_OFF(); + if (i2c2.status == I2CIdle) + { + LED1_ON(); + LED1_OFF(); + } + else if (i2c2.status == I2CStartRequested) + { + LED1_ON(); + LED1_OFF(); + LED1_ON(); + LED1_OFF(); + + } + LED2_OFF(); + + //regs = (I2C_TypeDef *) i2c2.reg_addr; + //LED_SHOW_ACTIVE_BITS(regs); + + __enable_irq(); + } +#endif + + + //if (i2c2.status == I2CIdle) + { + //if (i2c_idle(&i2c2)) + { + //__disable_irq(); + // More work to do + //if (i2c2.trans_extract_idx != i2c2.trans_insert_idx) + { + // Restart transaction doing the Rx part now + //PPRZ_I2C_SEND_START(&i2c2); + } + //__enable_irq(); + } + } +#endif +} + +///////////////////////////////////////////////////////// +// Implement Interface Functions + +bool_t i2c_submit(struct i2c_periph* periph, struct i2c_transaction* t) { uint8_t temp; - temp = p->trans_insert_idx + 1; + temp = periph->trans_insert_idx + 1; if (temp >= I2C_TRANSACTION_QUEUE_LEN) temp = 0; - if (temp == p->trans_extract_idx) + if (temp == periph->trans_extract_idx) return FALSE; // queue full t->status = I2CTransPending; - __disable_irq(); /* put transacation in queue */ - p->trans[p->trans_insert_idx] = t; - p->trans_insert_idx = temp; + periph->trans[periph->trans_insert_idx] = t; + periph->trans_insert_idx = temp; /* if peripheral is idle, start the transaction */ - if (p->status == I2CIdle) - start_transaction(p); + // if (PPRZ_I2C_IS_IDLE(p)) + if (periph->status == I2CIdle) + { + //if (i2c_idle(periph)) + { +#ifdef I2C_DEBUG_LED +#ifdef USE_I2C1 + if (periph == &i2c1) + { + + } + else +#endif +#endif + { +#ifdef I2C_DEBUG_LED + LED2_ON(); + LED2_OFF(); +#endif + PPRZ_I2C_SEND_START(periph); + } + } + } /* else it will be started by the interrupt handler when the previous transactions completes */ __enable_irq(); return TRUE; } +bool_t i2c_idle(struct i2c_periph* periph) +{ + // This is actually a difficult function: + // -simply reading the status flags can clear bits and corrupt the transaction -static void start_transaction(struct i2c_periph* p) { - p->idx_buf = 0; - p->status = I2CStartRequested; - I2C_ITConfig(p->reg_addr, I2C_IT_EVT, ENABLE); - I2C_GenerateSTART(p->reg_addr, ENABLE); + u32 i2c = (u32) periph->reg_addr; + +#ifdef I2C_DEBUG_LED +#ifdef USE_I2C1 + if (periph == &i2c1) + { + return TRUE; + } +#endif +#endif + + // First we check if the software thinks it is ready + if (periph->status == I2CIdle) + return ! (BIT_X_IS_SET_IN_REG( I2C_SR2_BUSY, I2C_SR2(i2c) ) ); + else + return FALSE; } + + diff --git a/sw/airborne/arch/stm32/mcu_periph/i2c_arch.rewritten.c b/sw/airborne/arch/stm32/mcu_periph/i2c_arch.rewritten.c deleted file mode 100644 index 2da4cd09de..0000000000 --- a/sw/airborne/arch/stm32/mcu_periph/i2c_arch.rewritten.c +++ /dev/null @@ -1,1468 +0,0 @@ -#include "mcu_periph/i2c.h" - -#include -#include -#include -#include - -//#define I2C_DEBUG_LED - -/////////// DEBUGGING ////////////// -// TODO: remove this - - -#ifdef I2C_DEBUG_LED - -static inline void LED1_ON(void) -{ - GPIO_WriteBit(GPIOB, GPIO_Pin_6 , Bit_SET ); -} - -static inline void LED1_OFF(void) -{ - GPIO_WriteBit(GPIOB, GPIO_Pin_6 , !Bit_SET ); -} - -static inline void LED2_ON(void) -{ - GPIO_WriteBit(GPIOB, GPIO_Pin_7 , Bit_SET ); -} - -static inline void LED2_OFF(void) -{ - GPIO_WriteBit(GPIOB, GPIO_Pin_7 , !Bit_SET ); -} - -static inline void LED_INIT(void) -{ - GPIO_InitTypeDef GPIO_InitStructure; - RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_Init(GPIOB, &GPIO_InitStructure); - - LED1_OFF(); - LED2_OFF(); -} - - -static inline void LED_ERROR(uint8_t base, uint8_t nr) -{ - LED2_ON(); - for (int i=0;i<(base+nr);i++) - { - LED1_ON(); - LED1_OFF(); - } - LED2_OFF(); -} -#endif - -////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////// - -/* Not used in libopencm3 implementation. Keeping commented for reference. */ -#if 0 -#ifdef USE_I2C1 -static I2C_InitTypeDef I2C1_InitStruct = { - .I2C_Mode = I2C_Mode_I2C, - .I2C_DutyCycle = I2C_DutyCycle_2, - .I2C_OwnAddress1 = 0x00, - .I2C_Ack = I2C_Ack_Enable, - .I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit, - .I2C_ClockSpeed = 40000 -}; -#endif - -#ifdef USE_I2C2 -static I2C_InitTypeDef I2C2_InitStruct = { - .I2C_Mode = I2C_Mode_I2C, - .I2C_DutyCycle = I2C_DutyCycle_2, - .I2C_OwnAddress1 = 0x00, - .I2C_Ack = I2C_Ack_Enable, - .I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit, -// .I2C_ClockSpeed = 37500 // Like on LPC - .I2C_ClockSpeed = 300000 -}; -#endif -#endif - -#define I2C1_CLOCK_SPEED 40000 -#define I2C2_CLOCK_SPEED 30000 - -////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////// - -// Bypassing the libSTM I2C functions to have more control over the reading of registers -// e.g. SR1 and SR2 should not always be read together as it might unwantedly clear ADDR flags etc. - -// Referring to STM32 manual: -// -Doc ID 13902 Rev 11 - -/* This macros are defined in libopencm3 already. Keeping commented for reference. */ - -#if 0 -// Status Register 1 - -#define I2C_SR1_BIT_SB (1<<0) // Start Condition Met -#define I2C_SR1_BIT_ADDR (1<<1) // Address Sent -#define I2C_SR1_BIT_BTF (1<<2) // SCL held low -#define I2C_SR1_BIT_RXNE (1<<6) // Data Read Available -#define I2C_SR1_BIT_TXE (1<<7) // TX buffer space available - -#define I2C_SR1_BIT_ERR_BUS (1<<8) // Misplaced Start/Stop (usually interference) -#define I2C_SR1_BIT_ERR_ARLO (1<<9) // Arbitration Lost (in multimaster) or SDA short-to-ground (in single master) -#define I2C_SR1_BIT_ERR_AF (1<<10) // Ack Failure (too fast/too soon/no sensor/wiring break/...) -#define I2C_SR1_BIT_ERR_OVR (1<<11) // Overrun [data loss] (in slave) or SCL short-to-ground (in single master) - -#define I2C_SR1_BITS_ERR ((1<<8)|(1<<9)|(1<<10)|(1<<11)|(1<<12)|(1<<14)|(1<<15)) - -// Status Register 2 - -#define I2C_SR2_BIT_TRA (1<<2) // Transmitting -#define I2C_SR2_BIT_BUSY (1<<1) // Busy -#define I2C_SR2_BIT_MSL (1<<0) // Master Selected - -// Control Register 1 - -#define I2C_CR1_BIT_PE (1<<0) // Peripheral Enable -#define I2C_CR1_BIT_START (1<<8) // Generate a Start -#define I2C_CR1_BIT_STOP (1<<9) // Generate a Stop -#define I2C_CR1_BIT_ACK (1<<10) // ACK / NACK -#define I2C_CR1_BIT_POS (1<<11) // Ack will control not the next but secondnext received byte -#define I2C_CR1_BIT_SWRST (1<<15) // Clear Busy Condition when no stop was detected - -// Control Register 2 - -#define I2C_CR2_BIT_ITERREN (1<<8) // Error Interrupt -#define I2C_CR2_BIT_ITEVTEN (1<<9) // Event Interrupt -#define I2C_CR2_BIT_ITBUFEN (1<<10) // Buffer Interrupt - -#endif - -// Error bit mask -// XXX: consider moving this define into libopencm3 -#define I2C_SR1_ERR_MASK (I2C_SR1_SMBALERT | \ - I2C_SR1_TIMEOUT | \ - I2C_SR1_PECERR | \ - I2C_SR1_OVR | \ - I2C_SR1_AF | \ - I2C_SR1_ARLO | \ - I2C_SR1_BERR) - -// Bit Control - -#define BIT_X_IS_SET_IN_REG(X,REG) (((REG) & (X)) == (X)) - -// disable and enable irq functions are not implemented in libopencm3 defining them here -// XXX: consider moving this definitions into libopencm3 -static inline void __disable_irq(void) { asm volatile ("cpsid i"); } -static inline void __enable_irq(void) { asm volatile ("cpsie i"); } - -// Critical Zones - -#define __I2C_REG_CRITICAL_ZONE_START __disable_irq(); -#define __I2C_REG_CRITICAL_ZONE_STOP __enable_irq(); - - -////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////// - -// TODO: remove debug -#ifdef I2C_DEBUG_LED - -static inline void LED_SHOW_ACTIVE_BITS(I2C_TypeDef *regs) -{ - uint16_t CR1 = regs->CR1; - uint16_t SR1 = regs->SR1; - uint16_t SR2 = regs->SR2; - // Note: reading SR1 and then SR2 will clear ADDR bits - - LED1_ON(); - - // 1 Start - if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) - LED2_ON(); - else - LED2_OFF(); - LED2_OFF(); - - // 2 Addr - if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_ADDR, SR1 ) ) - LED2_ON(); - else - LED2_OFF(); - LED2_OFF(); - - // 3 BTF - if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_BTF, SR1 ) ) - LED2_ON(); - else - LED2_OFF(); - LED2_OFF(); - - // 4 ERROR - if (( SR1 & I2C_SR1_BITS_ERR ) != 0x0000) - LED2_ON(); - else - LED2_OFF(); - LED2_OFF(); - - // Anything? - if (( SR1 + SR2) != 0x0000) - LED2_ON(); - else - LED2_OFF(); - LED2_OFF(); - - LED1_OFF(); - - - LED1_ON(); - - // 1 Start - if (BIT_X_IS_SET_IN_REG( I2C_CR1_BIT_START, CR1 ) ) - LED2_ON(); - else - LED2_OFF(); - LED2_OFF(); - - // 2 Stop - if (BIT_X_IS_SET_IN_REG( I2C_CR1_BIT_STOP, CR1 ) ) - LED2_ON(); - else - LED2_OFF(); - LED2_OFF(); - - // 3 Busy - if (BIT_X_IS_SET_IN_REG( I2C_SR2_BIT_BUSY, SR2 ) ) - LED2_ON(); - else - LED2_OFF(); - LED2_OFF(); - - // 4 Tra - if (BIT_X_IS_SET_IN_REG( I2C_SR2_BIT_TRA, SR2 ) ) - LED2_ON(); - else - LED2_OFF(); - LED2_OFF(); - - // 5 Master - if (BIT_X_IS_SET_IN_REG( I2C_SR2_BIT_MSL, SR2 ) ) - LED2_ON(); - else - LED2_OFF(); - LED2_OFF(); - LED1_OFF(); - -//#define I2C_DEBUG_LED_CONTROL -#ifdef I2C_DEBUG_LED_CONTROL - - - LED1_ON(); - - // 1 Anything CR? - if (( CR1) != 0x0000) - LED2_ON(); - else - LED2_OFF(); - LED2_OFF(); - - // 2 PE - if (BIT_X_IS_SET_IN_REG( I2C_CR1_BIT_PE, CR1 ) ) - LED2_ON(); - else - LED2_OFF(); - LED2_OFF(); - - // 3 SWRESET - if (BIT_X_IS_SET_IN_REG( I2C_CR1_BIT_SWRST, CR1 ) ) - LED2_ON(); - else - LED2_OFF(); - LED2_OFF(); - - LED1_OFF(); -#endif - -} -#endif - -static inline void PPRZ_I2C_SEND_STOP(u32 i2c) -{ - // Man: p722: Stop generation after the current byte transfer or after the current Start condition is sent. - I2C_CR1(i2c) |= I2C_CR1_STOP; - -#ifdef I2C_DEBUG_LED - LED2_ON(); - LED1_ON(); - LED1_OFF(); - LED2_OFF(); -#endif -} - -// (RE)START - -static inline void PPRZ_I2C_SEND_START(struct i2c_periph *periph) -{ - u32 i2c = (u32) periph->reg_addr; - - // Reset the buffer pointer to the first byte - periph->idx_buf = 0; - -#ifdef I2C_DEBUG_LED - LED_SHOW_ACTIVE_BITS(regs); - - LED2_ON(); - LED1_ON(); - LED1_OFF(); - LED1_ON(); - LED1_OFF(); - LED1_ON(); - LED1_OFF(); - LED2_OFF(); - -#endif - - // Enable Error IRQ, Event IRQ but disable Buffer IRQ - I2C_CR2(i2c) |= I2C_CR2_ITERREN; - I2C_CR2(i2c) |= I2C_CR2_ITEVTEN; - I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; - - // Issue a new start - I2C_CR1(i2c) = (I2C_CR1_START | I2C_CR1_PE); - periph->status = I2CStartRequested; - -} - -// STOP - -/////////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// SUBTRANSACTION SEQUENCES -// -We arrive here every time a ISR is called with no error - -enum STMI2CSubTransactionStatus { - STMI2C_SubTra_Busy, - STMI2C_SubTra_Ready_StopRequested, - STMI2C_SubTra_Ready, - STMI2C_SubTra_Error -}; - -// Doc ID 13902 Rev 11 p 710/1072 -// Transfer Sequence Diagram for Master Transmitter -static inline enum STMI2CSubTransactionStatus stmi2c_send(u32 i2c, struct i2c_periph *periph, struct i2c_transaction *trans) -{ - uint16_t SR1 = I2C_SR1(i2c); - - // Start Condition Was Just Generated - if (BIT_X_IS_SET_IN_REG( I2C_SR1_SB, SR1 ) ) - { - // Disable buffer interrupt - I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; - // Send Slave address and wait for ADDR interrupt - I2C_DR(i2c) = trans->slave_addr; - // Document the current Status - periph->status = I2CAddrWrSent; - } - // Address Was Sent - else if (BIT_X_IS_SET_IN_REG(I2C_SR1_ADDR, SR1) ) - { - // Now read SR2 to clear the ADDR status Bit - uint16_t SR2 __attribute__ ((unused)) = I2C_SR2(i2c); - - // Maybe check we are transmitting (did not loose arbitration for instance) - // if (! BIT_X_IS_SET_IN_REG(I2C_SR2_TRA, SR2)) { } - // update: this should be caught by the ARLO error: so we will not arrive here - - // Send First max 2 bytes - I2C_DR(i2c) = trans->buf[0]; - if (trans->len_w > 1) - { - I2C_DR(i2c) = trans->buf[1]; - periph->idx_buf = 2; - } - else - { - periph->idx_buf = 1; - } - - // Enable buffer-space available interrupt - // only if there is more to send: wait for TXE, no more to send: wait for BTF - if ( periph->idx_buf < trans->len_w) - I2C_CR2(i2c) |= I2C_CR2_ITBUFEN; - - // Document the current Status - periph->status = I2CSendingByte; - } - // The buffer is not full anymore AND we were not waiting for BTF - else if ((BIT_X_IS_SET_IN_REG(I2C_SR1_TxE, SR1) ) && (BIT_X_IS_SET_IN_REG(I2C_CR2_ITBUFEN, I2C_CR2(i2c))) ) - { - // Send the next byte - I2C_DR(i2c) = trans->buf[periph->idx_buf]; - periph->idx_buf++; - - // All bytes Sent? Then wait for BTF instead - if ( periph->idx_buf >= trans->len_w) - { - // Not interested anymore to know the buffer has space left - I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; - // Next interrupt will be BTF (or error) - } - } - // BTF: means last byte was sent - else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BTF, SR1) ) - { - if (trans->type == I2CTransTx) - { - // Tell the driver we are ready - trans->status = I2CTransSuccess; - } - // Otherwise we still need to do the receiving part - - return STMI2C_SubTra_Ready; - } - else // Event Logic Error - { - return STMI2C_SubTra_Error; - } - - return STMI2C_SubTra_Busy; -} - -// Doc ID 13902 Rev 11 p 714/1072 -// Transfer Sequence Diagram for Master Receiver for N=1 -static inline enum STMI2CSubTransactionStatus stmi2c_read1(u32 i2c, struct i2c_periph *periph, struct i2c_transaction *trans) -{ - uint16_t SR1 = I2C_SR1(i2c); - - // Start Condition Was Just Generated - if (BIT_X_IS_SET_IN_REG( I2C_SR1_SB, SR1 ) ) - { - I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; - I2C_DR(i2c) = trans->slave_addr | 0x01; - - // Document the current Status - periph->status = I2CAddrRdSent; - } - // Address Was Sent - else if (BIT_X_IS_SET_IN_REG(I2C_SR1_ADDR, SR1) ) - { - // First Clear the ACK bit: after the next byte we do not want new bytes - I2C_CR1(i2c) &= ~ I2C_CR1_POS; - I2C_CR1(i2c) &= ~ I2C_CR1_ACK; - - // --- next to steps MUST be executed together to avoid missing the stop - __I2C_REG_CRITICAL_ZONE_START; - - // Only after setting ACK, read SR2 to clear the ADDR (next byte will start arriving) - uint16_t SR2 __attribute__ ((unused)) = I2C_SR2(i2c); - - // Schedule a Stop - PPRZ_I2C_SEND_STOP(i2c); - - __I2C_REG_CRITICAL_ZONE_STOP; - // --- end of critical zone ----------- - - // Enable the RXNE: it will trigger as soon as the 1 byte is received to get the result - I2C_CR2(i2c) |= I2C_CR2_ITBUFEN; - - // Document the current Status - periph->status = I2CReadingLastByte; - } - // As soon as there is 1 byte ready to read, we have our byte - else if (BIT_X_IS_SET_IN_REG(I2C_SR1_RxNE, SR1) ) - { - I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; - trans->buf[0] = I2C_DR(i2c); - - // We got all the results (stop condition might still be in progress but this is the last interrupt) - trans->status = I2CTransSuccess; - - // Document the current Status: - // -the stop was actually already requested in the previous step - periph->status = I2CStopRequested; - - return STMI2C_SubTra_Ready_StopRequested; - } - else // Event Logic Error - { - return STMI2C_SubTra_Error; - } - - return STMI2C_SubTra_Busy; -} - -// Doc ID 13902 Rev 11 p 713/1072 -// Transfer Sequence Diagram for Master Receiver for N=2 -static inline enum STMI2CSubTransactionStatus stmi2c_read2(u32 i2c, struct i2c_periph *periph, struct i2c_transaction *trans) -{ - uint16_t SR1 = I2C_SR1(i2c); - - // Start Condition Was Just Generated - if (BIT_X_IS_SET_IN_REG( I2C_SR1_SB, SR1 ) ) - { - // according to the datasheet: instantly shedule a NAK on the second received byte: - I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; - I2C_CR1(i2c) |= I2C_CR1_ACK; - I2C_CR1(i2c) |= I2C_CR1_POS; - I2C_DR(i2c) = trans->slave_addr | 0x01; - - // Document the current Status - periph->status = I2CAddrRdSent; - } - // Address Was Sent - else if (BIT_X_IS_SET_IN_REG(I2C_SR1_ADDR, SR1) ) - { - // --- make absolutely sure this command is not delayed too much after the previous: - // --- the NAK bits must be set before the first byte arrived: allow other interrupts here - __I2C_REG_CRITICAL_ZONE_START; - - // if transfer of DR was finished already then we will get too many bytes - // BEFORE clearing ACK, read SR2 to clear the ADDR (next byte will start arriving) - // clearing ACK after the byte transfer has already started will NACK the next (2nd) - uint16_t SR2 __attribute__ ((unused)) = I2C_SR2(i2c); - - // NOT First Clear the ACK bit but only AFTER clearing ADDR - I2C_CR1(i2c) &= ~ I2C_CR1_ACK; - - // Disable the RXNE and wait for BTF - I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; - - __I2C_REG_CRITICAL_ZONE_STOP; - // --- end of critical zone ----------- - - // We do not set the RxE but wait for both bytes to arrive using BTF - - // Document the current Status - periph->status = I2CReadingByte; - } - // Receive buffer if full, master is halted: BTF - else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BTF, SR1) ) - { - // Stop condition MUST be set BEFORE reading the DR - // otherwise since there is new buffer space a new byte will be read - PPRZ_I2C_SEND_STOP(i2c); - - // Document the current Status - periph->status = I2CStopRequested; - - trans->buf[0] = I2C_DR(i2c); - trans->buf[1] = I2C_DR(i2c); - - // We got all the results - trans->status = I2CTransSuccess; - - return STMI2C_SubTra_Ready_StopRequested; - } - else // Event Logic Error - { - return STMI2C_SubTra_Error; - } - - return STMI2C_SubTra_Busy; -} - -// Doc ID 13902 Rev 11 p 712/1072 -// Transfer Sequence Diagram for Master Receiver for N>2 -static inline enum STMI2CSubTransactionStatus stmi2c_readmany(u32 i2c, struct i2c_periph *periph, struct i2c_transaction *trans) -{ - uint16_t SR1 = I2C_SR1(i2c); - - // Start Condition Was Just Generated - if (BIT_X_IS_SET_IN_REG( I2C_SR1_SB, SR1 ) ) - { - I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; - // The first data byte will be acked in read many so the slave knows it should send more - I2C_CR1(i2c) &= ~ I2C_CR1_POS; - I2C_CR1(i2c) |= I2C_CR1_ACK; - // Clear the SB flag - I2C_DR(i2c) = trans->slave_addr | 0x01; - - // Document the current Status - periph->status = I2CAddrRdSent; - } - // Address Was Sent - else if (BIT_X_IS_SET_IN_REG(I2C_SR1_ADDR, SR1) ) - { - periph->idx_buf = 0; - - // Enable RXNE: receive an interrupt any time a byte is available - // only enable if MORE than 3 bytes need to be read - if (periph->idx_buf < (trans->len_r - 3)) - { - I2C_CR2(i2c) |= I2C_CR2_ITBUFEN; - } - - // ACK is still on to get more DATA - // Read SR2 to clear the ADDR (next byte will start arriving) - uint16_t SR2 __attribute__ ((unused)) = I2C_SR2(i2c); - - // Document the current Status - periph->status = I2CReadingByte; - } - // one or more bytes are available AND we were interested in Buffer interrupts - else if ( (BIT_X_IS_SET_IN_REG(I2C_SR1_RxNE, SR1) ) && (BIT_X_IS_SET_IN_REG(I2C_CR2_ITBUFEN, I2C_CR2(i2c))) ) - { - // read byte until 3 bytes remain to be read (e.g. len_r = 6, -> idx=3 means idx 3,4,5 = 3 remain to be read - if (periph->idx_buf < (trans->len_r - 3)) - { - trans->buf[periph->idx_buf] = I2C_DR(i2c); - periph->idx_buf ++; - } - // from : 3bytes -> last byte: do nothing - // - // finally: this was the last byte - else if (periph->idx_buf >= (trans->len_r - 1)) - { - I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; - - // Last Value - trans->buf[periph->idx_buf] = I2C_DR(i2c); - periph->idx_buf ++; - - // We got all the results - trans->status = I2CTransSuccess; - - return STMI2C_SubTra_Ready_StopRequested; - } - - // Check for end of transaction: start waiting for BTF instead of RXNE - if (periph->idx_buf < (trans->len_r - 3)) - { - I2C_CR2(i2c) |= I2C_CR2_ITBUFEN; - } - else // idx >= len-3: there are 3 bytes to be read - { - // We want to halt I2C to have sufficient time to clear ACK, so: - // Stop listening to RXNE as it will be triggered infinitely since we did not empty the buffer - // on the next (second in buffer) received byte BTF will be set (buffer full and I2C halted) - I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; - } - } - // Buffer is full while this was not a RXNE interrupt - else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BTF, SR1) ) - { - // Now the shift register and data register contain data(n-2) and data(n-1) - // And I2C is halted so we have time - - // --- Make absolutely sure the next 2 I2C actions are performed with no delay - __I2C_REG_CRITICAL_ZONE_START; - - // First we clear the ACK while the SCL is held low by BTF - I2C_CR1(i2c) &= ~ I2C_CR1_ACK; - - // Now that ACK is cleared we read one byte: instantly the last byte is being clocked in... - trans->buf[periph->idx_buf] = I2C_DR(i2c); - periph->idx_buf ++; - - // Now the last byte is being clocked. Stop in MUST be set BEFORE the transfer of the last byte is complete - PPRZ_I2C_SEND_STOP(i2c); - - __I2C_REG_CRITICAL_ZONE_STOP; - // --- end of critical zone ----------- - - // Document the current Status - periph->status = I2CStopRequested; - - // read the byte2 we had in the buffer (BTF means 2 bytes available) - trans->buf[periph->idx_buf] = I2C_DR(i2c); - periph->idx_buf ++; - - // Ask for an interrupt to read the last byte (which is normally still busy now) - // The last byte will be received with RXNE - I2C_CR2(i2c) |= I2C_CR2_ITBUFEN; - } - else // Event Logic Error - { - return STMI2C_SubTra_Error; - } - - return STMI2C_SubTra_Busy; -} - -//////////////////////////////////////////////// -// Restore bus conditions to normal after errors - -static inline void i2c_error(struct i2c_periph *periph) -{ - uint8_t err_nr = 0; - periph->errors->er_irq_cnt; - if ((I2C_SR1((u32)periph->reg_addr) & I2C_SR1_AF) != 0) { /* Acknowledge failure */ - periph->errors->ack_fail_cnt++; - I2C_SR1((u32)periph->reg_addr) &= ~I2C_SR1_AF; - err_nr = 1; - } - if ((I2C_SR1((u32)periph->reg_addr) & I2C_SR1_BERR) != 0) { /* Misplaced Start or Stop condition */ - periph->errors->miss_start_stop_cnt++; - I2C_SR1((u32)periph->reg_addr) &= ~I2C_SR1_BERR; - err_nr = 2; - } - if ((I2C_SR1((u32)periph->reg_addr) & I2C_SR1_ARLO) != 0) { /* Arbitration lost */ - periph->errors->arb_lost_cnt++; - I2C_SR1((u32)periph->reg_addr) &= ~I2C_SR1_ARLO; - err_nr = 3; - } - if ((I2C_SR1((u32)periph->reg_addr) & I2C_SR1_OVR) != 0) { /* Overrun/Underrun */ - periph->errors->over_under_cnt++; - I2C_SR1((u32)periph->reg_addr) &= ~I2C_SR1_OVR; - err_nr = 4; - } - if ((I2C_SR1((u32)periph->reg_addr) & I2C_SR1_PECERR) != 0) { /* PEC Error in reception */ - periph->errors->pec_recep_cnt++; - I2C_SR1((u32)periph->reg_addr) &= ~I2C_SR1_PECERR; - err_nr = 5; - } - if ((I2C_SR1((u32)periph->reg_addr) & I2C_SR1_TIMEOUT) != 0) { /* Timeout or Tlow error */ - periph->errors->timeout_tlow_cnt++; - I2C_SR1((u32)periph->reg_addr) &= ~I2C_SR1_TIMEOUT; - err_nr = 6; - } - if ((I2C_SR1((u32)periph->reg_addr) & I2C_SR1_SMBALERT) != 0) { /* SMBus alert */ - periph->errors->smbus_alert_cnt++; - I2C_SR1((u32)periph->reg_addr) &= ~I2C_SR1_SMBALERT; - err_nr = 7; - } - -#ifdef I2C_DEBUG_LED - LED_ERROR(20, err_nr); -#endif - - return; -} - - -static inline void stmi2c_clear_pending_interrupts(u32 i2c) -{ - uint16_t SR1 = I2C_SR1(i2c); - - // Certainly do not wait for buffer interrupts: - // ------------------------------------------- - I2C_CR2(i2c) &= ~ I2C_CR2_ITBUFEN; // Disable TXE, RXNE - - // Error interrupts are handled separately: - // --------------------------------------- - - // Clear Event interrupt conditions: - // -------------------------------- - - // Start Condition Was Generated - if (BIT_X_IS_SET_IN_REG( I2C_SR1_SB, SR1 ) ) - { - // SB: cleared by software when reading SR1 and writing to DR - I2C_DR(i2c) = 0x00; - } - // Address Was Sent - if (BIT_X_IS_SET_IN_REG(I2C_SR1_ADDR, SR1) ) - { - // ADDR: Cleared by software when reading SR1 and then SR2 - uint16_t SR2 __attribute__ ((unused)) = I2C_SR2(i2c); - } - // Byte Transfer Finished - if (BIT_X_IS_SET_IN_REG(I2C_SR1_BTF, SR1) ) - { - // SB: cleared by software when reading SR1 and reading/writing to DR - uint8_t dummy __attribute__ ((unused)) = I2C_DR(i2c); - I2C_DR(i2c) = 0x00; - } - -} - - -//////////////////////////////////////////////// -// Restore bus conditions to normal after errors - -static inline void i2c_irq(struct i2c_periph *periph) -{ - - /* - There are 7 possible event reasons to get here + all errors - - If IT_EV_FEN - ------------------------- - - We are always interested in all IT_EV_FEV: all are required. - - 1) SB // Start Condition Success in Master mode - 2) ADDR // Address sent received Acknoledge - [ADDR10] // -- 10bit address stuff: not used - [STOPF] // -- only for slaves: master has no stop interrupt: not used - 3) BTF // I2C has stopped working (it is waiting for new data, all buffers are tx_empty/rx_full) - - // Beware: using the buffered I2C has some interesting properties: - -in master receive mode: BTF only occurs after the 2nd received byte: after the first byte is received it is - in RD but the I2C can still receive a second byte. Only when the 2nd byte is received while the RxNE is 1 - then a BTF occurs (I2C can not continue receiving bytes or they will get lost). During BTF I2C is halted (SCL held low) - -in master transmit mode: when writing a byte to WD, you instantly get a new TxE interrupt while the first is not - transmitted yet. The byte was pushed to the I2C shift register and the buffer is ready for more. You can already - fill new data in the buffer while the first is still being transmitted for max performance transmission. - - // Beware: besides data buffering you can/must plan several consecutive actions. You can send 2 bytes to the buffer, ask for a stop and - a new start in one go. - - -thanks to / because of this buffering and event sheduling there is not 1 interrupt per start / byte / stop - This also means you must think more in advance and a transaction could be popped from the transaction stack even before it's - stop condition is actually generated. - - // Beware: the order in which Status (and other register) is read determines how flags are cleared. - You should NOT simply read SR1 & SR2 every time - - If IT_EV_FEN AND IT_EV_BUF - -------------------------- - - Buffer event are not always wanted and are typically switched on during longer data transfers. Make sure to turn off in time. - - 4) RxNE - 5) TxE - - -------------------------------------------------------------------------------------------------- - - The STM waits indefinately (holding SCL low) for user interaction: - a) after a master-start (waiting for address) - b) after an address (waiting for data) - not during data sending when using buffered - c) after the last byte is transmitted (waiting for either stop or restart) - not during data receiving when using buffered - not after the last byte is received - - -The STM I2C stalls indefinately when a stop condition was attempted that - did not succeed. The BUSY flag remains on. - -There is no STOP interrupt. - - Caution Reading the status: - - Caution: this clears several flags and can start transmissions etc... - - Certain flags like STOP / (N)ACK need to be guaranteed to be set before - the transmission of the byte is finished. At higher clock rates that can be - quite fast: so we allow no other interrupt to be triggered in between - reading the status and setting all needed flags - - */ - - // Here we go ... - - // Apparently we got an I2C interrupt: EVT BUF or ERR - -#ifdef I2C_DEBUG_LED - // Notify ISR is triggered - LED1_ON(); - LED1_OFF(); -#endif - - // Save Some Direct Access to the I2C Registers ... - u32 i2c = (u32) periph->reg_addr; - - ///////////////////////////// - // Check if we were ready ... - if (periph->trans_extract_idx == periph->trans_insert_idx) - { - // Nothing Left To Do - -#ifdef I2C_DEBUG_LED - LED2_ON(); - LED1_ON(); - LED2_OFF(); - LED1_OFF(); - - // no transaction and also an error? - LED_SHOW_ACTIVE_BITS(regs); -#endif - - // If we still get an interrupt but there are no more things to do - // (which can happen if an event was sheduled just before a bus error occurs) - // (or can happen if both error and event interrupts were called together [the 2nd will then get this error]) - - // since there is nothing more to do: its easy: just stop: clear all interrupt generating bits - - // Count The Errors - i2c_error(periph); - - // Clear Running Events - stmi2c_clear_pending_interrupts(i2c); - - // Mark this as a special error - periph->errors->last_unexpected_event++; - - // Document the current Status - periph->status = I2CIdle; - - // There are no transactions anymore: return - // further-on in this routine we need a transaction pointer: so we are not allowed to continue - return; - } - - // get the I2C transaction we were working on ... - - enum STMI2CSubTransactionStatus ret = 0; - struct i2c_transaction* trans = periph->trans[periph->trans_extract_idx]; - - /////////////////////////// - // If there was an error: - if (( I2C_SR1(i2c) & I2C_SR1_ERR_MASK ) != 0x0000) - { - -#ifdef I2C_DEBUG_LED - LED1_ON(); - LED2_ON(); - LED1_OFF(); - LED2_OFF(); - - LED_SHOW_ACTIVE_BITS(regs); -#endif - - // Notify everyone about the error ... - - // Set result in transaction - trans->status = I2CTransFailed; - - // Document the current Status - periph->status = I2CFailed; - - // Make sure a TxRx does not Restart - trans->type = I2CTransRx; - - // Count The Errors - i2c_error(periph); - - // Clear Running Events - stmi2c_clear_pending_interrupts(i2c); - - // Now continue as if everything was normal from now on - ret = STMI2C_SubTra_Ready; - - } - - /////////////////////////// - // Normal Event: - else - { - - /////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // SUB-TRANSACTION HANDLER - - if (trans->type == I2CTransRx) // TxRx are converted to Rx after the Tx Part - { - switch (trans->len_r) - { - case 1: - ret = stmi2c_read1(i2c,periph,trans); - break; - case 2: - ret = stmi2c_read2(i2c,periph,trans); - break; - default: - ret = stmi2c_readmany(i2c,periph,trans); - break; - } - } - else // TxRx or Tx - { - ret = stmi2c_send(i2c,periph,trans); - } - } - - ///////////////////////////////// - // Sub-transaction has finished - if (ret != STMI2C_SubTra_Busy) - { - // Ready or SubTraError - // -ready: with or without stop already asked - - // In case of unexpected event condition during subtransaction handling: - if (ret == STMI2C_SubTra_Error) - { - // Tell everyone about the subtransaction error: - // this is the previously called SPURRIOUS INTERRUPT - periph->status = I2CFailed; - trans->type = I2CTransRx; // Avoid possible restart - trans->status = I2CTransFailed; // Notify Ready - periph->errors->unexpected_event_cnt++; - - // Error -#ifdef I2C_DEBUG_LED - LED2_ON(); - LED1_ON(); - LED2_OFF(); - LED1_OFF(); - - LED_SHOW_ACTIVE_BITS(regs); -#endif - - // Clear Running Events - stmi2c_clear_pending_interrupts(i2c); - } - - // RxTx -> Restart and do Rx part - if (trans->type == I2CTransTxRx) - { - trans->type = I2CTransRx; - periph->status = I2CStartRequested; - I2C_CR1(i2c) |= I2C_CR1_START; - - // Silent any BTF that would occur before SB - I2C_DR(i2c) = 0x00; - } - // If a restart is not needed: Rx part or Tx-only - else - { - // Ready, no stop condition set yet - if (ret == STMI2C_SubTra_Ready) - { - - // Program a stop - PPRZ_I2C_SEND_STOP(i2c); - - // Silent any BTF that would occur before STOP is executed - I2C_DR(i2c) = 0x00; - } - - // Jump to the next transaction - periph->trans_extract_idx++; - if (periph->trans_extract_idx >= I2C_TRANSACTION_QUEUE_LEN) - periph->trans_extract_idx = 0; - - // Tell everyone we are ready - periph->status = I2CIdle; - - - // if we have no more transaction to process, stop here - if (periph->trans_extract_idx == periph->trans_insert_idx) - { - -#ifdef I2C_DEBUG_LED - LED2_ON(); - LED1_ON(); - LED1_OFF(); - LED1_ON(); - LED1_OFF(); - LED2_OFF(); -#endif - } - // if not, start next transaction - else - { - // Restart transaction doing the Rx part now -// --- moved to idle function - PPRZ_I2C_SEND_START(periph); -// ------ - } - } - } - - return; -} - - -/* - // Make sure the bus is free before resetting (p722) - if (regs->SR2 & (I2C_FLAG_BUSY >> 16)) { - // Reset the I2C block - I2C_SoftwareResetCmd(periph->reg_addr, ENABLE); - I2C_SoftwareResetCmd(periph->reg_addr, DISABLE); - } -*/ - - -#ifdef USE_I2C1 - -struct i2c_errors i2c1_errors; -volatile uint32_t i2c1_watchdog_counter; - -void i2c1_hw_init(void) { - - i2c1.reg_addr = (void *)I2C1; - i2c1.init_struct = NULL; - i2c1.scl_pin = GPIO_I2C1_SCL; - i2c1.sda_pin = GPIO_I2C1_SDA; - i2c1.errors = &i2c1_errors; - i2c1_watchdog_counter = 0; - - /* zeros error counter */ - ZEROS_ERR_COUNTER(i2c1_errors); - - // Extra -#ifdef I2C_DEBUG_LED - LED_INIT(); -#else - - /* reset peripheral to default state ( sometimes not achieved on reset :( ) */ - //i2c_reset(I2C1); - - /* Configure priority grouping 0 bits for pre-emption priority and 4 bits for sub-priority. */ - scb_set_priority_grouping(SCB_AIRCR_PRIGROUP_NOGROUP_SUB16); - - /* Configure and enable I2C1 event interrupt --------------------------------*/ - nvic_set_priority(NVIC_I2C1_EV_IRQ, 0); - nvic_enable_irq(NVIC_I2C1_EV_IRQ); - - /* Configure and enable I2C1 err interrupt ----------------------------------*/ - nvic_set_priority(NVIC_I2C1_ER_IRQ, 1); - nvic_enable_irq(NVIC_I2C1_ER_IRQ); - - /* Enable peripheral clocks -------------------------------------------------*/ - /* Enable I2C1 clock */ - rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_I2C1EN); - /* Enable GPIOB clock */ - rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPBEN); - - gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ, - GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, - i2c1.scl_pin | i2c1.sda_pin); - - i2c_reset(I2C1); - - // enable peripheral - i2c_peripheral_enable(I2C1); - - /* - * XXX: there is a function to do that already in libopencm3 but I am not - * sure if it is correct, using direct register instead (esden) - */ - //i2c_set_own_7bit_slave_address(I2C1, 0); - I2C_OAR1(I2C1) = 0 | 0x4000; - - // enable error interrupts - I2C_CR2(I2C1) |= I2C_CR2_ITERREN; - - i2c_setbitrate(&i2c2, I2C2_CLOCK_SPEED); -#endif -} - -void i2c1_ev_isr(void) { - u32 i2c = (u32) i2c1.reg_addr; - I2C_CR2(i2c) &= ~I2C_CR2_ITERREN; - i2c_irq(&i2c1); - i2c1_watchdog_counter = 0; - I2C_CR2(i2c) |= I2C_CR2_ITERREN; -} - -void i2c1_er_isr(void) { - u32 i2c = (u32) i2c1.reg_addr; - I2C_CR2(i2c) &= ~I2C_CR2_ITEVTEN; - i2c_irq(&i2c1); - i2c1_watchdog_counter = 0; - I2C_CR2(i2c) |= I2C_CR2_ITEVTEN; -} - -#endif /* USE_I2C1 */ - -#ifdef USE_I2C2 - -struct i2c_errors i2c2_errors; -volatile uint32_t i2c2_watchdog_counter; - -void i2c2_hw_init(void) { - - i2c2.reg_addr = (void *)I2C2; - i2c2.init_struct = NULL; - i2c2.scl_pin = GPIO_I2C2_SCL; - i2c2.sda_pin = GPIO_I2C2_SDA; - i2c2.errors = &i2c2_errors; - i2c2_watchdog_counter = 0; - - /* zeros error counter */ - ZEROS_ERR_COUNTER(i2c2_errors); - - /* reset peripheral to default state ( sometimes not achieved on reset :( ) */ - //i2c_reset(I2C2); - - /* Configure priority grouping 0 bits for pre-emption priority and 4 bits for sub-priority. */ - scb_set_priority_grouping(SCB_AIRCR_PRIGROUP_NOGROUP_SUB16); - - /* Configure and enable I2C2 event interrupt --------------------------------*/ - nvic_set_priority(NVIC_I2C2_EV_IRQ, 0); - nvic_enable_irq(NVIC_I2C2_EV_IRQ); - - /* Configure and enable I2C2 err interrupt ----------------------------------*/ - nvic_set_priority(NVIC_I2C2_ER_IRQ, 1); - nvic_enable_irq(NVIC_I2C2_ER_IRQ); - - /* Enable peripheral clocks -------------------------------------------------*/ - /* Enable I2C2 clock */ - rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_I2C2EN); - /* Enable GPIOB clock */ - rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPBEN); - - gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ, - GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, - i2c2.scl_pin | i2c2.sda_pin); - - i2c_reset(I2C2); - - // enable peripheral - i2c_peripheral_enable(I2C2); - - /* - * XXX: there is a function to do that already in libopencm3 but I am not - * sure if it is correct, using direct register instead (esden) - */ - //i2c_set_own_7bit_slave_address(I2C2, 0); - I2C_OAR1(I2C2) = 0 | 0x4000; - - // enable error interrupts - I2C_CR2(I2C1) |= I2C_CR2_ITERREN; - - i2c_setbitrate(&i2c2, I2C2_CLOCK_SPEED); -} - -void i2c2_ev_isr(void) { - u32 i2c = (u32) i2c2.reg_addr; - I2C_CR2(i2c) &= ~I2C_CR2_ITERREN; - i2c_irq(&i2c2); - i2c2_watchdog_counter = 0; - I2C_CR2(i2c) |= I2C_CR2_ITERREN; -} - -void i2c2_er_isr(void) { - u32 i2c = (u32) i2c2.reg_addr; - I2C_CR2(i2c) &= ~I2C_CR2_ITEVTEN; - i2c_irq(&i2c2); - i2c2_watchdog_counter = 0; - I2C_CR2(i2c) |= I2C_CR2_ITEVTEN; -} - -#endif /* USE_I2C2 */ - -////////////////////////////////////////////////// -// Set Bitrate to Match your application: -// -short wires, low capacitance bus: IMU: high speed -// -long wires with a lot of capacitance: motor controller: put speed as low as possible - -void i2c_setbitrate(struct i2c_periph *periph, int bitrate) -{ - // If NOT Busy - if (i2c_idle(periph)) - { - volatile int devider; - volatile int risetime; - - u32 i2c = (u32) periph->reg_addr; - -/***************************************************** - Bitrate: - - -CR2 + CCR + TRISE registers - -only change when PE=0 - - e.g. - - 10kHz: 36MHz + Standard 0x708 + 0x25 - 70kHz: 36MHz + Standard 0x101 + - 400kHz: 36MHz + Fast 0x1E + 0xb - - // 1) Program peripheral input clock CR2: to get correct timings - // 2) Configure clock control registers - // 3) Configure rise time register -******************************************************/ - - if (bitrate < 3000) - bitrate = 3000; - - // 36MHz, fast scl: 2counts low 1 count high -> / 3: - devider = 18000 / (bitrate/1000); - - // never allow faster than 600kbps - if (devider < 20) - devider = 20; - - // no overflow either - if (devider >=4095) - devider = 4095; - - // risetime can be up to 1/6th of the period - risetime = 1000000 / (bitrate/1000) / 6 / 28; - - if (risetime < 10) - risetime = 10; - - // more will overflow the register: for more you should lower the FREQ - if (risetime >=31) - risetime = 31; - - // we do not expect an interrupt as the interface should have been idle, but just in case... - __disable_irq(); // this code is in user space: - - // CCR can only be written when PE is disabled - // p731 note 5 - I2C_CR1(i2c) &= ~ I2C_CR1_PE; - - // 1) - I2C_CR2(i2c) = 0x0324; - // 2) - //I2C_CCR(i2c) = 0x8000 + devider; - I2C_CCR(i2c) = 0x0000 + devider; - // 3) - I2C_TRISE(i2c) = risetime; - - // Re-Enable - I2C_CR1(i2c) |= I2C_CR1_PE; - - __enable_irq(); - -#ifdef I2C_DEBUG_LED - __disable_irq(); // this code is in user space: - - LED2_ON(); - LED1_ON(); - LED2_OFF(); - LED1_OFF(); - LED2_ON(); - LED1_ON(); - LED2_OFF(); - LED1_OFF(); - - __enable_irq(); -#endif - - } -} - - -// TODO: TODO: TODO: -// Watchdog timer -void i2c_event(void) -{ -#ifdef USE_I2C1 - i2c1_watchdog_counter++; -#endif - -#ifdef USE_I2C2 - i2c2_watchdog_counter++; - - if (i2c2_watchdog_counter > 10000) - { - i2c2.errors->timeout_tlow_cnt++; - i2c2_watchdog_counter = 0; - } - - -#ifdef I2C_DEBUG_LED - if (i2c2_watchdog_counter == 0) - { - __disable_irq(); - - LED2_ON(); - LED1_ON(); - LED1_OFF(); - LED1_ON(); - LED1_OFF(); - LED1_ON(); - LED1_OFF(); - LED1_ON(); - LED1_OFF(); - if (i2c2.status == I2CIdle) - { - LED1_ON(); - LED1_OFF(); - } - else if (i2c2.status == I2CStartRequested) - { - LED1_ON(); - LED1_OFF(); - LED1_ON(); - LED1_OFF(); - - } - LED2_OFF(); - - //regs = (I2C_TypeDef *) i2c2.reg_addr; - //LED_SHOW_ACTIVE_BITS(regs); - - __enable_irq(); - } -#endif - - - //if (i2c2.status == I2CIdle) - { - //if (i2c_idle(&i2c2)) - { - //__disable_irq(); - // More work to do - //if (i2c2.trans_extract_idx != i2c2.trans_insert_idx) - { - // Restart transaction doing the Rx part now - //PPRZ_I2C_SEND_START(&i2c2); - } - //__enable_irq(); - } - } -#endif -} - -///////////////////////////////////////////////////////// -// Implement Interface Functions - -bool_t i2c_submit(struct i2c_periph* periph, struct i2c_transaction* t) { - - uint8_t temp; - temp = periph->trans_insert_idx + 1; - if (temp >= I2C_TRANSACTION_QUEUE_LEN) temp = 0; - if (temp == periph->trans_extract_idx) - return FALSE; // queue full - - t->status = I2CTransPending; - - __disable_irq(); - /* put transacation in queue */ - periph->trans[periph->trans_insert_idx] = t; - periph->trans_insert_idx = temp; - - /* if peripheral is idle, start the transaction */ - // if (PPRZ_I2C_IS_IDLE(p)) - if (periph->status == I2CIdle) - { - //if (i2c_idle(periph)) - { -#ifdef I2C_DEBUG_LED -#ifdef USE_I2C1 - if (periph == &i2c1) - { - - } - else -#endif -#endif - { -#ifdef I2C_DEBUG_LED - LED2_ON(); - LED2_OFF(); -#endif - PPRZ_I2C_SEND_START(periph); - } - } - } - /* else it will be started by the interrupt handler when the previous transactions completes */ - __enable_irq(); - - return TRUE; -} - -bool_t i2c_idle(struct i2c_periph* periph) -{ - // This is actually a difficult function: - // -simply reading the status flags can clear bits and corrupt the transaction - - u32 i2c = (u32) periph->reg_addr; - -#ifdef I2C_DEBUG_LED -#ifdef USE_I2C1 - if (periph == &i2c1) - { - return TRUE; - } -#endif -#endif - - // First we check if the software thinks it is ready - if (periph->status == I2CIdle) - return ! (BIT_X_IS_SET_IN_REG( I2C_SR2_BUSY, I2C_SR2(i2c) ) ); - else - return FALSE; -} - - diff --git a/sw/airborne/arch/stm32/mcu_periph/obsolete/i2c_arch.old.c b/sw/airborne/arch/stm32/mcu_periph/obsolete/i2c_arch.old.c new file mode 100644 index 0000000000..8d69b3f9da --- /dev/null +++ b/sw/airborne/arch/stm32/mcu_periph/obsolete/i2c_arch.old.c @@ -0,0 +1,616 @@ +#include "mcu_periph/i2c.h" + +#include +#include +#include +#include + + +static void start_transaction(struct i2c_periph* p); +static inline void end_of_transaction(struct i2c_periph *p); +static inline void i2c_hard_reset(struct i2c_periph *p); +static inline void i2c_reset_init(struct i2c_periph *p); + +#define I2C_BUSY 0x20 + +#ifdef DEBUG_I2C +#define SPURIOUS_INTERRUPT(_periph, _status, _event) { while(1); } +#define OUT_OF_SYNC_STATE_MACHINE(_periph, _status, _event) { while(1); } +#else +//#define SPURIOUS_INTERRUPT(_periph, _status, _event) { periph->errors->unexpected_event_cnt++; abort_and_reset(_periph);} +#define SPURIOUS_INTERRUPT(_periph, _status, _event) { if (_status == I2CAddrWrSent) abort_and_reset(_periph);} +#define OUT_OF_SYNC_STATE_MACHINE(_periph, _status, _event) { abort_and_reset(_periph);} +#endif + +#ifdef USE_I2C1 +static I2C_InitTypeDef I2C1_InitStruct = { + .I2C_Mode = I2C_Mode_I2C, + .I2C_DutyCycle = I2C_DutyCycle_2, + .I2C_OwnAddress1 = 0x00, + .I2C_Ack = I2C_Ack_Enable, + .I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit, + .I2C_ClockSpeed = 200000 +}; +#endif + +#ifdef USE_I2C2 +static I2C_InitTypeDef I2C2_InitStruct = { + .I2C_Mode = I2C_Mode_I2C, + .I2C_DutyCycle = I2C_DutyCycle_2, + .I2C_OwnAddress1 = 0x00, + .I2C_Ack = I2C_Ack_Enable, + .I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit, + .I2C_ClockSpeed = 300000 +}; +#endif + +static inline void i2c_delay(void) +{ + for (__IO int j = 0; j < 50; j++); +} + +static inline void i2c_apply_config(struct i2c_periph *p) +{ + I2C_Init(p->reg_addr, p->init_struct); +} + +static inline void end_of_transaction(struct i2c_periph *p) +{ + p->trans_extract_idx++; + if (p->trans_extract_idx >= I2C_TRANSACTION_QUEUE_LEN) + p->trans_extract_idx = 0; + /* if we have no more transaction to process, stop here */ + if (p->trans_extract_idx == p->trans_insert_idx) + p->status = I2CIdle; + /* if not, start next transaction */ + else + start_transaction(p); +} + +static inline void abort_and_reset(struct i2c_periph *p) { + struct i2c_transaction* trans = p->trans[p->trans_extract_idx]; + trans->status = I2CTransFailed; + I2C_ITConfig(p->reg_addr, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE); + i2c_hard_reset(p); + I2C_ITConfig(p->reg_addr, I2C_IT_ERR, ENABLE); + end_of_transaction(p); +} + +#ifdef USE_I2C2 +static inline void on_status_start_requested(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event); +static inline void on_status_addr_wr_sent(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event); +static inline void on_status_sending_byte(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event); +static inline void on_status_stop_requested(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event); +static inline void on_status_addr_rd_sent(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event); +static inline void on_status_reading_byte(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event); +static inline void on_status_reading_last_byte(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event); +static inline void on_status_restart_requested(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event); + +/* + * Start Requested + * + */ +static inline void on_status_start_requested(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event) { + if (event & I2C_FLAG_SB) { + if(trans->type == I2CTransRx) { + I2C_Send7bitAddress(periph->reg_addr, trans->slave_addr, I2C_Direction_Receiver); + periph->status = I2CAddrRdSent; + } + else { + I2C_Send7bitAddress(periph->reg_addr, trans->slave_addr, I2C_Direction_Transmitter); + periph->status = I2CAddrWrSent; + } + } + // else + // SPURIOUS_INTERRUPT(periph, I2CStartRequested, event); + // FIXME: this one seems to get called all the time with mkk controllers +} + +/* + * Addr WR sent + * + */ +static inline void on_status_addr_wr_sent(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event) { + if ((event & I2C_FLAG_ADDR) && (event & I2C_FLAG_TRA)) { + I2C_SendData(periph->reg_addr, trans->buf[0]); + if (trans->len_w > 1) { + I2C_SendData(periph->reg_addr, trans->buf[1]); + periph->idx_buf = 2; + I2C_ITConfig(periph->reg_addr, I2C_IT_BUF, ENABLE); + periph->status = I2CSendingByte; + } + else { + periph->idx_buf = 1; + if (trans->type == I2CTransTx) { + I2C_GenerateSTOP(periph->reg_addr, ENABLE); + periph->status = I2CStopRequested; + } + else { + I2C_GenerateSTART(periph->reg_addr, ENABLE); + periph->status = I2CRestartRequested; + } + } + } + else { + SPURIOUS_INTERRUPT(periph, I2CAddrWrSent, event); + // FIXME: this was where the code would break with mkk controllers on april 10 2011 + // now have SPURIOUS_INTERRUPT call abort_and_reset + } +} + +/* + * Sending Byte + * + */ +static inline void on_status_sending_byte(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event) { + I2C_TypeDef *regs = (I2C_TypeDef *) periph->reg_addr; + if (event & I2C_FLAG_TXE) { + if (periph->idx_buf < trans->len_w) { + I2C_SendData(periph->reg_addr, trans->buf[periph->idx_buf]); + periph->idx_buf++; + } + else { + I2C_ITConfig(periph->reg_addr, I2C_IT_BUF, DISABLE); + if (trans->type == I2CTransTx) { + I2C_GenerateSTOP(periph->reg_addr, ENABLE); + /* Make sure that the STOP bit is cleared by Hardware */ + static __IO uint8_t counter = 0; + while ((regs->CR1 & 0x200) == 0x200) { + counter++; + if (counter > 100) break; + } + periph->status = I2CStopRequested; + } + else { + I2C_GenerateSTART(periph->reg_addr, ENABLE); + periph->status = I2CRestartRequested; + } + } + } + else + SPURIOUS_INTERRUPT(periph, I2CSendingByte, event); +} + +/* + * Stop Requested + * + */ +static inline void on_status_stop_requested(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event) { + /* bummer.... */ + if (event & I2C_FLAG_RXNE) { + uint8_t read_byte = I2C_ReceiveData(periph->reg_addr); + if (periph->idx_buf < trans->len_r) { + trans->buf[periph->idx_buf] = read_byte; + } + } + I2C_ITConfig(periph->reg_addr, I2C_IT_EVT|I2C_IT_BUF, DISABLE); // should only need to disable evt, buf already disabled + trans->status = I2CTransSuccess; + end_of_transaction(periph); +} + +/* + * Addr RD sent + * + */ +static inline void on_status_addr_rd_sent(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event) { + I2C_TypeDef *regs = (I2C_TypeDef *) periph->reg_addr; + + if ((event & I2C_FLAG_ADDR) && !(event & I2C_FLAG_TRA)) { + periph->idx_buf = 0; + if(trans->len_r == 1) { // If we're going to read only one byte + I2C_AcknowledgeConfig(periph->reg_addr, DISABLE); // make sure it's gonna be nacked + I2C_GenerateSTOP(periph->reg_addr, ENABLE); // and followed by a stop + /* Make sure that the STOP bit is cleared by Hardware */ + static __IO uint8_t counter = 0; + while ((regs->CR1 & 0x200) == 0x200) { + counter++; + if (counter > 100) break; + } + periph->status = I2CReadingLastByte; // and remember we did + } + else { + I2C_AcknowledgeConfig(periph->reg_addr, ENABLE); // if it's more than one byte, ack it + I2C_ITConfig(periph->reg_addr, I2C_IT_BUF, ENABLE); + periph->status = I2CReadingByte; // and remember we did + } + } + else + SPURIOUS_INTERRUPT(periph, I2CAddrRdSent, event); +} + + +/* + * Reading byte + * + */ +static inline void on_status_reading_byte(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event) { + I2C_TypeDef *regs = (I2C_TypeDef *) periph->reg_addr; + if (event & I2C_FLAG_RXNE) { + uint8_t read_byte = I2C_ReceiveData(periph->reg_addr); + if (periph->idx_buf < trans->len_r) { + trans->buf[periph->idx_buf] = read_byte; + periph->idx_buf++; + if (periph->idx_buf >= trans->len_r-1) { // We're reading our last byte + I2C_AcknowledgeConfig(periph->reg_addr, DISABLE); // give them a nack once it's done + I2C_GenerateSTOP(periph->reg_addr, ENABLE); // and follow with a stop + /* Make sure that the STOP bit is cleared by Hardware */ + static __IO uint8_t counter = 0; + while ((regs->CR1 & 0x200) == 0x200) { + counter++; + if (counter > 100) break; + } + periph->status = I2CStopRequested; // remember we already trigered the stop + } + } // else { something very wrong has happened } + } + else + SPURIOUS_INTERRUPT(periph, I2CReadingByte, event); +} + +/* + * Reading last byte + * + */ +static inline void on_status_reading_last_byte(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event) { + if (event & I2C_FLAG_BTF) { + uint8_t read_byte = I2C_ReceiveData(periph->reg_addr); + trans->buf[periph->idx_buf] = read_byte; + I2C_GenerateSTOP(periph->reg_addr, ENABLE); + periph->status = I2CStopRequested; + } + else if (event & I2C_FLAG_RXNE) { // should really be BTF ? + uint8_t read_byte = I2C_ReceiveData(periph->reg_addr); + trans->buf[periph->idx_buf] = read_byte; + periph->status = I2CStopRequested; + } + else + SPURIOUS_INTERRUPT(periph, I2CReadingLastByte, event); +} + +/* + * Restart requested + * + */ +static inline void on_status_restart_requested(struct i2c_periph *periph, struct i2c_transaction* trans, uint32_t event) { + if (event & I2C_FLAG_SB) { + I2C_Send7bitAddress(periph->reg_addr, trans->slave_addr, I2C_Direction_Receiver); + periph->status = I2CAddrRdSent; + } +} + + + +static inline void i2c_event(struct i2c_periph *p, uint32_t event) +{ + struct i2c_transaction* trans = p->trans[p->trans_extract_idx]; + switch (p->status) { + case I2CStartRequested: + on_status_start_requested(p, trans, event); + break; + case I2CAddrWrSent: + on_status_addr_wr_sent(p, trans, event); + break; + case I2CSendingByte: + on_status_sending_byte(p, trans, event); + break; + case I2CStopRequested: + on_status_stop_requested(p, trans, event); + break; + case I2CAddrRdSent: + on_status_addr_rd_sent(p, trans, event); + break; + case I2CReadingByte: + on_status_reading_byte(p, trans, event); + break; + case I2CReadingLastByte: + on_status_reading_last_byte(p, trans, event); + break; + case I2CRestartRequested: + on_status_restart_requested(p, trans, event); + break; + default: + OUT_OF_SYNC_STATE_MACHINE(p, p->status, event); + break; + } +} + +static inline void i2c_error(struct i2c_periph *p) +{ + p->errors->er_irq_cnt; + if (I2C_GetITStatus(p->reg_addr, I2C_IT_AF)) { /* Acknowledge failure */ + p->errors->ack_fail_cnt++; + I2C_ClearITPendingBit(p->reg_addr, I2C_IT_AF); + } + if (I2C_GetITStatus(p->reg_addr, I2C_IT_BERR)) { /* Misplaced Start or Stop condition */ + p->errors->miss_start_stop_cnt++; + I2C_ClearITPendingBit(p->reg_addr, I2C_IT_BERR); + } + if (I2C_GetITStatus(p->reg_addr, I2C_IT_ARLO)) { /* Arbitration lost */ + p->errors->arb_lost_cnt++; + I2C_ClearITPendingBit(p->reg_addr, I2C_IT_ARLO); + // I2C_AcknowledgeConfig(I2C2, DISABLE); + // uint8_t dummy __attribute__ ((unused)) = I2C_ReceiveData(I2C2); + // I2C_GenerateSTOP(I2C2, ENABLE); + } + if (I2C_GetITStatus(p->reg_addr, I2C_IT_OVR)) { /* Overrun/Underrun */ + p->errors->over_under_cnt++; + I2C_ClearITPendingBit(p->reg_addr, I2C_IT_OVR); + } + if (I2C_GetITStatus(p->reg_addr, I2C_IT_PECERR)) { /* PEC Error in reception */ + p->errors->pec_recep_cnt++; + I2C_ClearITPendingBit(p->reg_addr, I2C_IT_PECERR); + } + if (I2C_GetITStatus(p->reg_addr, I2C_IT_TIMEOUT)) { /* Timeout or Tlow error */ + p->errors->timeout_tlow_cnt++; + I2C_ClearITPendingBit(p->reg_addr, I2C_IT_TIMEOUT); + } + if (I2C_GetITStatus(p->reg_addr, I2C_IT_SMBALERT)) { /* SMBus alert */ + p->errors->smbus_alert_cnt++; + I2C_ClearITPendingBit(p->reg_addr, I2C_IT_SMBALERT); + } + + abort_and_reset(p); +} + + +static inline void i2c_hard_reset(struct i2c_periph *p) +{ + I2C_TypeDef *regs = (I2C_TypeDef *) p->reg_addr; + + I2C_DeInit(p->reg_addr); + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = p->scl_pin | p->sda_pin; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; + GPIO_SetBits(GPIOB, p->scl_pin | p->sda_pin); + GPIO_Init(GPIOB, &GPIO_InitStructure); + + while(GPIO_ReadInputDataBit(GPIOB, p->sda_pin) == Bit_RESET) { + // Raise SCL, wait until SCL is high (in case of clock stretching) + GPIO_SetBits(GPIOB, p->scl_pin); + while (GPIO_ReadInputDataBit(GPIOB, p->scl_pin) == Bit_RESET); + i2c_delay(); + + // Lower SCL, wait + GPIO_ResetBits(GPIOB, p->scl_pin); + i2c_delay(); + + // Raise SCL, wait + GPIO_SetBits(GPIOB, p->scl_pin); + i2c_delay(); + } + + // Generate a start condition followed by a stop condition + GPIO_SetBits(GPIOB, p->scl_pin); + i2c_delay(); + GPIO_ResetBits(GPIOB, p->sda_pin); + i2c_delay(); + GPIO_ResetBits(GPIOB, p->sda_pin); + i2c_delay(); + + // Raise both SCL and SDA and wait for SCL high (in case of clock stretching) + GPIO_SetBits(GPIOB, p->scl_pin | p->sda_pin); + while (GPIO_ReadInputDataBit(GPIOB, p->scl_pin) == Bit_RESET); + + // Wait for SDA to be high + while (GPIO_ReadInputDataBit(GPIOB, p->sda_pin) != Bit_SET); + + // SCL and SDA should be high at this point, bus should be free + // Return the GPIO pins to the alternate function + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + I2C_DeInit(p->reg_addr); + + i2c_apply_config(p); + + if (regs->SR2 & I2C_BUSY) { + // Reset the I2C block + I2C_SoftwareResetCmd(p->reg_addr, ENABLE); + I2C_SoftwareResetCmd(p->reg_addr, DISABLE); + } +} + +static inline void i2c_reset_init(struct i2c_periph *p) +{ + // Reset bus and configure GPIO pins + i2c_hard_reset(p); + + // enable peripheral + I2C_Cmd(p->reg_addr, ENABLE); + + // enable error interrupts + I2C_ITConfig(p->reg_addr, I2C_IT_ERR, ENABLE); +} +#endif /* USE_I2C2 */ + +#ifdef USE_I2C1 + +struct i2c_errors i2c1_errors; + +#include "my_debug_servo.h" + +void i2c1_hw_init(void) { + + i2c1.reg_addr = I2C1; + i2c1.init_struct = &I2C1_InitStruct; + i2c1.scl_pin = GPIO_Pin_6; + i2c1.sda_pin = GPIO_Pin_7; + i2c1.errors = &i2c1_errors; + + /* zeros error counter */ + ZEROS_ERR_COUNTER(i2c1_errors); + + /* reset peripheral to default state ( sometimes not achieved on reset :( ) */ + I2C_DeInit(I2C1); + + NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); + NVIC_InitTypeDef NVIC_InitStructure; + + /* Configure and enable I2C1 event interrupt -------------------------------*/ + NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + /* Configure and enable I2C1 err interrupt -------------------------------*/ + NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + /* Enable peripheral clocks --------------------------------------------------*/ + /* Enable I2C1 clock */ + RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); + /* Enable GPIOB clock */ + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); + + /* Configure I2C1 pins: SCL and SDA ------------------------------------------*/ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_StructInit(&GPIO_InitStructure); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + /* I2C configuration ----------------------------------------------------------*/ + + + /* Reset and initialize I2C HW */ + i2c_reset_init(&i2c1); + +} + + +void i2c1_ev_irq_handler(void) { + + uint32_t event = I2C_GetLastEvent(I2C1); + i2c_event(&i2c1, event); + +} + + +void i2c1_er_irq_handler(void) { + i2c_error(&i2c1); +} + +#endif /* USE_I2C1 */ + + + + + +#ifdef USE_I2C2 + +// dec hex +// 196609 30001 BUSY MSL | SB +// 458882 70082 TRA BUSY MSL | TXE ADDR +// 458884 70084 TRA BUSY MSL | TXE BTF +// 196609 30001 BUSY MSL | SB +// 196610 30002 BUSY MSL | ADDR +// + + +struct i2c_errors i2c2_errors; + +#include "my_debug_servo.h" + +void i2c2_hw_init(void) { + + i2c2.reg_addr = I2C2; + i2c2.init_struct = &I2C2_InitStruct; + i2c2.scl_pin = GPIO_Pin_10; + i2c2.sda_pin = GPIO_Pin_11; + i2c2.errors = &i2c2_errors; + + /* zeros error counter */ + ZEROS_ERR_COUNTER(i2c2_errors); + + /* reset peripheral to default state ( sometimes not achieved on reset :( ) */ + I2C_DeInit(I2C2); + + NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); + NVIC_InitTypeDef NVIC_InitStructure; + + /* Configure and enable I2C2 event interrupt --------------------------------*/ + NVIC_InitStructure.NVIC_IRQChannel = I2C2_EV_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + /* Configure and enable I2C2 err interrupt ----------------------------------*/ + NVIC_InitStructure.NVIC_IRQChannel = I2C2_ER_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + /* Enable peripheral clocks -------------------------------------------------*/ + /* Enable I2C2 clock */ + RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE); + /* Enable GPIOB clock */ + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); + + // Reset and initialize I2C HW + i2c_reset_init(&i2c2); + +} + + + + +void i2c2_ev_irq_handler(void) { + uint32_t event = I2C_GetLastEvent(I2C2); + i2c_event(&i2c2, event); +} + +void i2c2_er_irq_handler(void) { + i2c_error(&i2c2); + +} + +#endif /* USE_I2C2 */ + + + +bool_t i2c_idle(struct i2c_periph* p) +{ + return !I2C_GetFlagStatus(p->reg_addr, I2C_FLAG_BUSY); +} + +bool_t i2c_submit(struct i2c_periph* p, struct i2c_transaction* t) { + + uint8_t temp; + temp = p->trans_insert_idx + 1; + if (temp >= I2C_TRANSACTION_QUEUE_LEN) temp = 0; + if (temp == p->trans_extract_idx) + return FALSE; // queue full + + t->status = I2CTransPending; + + + __disable_irq(); + /* put transacation in queue */ + p->trans[p->trans_insert_idx] = t; + p->trans_insert_idx = temp; + + /* if peripheral is idle, start the transaction */ + if (p->status == I2CIdle) + start_transaction(p); + /* else it will be started by the interrupt handler when the previous transactions completes */ + __enable_irq(); + + return TRUE; +} + + +static void start_transaction(struct i2c_periph* p) { + p->idx_buf = 0; + p->status = I2CStartRequested; + I2C_ITConfig(p->reg_addr, I2C_IT_EVT, ENABLE); + I2C_GenerateSTART(p->reg_addr, ENABLE); +} diff --git a/sw/airborne/arch/stm32/mcu_periph/i2c_arch.old.c b/sw/airborne/arch/stm32/mcu_periph/obsolete/i2c_arch.very_old.c similarity index 100% rename from sw/airborne/arch/stm32/mcu_periph/i2c_arch.old.c rename to sw/airborne/arch/stm32/mcu_periph/obsolete/i2c_arch.very_old.c