diff --git a/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c b/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c index dd09fb265..a870a40ae 100644 --- a/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c +++ b/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c @@ -30,6 +30,7 @@ /* Driver local definitions. */ /*===========================================================================*/ +#if STM32_I2C_USE_DMA == TRUE #define DMAMODE_COMMON \ (STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE | \ STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE | \ @@ -50,6 +51,15 @@ #define I2C2_TX_DMA_CHANNEL \ STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_TX_DMA_STREAM, \ STM32_I2C2_TX_DMA_CHN) +#endif /* STM32_I2C_USE_DMA == TRUE */ + +#if STM32_I2C_USE_DMA == TRUE +#define i2c_lld_get_rxbytes(i2cp) dmaStreamGetTransactionSize((i2cp)->dmarx) +#define i2c_lld_get_txbytes(i2cp) dmaStreamGetTransactionSize((i2cp)->dmatx) +#else +#define i2c_lld_get_rxbytes(i2cp) (i2cp)->rxbytes +#define i2c_lld_get_txbytes(i2cp) (i2cp)->txbytes +#endif /*===========================================================================*/ /* Driver constants. */ @@ -108,22 +118,21 @@ static void i2c_lld_set_address(I2CDriver *i2cp, i2caddr_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) { +static void i2c_lld_setup_rx_transfer(I2CDriver *i2cp) { I2C_TypeDef *dp = i2cp->i2c; uint32_t reload; + size_t n; /* The unit can transfer 255 bytes maximum in a single operation.*/ + n = i2c_lld_get_rxbytes(i2cp); if (n > 255U) { - i2cp->tsize = n - 255U; n = 255U; reload = I2C_CR2_RELOAD; } else { - i2cp->tsize = 0U; reload = 0U; } @@ -137,22 +146,21 @@ static void i2c_lld_setup_rx_transfer(I2CDriver *i2cp, size_t n) { * @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) { +static void i2c_lld_setup_tx_transfer(I2CDriver *i2cp) { I2C_TypeDef *dp = i2cp->i2c; uint32_t reload; + size_t n; /* The unit can transfer 255 bytes maximum in a single operation.*/ + n = i2c_lld_get_txbytes(i2cp); if (n > 255U) { - i2cp->tsize = n - 255U; n = 255U; reload = I2C_CR2_RELOAD; } else { - i2cp->tsize = 0U; reload = 0U; } @@ -180,9 +188,13 @@ static void i2c_lld_abort_operation(I2CDriver *i2cp) { dp->CR1 |= I2C_CR1_PE; } +#if STM32_I2C_USE_DMA == TRUE /* Stops the associated DMA streams.*/ dmaStreamDisable(i2cp->dmatx); dmaStreamDisable(i2cp->dmarx); +#else + dp->CR1 &= ~(I2C_CR1_TXIE | I2C_CR1_RXIE); +#endif } /** @@ -198,73 +210,115 @@ static void i2c_lld_serve_interrupt(I2CDriver *i2cp, uint32_t isr) { /* Special case of a received NACK, the transfer is aborted.*/ if ((isr & I2C_ISR_NACKF) != 0U) { +#if STM32_I2C_USE_DMA == TRUE /* Stops the associated DMA streams.*/ dmaStreamDisable(i2cp->dmatx); dmaStreamDisable(i2cp->dmarx); +#endif /* 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); + /* Transaction finished sending the STOP.*/ + dp->CR2 |= I2C_CR2_STOP; - /* Disabling TX DMA channel.*/ - dmaStreamDisable(i2cp->dmatx); + /* Make sure no more interrupts.*/ + dp->CR1 &= ~(I2C_CR1_TCIE | I2C_CR1_TXIE | I2C_CR1_RXIE); - /* Starting receive phase if necessary.*/ - if (rxbytes > 0U) { - /* Setting up the peripheral.*/ - i2c_lld_setup_rx_transfer(i2cp, rxbytes); - - /* Enabling RX DMA.*/ - dmaStreamEnable(i2cp->dmarx); - - /* Starts the read operation.*/ - dp->CR2 |= I2C_CR2_START; - - /* 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); - } - } - } - - /* Transaction finished sending the STOP.*/ - dp->CR2 |= I2C_CR2_STOP; - - /* 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); + + return; } - else { + +#if STM32_I2C_USE_DMA == FALSE + /* Handling of data transfer if the DMA mode is disabled.*/ + { + uint32_t cr1 = dp->CR1; + + if (i2cp->state == I2C_ACTIVE_TX) { + /* Transmission phase.*/ + if (((cr1 &I2C_CR1_TXIE) != 0U) && ((isr & I2C_ISR_TXIS) != 0U)) { + dp->TXDR = (uint32_t)*i2cp->txptr; + i2cp->txptr++; + i2cp->txbytes--; + if (i2cp->txbytes == 0U) { + dp->CR1 &= ~I2C_CR1_TXIE; + } + } + } + else { + /* Receive phase.*/ + if (((cr1 & I2C_CR1_RXIE) != 0U) && ((isr & I2C_ISR_RXNE) != 0U)) { + *i2cp->rxptr = (uint8_t)dp->RXDR; + i2cp->rxptr++; + i2cp->rxbytes--; + if (i2cp->rxbytes == 0U) { + dp->CR1 &= ~I2C_CR1_RXIE; + } + } + } + } +#endif + + /* 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); + } + else { + i2c_lld_setup_rx_transfer(i2cp); + } + 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.*/ + +#if STM32_I2C_USE_DMA == TRUE + /* Disabling TX DMA channel.*/ + dmaStreamDisable(i2cp->dmatx); +#endif + + /* Starting receive phase if necessary.*/ + if (i2c_lld_get_rxbytes(i2cp) > 0U) { + /* Setting up the peripheral.*/ + i2c_lld_setup_rx_transfer(i2cp); + +#if STM32_I2C_USE_DMA == TRUE + /* Enabling RX DMA.*/ + dmaStreamEnable(i2cp->dmarx); +#else + /* RX interrupt enabled.*/ + dp->CR1 |= I2C_CR1_RXIE; +#endif + + /* Starts the read operation.*/ + dp->CR2 |= I2C_CR2_START; + + /* State change.*/ + i2cp->state = I2C_ACTIVE_RX; + + /* Note, returning because the transaction is not over yet.*/ + return; + } + } + else { + /* End of the receive phase.*/ +#if STM32_I2C_USE_DMA == TRUE + /* Disabling RX DMA channel.*/ + dmaStreamDisable(i2cp->dmarx); +#endif + } + + /* Transaction finished sending the STOP.*/ + dp->CR2 |= I2C_CR2_STOP; + + /* Make sure no more 'Transfer Complete' interrupts.*/ + dp->CR1 &= ~I2C_CR1_TCIE; + /* Normal transaction end.*/ _i2c_wakeup_isr(i2cp); } @@ -280,9 +334,14 @@ static void i2c_lld_serve_interrupt(I2CDriver *i2cp, uint32_t isr) { */ static void i2c_lld_serve_error_interrupt(I2CDriver *i2cp, uint32_t isr) { - /* Clears interrupt flags just to be safe.*/ +#if STM32_I2C_USE_DMA == TRUE + /* Clears DMA interrupt flags just to be safe.*/ dmaStreamDisable(i2cp->dmatx); dmaStreamDisable(i2cp->dmarx); +#else + /* Disabling RX and TX interrupts.*/ + i2cp->i2c->CR1 &= ~(I2C_CR1_TXIE | I2C_CR1_RXIE); +#endif if (isr & I2C_ISR_BERR) i2cp->errors |= I2C_BUS_ERROR; @@ -430,16 +489,20 @@ void i2c_lld_init(void) { i2cObjectInit(&I2CD1); I2CD1.thread = NULL; I2CD1.i2c = I2C1; +#if STM32_I2C_USE_DMA == TRUE I2CD1.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C1_RX_DMA_STREAM); I2CD1.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C1_TX_DMA_STREAM); +#endif #endif /* STM32_I2C_USE_I2C1 */ #if STM32_I2C_USE_I2C2 i2cObjectInit(&I2CD2); I2CD2.thread = NULL; I2CD2.i2c = I2C2; +#if STM32_I2C_USE_DMA == TRUE I2CD2.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C2_RX_DMA_STREAM); I2CD2.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C2_TX_DMA_STREAM); +#endif #endif /* STM32_I2C_USE_I2C2 */ } @@ -453,8 +516,11 @@ void i2c_lld_init(void) { void i2c_lld_start(I2CDriver *i2cp) { I2C_TypeDef *dp = i2cp->i2c; +#if STM32_I2C_USE_DMA == TRUE + /* Common DMA modes.*/ i2cp->txdmamode = DMAMODE_COMMON | STM32_DMA_CR_DIR_M2P; i2cp->rxdmamode = DMAMODE_COMMON | STM32_DMA_CR_DIR_P2M; +#endif /* Make sure I2C peripheral is disabled */ dp->CR1 &= ~I2C_CR1_PE; @@ -464,20 +530,30 @@ void i2c_lld_start(I2CDriver *i2cp) { #if STM32_I2C_USE_I2C1 if (&I2CD1 == i2cp) { - bool b; rccResetI2C1(); - b = dmaStreamAllocate(i2cp->dmarx, - STM32_I2C_I2C1_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(i2cp->dmatx, - STM32_I2C_I2C1_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); rccEnableI2C1(FALSE); +#if STM32_I2C_USE_DMA == TRUE + { + bool b; + + b = dmaStreamAllocate(i2cp->dmarx, + STM32_I2C_I2C1_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(i2cp->dmatx, + STM32_I2C_I2C1_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + + i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C1_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); + i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C1_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); + } +#endif /* STM32_I2C_USE_DMA == TRUE */ #if defined(STM32_I2C1_GLOBAL_NUMBER) || defined(__DOXYGEN__) nvicEnableVector(STM32_I2C1_GLOBAL_NUMBER, STM32_I2C_I2C1_IRQ_PRIORITY); @@ -487,30 +563,35 @@ void i2c_lld_start(I2CDriver *i2cp) { #else #error "I2C1 interrupt numbers not defined" #endif - - i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C1_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); - i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C1_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); } #endif /* STM32_I2C_USE_I2C1 */ #if STM32_I2C_USE_I2C2 if (&I2CD2 == i2cp) { - bool b; rccResetI2C2(); - b = dmaStreamAllocate(i2cp->dmarx, - STM32_I2C_I2C2_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(i2cp->dmatx, - STM32_I2C_I2C2_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); rccEnableI2C2(FALSE); +#if STM32_I2C_USE_DMA == TRUE + { + bool b; + + b = dmaStreamAllocate(i2cp->dmarx, + STM32_I2C_I2C2_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(i2cp->dmatx, + STM32_I2C_I2C2_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + + i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C2_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); + i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C2_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); + } +#endif /*STM32_I2C_USE_DMA == TRUE */ #if defined(STM32_I2C2_GLOBAL_NUMBER) || defined(__DOXYGEN__) nvicEnableVector(STM32_I2C2_GLOBAL_NUMBER, STM32_I2C_I2C2_IRQ_PRIORITY); @@ -520,18 +601,15 @@ void i2c_lld_start(I2CDriver *i2cp) { #else #error "I2C2 interrupt numbers not defined" #endif - - i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C2_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); - i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C2_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); } #endif /* STM32_I2C_USE_I2C2 */ } +#if STM32_I2C_USE_DMA == TRUE /* I2C registers pointed by the DMA.*/ dmaStreamSetPeripheral(i2cp->dmarx, &dp->RXDR); dmaStreamSetPeripheral(i2cp->dmatx, &dp->TXDR); +#endif /* Reset i2c peripheral, the TCIE bit will be handled separately.*/ dp->CR1 = i2cp->config->cr1 | I2C_CR1_ERRIE | I2C_CR1_NACKIE | @@ -558,8 +636,10 @@ void i2c_lld_stop(I2CDriver *i2cp) { /* I2C disable.*/ i2c_lld_abort_operation(i2cp); +#if STM32_I2C_USE_DMA == TRUE dmaStreamRelease(i2cp->dmatx); dmaStreamRelease(i2cp->dmarx); +#endif #if STM32_I2C_USE_I2C1 if (&I2CD1 == i2cp) { @@ -629,10 +709,15 @@ msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, /* Releases the lock from high level driver.*/ osalSysUnlock(); +#if STM32_I2C_USE_DMA == TRUE /* RX DMA setup.*/ dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); dmaStreamSetMemory0(i2cp->dmarx, rxbuf); dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes); +#else + i2cp->rxptr = rxbuf; + i2cp->rxbytes = rxbytes; +#endif /* Calculating the time window for the timeout on the busy bus condition.*/ start = osalOsGetSystemTimeX(); @@ -661,13 +746,19 @@ msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, i2c_lld_set_address(i2cp, addr); /* Setting up the peripheral.*/ - i2c_lld_setup_rx_transfer(i2cp, rxbytes); + i2c_lld_setup_rx_transfer(i2cp); +#if STM32_I2C_USE_DMA == TRUE /* Enabling RX DMA.*/ dmaStreamEnable(i2cp->dmarx); /* Transfer complete interrupt enabled.*/ dp->CR1 |= I2C_CR1_TCIE; +#else + + /* Transfer complete and RX interrupts enabled.*/ + dp->CR1 |= I2C_CR1_TCIE | I2C_CR1_RXIE; +#endif /* Starts the operation.*/ dp->CR2 |= I2C_CR2_START; @@ -723,6 +814,7 @@ msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, /* Releases the lock from high level driver.*/ osalSysUnlock(); +#if STM32_I2C_USE_DMA == TRUE /* TX DMA setup.*/ dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); dmaStreamSetMemory0(i2cp->dmatx, txbuf); @@ -732,6 +824,12 @@ msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); dmaStreamSetMemory0(i2cp->dmarx, rxbuf); dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes); +#else + i2cp->txptr = txbuf; + i2cp->txbytes = txbytes; + i2cp->rxptr = rxbuf; + i2cp->rxbytes = rxbytes; +#endif /* Calculating the time window for the timeout on the busy bus condition.*/ start = osalOsGetSystemTimeX(); @@ -760,13 +858,18 @@ msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, i2c_lld_set_address(i2cp, addr); /* Preparing the transfer.*/ - i2c_lld_setup_tx_transfer(i2cp, txbytes); + i2c_lld_setup_tx_transfer(i2cp); +#if STM32_I2C_USE_DMA == TRUE /* Enabling TX DMA.*/ dmaStreamEnable(i2cp->dmatx); /* Transfer complete interrupt enabled.*/ dp->CR1 |= I2C_CR1_TCIE; +#else + /* Transfer complete and TX interrupts enabled.*/ + dp->CR1 |= I2C_CR1_TCIE | I2C_CR1_TXIE; +#endif /* Starts the operation.*/ dp->CR2 |= I2C_CR2_START; diff --git a/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h b/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h index aa0e89de5..3a67bc121 100644 --- a/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h +++ b/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h @@ -98,6 +98,13 @@ #define STM32_I2C_I2C2_IRQ_PRIORITY 10 #endif +/** + * @brief DMA use switch. + */ +#if !defined(STM32_I2C_USE_DMA) || defined(__DOXYGEN__) +#define STM32_I2C_USE_DMA TRUE +#endif + /** * @brief I2C1 DMA priority (0..3|lowest..highest). * @note The priority level is used for both the TX and RX DMA streams but @@ -155,6 +162,7 @@ #error "Invalid IRQ priority assigned to I2C2" #endif +#if STM32_I2C_USE_DMA == TRUE #if STM32_I2C_USE_I2C1 && \ !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C1_DMA_PRIORITY) #error "Invalid DMA priority assigned to I2C1" @@ -208,8 +216,7 @@ #if !defined(STM32_DMA_REQUIRED) #define STM32_DMA_REQUIRED #endif - -/* Check clock range. */ +#endif /* STM32_I2C_USE_DMA == TRUE */ /*===========================================================================*/ /* Driver data structures and types. */ @@ -279,10 +286,7 @@ struct I2CDriver { * @brief Thread waiting for I/O completion. */ thread_reference_t thread; - /** - * @brief Current transfer size. - */ - i2caddr_t tsize; +#if (STM32_I2C_USE_DMA == TRUE) || defined(__DOXYGEN__) /** * @brief RX DMA mode bit mask. */ @@ -299,6 +303,24 @@ struct I2CDriver { * @brief Transmit DMA channel. */ const stm32_dma_stream_t *dmatx; +#else /* STM32_I2C_USE_DMA == FALSE */ + /** + * @brief Pointer to the next TX buffer location. + */ + const uint8_t *txptr; + /** + * @brief Number of bytes in TX phase. + */ + size_t txbytes; + /** + * @brief Pointer to the next RX buffer location. + */ + uint8_t *rxptr; + /** + * @brief Number of bytes in RX phase. + */ + size_t rxbytes; +#endif /* STM32_I2C_USE_DMA == FALSE */ /** * @brief Pointer to the I2Cx registers block. */ diff --git a/readme.txt b/readme.txt index 551c58aab..b056ff4f3 100644 --- a/readme.txt +++ b/readme.txt @@ -81,6 +81,7 @@ (bug #594). *** 3.0.0p4 *** +- NEW: Added no-DMA mode to STM32 I2Cv2 driver. - BLD: New "smart build" mode added to makefiles, now only used files are compiled. - HAL: Change to the Serial_USB driver, now the INT endpoint is no more