cancel
Showing results for 
Search instead for 
Did you mean: 

SCL line in I2C stuck at hight level when programming bare metal

AQuev.1
Associate II

I am using a NUCLEO board with a STM32H7A3 MCU for lab classes in engineering. I am giving basic notions for the inner workings of most digital peripherals, so I am avoiding using HAL functions. Using basic CMSIS macros, we can use register names and bit masks to program the peripherals, helping students to understand the interaction between software and hardware.

Now it is time to work on the I2C interface. We connected a 24C16 I2C EEPROM with SCL to PB8 and SDA to PB9, and external pull-ups. Then we tested the interface and the EEPROM, writing a program using CubeXM and HAL, which works perfectly. Then I proceeded to read the I2C chapter in the manual and write code to initialize the I2C interface (at 100kHz) and to write a sequence of bytes to the EEPROM. When I tested it, though, the program froze at TXIS flag test: the flag is never set. Then I connected a logic analyzer to the SDA and SCL pins, and noticed the SDA pin working correctly, sending the "10100000" sequence (device address to the EEPROM, shifted left). However, SCL csignal is stuck at High level...

So the situation is: I have my I2C interface basically configured "almost" correctly... but when I send a byte through the interface, the EEPROM does not send the ACK back (the device does not recognize the I2C transaction because there is no SCL changes). Thus, the cycle is aborted, and TXIE is never set.

Now, the question is: why my SCL line is stuck? It is not a harware problem, since it works when I use HAL. So, it is some bug in my code. I am attaching the code for the initialization function and the multiple byte send function (the ones I have written so far). The TIMINGR value was calculated using a program made for this purpose. It can be found at:
https://github.com/nemuisan/STM32_I2C_Timing_Keisan

System clock frequency is 64MHz, same frequency at PLL1, which is the clock source selected for the I2C kernel.

Thanks in advance,

 

 

 

 

void I2C_Init(uint32_t fck, uint32_t fi2c) {
		// Clock Gating
		RCC->AHB4ENR |= RCC_AHB4ENR_GPIOBEN;
		RCC->APB1LENR |= RCC_APB1LENR_I2C1EN;
		RCC->CDCCIP2R &= ~RCC_CDCCIP2R_I2C123SEL_Msk; //I2C123SEL = 0, PLL1

		// Pins PB8 (SCL) and PB9 (SDA) in AF4
		GPIOB->MODER &= ~(GPIO_MODER_MODE8_Msk | GPIO_MODER_MODE9_Msk);
		GPIOB->MODER |= GPIO_MODER_MODE8_1 | GPIO_MODER_MODE9_1;
		GPIOB->AFR[1] &= ~(GPIO_AFRH_AFSEL8_Msk | GPIO_AFRH_AFSEL9_Msk);
		GPIOB->AFR[1] |= GPIO_AFRH_AFSEL8_2 | GPIO_AFRH_AFSEL9_2;

                // COnfiguring I2C1
		I2C1->CR1 = 0;
		I2C1->CR2 = 0;
		I2C1->TIMINGR = 0x30420F13;
		I2C1->TIMEOUTR = 0;
		I2C1->PECR = 0;
		I2C1->CR1 |= I2C_CR1_PE; // Enable
}

// In my case, I will never need transfers with more than 256 bytes
void I2C_Transmit(uint8_t devaddr, uint8_t * buf, uint8_t n) {
	uint8_t i;

	I2C1->CR2 |= devaddr | (n << 16) | I2C_CR2_AUTOEND;
	I2C1->CR2 |= I2C_CR2_START;
	for(i = 0; i < n; i++) {
		while(!(I2C1->ISR & I2C_ISR_TXIS)){}
		I2C1->TXDR = *(buf + i);
	}
	while(!(I2C1->ISR & I2C_ISR_STOPF)) {}
	I2C1->ICR = I2C_ICR_STOPCF;
	I2C1->CR2 = 0;
}

 

 

 

 

1 ACCEPTED SOLUTION

Accepted Solutions

I used 10k ohm resistors. Anyway, I found the missing piece of the puzzle: Besides the TIMINGR mismatch, I did not configure GPIO_OTYPER for PB8 and PB9 pins to open drain. I thought that when configuring the pins for the alternate functions, they would be automatically open drain, but that was not the case.

I found the issue when comparing register values between my Cube/HAL project with the bare metal. I extented the comparison to GPIOB registers and noticed that OTYPER was set for pins PB8 and PB9. I added this configuration in my initialization function and it worked perfectly.

 

Thanks for the help.

View solution in original post

5 REPLIES 5

Read out and check/compare-to-working (e.g. just before sending the first data) or post here content of I2C and relevant GPIO registers.

It would be also interesting to see the I2C_SCL line on oscilloscope rather than LA. Have you tried the LA also on the working case using Cube?

JW

My Cube/HAL project shows both lines working perfectly.

I started to suspect the TIMINGR calculations. Thus, I decided to load the value I found in that register when running my Cube/HAL project. It was very different! (calculator 0x30420F13 x Cube/HAL 0x10707DBC). I changed the value in my bare metal project, and then I hot SCL line working! However, I got the cycle aborted anyway, because the device was not acknowledging (got a transmission of 0x50+write bit, and a NACK, and same byte transmitted in my Cube/HAL returned an ACK from my EEPROM device).

I checked the timing for both projects, and I found small differences: SCL th, tl and T (Cube 4.125us, 6.292us and 10.417us x Bare metal 4.042us, 6.042us and 10.083us), time from START (SDA low) to SCL low (Cube 4.083us x Bare metal 4.042us), and time from this SCL low to SDA rising again (Cube 417ns x Bare metal 167ns).

I wonder why the timing is different since TIMINGR register has same value in both projects, and if these differences may account for the NACK in my Bare Metal project... I am using PLL1 at 64MHz as I2C kernel clock in both cases (I checked in the clock tree for the Cube project, even changed the default Cube frequency to 64MHz).

Anyway, here are the I2C register values after my initialization function:

I2C1_CR1 = 1
I2C1_CR2 = 0
I2C1_OAR1 = 0
I2C1_OAR2 = 0
I2C_TIMINGR = 0x10707DBC
I2C_TIMEOUT = 0
I2C_ISR = 1 (TXE bit set)
I2C_ICR = (WRITE ONLY)
P2C_PECR = 0
I2C_RXDR = 0
I2C_TXDR = 0

What pullup values do you use?

JW

I used 10k ohm resistors. Anyway, I found the missing piece of the puzzle: Besides the TIMINGR mismatch, I did not configure GPIO_OTYPER for PB8 and PB9 pins to open drain. I thought that when configuring the pins for the alternate functions, they would be automatically open drain, but that was not the case.

I found the issue when comparing register values between my Cube/HAL project with the bare metal. I extented the comparison to GPIOB registers and noticed that OTYPER was set for pins PB8 and PB9. I added this configuration in my initialization function and it worked perfectly.

 

Thanks for the help.

Thanks for coming back with the solution. Please click on "Accept as solution" in that post so that the thread is marked as solved.

JW