AnsweredAssumed Answered

I2C Issues,STM32F440x and L3GD20

Question asked by Jon Henry on May 30, 2013
Latest reply on Jun 1, 2013 by Jon Henry

I’m working on getting a STM32F405RG communicating with a L3GD20 Gyroscope via I2C. I’m having issues getting real data back from the gyro.

My Experience:

I am a 33 yr old junior in Electrical Engineering at ULL. This is not a school project as the projects for school are much more mundane and simple, I could only wish we’d do something like this in school. I’ve worked professionally with electricity in some form or another since I was 17, first as an electrician, 6 years Air Force as an avionics tech, 6 years in the oilfield working with computer automation and instrumentation. I’ve always played around with software as a hobby and have taken enough classes to have a minor in computer science upon graduation. I’d classify myself as an intermediate to advanced hobbyist using C#, java and assembly. This is my first round with plain C.

Hardware-wise, I’m fairly experienced working with analog circuitry, PLC’s and high-level language uC’s like the Netduino , parallax and picaxe offerings.

The Hardware:

The board I’m using is actually a Netduino Plus 2 where Ive erased the chip with STDefuse.  I’m using Atollic Truestudio along with the STLink V2 debugger for debugging. The L3GD20 is a breakout board (MinIMU_V2) from Polulu containing the L3GD20 gyroscope along with a LSM303DLHC accelerometer/magnetometer. I believe my pull-ups were in the neighborhood 0f 2000 ohms which I came to by using my o-scope to get as close to or beneath 200 nS rise time for 100kHz I2C.  I got it to about 200 nS and I2C was quite sturdy with this setup. I was using the Netduino as it was with .NETMF and C# along with the MinIMU_V2 and two other I2c boards with no problems, so I’m positive the hardware is functional.

The Current Setup:

Currently, I’m using the erased Netduino Plus 2 with the MinImu_V2 connected to it via PB6 and PB7. These were the same pins being used by the netduino framework for I2C. I’ve removed the other I2C devices to simplify for troubleshooting. I’m still using the same pull-ups and have verified  my rise time is still good, I’m at about 195 nS.

The Problem:

I’ve written a program to read the out values from the L3GD20 using I2C. I adapted the L3G4200D platform-independent driver from ST for use with the L3GD20. Only real difference is ranges, register names and memory locations. I’m also using the stm32f4xx_i2c.h file from the Standard Peripherals Library Ver 1.1.0. The program compiles and debugs fine, I just don’t seem to get any real data back from the L3GD20. When I step through the project, any reads I make result in getting 0xD7 back. 0xD7 happens to be the 7-bit slave address of the L3GD20 (0x6B with SDO tied high) ORed with 0x01 after attaching the LSB for read.

I’m thinking my problem is in the sequencing of my I2C read and write. I’m not worried about multiple reads right now, I should be able to figure that out once I get single byte reads and writes straight. I’ve found small differences in the ‘guidelines’ sited in the STM32F4xx Reference Manual, the L3GD20 datasheet and the commented instructions inside the stm32f4xx_i2c.h file as far as sequence goes.

Here is some of my code:

Main:
#include "stm32f4xx.h"
#include "stm32f4xx_i2c.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "minimu_v2.h"

void I2C1_Init();
 
int main(void)
{
    I2C1_Init();
      MinIMU_V2_Init();
 
    while(1)
    {
        AHRS();
    }
    return 0;
}
  
void I2C1_Init()
{
    GPIO_InitTypeDef GPIO_InitStruct;
    I2C_InitTypeDef I2C_InitStruct;
  
    // enable clock for SCL and SDA pins
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    // enable APB1 peripheral clock for I2C1
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
  
    /* setup SCL and SDA pins
     * You can connect I2C1 to two different
     * pairs of pins:
     * 1. SCL on PB6 and SDA on PB7
     * 2. SCL on PB8 and SDA on PB9
     */
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6; //we are going to use PB6
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;//set pins to alt fn
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;  // set GPIO speed
    GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;//set output to OD
    GPIO_Init(GPIOB, &GPIO_InitStruct);        // init GPIOB
  
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7; // we are going to use PB7
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;//set pins to alt fn
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // set GPIO speed
    GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; // set output to OD
    GPIO_Init(GPIOB, &GPIO_InitStruct);      // init GPIOB
  
    // Connect I2C1 pins to AF
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1); // SCL
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1); // SDA
  
    // configure I2C1
    I2C_DeInit(I2C1);
    I2C_InitStruct.I2C_ClockSpeed = 100000;         // 100kHz
    I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;         // I2C mode
    I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; //50% duty cycle
    I2C_InitStruct.I2C_OwnAddress1 = 0x00;          // own address
    I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;//disable ack when read
 I2C_InitStruct.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;
    I2C_Init(I2C1, &I2C_InitStruct);                // init I2C1
  
    // enable I2C1
    I2C_Cmd(I2C1, ENABLE);
}

MinIMU_V2:
#include "minimu_v2.h"
#include "l3gd20.h"
#include "math.h"
 
AxesRaw_t Gyro;
 
void MinIMU_V2_Init() {
 
    //L3GD20 Initialization
    L3GD20_Init();
    //LSM303DLHC Initialization
    //LSM303DLHC_Init();
}
 
void AHRS() {
 
    L3GD20_GetAngRateRaw(&Gyro);
}

L3GD20:
#include "l3gd20.h"
#include "stm32f4xx_i2c.h"
#include "math.h"
 
/**********************************************************************
Master |ST|   |SAD+W|   |REG|  |SR|   |SAD+R|       |NMAK|SP|         
Slave     |EV5|     |EV6|  |EV8|  |EV5|     |EV6|EV7|DATA|  |
***********************************************************************
*      Transmitter Mode                 Receiver Mode                 *
*SAD + W = Slave address|0x00       SAD + R = Slave Address|0x01    
**********************************************************************/
status_t L3GD20_ReadReg(u8_t Reg, u8_t* Data) {
   
    //Wait until I2C1 is not busy any more
    while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
 
    //Re-enable acknowledge
    I2C_AcknowledgeConfig(I2C1, ENABLE);
 
    //Send I2C1 START condition
    I2C_GenerateSTART(I2C1, ENABLE);
    //Wait for I2C1 EV5 --> Slave has acknowledged start condition
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
 
    //Send slave Address for write
    I2C_Send7bitAddress(I2C1, L3GD20_MEMS_I2C_ADDRESS, I2C_Direction_Transmitter);
    //Wait for I2C1 EV6, check if slave has acknowledged Master transmitter
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
 
    //Send address of register
    I2C_SendData(I2C1, Reg);
    //Wait for I2C1 EV8, check if slave has acknowledged Master Transmitter
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING));
 
    //Send START condition a second time (Re-Start)
    I2C_GenerateSTART(I2C1, ENABLE);
    //Wait for I2C1 EV5 --> Slave has acknowledged start condition
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
 
    //Send slave Address for read
    I2C_Send7bitAddress(I2C1, L3GD20_MEMS_I2C_ADDRESS, I2C_Direction_Receiver);
    //Wait for I2C1 EV6, check if slave has acknowledged Master Receiver
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
     
    //Read data from I2C data register and return data byte
    *Data = I2C_ReceiveData(I2C1);
    //Wait for I2C1 EV7 --> One byte has been received
    while( !I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED) );
 
    //Send NMAK
    I2C_AcknowledgeConfig(I2C1, DISABLE);
    I2C_GenerateSTOP(I2C1, ENABLE);
    //stop bit flag
    while(I2C_GetFlagStatus(I2C1, I2C_FLAG_STOPF));
     
  return MEMS_SUCCESS;
}
 
/**********************************************************************
Master |ST|   |SAD+W|   |REG|     |DATA|   |SP| 
Slave     |EV5|     |EV6|   |EV8_2|    |EV5|   
***********************************************************************
*   Transmitter Mode                      Receiver Mode                
*SAD + W = Slave address|0x00     SAD + R = Slave Address|0x01     *
**********************************************************************/
status_t L3GD20_WriteReg(u8_t Reg, u8_t Data) {
     
    //I2C Write Sequence
 
    //Wait until I2C1 is not busy anymore
    while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
 
    // Send I2C1 START condition
    I2C_GenerateSTART(I2C1, ENABLE);
    //Wait for I2C1 EV5 --> Slave has acknowledged start condition
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
 
    //Send slave Address for write
    I2C_Send7bitAddress(I2C1, L3GD20_MEMS_I2C_ADDRESS, I2C_Direction_Transmitter);
    //Wait for I2C1 EV6, check if slave has acknowledged Master transmitter
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
 
    //Mode register address
    I2C_SendData(I2C1, Reg);
    //wait for byte send to complete
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
     
    //wait for byte send to complete
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
    //clear bits
    I2C_SendData(I2C1, Data);
     
    //generate stop
    I2C_GenerateSTOP(I2C1, ENABLE);
    //stop bit flag
    while(I2C_GetFlagStatus(I2C1, I2C_FLAG_STOPF));
 
    return MEMS_SUCCESS;
}
 
status_t L3GD20_Init(){
 
    L3GD20_SetODR(L3GD20_ODR_760Hz_BW_100);
    L3GD20_SetMode(L3GD20_NORMAL);
    L3GD20_SetAxis(L3GD20_X_ENABLE | L3GD20_Y_ENABLE | L3GD20_Z_ENABLE);
    L3GD20_SetFullScale(L3GD20_FULLSCALE_500);
    L3GD20_SetBDU(ENABLE);
    L3GD20_SetBLE(L3GD20_BLE_MSB);
    L3GD20_FIFOModeEnable(L3GD20_FIFO_DISABLE);
 
  return MEMS_SUCCESS;
}

status_t L3GD20_GetAngRateRaw(AxesRaw_t* buff) {

  //L3GD20_ReadBuffer(L3GD20_OUT_X_L, buff);

    u8_t valueL;
    u8_t valueH;

    if( !L3GD20_ReadReg(L3GD20_OUT_X_L, &valueL) )
        return MEMS_ERROR;

    if( !L3GD20_ReadReg(L3GD20_OUT_X_H, &valueH) )
        return MEMS_ERROR;

    buff->AXIS_X = (i16_t)( (valueH << 8) | valueL );

    if( !L3GD20_ReadReg(L3GD20_OUT_Y_L, &valueL) )
        return MEMS_ERROR;

    if( !L3GD20_ReadReg(L3GD20_OUT_Y_H, &valueH) )
        return MEMS_ERROR;

    buff->AXIS_Y = (i16_t)( (valueH << 8) | valueL );

    if( !L3GD20_ReadReg(L3GD20_OUT_Z_L, &valueL) )
        return MEMS_ERROR;

    if( !L3GD20_ReadReg(L3GD20_OUT_Z_H, &valueH) )
        return MEMS_ERROR;

    buff->AXIS_Z = (i16_t)( (valueH << 8) | valueL );

    if( !L3GD20_ReadReg(L3GD20_CTRL_REG1, &valueH) )  /*this was for debugging, just to see if I could read what*/
            return MEMS_ERROR;                        /*was actually set in Ctl reg 1, again it was 0xD7*/       

      return MEMS_SUCCESS;
}
When watching valueL and valueH in the L3GD20_GetAngRateRaw function during debugging, all i ever get back is 0xD7.


Of course there is more to the last file, but it would be a lot to post. I just wanna make sure my read and write sequences are correct then I can move on to any other issues that could be in there.

Anyone see any issues here?

Outcomes