From 33e5baffbde66053bc8780d6ab6a5d8768c28918 Mon Sep 17 00:00:00 2001 From: Giovanni Di Sirio Date: Fri, 17 Apr 2015 16:16:27 +0000 Subject: git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@7903 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c | 262 +++++++++++++++++++-------------- os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h | 4 +- 2 files changed, 156 insertions(+), 110 deletions(-) (limited to 'os/hal/ports') diff --git a/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c b/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c index 37a4b97b7..1f320808b 100644 --- a/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c +++ b/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c @@ -55,9 +55,6 @@ /* Driver constants. */ /*===========================================================================*/ -#define I2C_MASTER_TC \ - ((uint32_t)(I2C_ISR_BUSY | I2C_ISR_TC)) - #define I2C_ERROR_MASK \ ((uint32_t)(I2C_ISR_BERR | I2C_ISR_ARLO | I2C_ISR_OVR | I2C_ISR_PECERR | \ I2C_ISR_TIMEOUT | I2C_ISR_ALERT)) @@ -89,38 +86,80 @@ I2CDriver I2CD2; /*===========================================================================*/ /** - * @brief I2C transfer setup. - * @note The RW bit is not handled internally. + * @brief Slave address setup. + * @note The RW bit is set to zero internally. * * @param[in] i2cp pointer to the @p I2CDriver object * @param[in] addr slave device address - * @param[in] n size of the transfer * * @notapi */ -static void i2c_lld_setup_transfer(I2CDriver *i2cp, i2caddr_t addr, size_t n) { +static void i2c_lld_set_address(I2CDriver *i2cp, i2caddr_t addr) { I2C_TypeDef *dp = i2cp->i2c; - uint32_t cr2; - /* Adjust slave address (master mode) for 7-bit address mode */ + /* Address alignment depends on the addressing mode selected.*/ if ((i2cp->config->cr2 & I2C_CR2_ADD10) == 0U) - cr2 = ((uint32_t)addr & 0x7FU) << 1U; + dp->CR2 = ((uint32_t)addr & 0x7FU) << 1U; else - cr2 = (uint32_t)addr; + dp->CR2 = (uint32_t)addr; +} + +/** + * @brief I2C RX transfer setup. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] n size of the transfer + * + * @notapi + */ +static void i2c_lld_setup_rx_transfer(I2CDriver *i2cp, size_t n) { + I2C_TypeDef *dp = i2cp->i2c; + uint32_t reload; /* The unit can transfer 255 bytes maximum in a single operation.*/ if (n > 255U) { - cr2 |= I2C_CR2_RELOAD; + i2cp->tsize = n - 255U; n = 255U; + reload = I2C_CR2_RELOAD; + } + else { + i2cp->tsize = 0; + reload = 0; } /* Configures the CR2 registers with both the calculated and static settings.*/ - dp->CR2 = (i2cp->config->cr2 & ~(I2C_CR2_NBYTES | I2C_CR2_SADD)) | - (n << 16U) | cr2; + dp->CR2 = (dp->CR2 & ~I2C_CR2_NBYTES) | i2cp->config->cr2 | I2C_CR2_RD_WRN | + (n << 16U) | reload; +} - /* Transfer complete interrupt enabled.*/ - dp->CR1 |= I2C_CR1_TCIE; +/** + * @brief I2C TX transfer setup. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] n size of the transfer + * + * @notapi + */ +static void i2c_lld_setup_tx_transfer(I2CDriver *i2cp, size_t n) { + I2C_TypeDef *dp = i2cp->i2c; + uint32_t reload; + + /* The unit can transfer 255 bytes maximum in a single operation.*/ + if (n > 255U) { + i2cp->tsize = n - 255U; + n = 255U; + reload = I2C_CR2_RELOAD; + } + else { + i2cp->tsize = 0; + reload = 0; + } + + /* Configures the CR2 registers with both the calculated and static + settings.*/ + dp->CR2 = (dp->CR2 & ~I2C_CR2_NBYTES) | i2cp->config->cr2 | + (n << 16U) | reload; } /** @@ -157,46 +196,70 @@ static void i2c_lld_abort_operation(I2CDriver *i2cp) { static void i2c_lld_serve_interrupt(I2CDriver *i2cp, uint32_t isr) { I2C_TypeDef *dp = i2cp->i2c; - if ((isr & I2C_ISR_TC) && (i2cp->state == I2C_ACTIVE_TX)) { - size_t rxbytes; + /* Special case of a received NACK, the transfer is aborted.*/ + if ((isr & I2C_ISR_NACKF) != 0U) { + /* Stops the associated DMA streams.*/ + dmaStreamDisable(i2cp->dmatx); + dmaStreamDisable(i2cp->dmarx); + + /* Error flag.*/ + i2cp->errors |= I2C_ACK_FAILURE; + } + else { + /* Partial transfer handling, restarting the transfer and returning.*/ + if ((isr & I2C_ISR_TCR) != 0U) { + if (i2cp->state == I2C_ACTIVE_TX) { + i2c_lld_setup_tx_transfer(i2cp, i2cp->tsize); + } + else { + i2c_lld_setup_rx_transfer(i2cp, i2cp->tsize); + } + return; + } + + /* The following condition is true if a transfer phase has been completed.*/ + if ((isr & I2C_ISR_TC) != 0U) { + if (i2cp->state == I2C_ACTIVE_TX) { + /* End of the transmit phase.*/ + size_t rxbytes = dmaStreamGetTransactionSize(i2cp->dmarx); - /* Make sure no more 'Transfer complete' interrupts.*/ - dp->CR1 &= ~I2C_CR1_TCIE; + /* Disabling TX DMA channel.*/ + dmaStreamDisable(i2cp->dmatx); - rxbytes = dmaStreamGetTransactionSize(i2cp->dmarx); - if (rxbytes > 0) { - i2cp->state = I2C_ACTIVE_RX; + /* Starting receive phase if necessary.*/ + if (rxbytes > 0U) { + /* Setting up the peripheral.*/ + i2c_lld_setup_rx_transfer(i2cp, rxbytes); - /* Enable RX DMA */ - dmaStreamEnable(i2cp->dmarx); + /* Enabling RX DMA.*/ + dmaStreamEnable(i2cp->dmarx); - dp->CR2 &= ~I2C_CR2_NBYTES; - dp->CR2 |= rxbytes << 16; + /* Starts the read operation.*/ + dp->CR2 |= I2C_CR2_START; - /* Starts the read operation.*/ - dp->CR2 |= I2C_CR2_RD_WRN; - dp->CR2 |= I2C_CR2_START; - return; + /* State change.*/ + i2cp->state = I2C_ACTIVE_RX; + + /* Note, returning because the transaction is not over yet.*/ + return; + } + } + else { + /* End of the receive phase.*/ + + /* Disabling RX DMA channel.*/ + dmaStreamDisable(i2cp->dmarx); + } } - /* Nothing to receive - send STOP immediately.*/ - dp->CR2 |= I2C_CR2_STOP; } - if (isr & I2C_ISR_NACKF) { - /* Starts a STOP sequence immediately on error.*/ - dp->CR2 |= I2C_CR2_STOP; - /* Stops the associated DMA streams.*/ - dmaStreamDisable(i2cp->dmatx); - dmaStreamDisable(i2cp->dmarx); + /* Transaction finished sending the STOP.*/ + dp->CR2 |= I2C_CR2_STOP; - /* Error flag.*/ - i2cp->errors |= I2C_ACK_FAILURE; - } - if (isr & I2C_ISR_STOPF) { - /* Stops the associated DMA streams.*/ - dmaStreamDisable(i2cp->dmatx); - dmaStreamDisable(i2cp->dmarx); - } + /* Make sure no more 'Transfer Complete' interrupts.*/ + dp->CR1 &= ~I2C_CR1_TCIE; + + /* Thread wakeup at transaction end.*/ if (i2cp->errors) { /* Errors are signaled to the upper layer.*/ _i2c_wakeup_error_isr(i2cp); @@ -207,51 +270,6 @@ static void i2c_lld_serve_interrupt(I2CDriver *i2cp, uint32_t isr) { } } -/** - * @brief DMA RX end IRQ handler. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] flags pre-shifted content of the ISR register - * - * @notapi - */ -static void i2c_lld_serve_rx_end_irq(I2CDriver *i2cp, uint32_t flags) { - I2C_TypeDef *dp = i2cp->i2c; - - /* DMA errors handling.*/ -#if defined(STM32_I2C_DMA_ERROR_HOOK) - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - STM32_I2C_DMA_ERROR_HOOK(i2cp); - } -#else - (void)flags; -#endif - - dmaStreamDisable(i2cp->dmarx); - dp->CR2 |= I2C_CR2_STOP; -} - -/** - * @brief DMA TX end IRQ handler. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2c_lld_serve_tx_end_irq(I2CDriver *i2cp, uint32_t flags) { - - /* DMA errors handling.*/ -#if defined(STM32_I2C_DMA_ERROR_HOOK) - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - STM32_I2C_DMA_ERROR_HOOK(i2cp); - } -#else - (void)flags; -#endif - - dmaStreamDisable(i2cp->dmatx); -} - /** * @brief I2C error handler. * @@ -451,12 +469,12 @@ void i2c_lld_start(I2CDriver *i2cp) { rccResetI2C1(); b = dmaStreamAllocate(i2cp->dmarx, STM32_I2C_I2C1_IRQ_PRIORITY, - (stm32_dmaisr_t)i2c_lld_serve_rx_end_irq, + NULL, (void *)i2cp); osalDbgAssert(!b, "stream already allocated"); b = dmaStreamAllocate(i2cp->dmatx, STM32_I2C_I2C1_IRQ_PRIORITY, - (stm32_dmaisr_t)i2c_lld_serve_tx_end_irq, + NULL, (void *)i2cp); osalDbgAssert(!b, "stream already allocated"); rccEnableI2C1(FALSE); @@ -484,12 +502,12 @@ void i2c_lld_start(I2CDriver *i2cp) { rccResetI2C2(); b = dmaStreamAllocate(i2cp->dmarx, STM32_I2C_I2C2_IRQ_PRIORITY, - (stm32_dmaisr_t)i2c_lld_serve_rx_end_irq, + NULL, (void *)i2cp); osalDbgAssert(!b, "stream already allocated"); b = dmaStreamAllocate(i2cp->dmatx, STM32_I2C_I2C2_IRQ_PRIORITY, - (stm32_dmaisr_t)i2c_lld_serve_tx_end_irq, + NULL, (void *)i2cp); osalDbgAssert(!b, "stream already allocated"); rccEnableI2C2(FALSE); @@ -516,8 +534,8 @@ void i2c_lld_start(I2CDriver *i2cp) { dmaStreamSetPeripheral(i2cp->dmatx, &dp->TXDR); /* Reset i2c peripheral, the TCIE bit will be handled separately.*/ - dp->CR1 = i2cp->config->cr1 | I2C_CR1_ERRIE | I2C_CR1_STOPIE | - I2C_CR1_NACKIE | I2C_CR1_TXDMAEN | I2C_CR1_RXDMAEN; + dp->CR1 = i2cp->config->cr1 | I2C_CR1_ERRIE | I2C_CR1_NACKIE | + I2C_CR1_TXDMAEN | I2C_CR1_RXDMAEN; /* Set slave address field (master mode) */ dp->CR2 = (i2cp->config->cr2 & ~I2C_CR2_SADD); @@ -604,6 +622,7 @@ void i2c_lld_stop(I2CDriver *i2cp) { msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, uint8_t *rxbuf, size_t rxbytes, systime_t timeout) { + msg_t msg; I2C_TypeDef *dp = i2cp->i2c; systime_t start, end; @@ -641,18 +660,31 @@ msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, osalSysUnlock(); } + /* Setting up the slave address.*/ + i2c_lld_set_address(i2cp, addr); + /* Setting up the peripheral.*/ - i2c_lld_setup_transfer(i2cp, addr, rxbytes); + i2c_lld_setup_rx_transfer(i2cp, rxbytes); - /* Enable RX DMA */ + /* Enabling RX DMA.*/ dmaStreamEnable(i2cp->dmarx); + /* Transfer complete interrupt enabled.*/ + dp->CR1 |= I2C_CR1_TCIE; + /* Starts the operation.*/ - dp->CR2 |= I2C_CR2_RD_WRN; dp->CR2 |= I2C_CR2_START; /* Waits for the operation completion or a timeout.*/ - return osalThreadSuspendTimeoutS(&i2cp->thread, timeout); + msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); + + /* In case of a software timeout a STOP is sent as an extreme attempt + to release the bus.*/ + if (msg == MSG_TIMEOUT) { + dp->CR2 |= I2C_CR2_STOP; + } + + return msg; } /** @@ -684,6 +716,7 @@ msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, const uint8_t *txbuf, size_t txbytes, uint8_t *rxbuf, size_t rxbytes, systime_t timeout) { + msg_t msg; I2C_TypeDef *dp = i2cp->i2c; systime_t start, end; @@ -726,18 +759,31 @@ msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, osalSysUnlock(); } - /* Setting up the peripheral.*/ - i2c_lld_setup_transfer(i2cp, addr, txbytes); + /* Setting up the slave address.*/ + i2c_lld_set_address(i2cp, addr); - /* Enable TX DMA */ + /* Preparing the transfer.*/ + i2c_lld_setup_tx_transfer(i2cp, txbytes); + + /* Enabling TX DMA.*/ dmaStreamEnable(i2cp->dmatx); - /* Starts the operation as the very last thing, I2C_CR2_RD_WRN is already - zero.*/ + /* Transfer complete interrupt enabled.*/ + dp->CR1 |= I2C_CR1_TCIE; + + /* Starts the operation.*/ dp->CR2 |= I2C_CR2_START; /* Waits for the operation completion or a timeout.*/ - return osalThreadSuspendTimeoutS(&i2cp->thread, timeout); + msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); + + /* In case of a software timeout a STOP is sent as an extreme attempt + to release the bus.*/ + if (msg == MSG_TIMEOUT) { + dp->CR2 |= I2C_CR2_STOP; + } + + return msg; } #endif /* HAL_USE_I2C */ diff --git a/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h b/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h index b5d07bd2c..aa0e89de5 100644 --- a/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h +++ b/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h @@ -280,9 +280,9 @@ struct I2CDriver { */ thread_reference_t thread; /** - * @brief Current slave address without R/W bit. + * @brief Current transfer size. */ - i2caddr_t addr; + i2caddr_t tsize; /** * @brief RX DMA mode bit mask. */ -- cgit v1.2.3