diff --git a/conf/airframes/CDW/debug_i2c.xml b/conf/airframes/CDW/debug_i2c.xml index d8b773214f..cbc48adfdb 100644 --- a/conf/airframes/CDW/debug_i2c.xml +++ b/conf/airframes/CDW/debug_i2c.xml @@ -216,7 +216,7 @@ - + diff --git a/conf/airframes/CDW/debug_i2c_lpc.xml b/conf/airframes/CDW/debug_i2c_lpc.xml new file mode 100644 index 0000000000..18320f15cc --- /dev/null +++ b/conf/airframes/CDW/debug_i2c_lpc.xml @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+ + + + + +
+ +
+ + +
+ +
+ + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ +
+ + + + + + + +
+ +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/conf/airframes/fraser_lisa_m_rotorcraft.xml b/conf/airframes/fraser_lisa_m_rotorcraft.xml index 0761492743..1e50b49d2a 100644 --- a/conf/airframes/fraser_lisa_m_rotorcraft.xml +++ b/conf/airframes/fraser_lisa_m_rotorcraft.xml @@ -15,6 +15,7 @@ +
diff --git a/conf/autopilot/subsystems/shared/i2c_select.makefile b/conf/autopilot/subsystems/shared/i2c_select.makefile index 75241931af..395a061bc0 100644 --- a/conf/autopilot/subsystems/shared/i2c_select.makefile +++ b/conf/autopilot/subsystems/shared/i2c_select.makefile @@ -4,8 +4,11 @@ $(TARGET).srcs += mcu_periph/i2c.c ifeq ($(ARCH), stm32) +ifeq ($(USE_NEW_I2C_DRIVER), 1) +$(TARGET).srcs += $(SRC_ARCH)/mcu_periph/i2c_arch.rewritten.c +else $(TARGET).srcs += $(SRC_ARCH)/mcu_periph/i2c_arch.c -#$(TARGET).srcs += $(SRC_ARCH)/mcu_periph/i2c_arch.rewritten.c +endif else $(TARGET).srcs += $(SRC_ARCH)/mcu_periph/i2c_arch.c endif diff --git a/conf/modules/i2c_abuse_test.xml b/conf/modules/i2c_abuse_test.xml index f0b8b6f316..7c0a772d53 100644 --- a/conf/modules/i2c_abuse_test.xml +++ b/conf/modules/i2c_abuse_test.xml @@ -8,11 +8,21 @@ + - - + +ifeq ($(ARCH), lpc21) +$(TARGET).CFLAGS += -DI2C_ABUSE_LED=3 +$(TARGET).CFLAGS += -DUSE_I2C0 +$(TARGET).CFLAGS += -DI2C_ABUSE_PORT=i2c0 +else ifeq ($(ARCH), stm32) +$(TARGET).CFLAGS += -DI2C_ABUSE_LED=7 +$(TARGET).CFLAGS += -DI2C_ABUSE_PORT=i2c2 +endif + + diff --git a/conf/telemetry/default_rotorcraft.xml b/conf/telemetry/default_rotorcraft.xml index 61cdfac796..60b05eea78 100644 --- a/conf/telemetry/default_rotorcraft.xml +++ b/conf/telemetry/default_rotorcraft.xml @@ -16,6 +16,7 @@ + diff --git a/sw/airborne/arch/avr/mcu_periph/i2c_arch.c b/sw/airborne/arch/avr/mcu_periph/i2c_arch.c index f427a64008..c3953dfb3a 100644 --- a/sw/airborne/arch/avr/mcu_periph/i2c_arch.c +++ b/sw/airborne/arch/avr/mcu_periph/i2c_arch.c @@ -161,3 +161,7 @@ void i2c_init(void) { sbi(TWSR, TWPS0); */ } + +void i2c_event(void) { } +void i2c2_setbitrate(int bitrate __attribute__ ((unused))) { } + diff --git a/sw/airborne/arch/lpc21/mcu_periph/i2c_arch.c b/sw/airborne/arch/lpc21/mcu_periph/i2c_arch.c index 98f65140a9..eb990fdebe 100644 --- a/sw/airborne/arch/lpc21/mcu_periph/i2c_arch.c +++ b/sw/airborne/arch/lpc21/mcu_periph/i2c_arch.c @@ -340,3 +340,45 @@ bool_t i2c_submit(struct i2c_periph* p, struct i2c_transaction* t) { return TRUE; } + +void i2c_event(void) { } +void i2c_setbitrate(struct i2c_periph* p, int bitrate) +{ + int period = 15000000 / 2 / bitrate; + // Max 400kpbs + if (period < 19) + period = 19; + // Min 5kbps + if (period > 1500) + period = 1500; + +/* default clock speed 37.5KHz with our 15MHz PCLK + I2C1_CLOCK = PCLK / (I2C1_SCLL + I2C1_SCLH) */ + +#if (PCLK == 30000000) + period *= 2; +#endif + +#if (PCLK == 60000000) + period *= 4; +#endif + +#ifdef USE_I2C0 + if (p == &i2c0) + { + /* set bitrate */ + I2C0SCLL = period; + I2C0SCLH = period; + } +#endif +#ifdef USE_I2C1 + if (p == &i2c1) + { + /* set bitrate */ + I2C1SCLL = period; + I2C1SCLH = period; + } +#endif +} + + diff --git a/sw/airborne/arch/sim/mcu_periph/i2c_arch.c b/sw/airborne/arch/sim/mcu_periph/i2c_arch.c index ceb58123b6..34b43d9739 100644 --- a/sw/airborne/arch/sim/mcu_periph/i2c_arch.c +++ b/sw/airborne/arch/sim/mcu_periph/i2c_arch.c @@ -4,3 +4,6 @@ void i2c_hw_init ( void ) {} bool_t i2c_idle(struct i2c_periph *p __attribute__ ((unused))) { return TRUE; } bool_t i2c_submit(struct i2c_periph* p __attribute__ ((unused)), struct i2c_transaction* t __attribute__ ((unused))) { return TRUE;} +void i2c_event(void) { } +void i2c2_setbitrate(int bitrate __attribute__ ((unused))) { } + diff --git a/sw/airborne/arch/stm32/mcu_periph/i2c_arch.c b/sw/airborne/arch/stm32/mcu_periph/i2c_arch.c index c3c6377f1e..b09cfb9415 100644 --- a/sw/airborne/arch/stm32/mcu_periph/i2c_arch.c +++ b/sw/airborne/arch/stm32/mcu_periph/i2c_arch.c @@ -287,9 +287,11 @@ static inline void on_status_restart_requested(struct i2c_periph *periph, struct } } +void i2c_event(void) +{ +} - -static inline void i2c_event(struct i2c_periph *p, uint32_t event) +static inline void i2c_driver_event(struct i2c_periph *p, uint32_t event) { struct i2c_transaction* trans = p->trans[p->trans_extract_idx]; switch (p->status) { @@ -509,7 +511,7 @@ void i2c1_hw_init(void) { void i2c1_ev_irq_handler(void) { uint32_t event = I2C_GetLastEvent(I2C1); - i2c_event(&i2c1, event); + i2c_driver_event(&i2c1, event); } @@ -586,7 +588,7 @@ void i2c2_hw_init(void) { void i2c2_ev_irq_handler(void) { uint32_t event = I2C_GetLastEvent(I2C2); - i2c_event(&i2c2, event); + i2c_driver_event(&i2c2, event); } void i2c2_er_irq_handler(void) { diff --git a/sw/airborne/arch/stm32/mcu_periph/i2c_arch.h b/sw/airborne/arch/stm32/mcu_periph/i2c_arch.h index 0274829fc1..50519ea24c 100644 --- a/sw/airborne/arch/stm32/mcu_periph/i2c_arch.h +++ b/sw/airborne/arch/stm32/mcu_periph/i2c_arch.h @@ -34,8 +34,6 @@ #ifdef USE_I2C1 -extern struct i2c_errors i2c1_errors; - extern void i2c1_hw_init(void); extern void i2c1_ev_irq_handler(void); extern void i2c1_er_irq_handler(void); @@ -46,8 +44,6 @@ extern void i2c1_er_irq_handler(void); #ifdef USE_I2C2 -extern struct i2c_errors i2c2_errors; - extern void i2c2_hw_init(void); extern void i2c2_ev_irq_handler(void); extern void i2c2_er_irq_handler(void); diff --git a/sw/airborne/arch/stm32/mcu_periph/i2c_arch.rewritten.c b/sw/airborne/arch/stm32/mcu_periph/i2c_arch.rewritten.c new file mode 100644 index 0000000000..09f19ff27d --- /dev/null +++ b/sw/airborne/arch/stm32/mcu_periph/i2c_arch.rewritten.c @@ -0,0 +1,1452 @@ +#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 + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +#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 + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +// 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 + +// 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 + + +// Bit Control + +#define BIT_X_IS_SET_IN_REG(X,REG) (((REG) & (X)) == (X)) + +// 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(I2C_TypeDef *regs) +{ + // Man: p722: Stop generation after the current byte transfer or after the current Start condition is sent. + regs->CR1 |= I2C_CR1_BIT_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) +{ + I2C_TypeDef *regs = (I2C_TypeDef *) 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 + regs->CR2 |= I2C_CR2_BIT_ITERREN; + regs->CR2 |= I2C_CR2_BIT_ITEVTEN; + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + + // Issue a new start + regs->CR1 = (I2C_CR1_BIT_START | I2C_CR1_BIT_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(I2C_TypeDef *regs, struct i2c_periph *periph, struct i2c_transaction *trans) +{ + uint16_t SR1 = regs->SR1; + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + // Disable buffer interrupt + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + // Send Slave address and wait for ADDR interrupt + regs->DR = trans->slave_addr; + // Document the current Status + periph->status = I2CAddrWrSent; + } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_ADDR, SR1) ) + { + // Now read SR2 to clear the ADDR status Bit + uint16_t SR2 __attribute__ ((unused)) = regs->SR2; + + // Maybe check we are transmitting (did not loose arbitration for instance) + // if (! BIT_X_IS_SET_IN_REG(I2C_SR2_BIT_TRA, SR2)) { } + // update: this should be caught by the ARLO error: so we will not arrive here + + // Send First max 2 bytes + regs->DR = trans->buf[0]; + if (trans->len_w > 1) + { + regs->DR = 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) + regs->CR2 |= I2C_CR2_BIT_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_BIT_TXE, SR1) ) && (BIT_X_IS_SET_IN_REG(I2C_CR2_BIT_ITBUFEN, regs->CR2)) ) + { + // Send the next byte + regs->DR = 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 + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + // Next interrupt will be BTF (or error) + } + } + // BTF: means last byte was sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_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(I2C_TypeDef *regs, struct i2c_periph *periph, struct i2c_transaction *trans) +{ + uint16_t SR1 = regs->SR1; + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + regs->DR = trans->slave_addr | 0x01; + + // Document the current Status + periph->status = I2CAddrRdSent; + } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_ADDR, SR1) ) + { + // First Clear the ACK bit: after the next byte we do not want new bytes + regs->CR1 &= ~ I2C_CR1_BIT_POS; + regs->CR1 &= ~ I2C_CR1_BIT_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)) = regs->SR2; + + // Schedule a Stop + PPRZ_I2C_SEND_STOP(regs); + + __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 + regs->CR2 |= I2C_CR2_BIT_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_BIT_RXNE, SR1) ) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + trans->buf[0] = regs->DR; + + // 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(I2C_TypeDef *regs, struct i2c_periph *periph, struct i2c_transaction *trans) +{ + uint16_t SR1 = regs->SR1; + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + // according to the datasheet: instantly shedule a NAK on the second received byte: + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + regs->CR1 |= I2C_CR1_BIT_ACK; + regs->CR1 |= I2C_CR1_BIT_POS; + regs->DR = trans->slave_addr | 0x01; + + // Document the current Status + periph->status = I2CAddrRdSent; + } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_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)) = regs->SR2; + + // NOT First Clear the ACK bit but only AFTER clearing ADDR + regs->CR1 &= ~ I2C_CR1_BIT_ACK; + + // Disable the RXNE and wait for BTF + regs->CR2 &= ~ I2C_CR2_BIT_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_BIT_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(regs); + + // Document the current Status + periph->status = I2CStopRequested; + + trans->buf[0] = regs->DR; + trans->buf[1] = regs->DR; + + // 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(I2C_TypeDef *regs, struct i2c_periph *periph, struct i2c_transaction *trans) +{ + uint16_t SR1 = regs->SR1; + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + // The first data byte will be acked in read many so the slave knows it should send more + regs->CR1 &= ~ I2C_CR1_BIT_POS; + regs->CR1 |= I2C_CR1_BIT_ACK; + // Clear the SB flag + regs->DR = trans->slave_addr | 0x01; + + // Document the current Status + periph->status = I2CAddrRdSent; + } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_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)) + { + regs->CR2 |= I2C_CR2_BIT_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)) = regs->SR2; + + // 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_BIT_RXNE, SR1) ) && (BIT_X_IS_SET_IN_REG(I2C_CR2_BIT_ITBUFEN, regs->CR2)) ) + { + // 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] = regs->DR; + periph->idx_buf ++; + } + // from : 3bytes -> last byte: do nothing + // + // finally: this was the last byte + else if (periph->idx_buf >= (trans->len_r - 1)) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + + // Last Value + trans->buf[periph->idx_buf] = regs->DR; + 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)) + { + regs->CR2 |= I2C_CR2_BIT_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) + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + } + } + // Buffer is full while this was not a RXNE interrupt + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_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 + regs->CR1 &= ~ I2C_CR1_BIT_ACK; + + // Now that ACK is cleared we read one byte: instantly the last byte is being clocked in... + trans->buf[periph->idx_buf] = regs->DR; + 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(regs); + + __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] = regs->DR; + 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 + regs->CR2 |= I2C_CR2_BIT_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_GetITStatus(periph->reg_addr, I2C_IT_AF)) { /* Acknowledge failure */ + periph->errors->ack_fail_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_AF); + err_nr = 1; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_BERR)) { /* Misplaced Start or Stop condition */ + periph->errors->miss_start_stop_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_BERR); + err_nr = 2; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_ARLO)) { /* Arbitration lost */ + periph->errors->arb_lost_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_ARLO); + err_nr = 3; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_OVR)) { /* Overrun/Underrun */ + periph->errors->over_under_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_OVR); + err_nr = 4; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_PECERR)) { /* PEC Error in reception */ + periph->errors->pec_recep_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_PECERR); + err_nr = 5; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_TIMEOUT)) { /* Timeout or Tlow error */ + periph->errors->timeout_tlow_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_TIMEOUT); + err_nr = 6; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_SMBALERT)) { /* SMBus alert */ + periph->errors->smbus_alert_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_SMBALERT); + err_nr = 7; + } + +#ifdef I2C_DEBUG_LED + LED_ERROR(20, err_nr); +#endif + + return; +} + + +static inline void stmi2c_clear_pending_interrupts(I2C_TypeDef *regs) +{ + uint16_t SR1 = regs->SR1; + + // Certainly do not wait for buffer interrupts: + // ------------------------------------------- + regs->CR2 &= ~ I2C_CR2_BIT_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_BIT_SB, SR1 ) ) + { + // SB: cleared by software when reading SR1 and writing to DR + regs->DR = 0x00; + } + // Address Was Sent + if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_ADDR, SR1) ) + { + // ADDR: Cleared by software when reading SR1 and then SR2 + uint16_t SR2 __attribute__ ((unused)) = regs->SR2; + } + // Byte Transfer Finished + if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_BTF, SR1) ) + { + // SB: cleared by software when reading SR1 and reading/writing to DR + uint8_t dummy __attribute__ ((unused)) = regs->DR; + regs->DR = 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 ... + I2C_TypeDef *regs = (I2C_TypeDef *) 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(regs); + + // 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 (( regs->SR1 & I2C_SR1_BITS_ERR ) != 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(regs); + + // 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(regs,periph,trans); + break; + case 2: + ret = stmi2c_read2(regs,periph,trans); + break; + default: + ret = stmi2c_readmany(regs,periph,trans); + break; + } + } + else // TxRx or Tx + { + ret = stmi2c_send(regs,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(regs); + } + + // RxTx -> Restart and do Rx part + if (trans->type == I2CTransTxRx) + { + trans->type = I2CTransRx; + periph->status = I2CStartRequested; + regs->CR1 |= I2C_CR1_BIT_START; + + // Silent any BTF that would occur before SB + regs->DR = 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(regs); + + // Silent any BTF that would occur before STOP is executed + regs->DR = 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 = I2C1; + i2c1.init_struct = &I2C1_InitStruct; + i2c1.scl_pin = GPIO_Pin_6; + i2c1.sda_pin = GPIO_Pin_7; + 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); + + 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 = 0; + 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 = 1; + 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); + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = i2c1.scl_pin | i2c1.sda_pin; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + I2C_DeInit(I2C1); + + // enable peripheral + I2C_Cmd(I2C1, ENABLE); + + I2C_Init(I2C1, i2c1.init_struct); + + // enable error interrupts + I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE); + + i2c_setbitrate(&i2c2, I2C2_InitStruct.I2C_ClockSpeed); +#endif +} + +void i2c1_ev_irq_handler(void) { + I2C_TypeDef *regs = (I2C_TypeDef *) i2c1.reg_addr; + regs->CR2 &= ~ I2C_CR2_BIT_ITERREN; + i2c_irq(&i2c1); + i2c1_watchdog_counter = 0; + regs->CR2 |= I2C_CR2_BIT_ITERREN; +} + +void i2c1_er_irq_handler(void) { + I2C_TypeDef *regs = (I2C_TypeDef *) i2c1.reg_addr; + regs->CR2 &= ~ I2C_CR2_BIT_ITEVTEN; + i2c_irq(&i2c1); + i2c1_watchdog_counter = 0; + regs->CR2 |= I2C_CR2_BIT_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 = I2C2; + i2c2.init_struct = &I2C2_InitStruct; + i2c2.scl_pin = GPIO_Pin_10; + i2c2.sda_pin = GPIO_Pin_11; + 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); + + 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); + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = i2c2.scl_pin | i2c2.sda_pin; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + I2C_DeInit(I2C2); + + // enable peripheral + I2C_Cmd(I2C2, ENABLE); + + I2C_Init(I2C2, i2c2.init_struct); + + // enable error interrupts + I2C_ITConfig(I2C2, I2C_IT_ERR, ENABLE); + + i2c_setbitrate(&i2c2, I2C2_InitStruct.I2C_ClockSpeed); +} + +void i2c2_ev_irq_handler(void) { + I2C_TypeDef *regs = (I2C_TypeDef *) i2c2.reg_addr; + regs->CR2 &= ~ I2C_CR2_BIT_ITERREN; + i2c_irq(&i2c2); + i2c2_watchdog_counter = 0; + regs->CR2 |= I2C_CR2_BIT_ITERREN; +} + +void i2c2_er_irq_handler(void) { + I2C_TypeDef *regs = (I2C_TypeDef *) i2c2.reg_addr; + regs->CR2 &= ~ I2C_CR2_BIT_ITEVTEN; + i2c_irq(&i2c2); + i2c2_watchdog_counter = 0; + regs->CR2 |= I2C_CR2_BIT_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; + + I2C_TypeDef *regs = (I2C_TypeDef *) periph->reg_addr; + + // store (just for fun) + I2C2_InitStruct.I2C_ClockSpeed = bitrate; + +/***************************************************** + 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 + regs->CR1 &= ~ I2C_CR1_BIT_PE; + + // 1) + regs->CR2 = 0x0324; + // 2) + //regs->CCR = 0x8000 + devider; + regs->CCR = 0x0000 + devider; + // 3) + regs->TRISE = risetime; + + // Re-Enable + regs->CR1 |= I2C_CR1_BIT_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 + + I2C_TypeDef *regs = (I2C_TypeDef *) 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_BIT_BUSY, regs->SR2 ) ); + else + return FALSE; +} + + diff --git a/sw/airborne/arch/stm32/mcu_periph/obsolete/i2c_attempt1.c b/sw/airborne/arch/stm32/mcu_periph/obsolete/i2c_attempt1.c new file mode 100644 index 0000000000..5a0f71c84d --- /dev/null +++ b/sw/airborne/arch/stm32/mcu_periph/obsolete/i2c_attempt1.c @@ -0,0 +1,659 @@ +#include "mcu_periph/i2c.h" + +#include +#include +#include +#include + +//#include "led.h" + +/////////// DEBUGGING ////////////// + +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() +{ + 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 nr) +{ + for (int i=0;i<20;i++) + { + LED1_ON(); + LED1_OFF(); + if (nr == i) + LED2_OFF(); + else + LED2_ON(); + LED2_OFF(); + } +} + +static inline void LED_STROBE2(void) +{ +LED2_ON(); +LED2_OFF(); +LED1_ON(); +LED1_OFF(); +LED2_ON(); +LED2_OFF(); +LED1_ON(); +LED1_OFF(); +LED1_OFF(); +LED1_OFF(); +LED1_OFF(); + +} + +////////////////////////////////////// + +#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) { } +#define OUT_OF_SYNC_STATE_MACHINE(_periph, _status, _event) { } +#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 + + +#ifdef USE_I2C2 + + + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +// IDLE CHECK + +static bool_t PPRZ_I2C_IS_IDLE(struct i2c_periph* periph) +{ + return I2C_GetFlagStatus(periph->reg_addr, I2C_FLAG_BUSY) == RESET; +} + +// (RE)START + +static inline void PPRZ_I2C_SEND_START(struct i2c_periph *periph) +{ + periph->idx_buf = 0; + I2C_GenerateSTART(periph->reg_addr, ENABLE); + I2C_ITConfig(periph->reg_addr, I2C_IT_EVT | I2C_IT_ERR, ENABLE); + I2C_ITConfig(periph->reg_addr, I2C_IT_BUF, DISABLE); +} + +static void PPRZ_I2C_START_NEXT_TRANSACTION(struct i2c_periph* periph) +{ + /* if we have no more transaction to process, stop here */ + if (periph->trans_extract_idx == periph->trans_insert_idx) + { + // Should we disable just in case? normally not. So if more interrupts are + // triggered there is a problem and we want to know. + // I2C_ITConfig(periph->reg_addr, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE); + periph->status = I2CIdle; + } + /* if not, start next transaction */ + else + { + periph->status = I2CStartRequested; + PPRZ_I2C_SEND_START(periph); + } +} + +static inline void PPRZ_I2C_RESTART(struct i2c_periph *periph) +{ +//LED2_ON(); +// I2C_GenerateSTOP(periph->reg_addr, ENABLE); +// I2C_SendData(periph->reg_addr, 0); +// I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_BTF); + periph->status = I2CRestartRequested; + PPRZ_I2C_SEND_START(periph); +} + +// STOP + +static inline void PPRZ_I2C_HAS_FINISHED(struct i2c_periph *periph, struct i2c_transaction *trans, enum I2CTransactionStatus _status) +{ + // Finish Current + trans->status = _status; + + // When finished successfully the I2C_FLAG_MLS will be cleared after the stop condition was issued. + // However: we do not need to wait for it to go the the next step, but if no stop condition was + // sent yet than we are still talking to the same slave... + // When we are here all paths to this function with success have already issued a STOP, the others not. + // Man: p722: Stop generation after the current byte transfer or after the current Start condition is sent. + if (_status != I2CTransSuccess) + { + // TODO: we might need to do much more here: see reset functions of antoine... + I2C_GenerateSTOP(periph->reg_addr, ENABLE); + } + + // Jump to the next + periph->trans_extract_idx++; + if (periph->trans_extract_idx >= I2C_TRANSACTION_QUEUE_LEN) + periph->trans_extract_idx = 0; + + PPRZ_I2C_START_NEXT_TRANSACTION(periph); +} + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +static inline void i2c_event(struct i2c_periph *periph) +{ + // Referring to manual: + // -Doc ID 13902 Rev 11 + + + // Check to make sure that user space has an active transaction pending + if (periph->trans_extract_idx == periph->trans_insert_idx) + { + // no transaction? + periph->errors->unexpected_event_cnt++; + return; + } + + struct i2c_transaction* trans = periph->trans[periph->trans_extract_idx]; + + /* + There are 7 possible reasons to get here: + + 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 + [3 ADDR10] // -- 10bit address stuff + [4 STOPF] // -- only for slaves: master has no stop interrupt + 5) 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: + -when receiving 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) + -when transmitting, and 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 serializer 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: the order in which Status is read determines how flags are cleared. + + If IT_EV_FEN AND IT_EV_BUF + -------------------------- + + We are always interested in buffer interrupts IT_EV_BUF except in transmission when all data was sent + + 6) RxNE + 7) TxE + + -------------------------------------------------------------------------------------------------- + // This driver uses only a subset of the pprz_i2c_states for several reasons: + // -we have less interrupts than the I2CStatus states (for efficiency) + // -status register flags better correspond to reality, especially in case of I2C errors + + enum I2CStatus { + I2CIdle, // Dummy: Actual I2C Peripheral idle detection is safer with the + // hardware status register flag I2C_FLAG_BUSY. + + I2CStartRequested, // EV5: used to differentiate S en Sr + I2CRestartRequested, // EV5: used to differentiate S en Sr + + I2CSendingByte, // Not used: using the hardware status reg I2C_FLAG_TRA + I2CReadingByte, + I2CAddrWrSent, // Since we can do many things at once and we + I2CAddrRdSent, // have buffered sending, these states + I2CSendingLastByte, // do not correspond to the real state of the + I2CReadingLastByte, // STM I2C driver so they are not used + I2CStopRequested, + + I2CComplete, // Used to provide the result + I2CFailed + }; + + --------- + + The STM waits (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 + + */ + + + /////////////////////////////////////////////////////////////////////////////////// + // 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 + + + LED1_ON(); + + //I2C_TypeDef *regs = (I2C_TypeDef *) periph->reg_addr; + + //__disable_irq(); + uint32_t event = I2C_GetLastEvent(periph->reg_addr); + + //uint32_t event = (((uint32_t)(regs->SR2)) << 16) + regs->SR1; + + if (event & I2C_FLAG_TXE) + { + LED2_ON(); + } + + + /////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////// + // START: Start Condition in Master Mode: + // STM Manual Ev5 + if (event & I2C_FLAG_SB) + { + // Periph was waiting for Start + if (periph->status == I2CStartRequested) + { + // Send Read Slave Address + if(trans->type == I2CTransRx) + { + I2C_Send7bitAddress(periph->reg_addr, trans->slave_addr, I2C_Direction_Receiver); + } + // Send Write Slave Address + else + { + I2C_Send7bitAddress(periph->reg_addr, trans->slave_addr, I2C_Direction_Transmitter); + } + I2C_ITConfig(periph->reg_addr, I2C_IT_BUF, ENABLE); + } + // Waiting for Restart: Always Rx + else if (periph->status == I2CRestartRequested) + { + I2C_Send7bitAddress(periph->reg_addr, trans->slave_addr, I2C_Direction_Receiver); + I2C_ITConfig(periph->reg_addr, I2C_IT_BUF, ENABLE); + } + // Problem: this problem need to be triggerd as if the + // status was not OK then the buf size is also bad + else + { + PPRZ_I2C_HAS_FINISHED(periph, trans, I2CTransFailed); + } + periph->status = I2CAddrWrSent; + } + + /////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////// + // TRANSMIT: Buffer Can accept the next byte for transmission + // --> this means we HAVE TO fill the buffer and/or disable buf interrupts (otherwise this interrupt + // will be triggered until a start/stop occurs which can be quite long = many spurrious interrupts) + // STM Manual Ev8 + else if (event & I2C_FLAG_TXE) // only possible when TRA(nsmitter) and no flag tra/start/stop/addr + { + if (periph->status == I2CRestartRequested) + { + // Neglect this interrupt: We just issued the restart last session already + PPRZ_I2C_RESTART(periph); + } + + // Do we have more data? and there is buffer room? + // (neglect BTF: if it was set it just means we were too slow) + else if (periph->idx_buf < trans->len_w) + { + I2C_SendData(periph->reg_addr, trans->buf[periph->idx_buf]); + periph->idx_buf++; + // Was this one the Last? -> Disable the buf interrupt (until next start) and wait for BTF + // -we could gain a bit of efficiency by already starting the next action but for + // code-readability we will wait until the last tx-byte is sent + if (periph->idx_buf >= trans->len_w) + { + I2C_ITConfig(periph->reg_addr, I2C_IT_BUF, DISABLE); + // If this is followed by a restart: then we need to set the startbit to avoid extra interrupts. + if (trans->type != I2CTransTx) + { + I2C_GenerateSTOP(periph->reg_addr, ENABLE); + } + } + } + + else if (event & I2C_FLAG_BTF) + { + // Ready -> Stop + if (trans->type == I2CTransTx) + { + // STM Manual Ev8_2 + I2C_GenerateSTOP(periph->reg_addr, ENABLE); + PPRZ_I2C_HAS_FINISHED(periph, trans, I2CTransSuccess); + } + // Rx/Trans -> Restart: + // Do not wait for BTF + } + // If we had no more data but got no BTF then there is a problem + else + { + PPRZ_I2C_HAS_FINISHED(periph, trans, I2CTransFailed); + } + } + + /////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////// + // RECEIVE: + // while receiving: the master needs to signal to the slave if more data is needed + else if ((event & I2C_FLAG_ADDR) || (event & I2C_FLAG_RXNE)) + { + // data is available every time RXNE is set. If BTF is set it means that 2 bytes are + // ready to read (one in shift register) and I2C has stopped until the buffer can accept new data. + 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++; + } + } + + // This last byte has arrived + if (periph->idx_buf >= trans->len_r) + { + PPRZ_I2C_HAS_FINISHED(periph, trans, I2CTransSuccess); + } + // Tell the Slave it will be the last one + else if (periph->idx_buf >= trans->len_r-1) + { + 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 + } + // Ask the Slave to send more + else + { + I2C_AcknowledgeConfig(periph->reg_addr, ENABLE); + } + + } + + // Now re-enable IRQ... it's been too long + // __enable_irq(); + + + + LED2_OFF(); + LED1_OFF(); + + +} + +static inline void i2c_error(struct i2c_periph *periph) +{ + uint8_t err_nr = 0; + periph->errors->er_irq_cnt; + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_AF)) { /* Acknowledge failure */ + periph->errors->ack_fail_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_AF); + err_nr = 1; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_BERR)) { /* Misplaced Start or Stop condition */ + periph->errors->miss_start_stop_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_BERR); + err_nr = 2; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_ARLO)) { /* Arbitration lost */ + periph->errors->arb_lost_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_ARLO); + // I2C_AcknowledgeConfig(I2C2, DISABLE); + // uint8_t dummy __attribute__ ((unused)) = I2C_ReceiveData(I2C2); + // I2C_GenerateSTOP(I2C2, ENABLE); + err_nr = 3; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_OVR)) { /* Overrun/Underrun */ + periph->errors->over_under_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_OVR); + err_nr = 4; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_PECERR)) { /* PEC Error in reception */ + periph->errors->pec_recep_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_PECERR); + err_nr = 5; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_TIMEOUT)) { /* Timeout or Tlow error */ + periph->errors->timeout_tlow_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_TIMEOUT); + err_nr = 6; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_SMBALERT)) { /* SMBus alert */ + periph->errors->smbus_alert_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_SMBALERT); + err_nr = 7; + } + + + LED_ERROR(err_nr); + + + // Check to make sure that user space has an active transaction pending + if (periph->trans_extract_idx == periph->trans_insert_idx) + { + // no transaction? + periph->errors->unexpected_event_cnt++; + err_nr = 8; + return; + } + + struct i2c_transaction* trans = periph->trans[periph->trans_extract_idx]; + //abort_and_reset(p); + + + + PPRZ_I2C_HAS_FINISHED(periph, trans, I2CTransFailed); + + +} + + +/* + // 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); + } +*/ + +#endif /* USE_I2C2 */ + + + + +#ifdef USE_I2C1 + +struct i2c_errors i2c1_errors; + +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); + + // Extra + LED_INIT(); +} + +void i2c1_ev_irq_handler(void) { + i2c_event(&i2c1); +} + +void i2c1_er_irq_handler(void) { + i2c_error(&i2c1); +} + +#endif /* USE_I2C1 */ + +#ifdef USE_I2C2 + +struct i2c_errors i2c2_errors; + +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); + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = i2c2.scl_pin | i2c2.sda_pin; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + I2C_DeInit(I2C2); + + // enable peripheral + I2C_Cmd(I2C2, ENABLE); + + I2C_Init(I2C2, i2c2.init_struct); + +// I2C_SoftwareResetCmd(I2C2, ENABLE); +// I2C_SoftwareResetCmd(I2C2, DISABLE); + + // Reset and initialize I2C HW + // enable error interrupts + I2C_ITConfig(I2C2, I2C_IT_ERR, ENABLE); + +// i2c_reset_init(&i2c2); + +} + + +void i2c2_ev_irq_handler(void) { + i2c_event(&i2c2); +} + +void i2c2_er_irq_handler(void) { + i2c_error(&i2c2); +} + +#endif /* USE_I2C2 */ + + + +///////////////////////////////////////////////////////// +// 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) + PPRZ_I2C_START_NEXT_TRANSACTION(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) +{ + return PPRZ_I2C_IS_IDLE(periph); + //return periph->status == I2CIdle; +} + + diff --git a/sw/airborne/arch/stm32/mcu_periph/obsolete/i2c_attempt2_nolib.c b/sw/airborne/arch/stm32/mcu_periph/obsolete/i2c_attempt2_nolib.c new file mode 100644 index 0000000000..852cd2d535 --- /dev/null +++ b/sw/airborne/arch/stm32/mcu_periph/obsolete/i2c_attempt2_nolib.c @@ -0,0 +1,1064 @@ +#include "mcu_periph/i2c.h" + +#include +#include +#include +#include + +//#include "led.h" + +//#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 + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +#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 = 37000 + .I2C_ClockSpeed = 400000 +}; +#endif + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +// 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 + +// 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 + + +// Bit Control + +#define BIT_X_IS_SET_IN_REG(X,REG) (((REG) & (X)) == (X)) + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +// (RE)START + +static inline void PPRZ_I2C_SEND_START(struct i2c_periph *periph) +{ + I2C_TypeDef *regs = (I2C_TypeDef *) periph->reg_addr; + + periph->idx_buf = 0; + periph->status = I2CStartRequested; + + if (BIT_X_IS_SET_IN_REG( I2C_CR1_BIT_STOP, regs->CR1 ) ) + { + regs->CR1 &= ~ I2C_CR1_BIT_STOP; + +#ifdef I2C_DEBUG_LED + LED2_ON(); + LED1_ON(); + LED1_OFF(); + LED1_ON(); + LED1_OFF(); + LED1_ON(); + LED1_OFF(); + LED1_ON(); + LED1_OFF(); + LED2_OFF(); +#endif + } + else + { +#ifdef I2C_DEBUG_LED + LED2_ON(); + LED1_ON(); + LED1_OFF(); + LED1_ON(); + LED1_OFF(); + LED1_ON(); + LED1_OFF(); + LED2_OFF(); +#endif + } + + // Issue a new start + regs->CR1 |= I2C_CR1_BIT_START; + + // Enable Error IRQ, Event IRQ but disable Buffer IRQ + regs->CR2 |= I2C_CR2_BIT_ITERREN; + regs->CR2 |= I2C_CR2_BIT_ITEVTEN; + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; +} + +// TODO: remove debug +#ifdef I2C_DEBUG_LED + +static inline void LED_SHOW_ACTIVE_BITS(uint16_t SR1) +{ + LED1_ON(); + + // Start + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + LED2_ON(); + else + LED2_OFF(); + LED2_OFF(); + + // Addr + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_ADDR, SR1 ) ) + LED2_ON(); + else + LED2_OFF(); + LED2_OFF(); + + // BTF + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_BTF, SR1 ) ) + LED2_ON(); + else + LED2_OFF(); + LED2_OFF(); + + // ERROR + if (( SR1 & I2C_SR1_BITS_ERR ) != 0x0000) + LED2_ON(); + else + LED2_OFF(); + LED2_OFF(); + + + LED1_OFF(); +} +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// SUBTRANSACTION SEQUENCES + +enum STMI2CSubTransactionStatus { + STMI2C_SubTra_Busy, + STMI2C_SubTra_Ready_StopRequested, + STMI2C_SubTra_Ready +}; + +static inline enum STMI2CSubTransactionStatus stmi2c_send(I2C_TypeDef *regs, struct i2c_periph *periph, struct i2c_transaction *trans) +{ + uint16_t SR1 = regs->SR1; + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + // Disable buffer interrupt + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + // Send Slave address and wait for ADDR interrupt + regs->DR = trans->slave_addr; + } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_ADDR, SR1) ) + { + // Now read SR2 to clear the ADDR + uint16_t SR2 __attribute__ ((unused)) = regs->SR2; + + // Maybe check we are transmitting (did not loose arbitration for instance) + // if (! BIT_X_IS_SET_IN_REG(I2C_SR2_BIT_TRA, SR2)) { } + + // Send First max 2 bytes + regs->DR = trans->buf[0]; + if (trans->len_w > 1) + { + regs->DR = 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) + regs->CR2 |= I2C_CR2_BIT_ITBUFEN; + } + // The buffer is not full anymore AND we were not waiting for BTF + else if ((BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_TXE, SR1) ) && (BIT_X_IS_SET_IN_REG(I2C_CR2_BIT_ITBUFEN, regs->CR2)) ) + { + // Send the next byte + regs->DR = 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 + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + // Next interrupt will be BTF (or error) + } + } + // BTF: means last byte was sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_BTF, SR1) ) + { + if (trans->type == I2CTransTx) + { + // Tell the driver we are ready + trans->status = I2CTransSuccess; + } + + return STMI2C_SubTra_Ready; + } + + return STMI2C_SubTra_Busy; +} + +static inline enum STMI2CSubTransactionStatus stmi2c_read1(I2C_TypeDef *regs, struct i2c_transaction *trans) +{ + uint16_t SR1 = regs->SR1; + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + regs->DR = trans->slave_addr | 0x01; + } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_ADDR, SR1) ) + { + // First Clear the ACK bit: after the next byte we do not want new bytes + regs->CR1 &= ~ I2C_CR1_BIT_POS; + regs->CR1 &= ~ I2C_CR1_BIT_ACK; + + // --- next to steps MUST be executed together to avoid missing the stop + __disable_irq(); + + // Only after setting ACK, read SR2 to clear the ADDR (next byte will start arriving) + uint16_t SR2 __attribute__ ((unused)) = regs->SR2; + + // Schedule a Stop + regs->CR1 |= I2C_CR1_BIT_STOP; + + __enable_irq(); + // --- end of critical zone ----------- + +#ifdef I2C_DEBUG_LED + // Program a stop + LED2_ON(); + LED1_ON(); + LED1_OFF(); + LED2_OFF(); +#endif + + // Enable the RXNE to get the result + regs->CR2 |= I2C_CR2_BIT_ITBUFEN; + } + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_RXNE, SR1) ) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + trans->buf[0] = regs->DR; + + // We got all the results (stop condition might still be in progress but this is the last interrupt) + trans->status = I2CTransSuccess; + + return STMI2C_SubTra_Ready_StopRequested; + } + + return STMI2C_SubTra_Busy; +} + +static inline enum STMI2CSubTransactionStatus stmi2c_read2(I2C_TypeDef *regs, struct i2c_transaction *trans) +{ + uint16_t SR1 = regs->SR1; + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + regs->CR1 |= I2C_CR1_BIT_ACK; + regs->CR1 |= I2C_CR1_BIT_POS; + regs->DR = trans->slave_addr | 0x01; + } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_ADDR, SR1) ) + { + // 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)) = regs->SR2; + + // --- make absolutely sure this command is not delayed too much after the previous: + __disable_irq(); + // if transfer of DR was finished already then we will get too many bytes + // NOT First Clear the ACK bit but only AFTER clearing ADDR + regs->CR1 &= ~ I2C_CR1_BIT_ACK; + + // Disable the RXNE and wait for BTF + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + + __enable_irq(); + // --- end of critical zone ----------- + } + // Receive buffer if full, master is halted: BTF + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_BTF, SR1) ) + { + // Stop condition MUST be set BEFORE reading the DR + // otherwise since there is new buffer space a new byte will be read + regs->CR1 |= I2C_CR1_BIT_STOP; + // Program a stop +#ifdef I2C_DEBUG_LED + LED2_ON(); + LED1_ON(); + LED1_OFF(); + LED2_OFF(); +#endif + + trans->buf[0] = regs->DR; + trans->buf[1] = regs->DR; + + // We got all the results + trans->status = I2CTransSuccess; + + return STMI2C_SubTra_Ready_StopRequested; + } + + return STMI2C_SubTra_Busy; +} + +static inline enum STMI2CSubTransactionStatus stmi2c_readmany(I2C_TypeDef *regs, struct i2c_periph *periph, struct i2c_transaction *trans) +{ + uint16_t SR1 = regs->SR1; + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + // The first data byte will be acked in read many so the slave knows it should send more + regs->CR1 &= ~ I2C_CR1_BIT_POS; + regs->CR1 |= I2C_CR1_BIT_ACK; + // Clear the SB flag + regs->DR = trans->slave_addr | 0x01; + } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_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)) + { + regs->CR2 |= I2C_CR2_BIT_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)) = regs->SR2; + } + // one or more bytes are available AND we were interested in Buffer interrupts + else if ( (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_RXNE, SR1) ) && (BIT_X_IS_SET_IN_REG(I2C_CR2_BIT_ITBUFEN, regs->CR2)) ) + { + // 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] = regs->DR; + periph->idx_buf ++; + } + // from : 3bytes -> last byte: do nothing + // + // finally: this was the last byte + else if (periph->idx_buf >= (trans->len_r - 1)) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + + // Last Value + trans->buf[periph->idx_buf] = regs->DR; + 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)) + { + regs->CR2 |= I2C_CR2_BIT_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) + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + } + } + // Buffer is full while this was not a RXNE interrupt + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_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 + __disable_irq(); + + // First we clear the ACK while the SCL is held low by BTF + regs->CR1 &= ~ I2C_CR1_BIT_ACK; + + // Now that ACK is cleared we read one byte: instantly the last byte is being clocked in... + trans->buf[periph->idx_buf] = regs->DR; + periph->idx_buf ++; + + // Now the last byte is being clocked. Stop in MUST be set BEFORE the transfer of the last byte is complete + regs->CR1 |= I2C_CR1_BIT_STOP; + // Program a stop +#ifdef I2C_DEBUG_LED + LED2_ON(); + LED1_ON(); + LED1_OFF(); + LED2_OFF(); +#endif + + __enable_irq(); + // --- end of critical zone ----------- + + // read the byte2 we had in the buffer (BTF means 2 bytes available) + trans->buf[periph->idx_buf] = regs->DR; + 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 + regs->CR2 |= I2C_CR2_BIT_ITBUFEN; + } + else + { + // Error +#ifdef I2C_DEBUG_LED + LED2_ON(); + LED1_ON(); + LED2_OFF(); + LED1_OFF(); +#endif + } + + return STMI2C_SubTra_Busy; +} + + +static inline void stmi2c_clear_pending_interrupts(I2C_TypeDef *regs) +{ + uint16_t SR1 = regs->SR1; + + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + + // Start Condition Was Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + // SB: cleared by software when reading SR1 and writing to DR + regs->DR = 0x00; + } + // Address Was Sent + if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_ADDR, SR1) ) + { + // ADDR: Cleared by software when reading SR1 and then SR2 + uint16_t SR2 __attribute__ ((unused)) = regs->SR2; + } + // Byte Transfer Finished + if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_BTF, SR1) ) + { + // SB: cleared by software when reading SR1 and reading/writing to DR + uint8_t dummy __attribute__ ((unused)) = regs->DR; + regs->DR = 0x00; + } +} + + +static inline void i2c_error(struct i2c_periph *periph); + +static inline void i2c_event(struct i2c_periph *periph) +{ + + /* + There are 7 possible reasons to get here: + + 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 + [3 ADDR10] // -- 10bit address stuff + [4 STOPF] // -- only for slaves: master has no stop interrupt + 5) 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 transmitmode: 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 stack even before it is + actually completely transmitted. But then you would not know the result yet so you have to keep it until the result + is known. + + // Beware: the order in which Status is read determines how flags are cleared. You should not just read SR1 & SR2 every time + + If IT_EV_FEN AND IT_EV_BUF + -------------------------- + + Buffer event are not always wanted and are tipically switched on during longer data transfers. Make sure to turn off in time. + + 6) RxNE + 7) TxE + + -------------------------------------------------------------------------------------------------- + // This driver uses only a subset of the pprz_i2c_states for several reasons: + // -we have less interrupts than the I2CStatus states (for efficiency) + // -STM32 has such a powerfull I2C engine with plenty of status register flags that + only little extra status information needs to be stored. + + // Status is re-used (abused) to remember the last COMMAND THAT WAS SENT to the STM I2C hardware. + +// TODO: check which are used + enum I2CStatus { + I2CIdle, // No more last command + + I2CStartRequested, // Last command was start + I2CRestartRequested, // Last command was restart + I2CStopRequested, // Very important to not send double stop conditions + + I2CSendingByte, // Some address/data operation + + // Following are not used + I2CReadingByte, + I2CAddrWrSent, + I2CAddrRdSent, + I2CSendingLastByte, + I2CReadingLastByte, + I2CComplete, + I2CFailed + }; + + --------- + + 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: use needs another way to finish. + + */ + + + /////////////////////////////////////////////////////////////////////////////////// + // 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 + + // Direct Access to the I2C Registers + // Do not read SR2 as it might start the reading while an (n)ack bit might be needed first + I2C_TypeDef *regs = (I2C_TypeDef *) periph->reg_addr; + +#ifdef I2C_DEBUG_LED + LED1_ON(); + LED1_OFF(); +#endif + + + /////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // TRANSACTION HANDLER + + enum STMI2CSubTransactionStatus ret = 0; + uint8_t restart = 0; + + // Nothing Left To Do + if (periph->trans_extract_idx == periph->trans_insert_idx) + { + periph->status = I2CIdle; + periph->errors->unexpected_event_cnt++; + + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; // Disable TXE, RXNE +#ifdef I2C_DEBUG_LED + LED2_ON(); + LED1_ON(); + LED2_OFF(); + LED1_OFF(); + + // no transaction and also an error? + LED_SHOW_ACTIVE_BITS(regs->SR1); +#endif + + stmi2c_clear_pending_interrupts(regs); + i2c_error(periph); + + // There are no transactions anymore: so we are not allowed to continue + return; + } + + struct i2c_transaction* trans = periph->trans[periph->trans_extract_idx]; + + if (( regs->SR1 & I2C_SR1_BITS_ERR ) != 0x0000) + { + // Set result in transaction + trans->status = I2CTransFailed; + + // Close the Bus + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; // Disable TXE RXNE + +#ifdef I2C_DEBUG_LED + LED1_ON(); + LED2_ON(); + LED1_OFF(); + LED2_OFF(); +#endif + + // Prepare for next + ret = STMI2C_SubTra_Ready; + restart = 0; + + stmi2c_clear_pending_interrupts(regs); + + // Count it + i2c_error(periph); + } + else + { + + if (trans->type == I2CTransRx) // TxRx are converted to Rx after the Tx Part + { + switch (trans->len_r) + { + case 1: + ret = stmi2c_read1(regs,trans); + break; + case 2: + ret = stmi2c_read2(regs,trans); + break; + default: + ret = stmi2c_readmany(regs,periph, trans); + break; + } + } + else // TxRx or Tx + { + ret = stmi2c_send(regs,periph,trans); + if (trans->type == I2CTransTxRx) + { + restart = 1; + } + } + } + + // Sub-transaction not finished + if (ret == STMI2C_SubTra_Busy) + { + // Remember the last command was not start or stop + periph->status = I2CSendingByte; + } + else // Finished? + { + if (restart == 0) + { + if (ret == STMI2C_SubTra_Ready) + { + // Program a stop +#ifdef I2C_DEBUG_LED + LED2_ON(); + LED1_ON(); + LED1_OFF(); + LED2_OFF(); +#endif + // Man: p722: Stop generation after the current byte transfer or after the current Start condition is sent. + regs->CR1 |= I2C_CR1_BIT_STOP; + + // Silent any BTF that would occur before STOP is executed + regs->DR = 0x00; + + periph->status = I2CStopRequested; + } + + // Jump to the next transaction + periph->trans_extract_idx++; + if (periph->trans_extract_idx >= I2C_TRANSACTION_QUEUE_LEN) + periph->trans_extract_idx = 0; + + // if we have no more transaction to process, stop here + if (periph->trans_extract_idx == periph->trans_insert_idx) + { + periph->status = I2CIdle; +#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 + periph->status = I2CStartRequested; + PPRZ_I2C_SEND_START(periph); + } + + } + // RxTx -> Restart and do Rx part + else + { + trans->type = I2CTransRx; + periph->status = I2CStartRequested; + regs->CR1 |= I2C_CR1_BIT_START; + + // Silent any BTF that would occur before SB + regs->DR = 0x00; + } + } + + return; +} + +static inline void i2c_error(struct i2c_periph *periph) +{ + uint8_t err_nr = 0; + periph->errors->er_irq_cnt; + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_AF)) { /* Acknowledge failure */ + periph->errors->ack_fail_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_AF); + err_nr = 1; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_BERR)) { /* Misplaced Start or Stop condition */ + periph->errors->miss_start_stop_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_BERR); + err_nr = 2; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_ARLO)) { /* Arbitration lost */ + periph->errors->arb_lost_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_ARLO); + err_nr = 3; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_OVR)) { /* Overrun/Underrun */ + periph->errors->over_under_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_OVR); + err_nr = 4; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_PECERR)) { /* PEC Error in reception */ + periph->errors->pec_recep_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_PECERR); + err_nr = 5; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_TIMEOUT)) { /* Timeout or Tlow error */ + periph->errors->timeout_tlow_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_TIMEOUT); + err_nr = 6; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_SMBALERT)) { /* SMBus alert */ + periph->errors->smbus_alert_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_SMBALERT); + err_nr = 7; + } + +#ifdef I2C_DEBUG_LED + LED_ERROR(20, err_nr); +#endif + + 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); + } +*/ + +//#endif /* USE_I2C2 */ + + + + +#ifdef USE_I2C1 + +struct i2c_errors i2c1_errors; + +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); + + // Extra +#ifdef I2C_DEBUG_LED + LED_INIT(); +#endif +} + +void i2c1_ev_irq_handler(void) { + i2c_event(&i2c1); +} + +void i2c1_er_irq_handler(void) { + i2c_event(&i2c1); +} + +#endif /* USE_I2C1 */ + +#ifdef USE_I2C2 + +struct i2c_errors i2c2_errors; + +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); + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = i2c2.scl_pin | i2c2.sda_pin; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + I2C_DeInit(I2C2); + + // enable peripheral + I2C_Cmd(I2C2, ENABLE); + + I2C_Init(I2C2, i2c2.init_struct); + + // enable error interrupts + I2C_ITConfig(I2C2, I2C_IT_ERR, ENABLE); + +} + + +// TODO: this was a testing routine +void i2c2_setbitrate(int bitrate) +{ + I2C2_InitStruct.I2C_ClockSpeed = bitrate; + I2C_Init(I2C2, i2c2.init_struct); +} + + + +void i2c2_ev_irq_handler(void) { + i2c_event(&i2c2); +} + +void i2c2_er_irq_handler(void) { + i2c_event(&i2c2); +} + +#endif /* USE_I2C2 */ + + + +///////////////////////////////////////////////////////// +// 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) + { + // TODO: re-enable I2C1 + if (periph == &i2c1) + { +/* + LED2_ON(); + LED1_ON(); + LED1_OFF(); + LED1_ON(); + LED1_OFF(); + LED1_ON(); + LED1_OFF(); + LED1_ON(); + LED1_OFF(); + LED2_OFF(); +*/ + } + else + { + 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) +{ + return I2C_GetFlagStatus(periph->reg_addr, I2C_FLAG_BUSY) == RESET; + //return periph->status == I2CIdle; +} + + diff --git a/sw/airborne/arch/stm32/mcu_periph/obsolete/i2c_attempt3_subtra.c b/sw/airborne/arch/stm32/mcu_periph/obsolete/i2c_attempt3_subtra.c new file mode 100644 index 0000000000..91988428bd --- /dev/null +++ b/sw/airborne/arch/stm32/mcu_periph/obsolete/i2c_attempt3_subtra.c @@ -0,0 +1,1286 @@ +#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 + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +#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 = 37000 + .I2C_ClockSpeed = 400000 +}; +#endif + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +// 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 + +// 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 + + +// Bit Control + +#define BIT_X_IS_SET_IN_REG(X,REG) (((REG) & (X)) == (X)) + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +// 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(); + + 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 + +static inline void PPRZ_I2C_SEND_STOP(I2C_TypeDef *regs) +{ + // Man: p722: Stop generation after the current byte transfer or after the current Start condition is sent. + regs->CR1 |= I2C_CR1_BIT_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) +{ + I2C_TypeDef *regs = (I2C_TypeDef *) periph->reg_addr; + + 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 + +/* + if (BIT_X_IS_SET_IN_REG( I2C_CR1_BIT_STOP, regs->CR1 ) ) + { + regs->CR1 &= ~ I2C_CR1_BIT_STOP; + } +*/ + + // Enable Error IRQ, Event IRQ but disable Buffer IRQ + regs->CR2 |= I2C_CR2_BIT_ITERREN; + regs->CR2 |= I2C_CR2_BIT_ITEVTEN; + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + + // Issue a new start + regs->CR1 = (I2C_CR1_BIT_START | I2C_CR1_BIT_PE); + periph->status = I2CStartRequested; + + +#ifdef I2C_DEBUG_LED + LED_SHOW_ACTIVE_BITS(regs); +#endif +} + +// STOP + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// SUBTRANSACTION SEQUENCES + +enum STMI2CSubTransactionStatus { + STMI2C_SubTra_Busy, + STMI2C_SubTra_Ready_StopRequested, + STMI2C_SubTra_Ready +}; + +static inline enum STMI2CSubTransactionStatus stmi2c_send(I2C_TypeDef *regs, struct i2c_periph *periph, struct i2c_transaction *trans) +{ + uint16_t SR1 = regs->SR1; + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + // Disable buffer interrupt + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + // Send Slave address and wait for ADDR interrupt + regs->DR = trans->slave_addr; + } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_ADDR, SR1) ) + { + // Now read SR2 to clear the ADDR + uint16_t SR2 __attribute__ ((unused)) = regs->SR2; + + // Maybe check we are transmitting (did not loose arbitration for instance) + // if (! BIT_X_IS_SET_IN_REG(I2C_SR2_BIT_TRA, SR2)) { } + + // Send First max 2 bytes + regs->DR = trans->buf[0]; + if (trans->len_w > 1) + { + regs->DR = 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) + regs->CR2 |= I2C_CR2_BIT_ITBUFEN; + } + // The buffer is not full anymore AND we were not waiting for BTF + else if ((BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_TXE, SR1) ) && (BIT_X_IS_SET_IN_REG(I2C_CR2_BIT_ITBUFEN, regs->CR2)) ) + { + // Send the next byte + regs->DR = 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 + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + // Next interrupt will be BTF (or error) + } + } + // BTF: means last byte was sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_BTF, SR1) ) + { + if (trans->type == I2CTransTx) + { + // Tell the driver we are ready + trans->status = I2CTransSuccess; + } + + return STMI2C_SubTra_Ready; + } + + return STMI2C_SubTra_Busy; +} + +static inline enum STMI2CSubTransactionStatus stmi2c_read1(I2C_TypeDef *regs, struct i2c_transaction *trans) +{ + uint16_t SR1 = regs->SR1; + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + regs->DR = trans->slave_addr | 0x01; + } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_ADDR, SR1) ) + { + // First Clear the ACK bit: after the next byte we do not want new bytes + regs->CR1 &= ~ I2C_CR1_BIT_POS; + regs->CR1 &= ~ I2C_CR1_BIT_ACK; + + // --- next to steps MUST be executed together to avoid missing the stop + __disable_irq(); + + // Only after setting ACK, read SR2 to clear the ADDR (next byte will start arriving) + uint16_t SR2 __attribute__ ((unused)) = regs->SR2; + + // Schedule a Stop + PPRZ_I2C_SEND_STOP(regs); + + __enable_irq(); + // --- end of critical zone ----------- + + // Enable the RXNE to get the result + regs->CR2 |= I2C_CR2_BIT_ITBUFEN; + } + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_RXNE, SR1) ) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + trans->buf[0] = regs->DR; + + // We got all the results (stop condition might still be in progress but this is the last interrupt) + trans->status = I2CTransSuccess; + + return STMI2C_SubTra_Ready_StopRequested; + } + + return STMI2C_SubTra_Busy; +} + +static inline enum STMI2CSubTransactionStatus stmi2c_read2(I2C_TypeDef *regs, struct i2c_transaction *trans) +{ + uint16_t SR1 = regs->SR1; + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + regs->CR1 |= I2C_CR1_BIT_ACK; + regs->CR1 |= I2C_CR1_BIT_POS; + regs->DR = trans->slave_addr | 0x01; + } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_ADDR, SR1) ) + { + // 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)) = regs->SR2; + + // --- make absolutely sure this command is not delayed too much after the previous: + __disable_irq(); + // if transfer of DR was finished already then we will get too many bytes + // NOT First Clear the ACK bit but only AFTER clearing ADDR + regs->CR1 &= ~ I2C_CR1_BIT_ACK; + + // Disable the RXNE and wait for BTF + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + + __enable_irq(); + // --- end of critical zone ----------- + } + // Receive buffer if full, master is halted: BTF + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_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(regs); + + trans->buf[0] = regs->DR; + trans->buf[1] = regs->DR; + + // We got all the results + trans->status = I2CTransSuccess; + + return STMI2C_SubTra_Ready_StopRequested; + } + + return STMI2C_SubTra_Busy; +} + +static inline enum STMI2CSubTransactionStatus stmi2c_readmany(I2C_TypeDef *regs, struct i2c_periph *periph, struct i2c_transaction *trans) +{ + uint16_t SR1 = regs->SR1; + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + // The first data byte will be acked in read many so the slave knows it should send more + regs->CR1 &= ~ I2C_CR1_BIT_POS; + regs->CR1 |= I2C_CR1_BIT_ACK; + // Clear the SB flag + regs->DR = trans->slave_addr | 0x01; + } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_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)) + { + regs->CR2 |= I2C_CR2_BIT_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)) = regs->SR2; + } + // one or more bytes are available AND we were interested in Buffer interrupts + else if ( (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_RXNE, SR1) ) && (BIT_X_IS_SET_IN_REG(I2C_CR2_BIT_ITBUFEN, regs->CR2)) ) + { + // 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] = regs->DR; + periph->idx_buf ++; + } + // from : 3bytes -> last byte: do nothing + // + // finally: this was the last byte + else if (periph->idx_buf >= (trans->len_r - 1)) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + + // Last Value + trans->buf[periph->idx_buf] = regs->DR; + 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)) + { + regs->CR2 |= I2C_CR2_BIT_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) + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + } + } + // Buffer is full while this was not a RXNE interrupt + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_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 + __disable_irq(); + + // First we clear the ACK while the SCL is held low by BTF + regs->CR1 &= ~ I2C_CR1_BIT_ACK; + + // Now that ACK is cleared we read one byte: instantly the last byte is being clocked in... + trans->buf[periph->idx_buf] = regs->DR; + 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(regs); + + __enable_irq(); + // --- end of critical zone ----------- + + // read the byte2 we had in the buffer (BTF means 2 bytes available) + trans->buf[periph->idx_buf] = regs->DR; + 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 + regs->CR2 |= I2C_CR2_BIT_ITBUFEN; + } + else + { + // Error +#ifdef I2C_DEBUG_LED + LED2_ON(); + LED1_ON(); + LED2_OFF(); + LED1_OFF(); +#endif + } + + return STMI2C_SubTra_Busy; +} + + +static inline void stmi2c_clear_pending_interrupts(I2C_TypeDef *regs) +{ + uint16_t SR1 = regs->SR1; + + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; // Disable TXE, RXNE + + + // Start Condition Was Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + // SB: cleared by software when reading SR1 and writing to DR + regs->DR = 0x00; + } + // Address Was Sent + if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_ADDR, SR1) ) + { + // ADDR: Cleared by software when reading SR1 and then SR2 + uint16_t SR2 __attribute__ ((unused)) = regs->SR2; + } + // Byte Transfer Finished + if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_BTF, SR1) ) + { + // SB: cleared by software when reading SR1 and reading/writing to DR + uint8_t dummy __attribute__ ((unused)) = regs->DR; + regs->DR = 0x00; + } +} + + +static inline void i2c_error(struct i2c_periph *periph); + +static inline void i2c_irq(struct i2c_periph *periph) +{ + + /* + There are 7 possible reasons to get here: + + 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 + [3 ADDR10] // -- 10bit address stuff + [4 STOPF] // -- only for slaves: master has no stop interrupt + 5) 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 transmitmode: 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 stack even before it is + actually completely transmitted. But then you would not know the result yet so you have to keep it until the result + is known. + + // Beware: the order in which Status is read determines how flags are cleared. You should not just read SR1 & SR2 every time + + If IT_EV_FEN AND IT_EV_BUF + -------------------------- + + Buffer event are not always wanted and are tipically switched on during longer data transfers. Make sure to turn off in time. + + 6) RxNE + 7) TxE + + -------------------------------------------------------------------------------------------------- + // This driver uses only a subset of the pprz_i2c_states for several reasons: + // -we have less interrupts than the I2CStatus states (for efficiency) + // -STM32 has such a powerfull I2C engine with plenty of status register flags that + only little extra status information needs to be stored. + + // Status is re-used (abused) to remember the last COMMAND THAT WAS SENT to the STM I2C hardware. + +// TODO: check which are used + enum I2CStatus { + I2CIdle, // No more last command + + I2CStartRequested, // Last command was start + I2CRestartRequested, // Last command was restart + I2CStopRequested, // Very important to not send double stop conditions + + I2CSendingByte, // Some address/data operation + + // Following are not used + I2CReadingByte, + I2CAddrWrSent, + I2CAddrRdSent, + I2CSendingLastByte, + I2CReadingLastByte, + I2CComplete, + I2CFailed + }; + + --------- + + 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: use needs another way to finish. + + */ + + + /////////////////////////////////////////////////////////////////////////////////// + // 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 + + // Direct Access to the I2C Registers + // Do not read SR2 as it might start the reading while an (n)ack bit might be needed first + I2C_TypeDef *regs = (I2C_TypeDef *) periph->reg_addr; + +#ifdef I2C_DEBUG_LED + LED1_ON(); + LED1_OFF(); +#endif + + + /////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // TRANSACTION HANDLER + + enum STMI2CSubTransactionStatus ret = 0; + + /////////////////////// + // Nothing Left To Do + if (periph->trans_extract_idx == periph->trans_insert_idx) + { +#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) + // then its easy: just stop: clear all interrupt generating bits + + // Clear Running Events + stmi2c_clear_pending_interrupts(regs); + + // Count The Errors + i2c_error(periph); + + // Mark this as a special error + periph->errors->unexpected_event_cnt++; + + periph->status = I2CIdle; + + // There are no transactions anymore: + // furtheron we need a transaction pointer: so we are not allowed to continue + return; + } + + struct i2c_transaction* trans = periph->trans[periph->trans_extract_idx]; + + /////////////////////////// + // If there was an error: + if (( regs->SR1 & I2C_SR1_BITS_ERR ) != 0x0000) + { + +#ifdef I2C_DEBUG_LED + LED1_ON(); + LED2_ON(); + LED1_OFF(); + LED2_OFF(); + + LED_SHOW_ACTIVE_BITS(regs); +#endif + + // Set result in transaction + trans->status = I2CTransFailed; + + // Prepare for next + ret = STMI2C_SubTra_Ready; + + // Make sure a TxRx does not Restart + trans->type = I2CTransRx; + + // Clear Running Events + stmi2c_clear_pending_interrupts(regs); + + // Count The Errors + i2c_error(periph); + } + + + /////////////////////////// + // Normal Event: + else + { + + if (trans->type == I2CTransRx) // TxRx are converted to Rx after the Tx Part + { + switch (trans->len_r) + { + case 1: + ret = stmi2c_read1(regs,trans); + break; + case 2: + ret = stmi2c_read2(regs,trans); + break; + default: + ret = stmi2c_readmany(regs,periph, trans); + break; + } + } + else // TxRx or Tx + { + ret = stmi2c_send(regs,periph,trans); + } + } + + ///////////////////////////////// + // Sub-transaction has finished + if (ret != STMI2C_SubTra_Busy) + { + // If a restart is not needed + if (trans->type != I2CTransTxRx) + { + // Ready, no stop condition set yet + if (ret == STMI2C_SubTra_Ready) + { + + // Program a stop + PPRZ_I2C_SEND_STOP(regs); + + // Silent any BTF that would occur before STOP is executed + regs->DR = 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); +// ------ + } + + } + // RxTx -> Restart and do Rx part + else + { + trans->type = I2CTransRx; + periph->status = I2CStartRequested; + regs->CR1 |= I2C_CR1_BIT_START; + + // Silent any BTF that would occur before SB + regs->DR = 0x00; + } + } + + return; +} + +static inline void i2c_error(struct i2c_periph *periph) +{ + uint8_t err_nr = 0; + periph->errors->er_irq_cnt; + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_AF)) { /* Acknowledge failure */ + periph->errors->ack_fail_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_AF); + err_nr = 1; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_BERR)) { /* Misplaced Start or Stop condition */ + periph->errors->miss_start_stop_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_BERR); + err_nr = 2; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_ARLO)) { /* Arbitration lost */ + periph->errors->arb_lost_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_ARLO); + err_nr = 3; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_OVR)) { /* Overrun/Underrun */ + periph->errors->over_under_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_OVR); + err_nr = 4; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_PECERR)) { /* PEC Error in reception */ + periph->errors->pec_recep_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_PECERR); + err_nr = 5; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_TIMEOUT)) { /* Timeout or Tlow error */ + periph->errors->timeout_tlow_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_TIMEOUT); + err_nr = 6; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_SMBALERT)) { /* SMBus alert */ + periph->errors->smbus_alert_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_SMBALERT); + err_nr = 7; + } + +#ifdef I2C_DEBUG_LED + LED_ERROR(20, err_nr); +#endif + + 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); + } +*/ + +//#endif /* USE_I2C2 */ + + + + +#ifdef USE_I2C1 + +struct i2c_errors i2c1_errors; + +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); + + // Extra +#ifdef I2C_DEBUG_LED + LED_INIT(); +#else + + /* 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 = 0; + 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 = 1; + 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); + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = i2c1.scl_pin | i2c1.sda_pin; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + I2C_DeInit(I2C1); + + // enable peripheral + I2C_Cmd(I2C1, ENABLE); + + I2C_Init(I2C1, i2c1.init_struct); + + // enable error interrupts + I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE); + +#endif +} + +void i2c1_ev_irq_handler(void) { + i2c_irq(&i2c1); +} + +void i2c1_er_irq_handler(void) { + i2c_irq(&i2c1); +} + +#endif /* USE_I2C1 */ + +#ifdef USE_I2C2 + +struct i2c_errors i2c2_errors; + +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); + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = i2c2.scl_pin | i2c2.sda_pin; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + I2C_DeInit(I2C2); + + // enable peripheral + I2C_Cmd(I2C2, ENABLE); + + I2C_Init(I2C2, i2c2.init_struct); + + // enable error interrupts + I2C_ITConfig(I2C2, I2C_IT_ERR, ENABLE); + +} + + + +void i2c2_ev_irq_handler(void) { + i2c_irq(&i2c2); +} + +void i2c2_er_irq_handler(void) { + i2c_irq(&i2c2); +} + +#endif /* USE_I2C2 */ + + +void i2c_setbitrate(struct i2c_periph *periph, int bitrate) +{ + if (i2c_idle(periph)) + { + if (periph == &i2c2) + { + I2C2_InitStruct.I2C_ClockSpeed = bitrate; + I2C_Init(I2C2, i2c2.init_struct); + } + +#ifdef I2C_DEBUG_LED + __disable_irq(); + + LED2_ON(); + LED1_ON(); + LED2_OFF(); + LED1_OFF(); + LED2_ON(); + LED1_ON(); + LED2_OFF(); + LED1_OFF(); + + __enable_irq(); +#endif + + } +} + + +void i2c_event(void) +{ + static uint32_t cnt = 0; + I2C_TypeDef *regs; + cnt++; + if (cnt > 10000) cnt = 0; + +#ifndef I2C_DEBUG_LED +#ifdef USE_I2C1 + if (i2c1.status == I2CIdle) + { + if (i2c_idle(&i2c1)) + { + __disable_irq(); + // More work to do + if (i2c1.trans_extract_idx != i2c1.trans_insert_idx) + { + // Restart transaction doing the Rx part now + PPRZ_I2C_SEND_START(&i2c1); + } + __enable_irq(); + } + } +#endif +#endif + +#ifdef USE_I2C2 + +#ifdef I2C_DEBUG_LED + if (cnt == 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 + if (periph == &i2c1) + { + + } + else +#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) +{ + I2C_TypeDef *regs = (I2C_TypeDef *) periph->reg_addr; + +#ifdef I2C_DEBUG_LED + if (periph == &i2c1) + { + return TRUE; + } +#endif + if (periph->status == I2CIdle) + return ! (BIT_X_IS_SET_IN_REG( I2C_SR2_BIT_BUSY, regs->SR2 ) ); + else + return FALSE; +} + + diff --git a/sw/airborne/arch/stm32/mcu_periph/obsolete/i2c_attempt4_nodouble_isr.c b/sw/airborne/arch/stm32/mcu_periph/obsolete/i2c_attempt4_nodouble_isr.c new file mode 100644 index 0000000000..8fb1a4da48 --- /dev/null +++ b/sw/airborne/arch/stm32/mcu_periph/obsolete/i2c_attempt4_nodouble_isr.c @@ -0,0 +1,1358 @@ +#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 + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +#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 = 37000 + .I2C_ClockSpeed = 400000 +}; +#endif + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +// 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 + +// 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 + + +// Bit Control + +#define BIT_X_IS_SET_IN_REG(X,REG) (((REG) & (X)) == (X)) + +// Critical Zones + +#define __I2C_REG_CRITICAL_ZONE_START +#define __I2C_REG_CRITICAL_ZONE_STOP + + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +// 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(I2C_TypeDef *regs) +{ + // Man: p722: Stop generation after the current byte transfer or after the current Start condition is sent. + regs->CR1 |= I2C_CR1_BIT_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) +{ + I2C_TypeDef *regs = (I2C_TypeDef *) periph->reg_addr; + + 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 + +/* + if (BIT_X_IS_SET_IN_REG( I2C_CR1_BIT_STOP, regs->CR1 ) ) + { + regs->CR1 &= ~ I2C_CR1_BIT_STOP; + } +*/ + + // Enable Error IRQ, Event IRQ but disable Buffer IRQ + regs->CR2 |= I2C_CR2_BIT_ITERREN; + regs->CR2 |= I2C_CR2_BIT_ITEVTEN; + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + + // Issue a new start + regs->CR1 = (I2C_CR1_BIT_START | I2C_CR1_BIT_PE); + periph->status = I2CStartRequested; + + +#ifdef I2C_DEBUG_LED + LED_SHOW_ACTIVE_BITS(regs); +#endif +} + +// STOP + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// SUBTRANSACTION SEQUENCES + +enum STMI2CSubTransactionStatus { + STMI2C_SubTra_Busy, + STMI2C_SubTra_Ready_StopRequested, + STMI2C_SubTra_Ready, + STMI2C_SubTra_Error +}; + +static inline enum STMI2CSubTransactionStatus stmi2c_send(I2C_TypeDef *regs, struct i2c_periph *periph, struct i2c_transaction *trans) +{ + uint16_t SR1 = regs->SR1; + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + // Disable buffer interrupt + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + // Send Slave address and wait for ADDR interrupt + regs->DR = trans->slave_addr; + } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_ADDR, SR1) ) + { + // Now read SR2 to clear the ADDR + uint16_t SR2 __attribute__ ((unused)) = regs->SR2; + + // Maybe check we are transmitting (did not loose arbitration for instance) + // if (! BIT_X_IS_SET_IN_REG(I2C_SR2_BIT_TRA, SR2)) { } + + // Send First max 2 bytes + regs->DR = trans->buf[0]; + if (trans->len_w > 1) + { + regs->DR = 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) + regs->CR2 |= I2C_CR2_BIT_ITBUFEN; + } + // The buffer is not full anymore AND we were not waiting for BTF + else if ((BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_TXE, SR1) ) && (BIT_X_IS_SET_IN_REG(I2C_CR2_BIT_ITBUFEN, regs->CR2)) ) + { + // Send the next byte + regs->DR = 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 + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + // Next interrupt will be BTF (or error) + } + } + // BTF: means last byte was sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_BTF, SR1) ) + { + if (trans->type == I2CTransTx) + { + // Tell the driver we are ready + trans->status = I2CTransSuccess; + } + + return STMI2C_SubTra_Ready; + } + else // Hardware error + { + return STMI2C_SubTra_Error; + } + + return STMI2C_SubTra_Busy; +} + +static inline enum STMI2CSubTransactionStatus stmi2c_read1(I2C_TypeDef *regs, struct i2c_transaction *trans) +{ + uint16_t SR1 = regs->SR1; + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + regs->DR = trans->slave_addr | 0x01; + } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_ADDR, SR1) ) + { + // First Clear the ACK bit: after the next byte we do not want new bytes + regs->CR1 &= ~ I2C_CR1_BIT_POS; + regs->CR1 &= ~ I2C_CR1_BIT_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)) = regs->SR2; + + // Schedule a Stop + PPRZ_I2C_SEND_STOP(regs); + + __I2C_REG_CRITICAL_ZONE_STOP; + // --- end of critical zone ----------- + + // Enable the RXNE to get the result + regs->CR2 |= I2C_CR2_BIT_ITBUFEN; + } + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_RXNE, SR1) ) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + trans->buf[0] = regs->DR; + + // We got all the results (stop condition might still be in progress but this is the last interrupt) + trans->status = I2CTransSuccess; + + return STMI2C_SubTra_Ready_StopRequested; + } + else // Hardware error + { + return STMI2C_SubTra_Error; + } + + return STMI2C_SubTra_Busy; +} + +static inline enum STMI2CSubTransactionStatus stmi2c_read2(I2C_TypeDef *regs, struct i2c_transaction *trans) +{ + uint16_t SR1 = regs->SR1; + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + regs->CR1 |= I2C_CR1_BIT_ACK; + regs->CR1 |= I2C_CR1_BIT_POS; + regs->DR = trans->slave_addr | 0x01; + } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_ADDR, SR1) ) + { + // 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)) = regs->SR2; + + // --- make absolutely sure this command is not delayed too much after the previous: + __I2C_REG_CRITICAL_ZONE_START; + // if transfer of DR was finished already then we will get too many bytes + // NOT First Clear the ACK bit but only AFTER clearing ADDR + regs->CR1 &= ~ I2C_CR1_BIT_ACK; + + // Disable the RXNE and wait for BTF + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + + __I2C_REG_CRITICAL_ZONE_STOP; + // --- end of critical zone ----------- + } + // Receive buffer if full, master is halted: BTF + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_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(regs); + + trans->buf[0] = regs->DR; + trans->buf[1] = regs->DR; + + // We got all the results + trans->status = I2CTransSuccess; + + return STMI2C_SubTra_Ready_StopRequested; + } + else // Hardware error + { + return STMI2C_SubTra_Error; + } + + return STMI2C_SubTra_Busy; +} + +static inline enum STMI2CSubTransactionStatus stmi2c_readmany(I2C_TypeDef *regs, struct i2c_periph *periph, struct i2c_transaction *trans) +{ + uint16_t SR1 = regs->SR1; + + // Start Condition Was Just Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + // The first data byte will be acked in read many so the slave knows it should send more + regs->CR1 &= ~ I2C_CR1_BIT_POS; + regs->CR1 |= I2C_CR1_BIT_ACK; + // Clear the SB flag + regs->DR = trans->slave_addr | 0x01; + } + // Address Was Sent + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_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)) + { + regs->CR2 |= I2C_CR2_BIT_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)) = regs->SR2; + } + // one or more bytes are available AND we were interested in Buffer interrupts + else if ( (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_RXNE, SR1) ) && (BIT_X_IS_SET_IN_REG(I2C_CR2_BIT_ITBUFEN, regs->CR2)) ) + { + // 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] = regs->DR; + periph->idx_buf ++; + } + // from : 3bytes -> last byte: do nothing + // + // finally: this was the last byte + else if (periph->idx_buf >= (trans->len_r - 1)) + { + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + + // Last Value + trans->buf[periph->idx_buf] = regs->DR; + 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)) + { + regs->CR2 |= I2C_CR2_BIT_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) + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; + } + } + // Buffer is full while this was not a RXNE interrupt + else if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_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 + regs->CR1 &= ~ I2C_CR1_BIT_ACK; + + // Now that ACK is cleared we read one byte: instantly the last byte is being clocked in... + trans->buf[periph->idx_buf] = regs->DR; + 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(regs); + + __I2C_REG_CRITICAL_ZONE_STOP; + // --- end of critical zone ----------- + + // read the byte2 we had in the buffer (BTF means 2 bytes available) + trans->buf[periph->idx_buf] = regs->DR; + 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 + regs->CR2 |= I2C_CR2_BIT_ITBUFEN; + } + else // Hardware error + { + // Error +#ifdef I2C_DEBUG_LED + LED2_ON(); + LED1_ON(); + LED2_OFF(); + LED1_OFF(); +#endif + return STMI2C_SubTra_Error; + } + + return STMI2C_SubTra_Busy; +} + + +static inline void stmi2c_clear_pending_interrupts(I2C_TypeDef *regs) +{ + uint16_t SR1 = regs->SR1; + + regs->CR2 &= ~ I2C_CR2_BIT_ITBUFEN; // Disable TXE, RXNE + + //regs->CR1 &= ~ I2C_CR1_BIT_PE; // Disable Periferial + //regs->CR1 |= I2C_CR1_BIT_PE; // Enable Periferial + + // Start Condition Was Generated + if (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_SB, SR1 ) ) + { + // SB: cleared by software when reading SR1 and writing to DR + regs->DR = 0x00; + } + // Address Was Sent + if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_ADDR, SR1) ) + { + // ADDR: Cleared by software when reading SR1 and then SR2 + uint16_t SR2 __attribute__ ((unused)) = regs->SR2; + } + // Byte Transfer Finished + if (BIT_X_IS_SET_IN_REG(I2C_SR1_BIT_BTF, SR1) ) + { + // SB: cleared by software when reading SR1 and reading/writing to DR + uint8_t dummy __attribute__ ((unused)) = regs->DR; + regs->DR = 0x00; + } + + + // Still have a start sheduled +// if (BIT_X_IS_SET_IN_REG(I2C_CR1_BIT_START, regs->CR1) ) + { + // Clear pending start conditions +// regs->CR1 &= ~ I2C_CR1_BIT_START; + } + +} + + +static inline void i2c_error(struct i2c_periph *periph); + +static inline void i2c_irq(struct i2c_periph *periph) +{ + + /* + There are 7 possible reasons to get here: + + 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 + [3 ADDR10] // -- 10bit address stuff + [4 STOPF] // -- only for slaves: master has no stop interrupt + 5) 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 transmitmode: 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 stack even before it is + actually completely transmitted. But then you would not know the result yet so you have to keep it until the result + is known. + + // Beware: the order in which Status is read determines how flags are cleared. You should not just read SR1 & SR2 every time + + If IT_EV_FEN AND IT_EV_BUF + -------------------------- + + Buffer event are not always wanted and are tipically switched on during longer data transfers. Make sure to turn off in time. + + 6) RxNE + 7) TxE + + -------------------------------------------------------------------------------------------------- + // This driver uses only a subset of the pprz_i2c_states for several reasons: + // -we have less interrupts than the I2CStatus states (for efficiency) + // -STM32 has such a powerfull I2C engine with plenty of status register flags that + only little extra status information needs to be stored. + + // Status is re-used (abused) to remember the last COMMAND THAT WAS SENT to the STM I2C hardware. + +// TODO: check which are used + enum I2CStatus { + I2CIdle, // No more last command + + I2CStartRequested, // Last command was start + I2CRestartRequested, // Last command was restart + I2CStopRequested, // Very important to not send double stop conditions + + I2CSendingByte, // Some address/data operation + + // Following are not used + I2CReadingByte, + I2CAddrWrSent, + I2CAddrRdSent, + I2CSendingLastByte, + I2CReadingLastByte, + I2CComplete, + I2CFailed + }; + + --------- + + 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: use needs another way to finish. + + */ + + + /////////////////////////////////////////////////////////////////////////////////// + // 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 + + // Direct Access to the I2C Registers + // Do not read SR2 as it might start the reading while an (n)ack bit might be needed first + I2C_TypeDef *regs = (I2C_TypeDef *) periph->reg_addr; + +#ifdef I2C_DEBUG_LED + LED1_ON(); + LED1_OFF(); +#endif + + + /////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // TRANSACTION HANDLER + + enum STMI2CSubTransactionStatus ret = 0; + + /////////////////////// + // Nothing Left To Do + if (periph->trans_extract_idx == periph->trans_insert_idx) + { +#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) + // then its easy: just stop: clear all interrupt generating bits + + // Count The Errors + i2c_error(periph); + + // Clear Running Events + stmi2c_clear_pending_interrupts(regs); + + // Mark this as a special error + periph->errors->unexpected_event_cnt++; + + periph->status = I2CIdle; + + // There are no transactions anymore: + // furtheron we need a transaction pointer: so we are not allowed to continue + return; + } + + struct i2c_transaction* trans = periph->trans[periph->trans_extract_idx]; + + /////////////////////////// + // If there was an error: + if (( regs->SR1 & I2C_SR1_BITS_ERR ) != 0x0000) + { + +#ifdef I2C_DEBUG_LED + LED1_ON(); + LED2_ON(); + LED1_OFF(); + LED2_OFF(); + + LED_SHOW_ACTIVE_BITS(regs); +#endif + + // Set result in transaction + trans->status = I2CTransFailed; + + // Prepare for next + ret = STMI2C_SubTra_Ready; + + // Make sure a TxRx does not Restart + trans->type = I2CTransRx; + +/* + // There are 2 types of errors: some need a STOP, some better do without: Following will not get an extra stop + if ( + // Lost Arbitration + (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_ERR_ARLO, regs->SR1 ) ) + // Buss Error When Master Only + || ((BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_ERR_BUS, regs->SR1 ) ) && (!BIT_X_IS_SET_IN_REG( I2C_SR2_BIT_MSL, regs->SR2 ) )) + || (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_ERR_OVR, regs->SR1 ) ) + ) + { + ret = STMI2C_SubTra_Error; + } +*/ + + // Count The Errors + i2c_error(periph); + + // Clear Running Events + stmi2c_clear_pending_interrupts(regs); + + } + + + /////////////////////////// + // Normal Event: + else + { + + if (trans->type == I2CTransRx) // TxRx are converted to Rx after the Tx Part + { + switch (trans->len_r) + { + case 1: + ret = stmi2c_read1(regs,trans); + break; + case 2: + ret = stmi2c_read2(regs,trans); + break; + default: + ret = stmi2c_readmany(regs,periph, trans); + break; + } + } + else // TxRx or Tx + { + ret = stmi2c_send(regs,periph,trans); + } + } + + ///////////////////////////////// + // Sub-transaction has finished + if (ret != STMI2C_SubTra_Busy) + { + // If a restart is not needed + if (trans->type != I2CTransTxRx) + { + // Ready, no stop condition set yet + if (ret == STMI2C_SubTra_Ready) + { + + // Program a stop + PPRZ_I2C_SEND_STOP(regs); + + // Silent any BTF that would occur before STOP is executed + regs->DR = 0x00; + } + + // In case of unexpected condition: e.g. not slave, no event + if (ret == STMI2C_SubTra_Error) + { + + trans->status = I2CTransFailed; + + // Error +#ifdef I2C_DEBUG_LED + LED2_ON(); + LED1_ON(); + LED2_OFF(); + LED1_OFF(); +#endif + + LED_SHOW_ACTIVE_BITS(regs); + + // Clear Running Events + stmi2c_clear_pending_interrupts(regs); + + } + + + // 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); +// ------ + } + + } + // RxTx -> Restart and do Rx part + else + { + trans->type = I2CTransRx; + periph->status = I2CStartRequested; + regs->CR1 |= I2C_CR1_BIT_START; + + // Silent any BTF that would occur before SB + regs->DR = 0x00; + } + } + + return; +} + +static inline void i2c_error(struct i2c_periph *periph) +{ + uint8_t err_nr = 0; + periph->errors->er_irq_cnt; + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_AF)) { /* Acknowledge failure */ + periph->errors->ack_fail_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_AF); + err_nr = 1; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_BERR)) { /* Misplaced Start or Stop condition */ + periph->errors->miss_start_stop_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_BERR); + err_nr = 2; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_ARLO)) { /* Arbitration lost */ + periph->errors->arb_lost_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_ARLO); + err_nr = 3; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_OVR)) { /* Overrun/Underrun */ + periph->errors->over_under_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_OVR); + err_nr = 4; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_PECERR)) { /* PEC Error in reception */ + periph->errors->pec_recep_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_PECERR); + err_nr = 5; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_TIMEOUT)) { /* Timeout or Tlow error */ + periph->errors->timeout_tlow_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_TIMEOUT); + err_nr = 6; + } + if (I2C_GetITStatus(periph->reg_addr, I2C_IT_SMBALERT)) { /* SMBus alert */ + periph->errors->smbus_alert_cnt++; + I2C_ClearITPendingBit(periph->reg_addr, I2C_IT_SMBALERT); + err_nr = 7; + } + +#ifdef I2C_DEBUG_LED + LED_ERROR(20, err_nr); +#endif + + 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); + } +*/ + +//#endif /* USE_I2C2 */ + + + + +#ifdef USE_I2C1 + +struct i2c_errors i2c1_errors; + +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); + + // Extra +#ifdef I2C_DEBUG_LED + LED_INIT(); +#else + + /* 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 = 0; + 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 = 1; + 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); + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = i2c1.scl_pin | i2c1.sda_pin; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + I2C_DeInit(I2C1); + + // enable peripheral + I2C_Cmd(I2C1, ENABLE); + + I2C_Init(I2C1, i2c1.init_struct); + + // enable error interrupts + I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE); + +#endif +} + +void i2c1_ev_irq_handler(void) { + i2c_irq(&i2c1); +} + +void i2c1_er_irq_handler(void) { + i2c_irq(&i2c1); +} + +#endif /* USE_I2C1 */ + +#ifdef USE_I2C2 + +struct i2c_errors i2c2_errors; + +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); + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = i2c2.scl_pin | i2c2.sda_pin; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + I2C_DeInit(I2C2); + + // enable peripheral + I2C_Cmd(I2C2, ENABLE); + + I2C_Init(I2C2, i2c2.init_struct); + + // enable error interrupts + I2C_ITConfig(I2C2, I2C_IT_ERR, ENABLE); + +} + + + +void i2c2_ev_irq_handler(void) { + i2c_irq(&i2c2); +} + +void i2c2_er_irq_handler(void) { + i2c_irq(&i2c2); +} + +#endif /* USE_I2C2 */ + + +void i2c_setbitrate(struct i2c_periph *periph, int bitrate) +{ + if (i2c_idle(periph)) + { + if (periph == &i2c2) + { + I2C2_InitStruct.I2C_ClockSpeed = bitrate; + I2C_Init(I2C2, i2c2.init_struct); + } + +#ifdef I2C_DEBUG_LED + __disable_irq(); + + LED2_ON(); + LED1_ON(); + LED2_OFF(); + LED1_OFF(); + LED2_ON(); + LED1_ON(); + LED2_OFF(); + LED1_OFF(); + + __enable_irq(); +#endif + + } +} + +void i2c_event(void) +{ + static uint32_t cnt = 0; + //I2C_TypeDef *regs; + cnt++; + if (cnt > 10000) cnt = 0; + +#ifndef I2C_DEBUG_LED +#ifdef USE_I2C1 + if (i2c1.status == I2CIdle) + { + if (i2c_idle(&i2c1)) + { + __disable_irq(); + // More work to do + if (i2c1.trans_extract_idx != i2c1.trans_insert_idx) + { + // Restart transaction doing the Rx part now + PPRZ_I2C_SEND_START(&i2c1); + } + __enable_irq(); + } + } +#endif +#endif + +#ifdef USE_I2C2 + +#ifdef I2C_DEBUG_LED + if (cnt == 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 + if (periph == &i2c1) + { + + } + else +#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) +{ + I2C_TypeDef *regs = (I2C_TypeDef *) periph->reg_addr; + +#ifdef I2C_DEBUG_LED + if (periph == &i2c1) + { + return TRUE; + } +#endif + if (periph->status == I2CIdle) + return ! (BIT_X_IS_SET_IN_REG( I2C_SR2_BIT_BUSY, regs->SR2 ) ); + else + return FALSE; +} + + diff --git a/sw/airborne/boards/lisa_l/baro_board.c b/sw/airborne/boards/lisa_l/baro_board.c index 30699d68f2..63f85722e9 100644 --- a/sw/airborne/boards/lisa_l/baro_board.c +++ b/sw/airborne/boards/lisa_l/baro_board.c @@ -26,6 +26,7 @@ void baro_init(void) { void baro_periodic(void) { // check i2c_done if (!i2c_idle(&i2c2)) return; + switch (baro_board.status) { case LBS_UNINITIALIZED: baro_board_send_reset(); diff --git a/sw/airborne/boards/lisa_l/test_baro.c b/sw/airborne/boards/lisa_l/test_baro.c index 6dad99c825..7acc75d542 100644 --- a/sw/airborne/boards/lisa_l/test_baro.c +++ b/sw/airborne/boards/lisa_l/test_baro.c @@ -1,6 +1,4 @@ /* - * $Id$ - * * Copyright (C) 2009 Antoine Drouin * * This file is part of paparazzi. @@ -32,6 +30,7 @@ #include "mcu.h" #include "mcu_periph/sys_time.h" #include "mcu_periph/uart.h" +#include "mcu_periph/i2c.h" #include "led.h" @@ -47,7 +46,6 @@ static inline void main_event_task( void ); static inline void main_on_baro_diff(void); static inline void main_on_baro_abs(void); - int main(void) { main_init(); @@ -81,15 +79,15 @@ static inline void main_periodic_task( void ) { RunOnceEvery(256, { DOWNLINK_SEND_I2C_ERRORS(DefaultChannel, DefaultDevice, - &i2c2_errors.ack_fail_cnt, - &i2c2_errors.miss_start_stop_cnt, - &i2c2_errors.arb_lost_cnt, - &i2c2_errors.over_under_cnt, - &i2c2_errors.pec_recep_cnt, - &i2c2_errors.timeout_tlow_cnt, - &i2c2_errors.smbus_alert_cnt, - &i2c2_errors.unexpected_event_cnt, - &i2c2_errors.last_unexpected_event); + &i2c2.errors->ack_fail_cnt, + &i2c2.errors->miss_start_stop_cnt, + &i2c2.errors->arb_lost_cnt, + &i2c2.errors->over_under_cnt, + &i2c2.errors->pec_recep_cnt, + &i2c2.errors->timeout_tlow_cnt, + &i2c2.errors->smbus_alert_cnt, + &i2c2.errors->unexpected_event_cnt, + &i2c2.errors->last_unexpected_event); }); } diff --git a/sw/airborne/firmwares/fixedwing/main_ap.c b/sw/airborne/firmwares/fixedwing/main_ap.c index a6e0c6a37b..1859d46ffc 100644 --- a/sw/airborne/firmwares/fixedwing/main_ap.c +++ b/sw/airborne/firmwares/fixedwing/main_ap.c @@ -583,6 +583,10 @@ void monitor_task( void ) { /*********** EVENT ***********************************************************/ void event_task_ap( void ) { +#ifndef SINGLE_MCU + i2c_event(); +#endif + #if USE_AHRS #if USE_IMU ImuEvent(on_gyro_event, on_accel_event, on_mag_event); diff --git a/sw/airborne/firmwares/fixedwing/main_fbw.c b/sw/airborne/firmwares/fixedwing/main_fbw.c index cce4d39533..6fd9d48cda 100644 --- a/sw/airborne/firmwares/fixedwing/main_fbw.c +++ b/sw/airborne/firmwares/fixedwing/main_fbw.c @@ -43,6 +43,7 @@ #include "firmwares/fixedwing/autopilot.h" #include "fbw_downlink.h" #include "paparazzi.h" +#include "mcu_periph/i2c.h" #ifdef MCU_SPI_LINK #include "link_mcu.h" @@ -119,6 +120,7 @@ void event_task_fbw( void) { RadioControlEvent(handle_rc_frame); #endif + i2c_event(); #ifdef INTER_MCU #ifdef MCU_SPI_LINK diff --git a/sw/airborne/firmwares/rotorcraft/main.c b/sw/airborne/firmwares/rotorcraft/main.c index 6c77647499..ed193506ff 100644 --- a/sw/airborne/firmwares/rotorcraft/main.c +++ b/sw/airborne/firmwares/rotorcraft/main.c @@ -196,6 +196,8 @@ STATIC_INLINE void failsafe_check( void ) { STATIC_INLINE void main_event( void ) { + i2c_event(); + DatalinkEvent(); if (autopilot_rc) { diff --git a/sw/airborne/firmwares/rotorcraft/telemetry.h b/sw/airborne/firmwares/rotorcraft/telemetry.h index 88e22748d6..11686c0513 100644 --- a/sw/airborne/firmwares/rotorcraft/telemetry.h +++ b/sw/airborne/firmwares/rotorcraft/telemetry.h @@ -48,8 +48,8 @@ #endif #include "subsystems/ins.h" #include "subsystems/ahrs.h" -//FIXME: wtf ??!! -#include "mcu_periph/i2c_arch.h" +// I2C Error counters +#include "mcu_periph/i2c.h" #define PERIODIC_SEND_ALIVE(_trans, _dev) DOWNLINK_SEND_ALIVE(_trans, _dev, 16, MD5SUM) @@ -736,16 +736,43 @@ &ahrs.ltp_to_body_euler.psi); \ } -#define PERIODIC_SEND_I2C_ERRORS(_trans, _dev) { \ - DOWNLINK_SEND_I2C_ERRORS(_trans, _dev, \ - &i2c_errc_ack_fail, \ - &i2c_errc_miss_start_stop, \ - &i2c_errc_arb_lost, \ - &i2c_errc_over_under, \ - &i2c_errc_pec_recep, \ - &i2c_errc_timeout_tlow, \ - &i2c_errc_smbus_alert \ - ); \ +#ifdef USE_I2C1 +#define PERIODIC_SEND_I2C1_ERRORS(_trans, _dev) { \ + DOWNLINK_SEND_I2C_ERRORS(_trans, _dev, \ + &i2c1.errors->ack_fail_cnt, \ + &i2c1.errors->miss_start_stop_cnt, \ + &i2c1.errors->arb_lost_cnt, \ + &i2c1.errors->over_under_cnt, \ + &i2c1.errors->pec_recep_cnt, \ + &i2c1.errors->timeout_tlow_cnt, \ + &i2c1.errors->smbus_alert_cnt, \ + &i2c1.errors->unexpected_event_cnt, \ + &i2c1.errors->last_unexpected_event); \ + } +#else +#define PERIODIC_SEND_I2C1_ERRORS(_trans, _dev) {} +#endif + +#ifdef USE_I2C2 +#define PERIODIC_SEND_I2C2_ERRORS(_trans, _dev) { \ + DOWNLINK_SEND_I2C_ERRORS(_trans, _dev, \ + &i2c2.errors->ack_fail_cnt, \ + &i2c2.errors->miss_start_stop_cnt, \ + &i2c2.errors->arb_lost_cnt, \ + &i2c2.errors->over_under_cnt, \ + &i2c2.errors->pec_recep_cnt, \ + &i2c2.errors->timeout_tlow_cnt, \ + &i2c2.errors->smbus_alert_cnt, \ + &i2c2.errors->unexpected_event_cnt, \ + &i2c2.errors->last_unexpected_event); \ + } +#else +#define PERIODIC_SEND_I2C2_ERRORS(_trans, _dev) {} +#endif + +#define PERIODIC_SEND_I2C_ERRORS(_trans, _dev) { \ + PERIODIC_SEND_I2C1_ERRORS(_trans, _dev); \ + PERIODIC_SEND_I2C2_ERRORS(_trans, _dev); \ } // FIXME: still used?? or replace by EXTRA_ADC diff --git a/sw/airborne/lisa/test/lisa_test_hmc5843.c b/sw/airborne/lisa/test/lisa_test_hmc5843.c index 7fa7117b92..4b12e8afc8 100644 --- a/sw/airborne/lisa/test/lisa_test_hmc5843.c +++ b/sw/airborne/lisa/test/lisa_test_hmc5843.c @@ -41,6 +41,7 @@ #include "led.h" #include "mcu_periph/uart.h" +#include "mcu_periph/i2c.h" #include "peripherals/hmc5843.h" #include "my_debug_servo.h" #include "math/pprz_algebra_int.h" @@ -91,15 +92,15 @@ static inline void main_periodic_task( void ) { RunOnceEvery(256, { DOWNLINK_SEND_I2C_ERRORS(DefaultChannel, DefaultDevice, - &i2c2_errors.ack_fail_cnt, - &i2c2_errors.miss_start_stop_cnt, - &i2c2_errors.arb_lost_cnt, - &i2c2_errors.over_under_cnt, - &i2c2_errors.pec_recep_cnt, - &i2c2_errors.timeout_tlow_cnt, - &i2c2_errors.smbus_alert_cnt, - &i2c2_errors.unexpected_event_cnt, - &i2c2_errors.last_unexpected_event); + &i2c2.errors->ack_fail_cnt, + &i2c2.errors->miss_start_stop_cnt, + &i2c2.errors->arb_lost_cnt, + &i2c2.errors->over_under_cnt, + &i2c2.errors->pec_recep_cnt, + &i2c2.errors->timeout_tlow_cnt, + &i2c2.errors->smbus_alert_cnt, + &i2c2.errors->unexpected_event_cnt, + &i2c2.errors->last_unexpected_event); }); if (mag_state == 2) send_config(); diff --git a/sw/airborne/lisa/test/lisa_test_itg3200.c b/sw/airborne/lisa/test/lisa_test_itg3200.c index e52ec68a87..34db0cfc73 100644 --- a/sw/airborne/lisa/test/lisa_test_itg3200.c +++ b/sw/airborne/lisa/test/lisa_test_itg3200.c @@ -88,15 +88,15 @@ static inline void main_periodic_task( void ) { }); RunOnceEvery(256, { DOWNLINK_SEND_I2C_ERRORS(DefaultChannel, DefaultDevice, - &i2c2_errors.ack_fail_cnt, - &i2c2_errors.miss_start_stop_cnt, - &i2c2_errors.arb_lost_cnt, - &i2c2_errors.over_under_cnt, - &i2c2_errors.pec_recep_cnt, - &i2c2_errors.timeout_tlow_cnt, - &i2c2_errors.smbus_alert_cnt, - &i2c2_errors.unexpected_event_cnt, - &i2c2_errors.last_unexpected_event); + &i2c2.errors->ack_fail_cnt, + &i2c2.errors->miss_start_stop_cnt, + &i2c2.errors->arb_lost_cnt, + &i2c2.errors->over_under_cnt, + &i2c2.errors->pec_recep_cnt, + &i2c2.errors->timeout_tlow_cnt, + &i2c2.errors->smbus_alert_cnt, + &i2c2.errors->unexpected_event_cnt, + &i2c2.errors->last_unexpected_event); }); switch (gyro_state) { diff --git a/sw/airborne/lisa/test/test_board.c b/sw/airborne/lisa/test/test_board.c index 4f9b27744d..7e01422dd3 100644 --- a/sw/airborne/lisa/test/test_board.c +++ b/sw/airborne/lisa/test/test_board.c @@ -1,6 +1,4 @@ /* - * $Id$ - * * Copyright (C) 2008-2009 Antoine Drouin * * This file is part of paparazzi. @@ -31,6 +29,7 @@ #include "std.h" #include "mcu.h" #include "mcu_periph/uart.h" +#include "mcu_periph/i2c.h" #include "mcu_periph/sys_time.h" #include "subsystems/datalink/downlink.h" #include "led.h" @@ -157,15 +156,15 @@ static void test_baro_periodic(void) { RunOnceEvery(2, {baro_periodic();}); RunOnceEvery(100,{ DOWNLINK_SEND_I2C_ERRORS(DefaultChannel, DefaultDevice, - &i2c2_errors.ack_fail_cnt, - &i2c2_errors.miss_start_stop_cnt, - &i2c2_errors.arb_lost_cnt, - &i2c2_errors.over_under_cnt, - &i2c2_errors.pec_recep_cnt, - &i2c2_errors.timeout_tlow_cnt, - &i2c2_errors.smbus_alert_cnt, - &i2c2_errors.unexpected_event_cnt, - &i2c2_errors.last_unexpected_event); + &i2c2.errors->ack_fail_cnt, + &i2c2.errors->miss_start_stop_cnt, + &i2c2.errors->arb_lost_cnt, + &i2c2.errors->over_under_cnt, + &i2c2.errors->pec_recep_cnt, + &i2c2.errors->timeout_tlow_cnt, + &i2c2.errors->smbus_alert_cnt, + &i2c2.errors->unexpected_event_cnt, + &i2c2.errors->last_unexpected_event); }); } static void test_baro_event(void) {BaroEvent(test_baro_on_baro_abs, test_baro_on_baro_diff);} @@ -191,15 +190,15 @@ static void test_bldc_periodic(void) { RunOnceEvery(100,{ DOWNLINK_SEND_I2C_ERRORS(DefaultChannel, DefaultDevice, - &i2c1_errors.ack_fail_cnt, - &i2c1_errors.miss_start_stop_cnt, - &i2c1_errors.arb_lost_cnt, - &i2c1_errors.over_under_cnt, - &i2c1_errors.pec_recep_cnt, - &i2c1_errors.timeout_tlow_cnt, - &i2c1_errors.smbus_alert_cnt, - &i2c1_errors.unexpected_event_cnt, - &i2c1_errors.last_unexpected_event); + &i2c1.errors->ack_fail_cnt, + &i2c1.errors->miss_start_stop_cnt, + &i2c1.errors->arb_lost_cnt, + &i2c1.errors->over_under_cnt, + &i2c1.errors->pec_recep_cnt, + &i2c1.errors->timeout_tlow_cnt, + &i2c1.errors->smbus_alert_cnt, + &i2c1.errors->unexpected_event_cnt, + &i2c1.errors->last_unexpected_event); }); } diff --git a/sw/airborne/mcu_periph/i2c.h b/sw/airborne/mcu_periph/i2c.h index 93bd0f77ba..b0df164c10 100644 --- a/sw/airborne/mcu_periph/i2c.h +++ b/sw/airborne/mcu_periph/i2c.h @@ -133,6 +133,9 @@ extern void i2c2_init(void); extern void i2c_init(struct i2c_periph* p); extern bool_t i2c_idle(struct i2c_periph* p); extern bool_t i2c_submit(struct i2c_periph* p, struct i2c_transaction* t); +extern void i2c_setbitrate(struct i2c_periph* p, int bitrate); +extern void i2c_event(void); + #define I2CReceive(_p, _t, _s_addr, _len) { \ _t.type = I2CTransRx; \ diff --git a/sw/airborne/modules/benchmark/i2c_abuse_test.c b/sw/airborne/modules/benchmark/i2c_abuse_test.c index 7a9b078caa..d5bee73822 100644 --- a/sw/airborne/modules/benchmark/i2c_abuse_test.c +++ b/sw/airborne/modules/benchmark/i2c_abuse_test.c @@ -1,7 +1,7 @@ /* * $Id$ * - * Copyright (C) 2009 Gautier Hattenberger + * Copyright (C) 2011 Christophe De Wagter * * This file is part of paparazzi. * @@ -29,8 +29,8 @@ struct i2c_transaction i2c_test1; struct i2c_transaction i2c_test2; -uint8_t i2c_abuse_test_counter = 0; -uint16_t i2c_abuse_test_bitrate = 1000; +volatile uint8_t i2c_abuse_test_counter = 0; +volatile uint32_t i2c_abuse_test_bitrate = 1000; void init_i2c_abuse_test(void) { //LED_INIT(DEMO_MODULE_LED); @@ -62,59 +62,59 @@ static void i2c_abuse_send_transaction(uint8_t _init) i2c_test1.buf[2] = 0x01<<5; i2c_test1.buf[3] = 0x00; i2c_test1.len_w = 4; - i2c_submit(&i2c2,&i2c_test1); + i2c_submit(&I2C_ABUSE_PORT,&i2c_test1); break; case 2: i2c_test1.type = I2CTransTx; i2c_test1.buf[0] = 0x01; // set to gain to 1 Gauss i2c_test1.buf[1] = 0x01<<5; i2c_test1.len_w = 2; - i2c_submit(&i2c2,&i2c_test1); + i2c_submit(&I2C_ABUSE_PORT,&i2c_test1); break; case 3: i2c_test1.type = I2CTransTx; i2c_test1.buf[0] = 0x00; // set to continuous mode i2c_test1.len_w = 1; - i2c_submit(&i2c2,&i2c_test1); + i2c_submit(&I2C_ABUSE_PORT,&i2c_test1); break; case 4: i2c_test1.type = I2CTransRx; i2c_test1.len_r = 1; - i2c_submit(&i2c2,&i2c_test1); + i2c_submit(&I2C_ABUSE_PORT,&i2c_test1); break; case 5: i2c_test1.type = I2CTransRx; i2c_test1.len_r = 2; - i2c_submit(&i2c2,&i2c_test1); + i2c_submit(&I2C_ABUSE_PORT,&i2c_test1); break; case 6: i2c_test1.type = I2CTransRx; i2c_test1.len_r = 3; - i2c_submit(&i2c2,&i2c_test1); + i2c_submit(&I2C_ABUSE_PORT,&i2c_test1); break; case 7: i2c_test1.type = I2CTransRx; i2c_test1.len_r = 4; - i2c_submit(&i2c2,&i2c_test1); + i2c_submit(&I2C_ABUSE_PORT,&i2c_test1); break; case 8: i2c_test1.type = I2CTransRx; i2c_test1.len_r = 5; - i2c_submit(&i2c2,&i2c_test1); + i2c_submit(&I2C_ABUSE_PORT,&i2c_test1); break; case 9: // bad addr i2c_test1.slave_addr = 0x3C + 2; i2c_test1.type = I2CTransTx; i2c_test1.len_w = 1; - i2c_submit(&i2c2,&i2c_test1); + i2c_submit(&I2C_ABUSE_PORT,&i2c_test1); break; case 10: // 2 consecutive i2c_test1.type = I2CTransTx; i2c_test1.buf[0] = 0x00; // set to continuous mode i2c_test1.len_w = 1; - i2c_submit(&i2c2,&i2c_test1); + i2c_submit(&I2C_ABUSE_PORT,&i2c_test1); break; case 11: i2c_test1.slave_addr = 0x3C; @@ -122,7 +122,7 @@ static void i2c_abuse_send_transaction(uint8_t _init) i2c_test1.len_r = 1; i2c_test1.len_w = 1; i2c_test1.buf[0] = 0x03; - i2c_submit(&i2c2, &i2c_test1); + i2c_submit(&I2C_ABUSE_PORT, &i2c_test1); break; case 12: i2c_test1.slave_addr = 0x3C; @@ -130,7 +130,7 @@ static void i2c_abuse_send_transaction(uint8_t _init) i2c_test1.len_r = 2; i2c_test1.len_w = 1; i2c_test1.buf[0] = 0x03; - i2c_submit(&i2c2, &i2c_test1); + i2c_submit(&I2C_ABUSE_PORT, &i2c_test1); break; case 13: i2c_test1.slave_addr = 0x3C; @@ -138,7 +138,7 @@ static void i2c_abuse_send_transaction(uint8_t _init) i2c_test1.len_r = 3; i2c_test1.len_w = 1; i2c_test1.buf[0] = 0x03; - i2c_submit(&i2c2, &i2c_test1); + i2c_submit(&I2C_ABUSE_PORT, &i2c_test1); break; case 14: i2c_test1.slave_addr = 0x3C; @@ -146,7 +146,7 @@ static void i2c_abuse_send_transaction(uint8_t _init) i2c_test1.len_r = 4; i2c_test1.len_w = 1; i2c_test1.buf[0] = 0x03; - i2c_submit(&i2c2, &i2c_test1); + i2c_submit(&I2C_ABUSE_PORT, &i2c_test1); break; case 15: i2c_test1.slave_addr = 0x3C; @@ -154,7 +154,7 @@ static void i2c_abuse_send_transaction(uint8_t _init) i2c_test1.len_r = 4; i2c_test1.len_w = 2; i2c_test1.buf[0] = 0x03; - i2c_submit(&i2c2, &i2c_test1); + i2c_submit(&I2C_ABUSE_PORT, &i2c_test1); break; default: i2c_test1.slave_addr = 0x3C; @@ -162,25 +162,14 @@ static void i2c_abuse_send_transaction(uint8_t _init) i2c_test1.len_r = 5; i2c_test1.len_w = 1; i2c_test1.buf[0] = 0x03; - i2c_submit(&i2c2, &i2c_test1); + i2c_submit(&I2C_ABUSE_PORT, &i2c_test1); } } void event_i2c_abuse_test(void) { - if (i2c_idle(&i2c1)) - { - LED_ON(7); // green = idle - LED_OFF(6); - } - else - { - LED_ON(6); // red = busy - LED_OFF(7); - } - - if (i2c_idle(&i2c2)) + if (i2c_idle(&I2C_ABUSE_PORT)) { LED_ON(5); // green = idle LED_OFF(4); @@ -200,7 +189,7 @@ void event_i2c_abuse_test(void) i2c_test2.type = I2CTransRx; i2c_test2.slave_addr = 0x92; i2c_test2.len_r = 2; - i2c_submit(&i2c2,&i2c_test2); + i2c_submit(&I2C_ABUSE_PORT,&i2c_test2); } } @@ -214,31 +203,29 @@ void event_i2c_abuse_test(void) else { // wait until ready: - if (i2c_idle(&i2c2)) + if (i2c_idle(&I2C_ABUSE_PORT)) { i2c_abuse_test_counter = 1; - i2c_setbitrate(&i2c2, i2c_abuse_test_bitrate); + i2c_setbitrate(&I2C_ABUSE_PORT, i2c_abuse_test_bitrate); i2c_abuse_test_bitrate += 17000; - if (i2c_abuse_test_bitrate > 500000) + if (i2c_abuse_test_bitrate > 410000) { - i2c_abuse_test_bitrate -= 500000; + i2c_abuse_test_bitrate -= 410000; } - LED_TOGGLE(4); } } if (i2c_abuse_test_counter < 16) { + RunOnceEvery(100,LED_TOGGLE(I2C_ABUSE_LED)); i2c_abuse_send_transaction( i2c_abuse_test_counter ); - LED_TOGGLE(5); } } } void periodic_50Hz_i2c_abuse_test(void) { - // LED_TOGGLE(DEMO_MODULE_LED); } diff --git a/sw/airborne/modules/benchmark/i2c_abuse_test.h b/sw/airborne/modules/benchmark/i2c_abuse_test.h index e6e7b0634a..bc58dc900d 100644 --- a/sw/airborne/modules/benchmark/i2c_abuse_test.h +++ b/sw/airborne/modules/benchmark/i2c_abuse_test.h @@ -1,7 +1,7 @@ /* * $Id$ * - * Copyright (C) 2009 C. De Wagter + * Copyright (C) 2011 Christophe De Wagter * * This file is part of paparazzi. * diff --git a/sw/airborne/subsystems/imu/imu_aspirin.h b/sw/airborne/subsystems/imu/imu_aspirin.h index b96e2bf167..a00c60371b 100644 --- a/sw/airborne/subsystems/imu/imu_aspirin.h +++ b/sw/airborne/subsystems/imu/imu_aspirin.h @@ -151,6 +151,9 @@ extern void imu_aspirin_arch_init(void); static inline void gyro_read_i2c(void) { + imu_aspirin.i2c_trans_gyro.type = I2CTransTxRx; + imu_aspirin.i2c_trans_gyro.len_w = 1; + imu_aspirin.i2c_trans_gyro.len_r = 6; imu_aspirin.i2c_trans_gyro.buf[0] = ITG3200_REG_GYRO_XOUT_H; i2c_submit(&i2c2,&imu_aspirin.i2c_trans_gyro); imu_aspirin.reading_gyro = 1; diff --git a/sw/airborne/test/subsystems/test_ahrs.c b/sw/airborne/test/subsystems/test_ahrs.c index 797ef517ee..2bb6d1b67b 100644 --- a/sw/airborne/test/subsystems/test_ahrs.c +++ b/sw/airborne/test/subsystems/test_ahrs.c @@ -28,6 +28,7 @@ #include "mcu_periph/sys_time.h" #include "led.h" #include "mcu_periph/uart.h" +#include "mcu_periph/i2c.h" #include "messages.h" #include "subsystems/datalink/downlink.h" @@ -161,15 +162,15 @@ static inline void main_report(void) { { #ifdef USE_I2C2 DOWNLINK_SEND_I2C_ERRORS(DefaultChannel, DefaultDevice, - &i2c2_errors.ack_fail_cnt, - &i2c2_errors.miss_start_stop_cnt, - &i2c2_errors.arb_lost_cnt, - &i2c2_errors.over_under_cnt, - &i2c2_errors.pec_recep_cnt, - &i2c2_errors.timeout_tlow_cnt, - &i2c2_errors.smbus_alert_cnt, - &i2c2_errors.unexpected_event_cnt, - &i2c2_errors.last_unexpected_event); + &i2c2.errors->ack_fail_cnt, + &i2c2.errors->miss_start_stop_cnt, + &i2c2.errors->arb_lost_cnt, + &i2c2.errors->over_under_cnt, + &i2c2.errors->pec_recep_cnt, + &i2c2.errors->timeout_tlow_cnt, + &i2c2.errors->smbus_alert_cnt, + &i2c2.errors->unexpected_event_cnt, + &i2c2.errors->last_unexpected_event); #endif }, { diff --git a/sw/airborne/test/subsystems/test_imu.c b/sw/airborne/test/subsystems/test_imu.c index 0791bd086f..b7f439bfb2 100644 --- a/sw/airborne/test/subsystems/test_imu.c +++ b/sw/airborne/test/subsystems/test_imu.c @@ -31,6 +31,7 @@ #include "mcu_periph/sys_time.h" #include "led.h" #include "mcu_periph/uart.h" +#include "mcu_periph/i2c.h" #include "messages.h" #include "subsystems/datalink/downlink.h" @@ -82,18 +83,18 @@ static inline void main_periodic_task( void ) { #ifdef USE_I2C2 RunOnceEvery(111, { DOWNLINK_SEND_I2C_ERRORS(DefaultChannel, DefaultDevice, - &i2c2_errors.ack_fail_cnt, - &i2c2_errors.miss_start_stop_cnt, - &i2c2_errors.arb_lost_cnt, - &i2c2_errors.over_under_cnt, - &i2c2_errors.pec_recep_cnt, - &i2c2_errors.timeout_tlow_cnt, - &i2c2_errors.smbus_alert_cnt, - &i2c2_errors.unexpected_event_cnt, - &i2c2_errors.last_unexpected_event); + &i2c2.errors->ack_fail_cnt, + &i2c2.errors->miss_start_stop_cnt, + &i2c2.errors->arb_lost_cnt, + &i2c2.errors->over_under_cnt, + &i2c2.errors->pec_recep_cnt, + &i2c2.errors->timeout_tlow_cnt, + &i2c2.errors->smbus_alert_cnt, + &i2c2.errors->unexpected_event_cnt, + &i2c2.errors->last_unexpected_event); }); #endif - if (cpu_time_sec > 1) imu_periodic(); + if (sys_time.nb_sec > 1) imu_periodic(); RunOnceEvery(10, { LED_PERIODIC();}); }