From 65d82066a8267d7dd96d747d09f9ed3883d40078 Mon Sep 17 00:00:00 2001 From: yn386 Date: Wed, 14 Sep 2022 15:29:15 +0900 Subject: [PATCH] stm32/pyb_i2c: Fix failing pyb.I2C(dma=True) after receiving 1 byte. Excuting the code: i2c = I2C(1, I2C.CONTROLLER, dma=True) tmp = i2c.recv(1, i2c_addr) recv_data = bytearray(56) i2c.recv(recv_data, i2c_addr) The second i2c.recv() fails with OSError: [Errno 110] ETIMEDOUT. When receiving greater than or equal to 2 bytes at first i2c.recv(), the second i2c.recv() succeeds. This issue does not occur without DMA. Details of change: when executing I2C with DMA: - Bit 11 of I2Cx_CR2 (DMA Request Enable) should be 1 to indicate that DMA transfer is enabled. This bit is set after I2C event interrupt is enabled in HAL_I2C_Master_Transmit_DMA()/HAL_I2C_Master_Receive_DMA(), so DMA Request Enable bit might be 0 in IRQHandler. - In case of data receive: - When only 1 byte receiption, clear I2Cx_CR1's bit 10 (ACK). - When only 2 byte receiption, clear I2Cx_CR1's bit 10 (ACK) and set bit 11 (POS). - When greater than or equal to 2 byte receiption, bit 12 of I2Cx_CR2 (DMA Last Transfer) should set to generate NACK when DMA transfer completed. Otherwise, the I2C bus may be busy after received data from peripheral. --- ports/stm32/pyb_i2c.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ports/stm32/pyb_i2c.c b/ports/stm32/pyb_i2c.c index 0cbb640473..9aac9274e0 100644 --- a/ports/stm32/pyb_i2c.c +++ b/ports/stm32/pyb_i2c.c @@ -478,8 +478,20 @@ void i2c_ev_irq_handler(mp_uint_t i2c_id) { } else { hi2c->Instance->DR = I2C_7BIT_ADD_READ(hi2c->Devaddress); } + hi2c->Instance->CR2 |= I2C_CR2_DMAEN; } else if (hi2c->Instance->SR1 & I2C_FLAG_ADDR) { __IO uint32_t tmp_sr2; + if (hi2c->State == HAL_I2C_STATE_BUSY_RX) { + if (hi2c->XferCount == 1U) { + hi2c->Instance->CR1 &= ~I2C_CR1_ACK; + } else { + if (hi2c->XferCount == 2U) { + hi2c->Instance->CR1 &= ~I2C_CR1_ACK; + hi2c->Instance->CR1 |= I2C_CR1_POS; + } + hi2c->Instance->CR2 |= I2C_CR2_LAST; + } + } tmp_sr2 = hi2c->Instance->SR2; UNUSED(tmp_sr2); } else if (hi2c->Instance->SR1 & I2C_FLAG_BTF && hi2c->State == HAL_I2C_STATE_BUSY_TX) {