2
\$\begingroup\$

I'm starting out with I2C. I have a development board with stm32f103c8t6 to which I have connected GY-68 board with BMP180 barometric pressure sensor. Documentation of BMP180 describes Chip-id register [0xD0] which holds a constant value of [0x55] and can be a good way of testing if the I2C communication is working.

I am trying to read this register, but my code does not work. I am not sure if my setup is correct, because neither the BMP180 address nor register addres seem to be transmitted.

The board has an external clock set to 8MHz which I am confident is setup properly, because I have working UART which I use to check if the registers are set.

I have calculated CCR and TRISE with this instructions

void initI2C()
{
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN | RCC_APB2ENR_IOPAEN;
GPIOB->CRL |= (1 << 31);// Set pin B7(SDA) to alternate out open drain max 10MHz
GPIOB->CRL |= (1 << 30);
GPIOB->CRL &= ~(1 << 29);
GPIOB->CRL |= (1 << 28);

GPIOB->CRL |= (1 << 27);// Set pin B6(SCL) to alternate push pull max 10MHz
GPIOB->CRL &= ~(1 << 26);// I have also tried open drain 
GPIOB->CRL &= ~(1 << 25);
GPIOB->CRL |= (1 << 24);
I2C1->CR2 |= 0x08;       // Set clock to 8MHz
I2C1->CCR = 0x28;        // Set CCR
I2C1->CCR &= ~(1 << 15); // Sm Mode
I2C1->CCR &= ~(1 << 14); // Duty cycle for Fm mode
I2C1->TRISE = 0x09;      // Set clock rise time
I2C1->CR1 |= 1;          // Enable peripheral
I2C1->CR1 |= (1 << 8);   // Set start bit
while (!(I2C1->SR1 &= 1)) // Wait for start condition generated
{} //Never passes
I2C1->OAR1 = 0xEE; // Set Address
I2C1->DR = 0xD0; // Set address of id register
while (!(I2C1->SR1 &= (1 << 1))) // Wait for end of address transmission
{} // Never passes even with above while commented
while (!(I2C1->SR1 &= (1 << 6))) // Wait for data register not empty
{} // Never passes even with all above while commented
}

Now I know I don't read the register value here, but the the start condition never is generated, the address is never transmitted and the data register is not transmitted. I am reading some activity on both SDA and SCL pins with my multimeter, but I don't have oscilloscope to check exactly.

Any help would be appreciated.

\$\endgroup\$
3
  • \$\begingroup\$ Quickly reading (maybe too quickly) but where is "1.I2C peripheral must be disabled to set clock speed. Set PE[0] = 0 in CR1 register." in your code? That would be I2C1->CR1 |= 0 if i'm not mistaken \$\endgroup\$
    – Huisman
    Commented Feb 20, 2019 at 21:48
  • \$\begingroup\$ Yes that is mistaken, as ORing with zero does nothing. \$\endgroup\$
    – Justme
    Commented Feb 20, 2019 at 21:57
  • 1
    \$\begingroup\$ Waaay too many magic values in your code, making it mostly unreadable. \$\endgroup\$
    – Turbo J
    Commented Feb 20, 2019 at 22:51

2 Answers 2

3
\$\begingroup\$

The STM32F1 I2C peripheral is the subject of a number of major hardware errata. To get it to work correctly, read AN2824: "STM32F10xxx I2C optimized examples" carefully and implement read/write routines exactly as described. Do not deviate from the example code, even slightly -- the exact order in which you access certain registers during I2C read/write operations is critical for correct behavior.

\$\endgroup\$
1
  • \$\begingroup\$ Hi, thanks for the answer. I was not aware before your answer of existance of errata and examples. I am reading examples right now and I'm trying to understand the process. I thought maybe you could help me understand this sentence? "The interface waits for the SB flag to be set and then cleared by writing the slave address in the DR register. " What is the interface? Should I wait for the SB(start generated) flag or should I set SB flag manually? \$\endgroup\$
    – xstmpx
    Commented Feb 22, 2019 at 14:22
2
\$\begingroup\$

This might not answer all issues but a lot of them.

RCC->APB1ENR |= RCC_APB1ENR_I2C1EN | RCC_APB2ENR_IOPAEN;

By it's definition, the IOPAEN bit is in APB2, not APB1. Also what troubles me is that code would like to enable IOPA but port A is not used while port B is.

// Set pin B6(SCL) to alternate push pull max 10MHz

By definition, I2C uses open drain signaling. Don't use push pull, unless you really know that you can use it with your I2C chips, and even then, what would be the point of it.

while (!(I2C1->SR1 &= 1)) // Wait for start condition generated

While being valid C language, it will try to write back to the status register. Most likely you want:

while (!(I2C1->SR1 & 1)) // Wait for start condition generated
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.