Author Topic: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming  (Read 2121 times)

0 Members and 1 Guest are viewing this topic.

Offline electrovangaTopic starter

  • Contributor
  • Posts: 21
  • Country: de
Hello,

I have a discovery board with stm32f407vg-t6.There is a mems sensor on it and I am trying to read sensor values via I2C. However, my code is not working. I also upload my code.

I checked my code 4 times and couldn't find any mistake. I even messed up the code a little bit to be sure all the parameters were set correctly.

I connected physically the pins on the board like this below:

PA5->PB6
PA6->GND
PA7->PB7
Please help me to find the problem in this code.

main.c
Code: [Select]
#include <stdio.h>
#include <stdint.h>
#include "stm32f407xx.h"
#include "mems.h"

int16_t x,y,z;
float xg,yg,zg;

extern uint8_t data_rec[6];

int main(void){
mems_init();
while(1){
mems_read_values(Data_Start_ADDR);
x=((data_rec[1]<<8)| data_rec[0]);
y=((data_rec[3]<<8)| data_rec[2]);
z=((data_rec[5]<<8)| data_rec[4]);

xg = (x * 0.0078);
yg = (y * 0.0078);
zg = (z * 0.0078);
}
}


i2c.c
Code: [Select]
#include "stm32f407xx.h"
/*PB6 ----> SCL
 *PB7 ----> SDA */

void i2c1_init(void){
//Enable GPIO clock
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
//Set SCL pin mode as alternate function
GPIOB->MODER &= ~(1U<<12);
GPIOB->MODER |= 1U<<13;
//Set SDA pin mode as alternate function
GPIOB->MODER &= ~(1U<<14);
GPIOB->MODER |= 1U<<15;
//Select alternate function for SCL pin
GPIOB->AFR[0] &=~(1U<<24);
GPIOB->AFR[0] &=~(1U<<25);
GPIOB->AFR[0] |=(1U<<26);
GPIOB->AFR[0] &=~(1U<<27);
//Select alternate function for SDA pin
GPIOB->AFR[0] &=~(1U<<28);
GPIOB->AFR[0] &=~(1U<<29);
GPIOB->AFR[0] |=(1U<<30);
GPIOB->AFR[0] &=~(1U<<31);
//Configure the SCL pin output type
GPIOB->OTYPER |=(1U<<6);
GPIOB->OTYPER |=(1U<<7);
//Configure the SDA pin output type
//Configure the SCL pin pull-up register
GPIOB->PUPDR |=(1U<<12);
GPIOB->PUPDR &=~(1U<<13);
GPIOB->PUPDR |=(1U<<14);
GPIOB->PUPDR &=~(1U<<15);
//Configure the SDA pin pull-up register
//Enable the clock access to I2C1
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
//Software reset enable
I2C1->CR1 |= I2C_CR1_SWRST;
//Software reset disable
I2C1->CR1 &= ~I2C_CR1_SWRST;
//Configure I2C_CR2 peripheral clock frequency [16MHz]
I2C1->CR2 = (1U<<4);
//Configure I2C_CCR register standard frequency
I2C1->CCR = 80;
//Configure I2C_TRISE
I2C1->TRISE = 17;
//Enable I2C Control Register
I2C1->CR1 |= I2C_CR1_PE;
}
void i2c1_byteRead(char saddr, char maddr, char* data){

volatile int tmp;

while(I2C1->SR2 & I2C_SR2_BUSY){}

I2C1->CR1 |= I2C_CR1_START;

while(!(I2C1->SR1 & I2C_SR1_SB)){}

I2C1->DR = saddr<<1;

while(!(I2C1->SR1 & I2C_SR1_ADDR)){}

/*Clear the ADDR flag*/
tmp = I2C1->SR2; //This comes from the referance manual. When you read SR2 after reading I2C_SR1 clears ADDR flag

I2C1->DR = maddr;

while(!(I2C1->SR1 & I2C_SR1_TXE)){}

I2C1->CR1 |= I2C_CR1_START;

while(!(I2C1->SR1 & I2C_SR1_SB)){}

I2C1->DR = saddr<<1 | 1;

while(!(I2C1->SR1 & I2C_SR1_ADDR)){}

I2C1->CR1 &= ~I2C_CR1_ACK;

tmp = I2C1->SR2;

I2C1->CR1 |= I2C_CR1_STOP;

while(!(I2C1->SR1 & I2C_SR1_RXNE)){}

*data++ = I2C1->DR;

}
void i2c1_burstRead(char saddr,char maddr, int n, char* data){

volatile int tmp;
while(I2C1->SR2 & I2C_SR2_BUSY);
I2C1->CR1 |= I2C_CR1_START;
while(!(I2C1->SR1 & I2C_SR1_SB));
I2C1->DR = saddr <<1;
while(!(I2C1->SR1 & I2C_SR1_ADDR));
tmp = I2C1->SR2; //This comes from the referance manual. When you read SR2 after reading I2C_SR1 clears ADDR flag
while(!(I2C1->SR1 & I2C_SR1_TXE));
I2C1->DR = maddr;
while(!(I2C1->SR1 & I2C_SR1_TXE));
I2C1->CR1 |= I2C_CR1_START;
while(!(I2C1->SR1 & I2C_SR1_SB));
I2C1->DR = saddr <<1|1;
while(!(I2C1->SR1 & I2C_SR1_ADDR));
tmp= I2C1->SR2;
I2C1->CR1|=I2C_CR1_ACK;
while(n>0U){
if(n==1U){
I2C1->CR1 &= ~I2C_CR1_ACK;
I2C1->CR1 |= I2C_CR1_STOP;
while(!(I2C1->SR1 & I2C_SR1_RXNE));
*data++=I2C1->DR;
break;
}
else{
while(!(I2C1->SR1 & I2C_SR1_RXNE));
(*data++) =I2C1->DR;
n--;
}
}
}
void i2c1_burstWrite(char saddr, char maddr, int n, char* data){
volatile int tmp;
while(I2C1->SR2 & I2C_SR2_BUSY){}
I2C1->CR1 |= I2C_CR1_START;
while(!(I2C1->SR1 & I2C_SR1_SB)){}
I2C1->DR = saddr << 1;
while(!(I2C1->SR1 & I2C_SR1_ADDR)){}
tmp = I2C1->SR2;
while(!(I2C1->SR1 & I2C_SR1_TXE)){}
I2C1->DR = maddr;
for(int i = 0; i<n; i++){
while(!(I2C1->SR1 & I2C_SR1_TXE)){}
I2C1->DR = *data++;
}
while(!(I2C1->SR1 & I2C_SR1_BTF)){}
I2C1->CR1 |= I2C_CR1_STOP;
}


mems.c
Code: [Select]
#include "mems.h"
#include <stdint.h>

char data;
uint8_t data_rec[6];

void mems_read_address(uint8_t reg){
i2c1_byteRead(Who_Am_I_R, reg, &data);
}

void mems_write(uint8_t reg, char value){
char data[1];
data[0] = value;

i2c1_burstWrite(Who_Am_I_R, reg, 1, data);
}

void mems_read_values(uint8_t reg){
i2c1_burstRead(Who_Am_I_R,reg, 6, (char*)data_rec);
}

void mems_init(void){
i2c1_init();
mems_read_address(SAD_R);
mems_write(CTRL_REG1, CTRL1_EIGHT_G);
mems_write(CTRL_REG1,RESET );
mems_write(CTRL_REG1, POWER_CTRL);

}

i2c.h
Code: [Select]
#ifndef I2C_H_
#define I2C_H_

void i2c1_init(void);
void i2c1_byteRead(char saddr, char maddr, char* data);
void i2c1_burstRead(char saddr,char maddr, int n, char* data);
void i2c1_burstWrite(char saddr, char maddr, int n, char* data);

#endif /* I2C_H_ */

mems.h
Code: [Select]
#ifndef MEMS_H_
#define MEMS_H_
#include <stdint.h>
#include "i2c.h"

#define Who_Am_I_R 0x0F
#define SAD_R 0x1D
#define CTRL_REG1 0x20
#define Data_Start_ADDR 0x29

#define CTRL1_EIGHT_G 0U<<5
#define POWER_CTRL 1U<<6
#define DATA_RATE 0U<<7
#define RESET 0X0U


void mems_init(void);
void mems_read_values(uint8_t reg);

#endif /* MEMS_H_ */

« Last Edit: October 23, 2023, 10:37:05 am by electrovanga »
 

Offline wek

  • Frequent Contributor
  • **
  • Posts: 495
  • Country: sk
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #1 on: October 23, 2023, 10:09:47 am »
Try bit-banging first.

> I connected physically the pins on the board like this below:

Why?

JW
 

Offline AndyC_772

  • Super Contributor
  • ***
  • Posts: 4228
  • Country: gb
  • Professional design engineer
    • Cawte Engineering | Reliable Electronics
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #2 on: October 23, 2023, 10:22:38 am »
It's terrible IT security practice to download a 600k+ zip file from an unknown source.

I suggest posting the specific piece of code that's giving trouble, in plain text so we can see it - and please elaborate on "my code is not working", which tells us nothing.

Offline eutectique

  • Frequent Contributor
  • **
  • Posts: 392
  • Country: be
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #3 on: October 23, 2023, 10:40:04 am »
The pin 7 of LIS302 -- it is high or low?
 

Offline electrovangaTopic starter

  • Contributor
  • Posts: 21
  • Country: de
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #4 on: October 23, 2023, 10:44:35 am »
It's terrible IT security practice to download a 600k+ zip file from an unknown source.

I suggest posting the specific piece of code that's giving trouble, in plain text so we can see it - and please elaborate on "my code is not working", which tells us nothing.
Hi,
I have added the codes in my first post.

Try bit-banging first.

> I connected physically the pins on the board like this below:

Why?

JW
I haven't learn bit banging. I will search it.

I connected it because I just wanted to configure my own I2C pins from scratch as I am learning. Then I just checked which pins I should communicate with mems gyro sensor. I connected the cables at the end.

The pin 7 of LIS302 -- it is high or low?
Could you be more specific, please? I didn't get it as there are two pins PB7 and PA7 in my project. If you mean whether the I2C communication line is high or low. I would say I pulled-up with software the I2C line. I hope I could answer your question.
 

Offline eutectique

  • Frequent Contributor
  • **
  • Posts: 392
  • Country: be
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #5 on: October 23, 2023, 10:48:31 am »
Could you be more specific, please? I didn't get it as there are two pins PB7 and PA7 in my project.

LIS302 (MEMS accelerometer) has only one pin 7, named CS_I2C/SPI and connected to PE3. In your attached picture it is upper-right pin.

Is this pin high or low?
 

Offline electrovangaTopic starter

  • Contributor
  • Posts: 21
  • Country: de
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #6 on: October 23, 2023, 11:01:21 am »
Could you be more specific, please? I didn't get it as there are two pins PB7 and PA7 in my project.

LIS302 (MEMS accelerometer) has only one pin 7, named CS_I2C/SPI and connected to PE3. In your attached picture it is upper-right pin.

Is this pin high or low?
Wow. I have no idea about this pin. It is on the discovery board and I didn't do anything for it. I will check it. What is this PIN for?
 

Offline eutectique

  • Frequent Contributor
  • **
  • Posts: 392
  • Country: be
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #7 on: October 23, 2023, 11:12:27 am »
Wow. I have no idea about this pin. It is on the discovery board and I didn't do anything for it. I will check it. What is this PIN for?

As the datasheet explains, it selects between I2C and SPI interfaces. For I2C it should always be high, see Section 4 "Application hints" and Section 5 "Digital interfaces".
« Last Edit: October 23, 2023, 11:14:04 am by eutectique »
 
The following users thanked this post: electrovanga

Offline betocool

  • Regular Contributor
  • *
  • Posts: 97
  • Country: au
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #8 on: October 23, 2023, 01:40:30 pm »
Can't see on the schematic, but you're missing one 2.2KOhm pullup resistor on the SDA and on the SCL line each, unless they are somewhere else.

Without those pullups it's not going to work. I'm not sure adding the "pullup" option on the pin configuration helps in this case.

Cheers,

Alberto
 

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 8173
  • Country: fi
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #9 on: October 23, 2023, 01:50:27 pm »
I2C/SPI selection pin #7 is probably (a guess) sampled at power-up, in which case using an IO pin won't be a good idea (as it would be low when the power is simultaneously applied to the MCU driving it, and the sensor). Permanently wire it to Vcc for I2C mode.
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #10 on: October 23, 2023, 03:14:21 pm »
I2C/SPI selection pin #7 is probably (a guess) sampled at power-up, in which case using an IO pin won't be a good idea (as it would be low when the power is simultaneously applied to the MCU driving it, and the sensor). Permanently wire it to Vcc for I2C mode.
Almost there, but not quite!

That pin doubles as SPI /CS so, on the Disco board - which uses SPI - it's connected to a GPIO (PE3).
In fact, the OP posted a snippet of the actual board schematic!
So it's not sampled only at power up, and there's an internal pull up according to the AN and DS.

However, ST advises to tie it high for I²C operation: electrovanga, program PE3 as output and set it to 1, to rule this out as the cause of malfunction.

EtA: Electrovanga, I see you defined Who_Am_I_R as 0x0F, and you are using it as the I²C peripheral address.
The MEMS DS states that the address is 0b001110x, with x determined by the SDO pin, 0 as you tie SDO to GND, giving 0x1C.
« Last Edit: October 23, 2023, 03:30:30 pm by newbrain »
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline electrovangaTopic starter

  • Contributor
  • Posts: 21
  • Country: de
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #11 on: October 23, 2023, 08:55:46 pm »
I2C/SPI selection pin #7 is probably (a guess) sampled at power-up, in which case using an IO pin won't be a good idea (as it would be low when the power is simultaneously applied to the MCU driving it, and the sensor). Permanently wire it to Vcc for I2C mode.
Almost there, but not quite!

That pin doubles as SPI /CS so, on the Disco board - which uses SPI - it's connected to a GPIO (PE3).
In fact, the OP posted a snippet of the actual board schematic!
So it's not sampled only at power up, and there's an internal pull up according to the AN and DS.

However, ST advises to tie it high for I²C operation: electrovanga, program PE3 as output and set it to 1, to rule this out as the cause of malfunction.

EtA: Electrovanga, I see you defined Who_Am_I_R as 0x0F, and you are using it as the I²C peripheral address.
The MEMS DS states that the address is 0b001110x, with x determined by the SDO pin, 0 as you tie SDO to GND, giving 0x1C.

Hi,
I've added the code below inside of the init function. However, it doesn't work.
Before this code, I also tested the code by connecting PE3 to VDD or 3V. It didn't work either. Still I read 0 value in the debugging mode. What else can go wrong here?
*I have also changed the SDO pin electrical connection,its address and tested but not working.
Code: [Select]
void i2c1_init(void){
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN;
GPIOE->MODER |= GPIO_MODER_MODER3_0;
GPIOE->BSRR |= GPIO_BSRR_BS3;

Edit: I can't see sometimes even 0 values in the debug mode.
« Last Edit: October 23, 2023, 09:19:49 pm by electrovanga »
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #12 on: October 24, 2023, 05:39:39 am »
OK, but did you also change the peripheral address in Who_Am_I_R?
0x0F can't be right in any case.
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline electrovangaTopic starter

  • Contributor
  • Posts: 21
  • Country: de
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #13 on: October 24, 2023, 06:36:13 am »
OK, but did you also change the peripheral address in Who_Am_I_R?
0x0F can't be right in any case.
Yes, I changed it with 0x1C. However, it doesn't work still.
 

Offline electrovangaTopic starter

  • Contributor
  • Posts: 21
  • Country: de
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #14 on: October 24, 2023, 07:58:01 pm »
Any support, please?
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #15 on: October 25, 2023, 09:11:13 am »
Not much time these days, but I'm sure I've got that very board in a drawer at home1, about 2000 km from home0 here.

If I have a slow Sunday, I might even try your code out of curiosity - no guarantee though, and of course my advice is worth exactly what you pay for it, sometimes less!
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline electrovangaTopic starter

  • Contributor
  • Posts: 21
  • Country: de
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #16 on: October 25, 2023, 11:26:50 am »
Not much time these days, but I'm sure I've got that very board in a drawer at home1, about 2000 km from home0 here.

If I have a slow Sunday, I might even try your code out of curiosity - no guarantee though, and of course my advice is worth exactly what you pay for it, sometimes less!
Thanks a lot   :popcorn: I am also trying to solve it. I hope you can try and support me. I will be appreciated a lot.
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #17 on: October 29, 2023, 09:53:07 pm »
OK, the I2C code while questionable in some place (use of globals and some general awkwardness, e.g. the in I2C burst read cycle) seem workable, once the CS is pulled to high and the rigth I2C address is used.

The initialisation sequence, though, is not correct:
The initial read is pointelss - and, in fact, in a reserved space - just remove it.
Then you overwrite the control register writing to it three times and the last write, while it enables power, disable all three axis!
All the sequence could be reduced to:
Code: [Select]
void mems_init(void) {
    i2c1_init();
    mems_write(CTRL_REG1, 0x47);
}


There is one major question though:
Which version of the STM32F4 Disco do you have?
Mine has the more advanced LIS3DSH instead of the LIS302DL, which requires a different I2C address, and a totally different setup.

If you search online for pictures of the board, you'll find both versions.

The easy way to tell the accelerometers apart is to look at the their shape, it's U5, located between the two push-buttons.
  • LIS302DL is rectangular
  • LIS3DSH is square
As you can see in the attached picture, it would seem to work - I only changed the peripheral address to the right one for the LIS3DSH, and enabled the power in the conf register. I did not investigate why the X values do not change, though.
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline eutectique

  • Frequent Contributor
  • **
  • Posts: 392
  • Country: be
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #18 on: October 30, 2023, 03:09:17 pm »
Some years ago I experimented with F407 disco board and RIOT RTOS. The accelerometer there was LIS302, connected with SPI. I am not in possession of that board any more, but could dig up the code that was left.

Not exactly what you are after (bare-metal and I2C), but could potentially be helpful.
 

Offline electrovangaTopic starter

  • Contributor
  • Posts: 21
  • Country: de
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #19 on: October 30, 2023, 07:55:35 pm »
OK, the I2C code while questionable in some place (use of globals and some general awkwardness, e.g. the in I2C burst read cycle) seem workable, once the CS is pulled to high and the rigth I2C address is used.

The initialisation sequence, though, is not correct:
The initial read is pointelss - and, in fact, in a reserved space - just remove it.
Then you overwrite the control register writing to it three times and the last write, while it enables power, disable all three axis!
All the sequence could be reduced to:
Code: [Select]
void mems_init(void) {
    i2c1_init();
    mems_write(CTRL_REG1, 0x47);
}


There is one major question though:
Which version of the STM32F4 Disco do you have?
Mine has the more advanced LIS3DSH instead of the LIS302DL, which requires a different I2C address, and a totally different setup.

If you search online for pictures of the board, you'll find both versions.

The easy way to tell the accelerometers apart is to look at the their shape, it's U5, located between the two push-buttons.
  • LIS302DL is rectangular
  • LIS3DSH is square
As you can see in the attached picture, it would seem to work - I only changed the peripheral address to the right one for the LIS3DSH, and enabled the power in the conf register. I did not investigate why the X values do not change, though.

Hi newbrain,
Thanks for your feedback. Mine is rectengular. I will try again with your way.

Some years ago I experimented with F407 disco board and RIOT RTOS. The accelerometer there was LIS302, connected with SPI. I am not in possession of that board any more, but could dig up the code that was left.

Not exactly what you are after (bare-metal and I2C), but could potentially be helpful.
Thanks for your support.
 

Offline eutectique

  • Frequent Contributor
  • **
  • Posts: 392
  • Country: be
Re: STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming
« Reply #20 on: November 01, 2023, 06:55:59 pm »
Here we go, the patch for RIOT RTOS. You would need to get the RTOS, go 4 years back to the commit when I did this, create a branch there, apply the patch, build, flash, and enjoy!

Commands:

Code: [Select]
$ git clone [url]https://github.com/RIOT-OS/RIOT.git[/url]
$ cd RIOT
$ git switch -c f407disco-lis302 4f929e924c31011780ddb3ae0e80df222a35a5d0
$ git apply 0001-RIOT-4f929e924c31011780ddb3ae0e80df222a35a5d0_stm32f407disco+lis302dl.patch.txt
$ cd examples/accelerometer
$ COMPILER_INCLUDE_PATHS=/opt/gcc-arm-none-eabi-10.3-2021.10/arm-none-eabi/include make

Adjust COMPILER_INCLUDE_PATHS according to the location of your toolchain.

You will find accelerometer.{elf|hex|map} files in the relative bin/stm32f4discovery directory.

Also, I used Segger Ozone for debugging, for that I re-flashed the disco board with J-Link OB.
« Last Edit: November 02, 2023, 08:47:28 am by eutectique »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf