From: Thomas SCHÖPPING Date: Wed, 29 Apr 2015 18:15:23 +0200 Subject: [PATCH] STM32/I2Cv1: added the option to use I2C without DMA Signed-off-by: Thomas SCHÖPPING --- diff --git a/os/hal/platforms/STM32/I2Cv1/i2c_lld.c b/os/hal/platforms/STM32/I2Cv1/i2c_lld.c index 2a36776..4a27942 100644 --- a/os/hal/platforms/STM32/I2Cv1/i2c_lld.c +++ b/os/hal/platforms/STM32/I2Cv1/i2c_lld.c @@ -35,6 +35,7 @@ /* Driver local definitions. */ /*===========================================================================*/ +#if STM32_I2C_USE_DMA #define I2C1_RX_DMA_CHANNEL \ STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_RX_DMA_STREAM, \ STM32_I2C1_RX_DMA_CHN) @@ -58,6 +59,7 @@ #define I2C3_TX_DMA_CHANNEL \ STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_TX_DMA_STREAM, \ STM32_I2C3_TX_DMA_CHN) +#endif /* STM32_I2C_USE_DMA */ /*===========================================================================*/ /* Driver constants. */ @@ -73,6 +75,20 @@ #define I2C_EV6_MASTER_REC_MODE_SELECTED \ ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY)<< 16) | I2C_SR1_ADDR)) +#define I2C_EV7_MASTER_REC_BYTE_RECEIVED \ + ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY)<< 16) | I2C_SR1_RXNE)) + +#define I2C_EV7_MASTER_REC_BYTE_RECEIVED_STOP \ + ((uint32_t)( I2C_SR1_RXNE)) + +#define I2C_EV7_2_EV7_3_MASTER_REC_BYTE_QUEUED \ + ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY)<< 16) | \ + I2C_SR1_BTF | I2C_SR1_RXNE)) + +#define I2C_EV8_MASTER_BYTE_TRANSMITTING \ + ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY | I2C_SR2_TRA)<< 16) | \ + I2C_SR1_TXE)) + #define I2C_EV8_2_MASTER_BYTE_TRANSMITTED \ ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY | I2C_SR2_TRA) << 16) | \ I2C_SR1_BTF | I2C_SR1_TXE)) @@ -148,9 +164,11 @@ static void i2c_lld_abort_operation(I2CDriver *i2cp) { dp->CR2 = 0; dp->SR1 = 0; +#if STM32_I2C_USE_DMA /* Stops the associated DMA streams.*/ dmaStreamDisable(i2cp->dmatx); dmaStreamDisable(i2cp->dmarx); +#endif /* STM32_I2C_USE_DMA */ } /** @@ -242,7 +260,7 @@ static void i2c_lld_set_clock(I2CDriver *i2cp) { chDbgAssert(clock_div >= 0x01, "i2c_lld_set_clock(), #7", - "Clock divider less then 0x04 not allowed"); + "Clock divider less then 0x01 not allowed"); regCCR |= (I2C_CCR_FS | (clock_div & I2C_CCR_CCR)); /* Sets the Maximum Rise Time for fast mode.*/ @@ -295,12 +313,13 @@ static void i2c_lld_serve_event_interrupt(I2CDriver *i2cp) { uint32_t regSR2 = dp->SR2; uint32_t event = dp->SR1; +#if STM32_I2C_USE_DMA /* Interrupts are disabled just before dmaStreamEnable() because there is no need of interrupts until next transaction begin. All the work is done by the DMA.*/ switch (I2C_EV_MASK & (event | (regSR2 << 16))) { case I2C_EV5_MASTER_MODE_SELECT: - if ((i2cp->addr >> 8) > 0) { + if ((i2cp->addr >> 8) > 0) { /* 10-bit address: 1 1 1 1 0 X X R/W */ dp->DR = 0xF0 | (0x6 & (i2cp->addr >> 8)) | (0x1 & i2cp->addr); } else { @@ -340,8 +359,129 @@ static void i2c_lld_serve_event_interrupt(I2CDriver *i2cp) { /* Clear ADDR flag. */ if (event & (I2C_SR1_ADDR | I2C_SR1_ADD10)) (void)dp->SR2; +#else + switch (I2C_EV_MASK & (event | (regSR2 << 16))) { + case I2C_EV5_MASTER_MODE_SELECT: + dp->CR2 |= I2C_CR2_ITBUFEN; + dp->DR = i2cp->addr; + break; + case I2C_EV6_MASTER_TRA_MODE_SELECTED: + (void)dp->SR2; // clear ADDR flag + /* EV8_1 */ + dp->DR = *(i2cp->txbuf); + + ++i2cp->txbuf; + --i2cp->txbytes; + + /* if N == 1, skip the I2C_EV8_MASTER_BYTE_TRANSMITTING event + * but enter I2C_EV8_2_MASTER_BYTE_TRANSMITTED next */ + if (i2cp->txbytes == 0) { + dp->CR2 &= ~I2C_CR2_ITBUFEN; + } + break; + case I2C_EV6_MASTER_REC_MODE_SELECTED: + switch (i2cp->rxbytes) { + case 1: + dp->CR1 &= ~I2C_CR1_ACK; + (void)dp->SR2; // clear ADDR flag + dp->CR1 |= I2C_CR1_STOP; + break; + case 2: + (void)dp->SR2; // clear ADDR flag + /* EV6_1 */ + dp->CR1 |= I2C_CR1_POS; + dp->CR1 &= ~I2C_CR1_ACK; + dp->CR2 &= ~I2C_CR2_ITBUFEN; + break; + case 3: /* N == 3 is a very special case, since EV7 is completely skipped */ + (void)dp->SR2; // clear ADDR flag + /* Disable the I2C_EV7_MASTER_REC_BYTE_RECEIVED event + * but enter I2C_EV7_MASTER_REC_BYTE_RECEIVED_STOP next */ + dp->CR2 &= ~I2C_CR2_ITBUFEN; + break; + default: /* N > 2 */ + (void)dp->SR2; // clear ADDR flag + break; + } + break; + case I2C_EV7_MASTER_REC_BYTE_RECEIVED: + if (i2cp->rxbytes > 3) { + *(i2cp->rxbuf) = dp->DR; + ++i2cp->rxbuf; + --i2cp->rxbytes; + } + if (i2cp->rxbytes == 3) { + /* Disable this event for DataN-2, but force into event + * I2C_EV7_2_EV7_3_MASTER_REC_BYTE_RECEIVED_QUEUED by not reading dp->DR. */ + dp->CR2 &= ~I2C_CR2_ITBUFEN; + } + break; + case I2C_EV7_MASTER_REC_BYTE_RECEIVED_STOP: + chDbgAssert(i2cp->rxbytes == 1, "i2c_lld_serve_event_interrupt(), #1", + "more than 1 byte to be received"); + *(i2cp->rxbuf) = dp->DR; + --i2cp->rxbytes; + dp->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN); + wakeup_isr(i2cp, RDY_OK); + break; + case I2C_EV7_2_EV7_3_MASTER_REC_BYTE_QUEUED: + if (i2cp->rxbytes == 3) { + /* EV7_2 (N > 2) */ + dp->CR1 &= ~I2C_CR1_ACK; + *(i2cp->rxbuf) = dp->DR; + ++i2cp->rxbuf; + dp->CR1 |= I2C_CR1_STOP; + *(i2cp->rxbuf) = dp->DR; + ++i2cp->rxbuf; + i2cp->rxbytes -= 2; + /* enable I2C_EV7_MASTER_REC_BYTE_RECEIVED_STOP event */ + dp->CR2 |= I2C_CR2_ITBUFEN; + } else { + /* EV7_3 (N == 2) */ + dp->CR1 |= I2C_CR1_STOP; + *(i2cp->rxbuf) = dp->DR; + ++i2cp->rxbuf; + *(i2cp->rxbuf) = dp->DR; + i2cp->rxbytes -= 2; + + dp->CR1 &= ~I2C_CR1_POS; + dp->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN); + + wakeup_isr(i2cp, RDY_OK); + } + break; + case I2C_EV8_MASTER_BYTE_TRANSMITTING: + dp->DR = *(i2cp->txbuf); + ++i2cp->txbuf; + --i2cp->txbytes; + + /* if this was the last byte, ensure that this event is not entered again */ + if (i2cp->txbytes == 0) { + dp->CR2 &= ~I2C_CR2_ITBUFEN; + } + break; + case I2C_EV8_2_MASTER_BYTE_TRANSMITTED: + if (i2cp->rxbytes > 0) { + /* start "read after write" operation (LSB of address = 1 -> read) */ + i2cp-> addr |= 0x01; + dp->CR1 |= I2C_CR1_START | I2C_CR1_ACK; + } else { + dp->CR1 |= I2C_CR1_STOP; + + dp->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN); + + wakeup_isr(i2cp, RDY_OK); + } + break; + default: + chDbgAssert(i2cp->rxbytes != 1, "i2c_lld_serve_event_interrupt(), #1", + "more than 1 byte to be received"); + break; + } +#endif /* STM32_I2C_USE_DMA */ } +#if STM32_I2C_USE_DMA /** * @brief DMA RX end IRQ handler. * @@ -395,6 +535,7 @@ static void i2c_lld_serve_tx_end_irq(I2CDriver *i2cp, uint32_t flags) { of R/W transaction itself.*/ dp->CR2 |= I2C_CR2_ITEVTEN; } +#endif /* STM32_I2C_USE_DMA */ /** * @brief I2C error handler. @@ -406,9 +547,11 @@ static void i2c_lld_serve_tx_end_irq(I2CDriver *i2cp, uint32_t flags) { */ static void i2c_lld_serve_error_interrupt(I2CDriver *i2cp, uint16_t sr) { +#if STM32_I2C_USE_DMA /* Clears interrupt flags just to be safe.*/ dmaStreamDisable(i2cp->dmatx); dmaStreamDisable(i2cp->dmarx); +#endif /* STM32_I2C_USE_DMA */ i2cp->errors = I2CD_NO_ERROR; @@ -554,24 +697,30 @@ void i2c_lld_init(void) { i2cObjectInit(&I2CD1); I2CD1.thread = NULL; I2CD1.i2c = I2C1; +#if STM32_I2C_USE_DMA I2CD1.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C1_RX_DMA_STREAM); I2CD1.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C1_TX_DMA_STREAM); +#endif /* STM32_I2C_USE_DMA */ #endif /* STM32_I2C_USE_I2C1 */ #if STM32_I2C_USE_I2C2 i2cObjectInit(&I2CD2); I2CD2.thread = NULL; I2CD2.i2c = I2C2; +#if STM32_I2C_USE_DMA I2CD2.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C2_RX_DMA_STREAM); I2CD2.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C2_TX_DMA_STREAM); +#endif /* STM32_I2C_USE_DMA */ #endif /* STM32_I2C_USE_I2C2 */ #if STM32_I2C_USE_I2C3 i2cObjectInit(&I2CD3); I2CD3.thread = NULL; I2CD3.i2c = I2C3; +#if STM32_I2C_USE_DMA I2CD3.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C3_RX_DMA_STREAM); I2CD3.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C3_TX_DMA_STREAM); +#endif /* STM32_I2C_USE_DMA */ #endif /* STM32_I2C_USE_I2C3 */ } @@ -585,6 +734,7 @@ void i2c_lld_init(void) { void i2c_lld_start(I2CDriver *i2cp) { I2C_TypeDef *dp = i2cp->i2c; +#if STM32_I2C_USE_DMA i2cp->txdmamode = STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE | STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE | @@ -593,15 +743,17 @@ void i2c_lld_start(I2CDriver *i2cp) { STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE | STM32_DMA_CR_DIR_P2M; +#endif /* STM32_I2C_USE_DMA */ /* If in stopped state then enables the I2C and DMA clocks.*/ if (i2cp->state == I2C_STOP) { #if STM32_I2C_USE_I2C1 if (&I2CD1 == i2cp) { - bool_t b; rccResetI2C1(); +#if STM32_I2C_USE_DMA + bool_t b; b = dmaStreamAllocate(i2cp->dmarx, STM32_I2C_I2C1_IRQ_PRIORITY, (stm32_dmaisr_t)i2c_lld_serve_rx_end_irq, @@ -612,24 +764,28 @@ void i2c_lld_start(I2CDriver *i2cp) { (stm32_dmaisr_t)i2c_lld_serve_tx_end_irq, (void *)i2cp); chDbgAssert(!b, "i2c_lld_start(), #2", "stream already allocated"); +#endif /* STM32_I2C_USE_DMA */ rccEnableI2C1(FALSE); nvicEnableVector(I2C1_EV_IRQn, CORTEX_PRIORITY_MASK(STM32_I2C_I2C1_IRQ_PRIORITY)); nvicEnableVector(I2C1_ER_IRQn, CORTEX_PRIORITY_MASK(STM32_I2C_I2C1_IRQ_PRIORITY)); +#if STM32_I2C_USE_DMA 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 */ } #endif /* STM32_I2C_USE_I2C1 */ #if STM32_I2C_USE_I2C2 if (&I2CD2 == i2cp) { - bool_t b; rccResetI2C2(); +#if STM32_I2C_USE_DMA + bool_t b; b = dmaStreamAllocate(i2cp->dmarx, STM32_I2C_I2C2_IRQ_PRIORITY, (stm32_dmaisr_t)i2c_lld_serve_rx_end_irq, @@ -640,24 +796,28 @@ void i2c_lld_start(I2CDriver *i2cp) { (stm32_dmaisr_t)i2c_lld_serve_tx_end_irq, (void *)i2cp); chDbgAssert(!b, "i2c_lld_start(), #4", "stream already allocated"); +#endif /* STM32_I2C_USE_DMA */ rccEnableI2C2(FALSE); nvicEnableVector(I2C2_EV_IRQn, CORTEX_PRIORITY_MASK(STM32_I2C_I2C2_IRQ_PRIORITY)); nvicEnableVector(I2C2_ER_IRQn, CORTEX_PRIORITY_MASK(STM32_I2C_I2C2_IRQ_PRIORITY)); +#if STM32_I2C_USE_DMA 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 */ } #endif /* STM32_I2C_USE_I2C2 */ #if STM32_I2C_USE_I2C3 if (&I2CD3 == i2cp) { - bool_t b; rccResetI2C3(); +#if STM32_I2C_USE_DMA + bool_t b; b = dmaStreamAllocate(i2cp->dmarx, STM32_I2C_I2C3_IRQ_PRIORITY, (stm32_dmaisr_t)i2c_lld_serve_rx_end_irq, @@ -668,28 +828,37 @@ void i2c_lld_start(I2CDriver *i2cp) { (stm32_dmaisr_t)i2c_lld_serve_tx_end_irq, (void *)i2cp); chDbgAssert(!b, "i2c_lld_start(), #6", "stream already allocated"); +#endif /* STM32_I2C_USE_DMA */ rccEnableI2C3(FALSE); nvicEnableVector(I2C3_EV_IRQn, CORTEX_PRIORITY_MASK(STM32_I2C_I2C3_IRQ_PRIORITY)); nvicEnableVector(I2C3_ER_IRQn, CORTEX_PRIORITY_MASK(STM32_I2C_I2C3_IRQ_PRIORITY)); +#if STM32_I2C_USE_DMA i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C3_RX_DMA_CHANNEL) | STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY); i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C3_TX_DMA_CHANNEL) | STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY); +#endif /* STM32_I2C_USE_DMA */ } #endif /* STM32_I2C_USE_I2C3 */ } +#if STM32_I2C_USE_DMA /* I2C registers pointed by the DMA.*/ dmaStreamSetPeripheral(i2cp->dmarx, &dp->DR); dmaStreamSetPeripheral(i2cp->dmatx, &dp->DR); +#endif /* STM32_I2C_USE_DMA */ /* Reset i2c peripheral.*/ dp->CR1 = I2C_CR1_SWRST; dp->CR1 = 0; +#if STM32_I2C_USE_DMA dp->CR2 = I2C_CR2_ITERREN | I2C_CR2_DMAEN; +#else + dp->CR2 = I2C_CR2_ITERREN; +#endif /* STM32_I2C_USE_DMA */ /* Setup I2C parameters.*/ i2c_lld_set_clock(i2cp); @@ -713,8 +882,10 @@ void i2c_lld_stop(I2CDriver *i2cp) { /* I2C disable.*/ i2c_lld_abort_operation(i2cp); +#if STM32_I2C_USE_DMA dmaStreamRelease(i2cp->dmatx); dmaStreamRelease(i2cp->dmarx); +#endif /* STM32_I2C_USE_DMA */ #if STM32_I2C_USE_I2C1 if (&I2CD1 == i2cp) { @@ -786,10 +957,15 @@ msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, i2cp->addr = (addr << 1) | 0x01; i2cp->errors = 0; +#if STM32_I2C_USE_DMA /* RX DMA setup.*/ dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); dmaStreamSetMemory0(i2cp->dmarx, rxbuf); dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes); +#else + i2cp->rxbuf = rxbuf; + i2cp->rxbytes = rxbytes; +#endif /* STM32_I2C_USE_DMA */ /* Waits until BUSY flag is reset and the STOP from the previous operation is completed, alternatively for a timeout condition.*/ @@ -869,6 +1045,7 @@ msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, i2cp->addr = addr << 1; i2cp->errors = 0; +#if STM32_I2C_USE_DMA /* TX DMA setup.*/ dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); dmaStreamSetMemory0(i2cp->dmatx, txbuf); @@ -878,6 +1055,13 @@ 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->txbuf = txbuf; + i2cp->txbytes = txbytes; + + i2cp->rxbuf = rxbuf; + i2cp->rxbytes = rxbytes; +#endif /* STM32_I2C_USE_DMA */ /* Waits until BUSY flag is reset and the STOP from the previous operation is completed, alternatively for a timeout condition.*/ diff --git a/os/hal/platforms/STM32/I2Cv1/i2c_lld.h b/os/hal/platforms/STM32/I2Cv1/i2c_lld.h index 6b192dc..27b1263 100644 --- a/os/hal/platforms/STM32/I2Cv1/i2c_lld.h +++ b/os/hal/platforms/STM32/I2Cv1/i2c_lld.h @@ -76,6 +76,15 @@ #endif /** + * @brief I2C data transfer use dma switch. + * @details If set to @p TRUE the support for I2C DMA is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_I2C_USE_DMA) || defined(__DOXYGEN__) +#define STM32_I2C_USE_DMA TRUE +#endif + +/** * @brief I2C1 interrupt priority level setting. */ #if !defined(STM32_I2C_I2C1_IRQ_PRIORITY) || defined(__DOXYGEN__) @@ -227,6 +236,7 @@ #error "I2C driver activated but no I2C peripheral assigned" #endif +#if STM32_I2C_USE_DMA #if STM32_I2C_USE_I2C1 && \ !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_RX_DMA_STREAM, \ STM32_I2C1_RX_DMA_MSK) @@ -266,6 +276,7 @@ #if !defined(STM32_DMA_REQUIRED) #define STM32_DMA_REQUIRED #endif +#endif /* STM32_I2C_USE_DMA */ /* Check clock range. */ #if defined(STM32F4XX) @@ -386,6 +397,7 @@ struct I2CDriver { * @brief Current slave address without R/W bit. */ i2caddr_t addr; +#if STM32_I2C_USE_DMA /** * @brief RX DMA mode bit mask. */ @@ -402,6 +414,24 @@ struct I2CDriver { * @brief Transmit DMA channel. */ const stm32_dma_stream_t *dmatx; +#else + /** + * @brief Receive buffer. + */ + uint8_t *rxbuf; + /** + * @brief Receive buffer size. + */ + size_t rxbytes; + /** + * @brief Transmit buffer. + */ + const uint8_t *txbuf; + /** + * @brief Transmit buffer size. + */ + size_t txbytes; +#endif /* STM32_I2C_USE_DMA */ /** * @brief Pointer to the I2Cx registers block. */