I2C problem on STM32 (can't generate a start)

Status
Not open for further replies.

nicklasp

Newbie level 4
Hi!
I am new to this forum and seeking help. I am working on a PMBus compliance tester. PMBus extends SMBus which extends the I2C bus.

I think I have initialized the I2C peripheral on a STM32F103ZE correct. However, I cannot generate a start condition. I am using pull-up resistors and the signals is high all the time.

This is my code:
Code:
   unsigned int dier;
unsigned short cnt;
unsigned short tmpreg = 0;
unsigned short freqrange = 0;
unsigned short result = 0;

/* Enable periphereal clocks */
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;

/* Pin: Alternate function, open drain */
GPIOB->CRL |= (GPIO_CRL_CNF6_0  |
GPIO_CRL_CNF6_1  |
GPIO_CRL_CNF7_0  |
GPIO_CRL_CNF7_1  |
GPIO_CRL_CNF5_0  |
GPIO_CRL_CNF5_1  |
GPIO_CRL_MODE5_0 |
GPIO_CRL_MODE5_1 |
GPIO_CRL_MODE6_0 |
GPIO_CRL_MODE6_1 |
GPIO_CRL_MODE7_0 |
GPIO_CRL_MODE7_1 );

/* Enable I2C1 reset state */
RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST;
/* Release I2C1 from reset state */
RCC->APB1RSTR &= ~RCC_APB1RSTR_I2C1RST;

/*---------------------------- I2C1 CR2 Configuration ------------------------*/
/* Get the I2Cx CR2 value */
tmpreg = I2C1->CR2;
/* Clear frequency FREQ[5:0] bits */
tmpreg &= ((unsigned char)0xFFC0);
/* Get pclk1 frequency value */
RCC_GetClocksFreq(&rcc_clocks);
pclk1 = rcc_clocks.PCLK1_Frequency;
/* Set frequency bits depending on pclk1 value */
freqrange = (unsigned short)(pclk1 / 1000000);
/* Write to I2Cx CR2 */
I2C1->CR2 = ((freqrange) | I2C_CR2_ITEVTEN | I2C_CR2_ITERREN );

/*---------------------------- I2Cx CCR Configuration --------------------*/
/* Disable the selected I2C peripheral to configure TRISE */
I2C1->CR1 = 0;

/* Reset tmpreg value */
/* Clear F/S, DUTY and CCR[11:0] bits */
tmpreg = 0;

/* Configure speed in standard mode */
result = (unsigned short)(pclk1 / (I2C_CLOCKSPEED << 1));
/* Set speed value for standard mode */
tmpreg |= result;
/* Set Maximum Rise Time for standard mode */
I2C1->TRISE = freqrange + 1;
/* Write to I2C CCR */
I2C1->CCR = tmpreg;

/*---------------------------- I2Cx CR1 Configuration --------------------*/
/* Enable the selected I2C peripheral */
I2C1->CR1 =  I2C_CR1_PE;//       |
//   I2C_CR1_SMBUS   |
//   I2C_CR1_SMBTYPE ;
//  I2C_CR1_ENPEC   |
//   I2C_CR1_ACK     );

/*---------------------------- I2Cx OAR Configuration --------------------*/
I2C1->OAR1 = ((SMB_ADR_HOST << 1) + (1 << 14));

/*---------------------------- Setup I2C IRQs ----------------------------*/
NVIC_SetPriority(I2C1_EV_IRQn, SMB_IRQ_PRIO);
NVIC_EnableIRQ(I2C1_EV_IRQn);

NVIC_SetPriority(I2C1_ER_IRQn, SMB_IRQ_PRIO);
NVIC_EnableIRQ(I2C1_ER_IRQn);

/* Generates the start condition */
I2C1->CR1 |=  I2C_CR1_START;
/* Wait until the START condition is generated on the bus:
the START bit is cleared by hardware */
while ((I2C1->CR1&0x100) == 0x100);

Now, I am stuck at the last while-loop since the start condition is never generated.
I have been working with this problem for long time now and would really need som guidance.

Here are some printscreens from the debugger:

skogsjanne

thank you!

skogsjanne

Full Member level 3
We needed an I2C interface and failed to get it to work. My picture of what we found in the errata was that we need DMA and highest priority interrupt to get it to work.
My memory may be wrong, but since we only need to transfer very little data we decided to bitbang the I2C interface. Much less trouble than to work around the bugs.

Maybe you need higher throughput and in that case I guess you will have to go with the hardware interface. I suspect that if you read the errata again you will find the problem.

Good luck!

p.luc

Junior Member level 3
Try to check the SR1 bit0 instead of CR1.
You will have a lot of problems by checking the registers that way. Try to use the latest I2C libraries from ST; functions like I2C_CheckEvent() or I2C_GetLastEvent may help.
Someone at ST screwed up the I2C controller interface big-time. You may pass through a lot of states like "sending the N-1 or N-2" bytes in master mode, etc.
I will try to google some decent code, but I see that some few uses the I2C interrupt mode in the proper way.

nicklasp

Newbie level 4
Try to check the SR1 bit0 instead of CR1.
You will have a lot of problems by checking the registers that way. Try to use the latest I2C libraries from ST; functions like I2C_CheckEvent() or I2C_GetLastEvent may help.
Someone at ST screwed up the I2C controller interface big-time. You may pass through a lot of states like "sending the N-1 or N-2" bytes in master mode, etc.
I will try to google some decent code, but I see that some few uses the I2C interrupt mode in the proper way.

I have tried to outcomment that line that checks the CR1 register and still no interrupt is generated and no start condition is seen on the pins.
As soon as I set the START bit a start condition should be seen on the pins and an interrupt should be generated. But nothing happens.
I have looked at configuration hundred of times and I belive that the configuration should be correct. But there is propably some detail that I have missed.

p.luc

Junior Member level 3

If the pin is not moving you should check the other end of the I2C pins. Perhaps it's an output to 0 stretching down the line.
If it's not connected, try to configure the pin as an output and move it to exclude HW problems (you can do it directly in debug, with the keil interface you have in the screenshots).

nicklasp

Newbie level 4
If the pin is not moving you should check the other end of the I2C pins. Perhaps it's an output to 0 stretching down the line.

The I2C pins are not connected to anything else than the pull-up resistors so there is not another output stretching down the line.

If it's not connected, try to configure the pin as an output and move it to exclude HW problems (you can do it directly in debug, with the keil interface you have in the screenshots).
I am not sure if I understand what you mean... do you mean that there could be some problems with the I2C peripheral?

p.luc

Junior Member level 3
The I2C pins are not connected to anything else than the pull-up resistors so there is not another output stretching down the line.

I am not sure if I understand what you mean... do you mean that there could be some problems with the I2C peripheral?

You said that the pin is always high. There are 2 possibilities: you see the pin high because of the pull-up AND because the pin is still an input (misconfiguration, broken, etc), OR because the pin is an output and it's high.

For the first, try to set the pin as a low output and verify it goes down. If not, there is something very wrong.

Anyway, try to run some simple example from the ST library at least to check with the debugger the order of the initialization.

Zari44

Newbie level 1
hello, I have another problem with I2C on STM32F107VB.

I want to communicate with compass HMC5883L.
I2C initialisation:

Code C - [expand]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void HMC5883L_I2C_Init()
{
I2C_InitTypeDef  I2C_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;

/* Enable I2C and GPIO clocks */
RCC_APB1PeriphClockCmd(HMC5883L_I2C_RCC_Periph, ENABLE);
RCC_APB2PeriphClockCmd(HMC5883L_I2C_RCC_Port, ENABLE);

/* Configure I2C pins: SCL and SDA */
GPIO_InitStructure.GPIO_Pin           =  GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed         = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode          = GPIO_Mode_AF_OD;
GPIO_Init(HMC5883L_I2C_Port, &GPIO_InitStructure);

/* I2C configuration */
I2C_InitStructure.I2C_Mode                = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle           = I2C_DutyCycle_2;
// HMC5883L 7-bit adress = 0x1E;
I2C_InitStructure.I2C_Ack                 = I2C_Ack_Enable;
I2C_InitStructure.I2C_ClockSpeed          = 100000;

/* Apply I2C configuration after enabling it */
I2C_Init(I2C1, &I2C_InitStructure);

I2C_Cmd(I2C1, ENABLE);
}

When I want to write a byte to my I2C slave it goes ok when I send start command. But after start when I send device write adress my code goes into inifinite loop like this:

Code C - [expand]1
2
/* Test on EV6 and clear it */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

where I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED is ((uint32_t)0x00070082) /* BUSY, MSL, ADDR, TXE and TRA flags */

Unfotrunatelly instead of EV6 on I2C bus to occur in I2C Status registers 1 and 2 I can read:

SR2 0 _> Master Mode
SR2 1 -> Communication ongoint on the bus
SR1 10 -> ACK failure

Can anyone help me with it?
I got ready library and my slave device also worked few weeks ago on arduino.

Any ideas?

Last edited by a moderator:

Evgeny Soroka

Newbie level 2
I had an identical problem with STM32F429.

As insane as it sounds add a delay between this

Code:
RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST;

[B]insert delay here. I added 10000uS delay.[/B]

/* Release I2C1 from reset state */
RCC->APB1RSTR &= ~RCC_APB1RSTR_I2C1RST;

I had the same problem as you for 2 days until I realized that resetting the i2c peripheral and then turning off reset instantly does not actually reset anything. The reference manual and datasheet says absolutely nothing about this. I was extremely frustrated for days until I stumbled on to this.

If you add the delay you will see the start bit flag go high in i2c_SR1 and the interrupt I2C1_EV_IRQHandler will happen.

ftsolutions

Full Member level 5
Evgeny makes an important point - I haven't used the STM32 part, but I have observed issues with other peripherals in other vendors' ARM processors when you try to do too much and end up creating a RMW (Ready modify write) situation. In some cases, the peripheral doesn't have the time to respond to the initial bit change. In other cases, the compiler optimizes the operation so that either a cached/local copy of the hardware register's previous contents are used, or only the final results are written out. Be aware that peripheral controllers require some clock cycles of their own to process and transition to the next state reliably.

Evgeny Soroka

Evgeny Soroka

Points: 2

Evgeny Soroka

Newbie level 2
Evgeny makes an important point - I haven't used the STM32 part, but I have observed issues with other peripherals in other vendors' ARM processors when you try to do too much and end up creating a RMW (Ready modify write) situation. In some cases, the peripheral doesn't have the time to respond to the initial bit change. In other cases, the compiler optimizes the operation so that either a cached/local copy of the hardware register's previous contents are used, or only the final results are written out. Be aware that peripheral controllers require some clock cycles of their own to process and transition to the next state reliably.

Usually ST is good about warning users about these necessary delays in the reference manual, but in this case I can find no information about it. Its frustrating. Slows development time.

DTFT

Newbie level 6
Hi, if you still haven't figured it out, I have successfully configured the I2C on the STM32F4 before, which is similar. My code is in assembly and does not use the ST libraries, but I would be happy to post relevant functions. Let me know if you still need help.

Another suggestion in general would be to find an ST demo application such as Waveplayer that uses the I2C peripheral. That way you can make sure your hardware is not (or is) the problem.

babar_ali

Full Member level 3
Well here is my I2C code. Its works perfectly on stm32f407
Code:
void I2C3_init(uint32_t I2C_baudrate)
{
I2C_InitTypeDef I2C_InitStructure;
I2C_DeInit(I2C3); // Deinit I2C3
// configure I2C3
I2C_InitStructure.I2C_ClockSpeed 			= I2C_baudrate;		// 100kHz
I2C_InitStructure.I2C_Mode 					= I2C_Mode_I2C;		// I2C mode
I2C_InitStructure.I2C_DutyCycle 			= I2C_DutyCycle_2;	// 50% duty cycle --> standard
I2C_InitStructure.I2C_Ack 					= I2C_Ack_Enable;	// Enable acknowledge when reading (can be changed later on)
I2C_Init(I2C3, &I2C_InitStructure);			// init I2Cx
I2C_Cmd(I2C3, ENABLE); 	// Enable I2C3
}
void I2C_GPIO_Config()
{
GPIO_InitTypeDef GPIO_InitStructure;

//Configure the GPIO_LED pin
GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOI, &GPIO_InitStructure);

/* setup SCL and SDA pins
* You can connect the I2Cx functions to two different
* pins:
* 1. SCL on PH7
* 2. SDA on PH8
*/
GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_7 | GPIO_Pin_8; 	// we are going to use PB6 and PB9
GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_AF;				// set pins to alternate function
GPIO_InitStructure.GPIO_Speed	= GPIO_Speed_50MHz;			// set GPIO speed
GPIO_InitStructure.GPIO_OType	= GPIO_OType_OD;			// set output to open drain --> the line has to be only pulled low, not driven high
GPIO_InitStructure.GPIO_PuPd 	= GPIO_PuPd_UP;				// enable pull up resistors
GPIO_Init(GPIOH, &GPIO_InitStructure);

// Connect I2Cx pins to AF
GPIO_PinAFConfig(GPIOH, GPIO_PinSource7, GPIO_AF_I2C3);	// SCL
GPIO_PinAFConfig(GPIOH, GPIO_PinSource8, GPIO_AF_I2C3); // SDA
}
/*===============================================================================================================*/
/* I2C_Write_Byte */
/*===============================================================================================================*/
{
//USART_SendData_s( USART3,"I2C_GetFlagStatus:\r\n");
while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));/* While the bus is busy */

//USART_SendData_s( USART3,"I2C_GenerateSTART:\r\n");
I2C_GenerateSTART(I2Cx, ENABLE);/* Send I2C3 START condition */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));

while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

//USART_SendData_s( USART3,"Sending Data:\r\n");
I2C_SendData(I2Cx, reg_data);/* Send I2C3 EEPROM data */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

I2C_AcknowledgeConfig(I2Cx, DISABLE);/* Disable Acknowledgement */
Delay(1);/* Generate 12.285uS delay which is required*/
//USART_SendData_s( DEBUG_COM,"I2C_GenerateSTOP:\r\n");
I2C_GenerateSTOP(I2Cx, ENABLE);/* Send I2C3 STOP Condition */
Delay(1);/* Generate 12.285uS delay which is required*/

I2C_AcknowledgeConfig(I2Cx,ENABLE);/* Enable Acknowledgement to be ready for another reception */
}
/*===============================================================================================================*/
/*===============================================================================================================*/
{
char Red_reg;
/*------------------------------ Start Dummy write ----------------------------------------*/

//USART_SendData_s( DEBUG_COM,"I2C_GetFlagStatus:\r\n");
while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));/* While the bus is busy */

//USART_SendData_s( DEBUG_COM,"I2C_GenerateSTART:\r\n");
I2C_GenerateSTART(I2Cx, ENABLE);/* Send I2C3 START condition */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));

while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

/*------------------------------- Dummy write End -----------------------------------------*/

//USART_SendData_s( DEBUG_COM,"Sending Repeated start:\r\n");
I2C_GenerateSTART(I2Cx, ENABLE);/* Send STRAT condition a second time called repeated start*/
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));

/* Test on I2C3 EV7 and clear it */ /* Check for BUSY, MSL and RXNE flags =(EV7)*/

I2C_AcknowledgeConfig(I2Cx, DISABLE);/* Disable Acknowledgement */
Delay(1);/* Generate 12.285uS delay which is required*/
I2C_GenerateSTOP(I2Cx, ENABLE);/* Send I2C3 STOP Condition */
Delay(1);/* Generate 12.285uS delay which is required*/
//I2C_AcknowledgeConfig(I2C3,ENABLE);/* Enable Acknowledgement to be ready for another reception */

return Red_reg;/*Return data which is to be read from device*/
}
Enjoy!

richiechen

Newbie level 4
Gosh...

The I2C simply does not send any start...... Even at the very first cycle before address could be sent....

Best,
Richie

DTFT

Newbie level 6
Richie, what model STM32 are you using? Also can you post your I2C peripheral configuration code, including where you configure the peripheral clock - then we can have a look.

richiechen

Newbie level 4
Dear all...

The mistake turns out to be that we chose "simulator" instead of "debugger" in the project setting....
Best,
Richie

asa1365

asa1365

Points: 2

Realy

Junior Member level 1
i see your question.i'm a china engineer,We often use simulation of the iic,because it's not stable for STM32.so ......

asa1365

Points: 2