2020-06-07 05:54 AM
Hi everyone
I have a STM32F401RE Board and I am trying to get the I2C to work with an MPU6050. I have seen some examples but since I can not seem to find PB11 I realized I have to use PB3 as I2C2_SDA pin and PB10 as I2C2_SCL pin. The clock on this is 16Mhz but I read that the pins "Fast toggle capable of changing every two clock cycles" so I am assuming that it can toggle at 8Mhz? Do I have to set the port speed somewhere also?
I am also using the internal pull-up resistors so the wires to the MPU6050 board are connected directly to the board. I hope that these pull-up resistors are there for this application.
However when I run my code it gets stuck waiting for start condition...
" while (!(I2C2->SR1 & 0x0001)) {}; // wait for START condition (SB=1) "
I have been trying to get it past this step for a couple of days. I don't have an oscilloscope which makes this much harder to debug.
Since it could as well be my initialisation of the peripherals I thought I would ask here just to be clear and rule some errors out.
Here is my INIT code and the first write where it gets stuck.
int main(void)
{
// Init accelerometer chip
I2C2_Init(); // initialize I2C2 block
I2C2_WriteChar(0x6b,0x00); // wake-up!
I2C2_WriteChar(0x1b,0x10); // Gyro full scale = +/-1000 deg/s
//loop
while (1) {
short AccZ = I2C2_ReadShort(0x3f); // read acc in z axis
short RotZ = I2C2_ReadShort(0x47); // read gyro in z axis
for (int i=0; i<3000000; i++) {}; // waste time, ~100ms
}
};
void I2C2_Init(){
// declare and initialize pins to be used for I2C
RCC->AHB1ENR |= 0x00000002; // Enable clock for GPIOB
//GPIOB->AFR[1] |= 0x00004400; // select AF4 (I2C) for PB10,11 -> I2C2 0b100010000000000
GPIOB->AFR[1] |= ( 1 << 10 ); // select AF4 (I2C) for PB10
GPIOB->AFR[0] |= (1 << 14); // select AF4 (I2C) for PB3
GPIOB->MODER |= 0x00a00000; // PB10,11 => alternate functions
GPIOB->OTYPER |= 0x0c00; // use open-drain output on these pins!
GPIOB->PUPDR |= ( 1 << 6 | 1 << 20 ); //couple pull-up resistors for PB3 and PB10
// initialize I2C block
RCC->APB1ENR |= 0x00400000; // Enable clock for I2C2 0b10000000000000000000000
I2C2->CR2 |= 0x0008; // clock == 8MHz!
I2C2->CCR |= 0x0040; // clock control register (270kHz)
I2C2->TRISE |= 0x0009; // rise time register
I2C2->CR1 |= 0x0001; // I2C2 enable
}
void I2C2_WriteChar (char Adr, char Dat) {
I2C2->CR1 |= 0x0100; // send START bit
while (!(I2C2->SR1 & 0x0001)) {}; // wait for START condition (SB=1) //<<----GETS STUCK
I2C2->DR = 0xd0; // slave address -> DR & write
while (!(I2C2->SR1 & 0x0002)) {}; // wait for ADDRESS sent (ADDR=1)
int Status2 = I2C2->SR2; // read status to clear flag
I2C2->DR = Adr; // Address in chip -> DR & write
while (!(I2C2->SR1 & 0x0080)) {}; // wait for DR empty (TxE)
I2C2->DR = Dat; // Dat -> DR & write
while (!(I2C2->SR1 & 0x0080)) {}; // wait for DR empty (TxE)
while (!(I2C2->SR1 & 0x0004)) {}; // wait for Byte sent (BTF)
I2C2->CR1 |= 0x0200; // send STOP bit
};
short I2C2_ReadShort (char Adr) {
I2C2->CR1 |= 0x0100; // send START bit
while (!(I2C2->SR1 & 0x0001)) {}; // wait for START condition (SB=1)
I2C2->DR = 0xd0; // slave address -> DR (LSB=1)
while (!(I2C2->SR1 & 0x0002)) {}; // wait for ADDRESS sent (ADDR=1)
int Status2 = I2C2->SR2; // read SR2 to clear flag
I2C2->DR = Adr; // register in chip -> DR
while (!(I2C2->SR1 & 0x0080)) {}; // wait for DR empty (TxE=1)
while (!(I2C2->SR1 & 0x0004)) {}; // wait for ByteTransferred (BTF=1)
I2C2->CR1 |= 0x0100; // send START bit
while (!(I2C2->SR1 & 0x0001)) {}; // wait for START condition (SB=1)
I2C2->DR = 0xd1; // slave address -> DR (LSB=0)
while (!(I2C2->SR1 & 0x0002)) {}; // wait for ADDRESS sent (ADDR=1)
I2C2->CR1 |= 0x0800; // POS enable
int Status4 = I2C2->SR2; // read status to clear flag
while (!(I2C2->SR1 & 0x0004)) {}; // wait for ByteReceived (BTF=1)
I2C2->CR1 |= 0x0200; // send STOP bit
short x1 = I2C2->DR << 8; // safe place
short x2 = I2C2->DR; // safe place
return ((short)(x1 + x2)); // return combined bytes
}
char I2C2_ReadChar (char Adr) { // procedure: RM0090, pg. 584!
I2C2->CR1 |= 0x0100; // send START bit
while (!(I2C2->SR1 & 0x0001)) {}; // wait for START condition (SB=1)
I2C2->DR = 0xd0; // slave address -> DR (LSB=1)
while (!(I2C2->SR1 & 0x0002)) {}; // wait for ADDRESS sent (ADDR=1)
int Status2 = I2C2->SR2; // read SR2 to clear flag
I2C2->DR = Adr; // register in chip -> DR
while (!(I2C2->SR1 & 0x0080)) {}; // wait for DR empty (TxE=1)
while (!(I2C2->SR1 & 0x0004)) {}; // wait for ByteTransferred (BTF=1)
I2C2->CR1 |= 0x0100; // send START bit
while (!(I2C2->SR1 & 0x0001)) {}; // wait for START condition (SB=1)
I2C2->DR = 0xd1; // slave address -> DR (LSB=0)
while (!(I2C2->SR1 & 0x0002)) {}; // wait for ADDRESS sent (ADDR=1)
int Status4 = I2C2->SR2; // read status to clear flag
while (!(I2C2->SR1 & 0x0040)) {}; // wait for ByteReceived (RxNE=1)
I2C2->CR1 |= 0x0200; // send STOP bit
return ((char)I2C2->DR); // return byte
}
2020-06-07 06:35 AM
Might work okay if you have a really low clock speed. Really not how I2C is meant to be run.
Port speed is set in GPIO_OSPEEDR.
You should delay a few clock cycles after enabling the clock and before using the peripheral. See __HAL_RCC_I2C2_CLK_ENABLE. May or may not be the issue you're running into.
2020-06-07 07:32 AM
I would first try with GPIO SW emulated I2C Master interface. Get the HW construct working, then optimize using peripherals...
There are many bitbang code example, I've been using the same one since 1999... and still works great, usually it helps be focus to validate the HW, test the slave addresses sweep and get the slave device ID ok. You can also take a detour by adding the HW I2C Driver as additional challenge simultaneously...
2020-06-07 07:40 AM
Ok so which part of this implementation is wrong though? Is it perhaps because of all the while loops wasting cpu time etc?
What do you mean only on a really low clock speed?
Do you mean there is some hardware version of this that can be utilised to run behind the scenes?
Thanks
2020-06-07 07:44 AM
Could you perhaps link the old example you have? I sometimes find the less abstracted stuff easier to understand and then go from there. Not that I2C is very complicated but more the tools you are given to work with etc.
HW I2C Driver? Is there embedded circuitry that handles all of this for you after setup... and then just read some register for the data?
That would be ideal since I do not really want my CPU to just work on things like I2C comms handling.
Thanks
2020-06-07 07:57 AM
> Ok so which part of this implementation is wrong though? Is it perhaps because of all the while loops wasting cpu time etc?
That's for you to debug. I gave you some leads. Wasting CPU time isn't the problem.
> What do you mean only on a really low clock speed?
I2C usually assumes a pullup of around 4.7kOhm. The internal pullup is much weaker, around 60kOhm.
> Do you mean there is some hardware version of this that can be utilised to run behind the scenes?
You could use DMA, but I did not mention that.
2020-06-09 02:30 AM
Hi @CMcC.1 ,
Look to the I2C registers to check if expected implementation is well done then if there is any error flag set.
-Amel
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.