Differences between the I2C1 and I2C2 modules on STM32L0
I am trying to get a simple I2C connection running and the I2C1 peripheral on a STM32L052 is giving me hell. Here is what I have done so far:
I have written a simple driver for STM32L053 using I2C2 with interrupts. I have tried it on a couple of different slaves - it works with no issues, both reading and writing. I am using 4.7kOhm pull-ups, SCL clock is 100kHz, master and slave are both powered from 3v3 LDO, clock is HSI@16MHz. There is a 5us delay between successive I2C transfers (required by a slave device). Here is the code:
uint8_t *i2c_txBuffer;
uint8_t *i2c_rxBuffer;
void I2C_Init()
{
RCC->IOPENR |= RCC_IOPENR_GPIOBEN;
MODIFY_REG(GPIOB->MODER, GPIO_MODER_MODE10 | GPIO_MODER_MODE11, GPIO_MODER_MODE10_0 | GPIO_MODER_MODE11_0);
SET_BIT(GPIOB->OTYPER, GPIO_OTYPER_OT_10 | GPIO_OTYPER_OT_11);
MODIFY_REG(GPIOB->AFR[1], GPIO_AFRH_AFSEL10 | GPIO_AFRH_AFSEL11, 0x00006600);
RCC->APB1ENR |= RCC_APB1ENR_I2C2EN;
I2C2->CR1 |= I2C_CR1_TXIE | I2C_CR1_RXIE;
I2C2->TIMINGR = 0x30420F13;
NVIC_SetPriority(I2C2_IRQn, 2);
NVIC_EnableIRQ(I2C2_IRQn);
I2C2->CR1 |= I2C_CR1_PE;
while((I2C2->CR1 & I2C_CR1_PE) == 0);
}
uint8_t I2C_Transmit(uint8_t *buffer, uint8_t length, uint8_t i2c_Address)
{
i2c_txBuffer = buffer;
I2C2->TXDR = *i2c_txBuffer++;
I2C2->CR2 = I2C_CR2_AUTOEND | (length << I2C_CR2_NBYTES_Pos) | i2c_Address | I2C_CR2_START;
while(I2C2->ISR & I2C_ISR_BUSY);
return (I2C2->ISR & I2C_ISR_NACKF) ? 0 : 1;
}
void I2C2_IRQHandler(void)
{
///====================== [Transmit buffer empty & ACK received] ======================///
if(I2C2->ISR & I2C_ISR_TXIS)
{
I2C2->TXDR = *i2c_txBuffer++;
}
///===================== [Receive buffer NOT empty] ====================///
if(I2C2->ISR & I2C_ISR_RXNE)
{
*i2c_rxBuffer++ = I2C2->RXDR;
}
}Then I tried porting it to STM32L052 and I2C1 on a different board. On it the slave power is controlled by a load switch. The rise time for VCC is 15us and SCL and SDA rise to 3v3 after around 150us. Those values were measured with a scope. Again, 4.7kOhm pull-ups, clock is 16MHz. And here is where the nightmare begins:
Literally the SAME code (with the exception of some 100ms delay for voltage stabilization after the load switch is turned on, references to I2C2 changed to I2C1 and proper GPIO configuration) hangs on the BUSY flag. I see nothing on the SCL and SDA lines with the scope (both pulled up). I found out that for a different family (STM32F1) there is a mention in the errata about proper I2C initialization - something about analog filters getting stuck. I tried the suggested workaround - nothing changed. So I reviewed the errata for STM32L0 - nothing about such an issue. In a different forum topic somebody mentioned, that the GPIOs should be configured after the I2C peripheral. Out of desperation I tried that - and it worked! I saw both SCL and SDA toggle with the data I was trying to send. So I hooked up a slave device. The slave was responding with ACKs, however something was off. I was sending 3 bytes, but on the line I saw the first one ACKed, then a pause with SCL LOW and after some time the other 2 bytes, both ACKed. It turned out, that the BUSY flag was cleared after the first byte was sent and I was exiting the while loop earlier. According to the datasheet the BUSY flag is set after detecting a START condition and cleared after a STOP or PE bit in CR1 set to 0. This PE bit also restarts the internal state machine and clears all flags, so I added this to the initialization, for the sake of completion. I also modified the transmit function to wait for the STOP flag to be set (instead of BUSY to be cleared), clear it and then continue with the rest. And it finally worked. Sort of.
The next problem came when I tried doing this in a loop after putting the MCU in stop mode and waking it up with the RTC. The result was this: load switch turned on, 100ms delay, PE=0, initialize I2C1, set PE=1, initialize GPIOs, start communication - all well. Set PE=0, disable RCC clock, set GPIOs to analog mode. Go to stop mode. Exit stop mode on RTC interrupt, configure again clocks, perform the exact same init procedure - the MCU gets stuck on the check for the STOP flag on the first transfer. On the SDA line I see the address and the first byte being ACKed. The rest 2 bytes do not get sent at all, as SCL remains LOW. If I try the same thing, but on I2C2 with check of the BUSY flag according to the original code, I get the same: the first run it works, then SCL goes LOW after the first byte is ACKed. What am I doing wrong?
- Why is there a difference in the way that I2C1 and I2C2 should be initialized?
- Why does polling the BUSY flag for I2C1 does not work the same as with I2C2?
- Could this SCL LOW after the first RTC wake-up be a result of the slave stretching the clock?
- Has anyone experienced any of these differences between the two I2C modules?
I assume that the problem is in the I2C code/peripheral, because the power reset through the load switch should guarantee that the state machine of the slave is always properly initialized.