diff --git a/os/hal/include/i2c.h b/os/hal/include/i2c.h index 19eab3b35..16d6f77b6 100644 --- a/os/hal/include/i2c.h +++ b/os/hal/include/i2c.h @@ -79,10 +79,11 @@ */ typedef enum { /* master part */ - I2C_UNINIT = 0, /**< @brief Not initialized. */ - I2C_STOP = 1, /**< @brief Stopped. */ - I2C_READY = 2, /**< @brief Ready. */ - I2C_ACTIVE = 3, /**< @brief In communication. */ + I2C_UNINIT = 0, /**< @brief Not initialized. */ + I2C_STOP = 1, /**< @brief Stopped. */ + I2C_READY = 2, /**< @brief Ready. */ + I2C_ACTIVE_TRANSMIT = 3,/**< @brief Transmit in progress. */ + I2C_ACTIVE_RECEIVE = 4, /**< @brief Receive in progress. */ /* Slave part. Not realized. */ I2C_SACTIVE = 10, diff --git a/os/hal/platforms/STM32/i2c_lld.c b/os/hal/platforms/STM32/i2c_lld.c index 086af776e..84c395fb5 100644 --- a/os/hal/platforms/STM32/i2c_lld.c +++ b/os/hal/platforms/STM32/i2c_lld.c @@ -23,6 +23,11 @@ * Otherwise there is a risk of setting a second STOP, START or PEC request. */ +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ +#define I2C_STOP_GPT_TIMEOUT 50 /* waiting timer value */ +#define I2C_START_GPT_TIMEOUT 50 /* waiting timer value */ /*===========================================================================*/ /* Driver exported variables. */ @@ -48,27 +53,83 @@ static volatile uint16_t dbgSR1 = 0; static volatile uint16_t dbgSR2 = 0; static volatile uint16_t dbgCR1 = 0; static volatile uint16_t dbgCR2 = 0; - -static uint32_t polling_time_worst = 0; -static uint32_t polling_time_begin = 0; -static uint32_t polling_time_delta = 0; - #endif /* CH_DBG_ENABLE_ASSERTS */ +/* defines for convenience purpose */ +#define txBuffp (i2cp->txbuff_p) +#define rxBuffp (i2cp->rxbuff_p) + /*===========================================================================*/ /* Driver local functions. */ /*===========================================================================*/ #if STM32_I2C_USE_POLLING_WAIT #else -VirtualTimer i2c_waiting_vt; +/* + * GPT1 callback. + */ +static void gpt1cb(GPTDriver *gptp) { + (void)gptp; + I2CDriver *i2cp = &I2CD1; + + chSysLockFromIsr(); + i2cp->flags &= ~I2C_FLG_TIMER_ARMED; + + switch(i2cp->id_state){ + case I2C_ACTIVE_TRANSMIT: + i2c_lld_master_transmit(i2cp, i2cp->slave_addr, i2cp->txbuf, i2cp->txbytes, i2cp->rxbuf, i2cp->rxbytes); + break; + + case I2C_ACTIVE_RECEIVE: + i2c_lld_master_receive(i2cp, i2cp->slave_addr, i2cp->rxbuf, i2cp->rxbytes); + break; + + default: + break; + } + chSysUnlockFromIsr(); +} + + + +/* + * GPT2 callback. + */ +static void gpt2cb(GPTDriver *gptp) { + (void)gptp; + I2CDriver *i2cp = &I2CD2; + + chSysLockFromIsr(); + i2cp->flags &= ~I2C_FLG_TIMER_ARMED; + + switch(i2cp->id_state){ + case I2C_ACTIVE_TRANSMIT: + i2c_lld_master_transmit(i2cp, i2cp->slave_addr, i2cp->txbuf, i2cp->txbytes, i2cp->rxbuf, i2cp->rxbytes); + break; + + case I2C_ACTIVE_RECEIVE: + i2c_lld_master_receive(i2cp, i2cp->slave_addr, i2cp->rxbuf, i2cp->rxbytes); + break; + + default: + break; + } + chSysUnlockFromIsr(); +} + +/* GPT1 configuration. */ +static const GPTConfig gpt1cfg = { + 1000000, /* 1MHz timer clock.*/ + gpt1cb /* Timer callback.*/ +}; + +/* GPT2 configuration. */ +static const GPTConfig gpt2cfg = { + 1000000, /* 1MHz timer clock.*/ + gpt2cb /* Timer callback.*/ +}; #endif /* STM32_I2C_USE_POLLING_WAIT */ - -/* defines for convenience purpose */ -#define txBuffp (i2cp->txbuff_p) -#define rxBuffp (i2cp->rxbuff_p) - /** * @brief Function for I2C debugging purpose. * @note Internal use only. @@ -408,15 +469,33 @@ void i2c_lld_init(void) { RCC->APB1RSTR = RCC_APB1RSTR_I2C1RST; /* reset I2C 1 */ RCC->APB1RSTR = 0; i2cObjectInit(&I2CD1); - I2CD1.id_i2c = I2C1; -#endif + I2CD1.id_i2c = I2C1; + +#if !(STM32_I2C_I2C1_USE_POLLING_WAIT) + I2CD1.timer = &GPTD1;//TODO: remove hardcode + I2CD1.timer_cfg = &gpt1cfg;//TODO: remove hardcode +#else + I2CD1.timer = NULL; + I2CD1.timer_cfg = NULL; +#endif /* !(STM32_I2C_I2C1_USE_POLLING_WAIT) */ + +#endif /* STM32_I2C_USE_I2C */ #if STM32_I2C_USE_I2C2 RCC->APB1RSTR = RCC_APB1RSTR_I2C2RST; /* reset I2C 2 */ RCC->APB1RSTR = 0; i2cObjectInit(&I2CD2); - I2CD2.id_i2c = I2C2; -#endif + I2CD2.id_i2c = I2C2; + +#if !(STM32_I2C_I2C2_USE_POLLING_WAIT) + I2CD2.timer = &GPTD2;//TODO: remove hardcode + I2CD2.timer_cfg = &gpt2cfg;//TODO: remove hardcode +#else + I2CD2.timer = NULL; + I2CD2.timer_cfg = NULL; +#endif /* !(STM32_I2C_I2C2_USE_POLLING_WAIT) */ + +#endif /* STM32_I2C_USE_I2C2 */ } /** @@ -425,6 +504,9 @@ void i2c_lld_init(void) { * @param[in] i2cp pointer to the @p I2CDriver object */ void i2c_lld_start(I2CDriver *i2cp) { + if (i2cp->timer != NULL || i2cp->timer_cfg != NULL) + gptStart(i2cp->timer, i2cp->timer_cfg); + if (i2cp->id_state == I2C_STOP) { /* If in stopped state then enables the I2C clock.*/ #if STM32_I2C_USE_I2C1 if (&I2CD1 == i2cp) { @@ -633,22 +715,22 @@ void i2c_lld_master_transmit(I2CDriver *i2cp, uint16_t slave_addr, i2cp->slave_addr1 = ((slave_addr <<1) & 0x00FE); /* LSB = 0 -> write */ } + chDbgAssert(!(i2cp->flags & I2C_FLG_TIMER_ARMED), + "i2c_lld_master_transmit(), #1", "time to STOP is out"); + if ((i2cp->id_i2c->CR1 & I2C_CR1_STOP) && i2cp->timer != NULL && i2cp->timer_cfg != NULL){ + gptStartOneShot(i2cp->timer, I2C_STOP_GPT_TIMEOUT); + i2cp->flags |= I2C_FLG_TIMER_ARMED; + return; + } + else{ + while(i2cp->id_i2c->CR1 & I2C_CR1_STOP) + ; + } + i2cp->flags = 0; i2cp->errors = 0; - - #if CH_DBG_ENABLE_ASSERTS - polling_time_begin = PWMD4.tim->CNT; - #endif - while(i2cp->id_i2c->CR1 & I2C_CR1_STOP) - ; - #if CH_DBG_ENABLE_ASSERTS - polling_time_delta = PWMD4.tim->CNT - polling_time_begin; - if (polling_time_delta > polling_time_worst) - polling_time_worst = polling_time_delta; - #endif - i2cp->id_i2c->CR1 &= ~I2C_CR1_POS; - i2cp->id_i2c->CR1 |= I2C_CR1_START; /* send start bit */ + i2cp->id_i2c->CR1 |= I2C_CR1_START; i2cp->id_i2c->CR2 |= (I2C_CR2_ITERREN|I2C_CR2_ITEVTEN|I2C_CR2_ITBUFEN); /* enable ERR, EVT & BUF ITs */ } @@ -675,6 +757,7 @@ void i2c_lld_master_receive(I2CDriver *i2cp, uint16_t slave_addr, i2cp->rxbytes = rxbytes; i2cp->rxbuf = rxbuf; + if(slave_addr & 0x8000){ /* 10-bit mode used */ i2cp->slave_addr1 = ((slave_addr >>7) & 0x0006); /* add the two msb of 10-bit address to the header */ i2cp->slave_addr1 |= 0xF0; /* add the header bits (the LSB -> 1 will be add to second */ @@ -684,19 +767,22 @@ void i2c_lld_master_receive(I2CDriver *i2cp, uint16_t slave_addr, i2cp->slave_addr1 = ((slave_addr <<1) | 0x01); /* LSB = 1 -> receive */ } - i2cp->flags = I2C_FLG_MASTER_RECEIVER; - i2cp->errors = 0; - #if CH_DBG_ENABLE_ASSERTS - polling_time_begin = PWMD4.tim->CNT; - #endif - while(i2cp->id_i2c->CR1 & I2C_CR1_STOP) - ; - #if CH_DBG_ENABLE_ASSERTS - polling_time_delta = PWMD4.tim->CNT - polling_time_begin; - if (polling_time_delta > polling_time_worst) - polling_time_worst = polling_time_delta; - #endif + chDbgAssert(!(i2cp->flags & I2C_FLG_TIMER_ARMED), + "i2c_lld_master_receive(), #1", "time to STOP is out"); + if ((i2cp->id_i2c->CR1 & I2C_CR1_STOP) && i2cp->timer != NULL && i2cp->timer_cfg != NULL){ + gptStartOneShot(i2cp->timer, I2C_STOP_GPT_TIMEOUT); + i2cp->flags |= I2C_FLG_TIMER_ARMED; + return; + } + else{ + while(i2cp->id_i2c->CR1 & I2C_CR1_STOP) + ; + } + + + i2cp->flags |= I2C_FLG_MASTER_RECEIVER; + i2cp->errors = 0; i2cp->id_i2c->CR1 |= I2C_CR1_ACK; /* acknowledge returned */ i2cp->id_i2c->CR1 &= ~I2C_CR1_POS; @@ -728,11 +814,6 @@ void i2c_lld_master_transceive(I2CDriver *i2cp){ "i2c_lld_master_transceive(), #1", ""); - i2cp->flags = I2C_FLG_MASTER_RECEIVER; - i2cp->errors = 0; - i2cp->id_i2c->CR1 |= I2C_CR1_ACK; /* acknowledge returned */ - i2cp->id_i2c->CR1 &= ~I2C_CR1_POS; - if(i2cp->slave_addr & 0x8000){ /* 10-bit mode used */ i2cp->slave_addr1 = ((i2cp->slave_addr >>7) & 0x0006);/* add the two msb of 10-bit address to the header */ i2cp->slave_addr1 |= 0xF0; /* add the header bits (the LSB -> 1 will be add to second */ @@ -742,6 +823,14 @@ void i2c_lld_master_transceive(I2CDriver *i2cp){ i2cp->slave_addr1 |= 0x01; } + + + + i2cp->flags |= I2C_FLG_MASTER_RECEIVER; + i2cp->errors = 0; + i2cp->id_i2c->CR1 |= I2C_CR1_ACK; /* acknowledge returned */ + i2cp->id_i2c->CR1 &= ~I2C_CR1_POS; + if(i2cp->rxbytes == 1) { /* Only one byte to be received */ i2cp->flags |= I2C_FLG_1BTR; } @@ -750,6 +839,9 @@ void i2c_lld_master_transceive(I2CDriver *i2cp){ i2cp->id_i2c->CR1 |= I2C_CR1_POS; /* Acknowledge Position */ } + +//TODO: use timer here also!! + i2cp->id_i2c->CR1 |= I2C_CR1_START; /* send start bit */ uint32_t timeout = I2C_START_TIMEOUT; diff --git a/os/hal/platforms/STM32/i2c_lld.h b/os/hal/platforms/STM32/i2c_lld.h index d964b342a..b5f75e77f 100644 --- a/os/hal/platforms/STM32/i2c_lld.h +++ b/os/hal/platforms/STM32/i2c_lld.h @@ -20,16 +20,45 @@ /*===========================================================================*/ /** - * @brief Waiting method switch. + * @brief TODO!!!!!!!! * @details If set to @p TRUE than waiting of STOP generation will use * while() loop polling. Otherwise -- virtual timer will be used. * @note The default is @p TRUE. * @note Virtual timer resolution is 1/@p CH_FREQUENCY seconds. */ -#if !defined(STM32_I2C_USE_POLLING_WAIT) || defined(__DOXYGEN__) -#define STM32_I2C_USE_POLLING_WAIT TRUE +#if !defined(STM32_I2C_I2C1_USE_GPT_TIM1) || \ + !defined(STM32_I2C_I2C1_USE_GPT_TIM2) || \ + !defined(STM32_I2C_I2C1_USE_GPT_TIM3) || \ + !defined(STM32_I2C_I2C1_USE_GPT_TIM4) || \ + !defined(STM32_I2C_I2C1_USE_GPT_TIM5) || \ + !defined(STM32_I2C_I2C1_USE_GPT_TIM8) || \ + !defined(STM32_I2C_I2C1_USE_VIRTUAL_TIMER) || \ + !defined(STM32_I2C_I2C1_USE_POLLING_WAIT) || \ + defined(__DOXYGEN__) +#define STM32_I2C_I2C1_USE_POLLING_WAIT TRUE #endif + + +#if !defined(STM32_I2C_I2C2_USE_GPT_TIM1) || \ + !defined(STM32_I2C_I2C2_USE_GPT_TIM2) || \ + !defined(STM32_I2C_I2C2_USE_GPT_TIM3) || \ + !defined(STM32_I2C_I2C2_USE_GPT_TIM4) || \ + !defined(STM32_I2C_I2C2_USE_GPT_TIM5) || \ + !defined(STM32_I2C_I2C2_USE_GPT_TIM8) || \ + !defined(STM32_I2C_I2C2_USE_VIRTUAL_TIMER) || \ + !defined(STM32_I2C_I2C2_USE_POLLING_WAIT) || \ + defined(__DOXYGEN__) +#define STM32_I2C_I2C2_USE_POLLING_WAIT TRUE +#endif + + + + + + + + /** * @brief I2C1 driver enable switch. * @details If set to @p TRUE the support for I2C1 is included. @@ -89,6 +118,7 @@ #define I2C_FLG_3BTR 0x04 /* Last three received bytes to be processed */ #define I2C_FLG_MASTER_RECEIVER 0x10 #define I2C_FLG_HEADER_SENT 0x80 +#define I2C_FLG_TIMER_ARMED 0x40 /* Used to check locks on the bus */ #define EV6_SUBEV_MASK (I2C_FLG_1BTR|I2C_FLG_2BTR|I2C_FLG_MASTER_RECEIVER) #define EV7_SUBEV_MASK (I2C_FLG_2BTR|I2C_FLG_3BTR|I2C_FLG_MASTER_RECEIVER) @@ -197,6 +227,17 @@ struct I2CDriver{ * @brief Pointer to the I2Cx registers block. */ I2C_TypeDef *id_i2c; + + /** + * @brief Timer for waiting STOP condition on the bus. + * @details Workaround for STM32 buggy I2C cell. + */ + GPTDriver *timer; + + /** + * @brief Config for workaround timer. + */ + const GPTConfig *timer_cfg; } ; diff --git a/os/hal/src/i2c.c b/os/hal/src/i2c.c index b169fb70d..b233764f5 100644 --- a/os/hal/src/i2c.c +++ b/os/hal/src/i2c.c @@ -176,7 +176,7 @@ void i2cMasterTransmit(I2CDriver *i2cp, chDbgAssert(i2cp->id_state == I2C_READY, "i2cMasterTransmit(), #1", "not ready"); - i2cp->id_state = I2C_ACTIVE; + i2cp->id_state = I2C_ACTIVE_TRANSMIT; i2c_lld_master_transmit(i2cp, slave_addr, txbuf, txbytes, rxbuf, rxbytes); _i2c_wait_s(i2cp); } @@ -214,7 +214,7 @@ void i2cMasterReceive(I2CDriver *i2cp, chDbgAssert(i2cp->id_state == I2C_READY, "i2cMasterReceive(), #1", "not ready"); - i2cp->id_state = I2C_ACTIVE; + i2cp->id_state = I2C_ACTIVE_RECEIVE; i2c_lld_master_receive(i2cp, slave_addr, rxbuf, rxbytes); _i2c_wait_s(i2cp); }