STM32F407VG I2C MEMS Sensor Value Reading - Bare Metal Programming - Page 1
Forum section: electronics / general

author: electrovanga
author_rank: Contributor
timestamp: October 23, 2023, 09:47:23 am
content: 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->PB6PA6->GNDPA7->PB7Please help me to find the problem in this code.main.cCode:#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;int main(void){mems_init();while(1){mems_read_values(Data_Start_ADDR);x=((data_rec<<8)| data_rec);y=((data_rec<<8)| data_rec);z=((data_rec<<8)| data_rec);xg = (x * 0.0078);yg = (y * 0.0078);zg = (z * 0.0078);}}i2c.cCode:#include "stm32f407xx.h"/*PB6 ----> SCL*PB7 ----> SDA */void i2c1_init(void){//Enable GPIO clockRCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;//Set SCL pin mode as alternate functionGPIOB->MODER &= ~(1U<<12);GPIOB->MODER |= 1U<<13;//Set SDA pin mode as alternate functionGPIOB->MODER &= ~(1U<<14);GPIOB->MODER |= 1U<<15;//Select alternate function for SCL pinGPIOB->AFR &=~(1U<<24);GPIOB->AFR &=~(1U<<25);GPIOB->AFR |=(1U<<26);GPIOB->AFR &=~(1U<<27);//Select alternate function for SDA pinGPIOB->AFR &=~(1U<<28);GPIOB->AFR &=~(1U<<29);GPIOB->AFR |=(1U<<30);GPIOB->AFR &=~(1U<<31);//Configure the SCL pin output typeGPIOB->OTYPER |=(1U<<6);GPIOB->OTYPER |=(1U<<7);//Configure the SDA pin output type//Configure the SCL pin pull-up registerGPIOB->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 I2C1RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;//Software reset enableI2C1->CR1 |= I2C_CR1_SWRST;//Software reset disableI2C1->CR1 &= ~I2C_CR1_SWRST;//Configure I2C_CR2 peripheral clock frequency I2C1->CR2 = (1U<<4);//Configure I2C_CCR register standard frequencyI2C1->CCR = 80;//Configure I2C_TRISEI2C1->TRISE = 17;//Enable I2C Control RegisterI2C1->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 flagI2C1->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 flagwhile(!(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.cCode:#include "mems.h"#include <stdint.h>char data;uint8_t data_rec;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;data = 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.hCode:#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.hCode:#ifndef MEMS_H_#define MEMS_H_#include <stdint.h>#include "i2c.h"#define Who_Am_I_R0x0F#define SAD_R0x1D#define CTRL_REG10x20#define Data_Start_ADDR0x29#define CTRL1_EIGHT_G0U<<5#define POWER_CTRL1U<<6#define DATA_RATE0U<<7#define RESET0X0Uvoid mems_init(void);void mems_read_values(uint8_t reg);#endif /* MEMS_H_ */

author: wek
author_rank: Frequent Contributor
timestamp: October 23, 2023, 10:09:47 am
content: Try bit-banging first.> I connected physically the pins on the board like this below:Why?JW

author: AndyC_772
author_rank: Super Contributor
timestamp: October 23, 2023, 10:22:38 am
content: 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.

author: eutectique
author_rank: Frequent Contributor
timestamp: October 23, 2023, 10:40:04 am
content: The pin 7 of LIS302 -- it is high or low?

author: electrovanga
author_rank: Contributor
timestamp: October 23, 2023, 10:44:35 am
content: Quote from: AndyC_772 on October 23, 2023, 10:22:38 amHi,I have added the codes in my first post.Quote from: wek on October 23, 2023, 10:09:47 amI 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.Quote from: eutectique on October 23, 2023, 10:40:04 amCould 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.

author: eutectique
author_rank: Frequent Contributor
timestamp: October 23, 2023, 10:48:31 am
content: Quote from: electrovanga on October 23, 2023, 10:44:35 amLIS302 (MEMS accelerometer) has only one pin 7, namedCS_I2C/SPIand connected toPE3. In your attached picture it is upper-right pin.Is this pin high or low?

author: electrovanga
author_rank: Contributor
timestamp: October 23, 2023, 11:01:21 am
content: Quote from: eutectique on October 23, 2023, 10:48:31 amWow. 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?

author: eutectique
author_rank: Frequent Contributor
timestamp: October 23, 2023, 11:12:27 am
content: Quote from: electrovanga on October 23, 2023, 11:01:21 amAs thedatasheetexplains, it selects between I2C and SPI interfaces. For I2C it should always be high, see Section 4 "Application hints" and Section 5 "Digital interfaces".

author: betocool
author_rank: Regular Contributor
timestamp: October 23, 2023, 01:40:30 pm
content: 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

author: Siwastaja
author_rank: Super Contributor
timestamp: October 23, 2023, 01:50:27 pm
content: 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.

author: newbrain
author_rank: Super Contributor
timestamp: October 23, 2023, 03:14:21 pm
content: Quote from: Siwastaja on October 23, 2023, 01:50:27 pmAlmost 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 definedWho_Am_I_Ras 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.

author: electrovanga
author_rank: Contributor
timestamp: October 23, 2023, 08:55:46 pm
content: Quote from: newbrain on October 23, 2023, 03:14:21 pmHi,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: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.

author: newbrain
author_rank: Super Contributor
timestamp: October 24, 2023, 05:39:39 am
content: OK, but did you also change the peripheral address in Who_Am_I_R?0x0F can't be right in any case.

author: electrovanga
author_rank: Contributor
timestamp: October 24, 2023, 06:36:13 am
content: Quote from: newbrain on October 24, 2023, 05:39:39 amYes, I changed it with 0x1C. However, it doesn't work still.

author: electrovanga
author_rank: Contributor
timestamp: October 24, 2023, 07:58:01 pm
content: Any support, please?

author: newbrain
author_rank: Super Contributor
timestamp: October 25, 2023, 09:11:13 am
content: 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!

author: electrovanga
author_rank: Contributor
timestamp: October 25, 2023, 11:26:50 am
content: Quote from: newbrain on October 25, 2023, 09:11:13 amThanks a lotI am also trying to solve it. I hope you can try and support me. I will be appreciated a lot.

author: newbrain
author_rank: Super Contributor
timestamp: October 29, 2023, 09:53:07 pm
content: 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: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 rectangularLIS3DSH is squareAs 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.

author: eutectique
author_rank: Frequent Contributor
timestamp: October 30, 2023, 03:09:17 pm
content: 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.

author: electrovanga
author_rank: Contributor
timestamp: October 30, 2023, 07:55:35 pm
content: Quote from: newbrain on October 29, 2023, 09:53:07 pmHi newbrain,Thanks for your feedback. Mine is rectengular. I will try again with your way.Quote from: eutectique on October 30, 2023, 03:09:17 pmThanks for your support.

author: eutectique
author_rank: Frequent Contributor
timestamp: November 01, 2023, 06:55:59 pm
content: 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:$ git clone https://github.com/RIOT-OS/RIOT.git$ 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 makeAdjustCOMPILER_INCLUDE_PATHSaccording to the location of your toolchain.You will findaccelerometer.{elf|hex|map}files in the relativebin/stm32f4discoverydirectory.Also, I used Segger Ozone for debugging, for that I re-flashed the disco board withJ-Link OB.