cancel
Showing results for 
Search instead for 
Did you mean: 

Reading data from L3GD20 (SPI)

Ala1980
Associate II

Hi,

did anyone try to connect to the gyro (L3GD20) on F303Discovery board over SPI?

I have a problem even with reading the WHO_AM_I register:

My code is generated with Cube and I added the following lines:

𝐮𝐢𝐧𝐭𝟖_𝐭 𝐝𝐚𝐭𝐚_𝐰𝐡𝐨𝐚𝐦𝐢, 𝐚𝐝𝐝𝐫𝐞𝐬𝐬_𝐰𝐡𝐨𝐚𝐦𝐢;

𝐚𝐝𝐝𝐫𝐞𝐬𝐬_𝐰𝐡𝐨𝐚𝐦𝐢 = 𝟎𝐱𝟎𝐟 | 𝟎𝐱𝟖𝟎; -- set RW

𝐇𝐀𝐋_𝐆𝐏𝐈𝐎_𝐖𝐫𝐢𝐭𝐞𝐏𝐢𝐧(𝐆𝐏𝐈𝐎𝐄,𝐆𝐏𝐈𝐎_𝐏𝐈𝐍_𝟑, 𝐆𝐏𝐈𝐎_𝐏𝐈𝐍_𝐑𝐄𝐒𝐄𝐓);--reset CS on SPI

𝐇𝐀𝐋_𝐒𝐏𝐈_𝐓𝐫𝐚𝐧𝐬𝐦𝐢𝐭𝐑𝐞𝐜𝐞𝐢𝐯𝐞(&𝐡𝐬𝐩𝐢𝟏, &𝐚𝐝𝐝𝐫𝐞𝐬𝐬_𝐰𝐡𝐨𝐚𝐦𝐢,&𝐝𝐚𝐭𝐚_𝐰𝐡𝐨𝐚𝐦𝐢,𝟐,𝟓𝟎);

𝐇𝐀𝐋_𝐆𝐏𝐈𝐎_𝐖𝐫𝐢𝐭𝐞𝐏𝐢𝐧(𝐆𝐏𝐈𝐎𝐄,𝐆𝐏𝐈𝐎_𝐏𝐈𝐍_𝟑, 𝐆𝐏𝐈𝐎_𝐏𝐈𝐍_𝐒𝐄𝐓);--set CS on SPI

Having that the 𝐌𝐗_𝐒𝐏𝐈𝟏_𝐈𝐧𝐢𝐭() is called before so that the SPI should be initialized correctly, but the problem I can't read the expected WHO_AM_I value (D4)

1 ACCEPTED SOLUTION

Accepted Solutions

Hi @Ala1980​ ,

you have to divide by 1000 to get dps instead of milli - dps (which is the unit of measure of the sensitivity). 300 LSB *8.45 means about 2500 mdps, i.e. 2.5 dps, which is the typical noise you have to expect from L3GD20 device, as you can see from the zero rate level parameter values in the datasheet, p. 9:

0693W000004JNhRQAW.png 

I can say you that the L3GD20 is not the best-in-class gyroscope for the noise purpose: you should switch to more recent products such as LSM6DSO family IMUs, to reduce noise by a 10 factor...

However, to reduce the noise in L3GD20 device, you may enable the high pass filter by setting 1 the HPen bit of CTRL_REG5 (24h) register and configuring the appropriate bits in CTRL_REG2 (21h) register.

-Eleon

View solution in original post

29 REPLIES 29
Eleon BORLINI
ST Employee

Hi @Ala1980​ ,

I suggest you to check the STM32CubeF3 package, especially the L3GD20-related functions you can find in the driver folder at Drivers\BSP\STM32F3-Discovery.

To have a more complete overview of an entire project including the gyroscope SPI communication and data acquisition, you may start from the Demo available in the projects folder at \Projects\STM32F3-Discovery\Demonstrations\SW4STM32\STM32F3-Discovery_Demo.

You may insert these lines in your code, or at least check if you are doing well in your code (both for the low-level SPI configuration and for the mid-level gyro read/write register functions):

/********************************* LINK GYROSCOPE *****************************/
/**
  * @brief  Configures the GYROSCOPE SPI interface.
  * @retval None
  */
void GYRO_IO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
  
  /* Configure the Gyroscope Control pins ------------------------------------------*/
  /* Enable CS GPIO clock and  Configure GPIO PIN for Gyroscope Chip select */  
  GYRO_CS_GPIO_CLK_ENABLE();  
  GPIO_InitStructure.Pin = GYRO_CS_PIN;
  GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStructure.Pull  = GPIO_NOPULL;
  GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GYRO_CS_GPIO_PORT, &GPIO_InitStructure);
 
  /* Deselect : Chip Select high */
  GYRO_CS_HIGH();
 
  /* Enable INT1, INT2 GPIO clock and Configure GPIO PINs to detect Interrupts */
  GYRO_INT_GPIO_CLK_ENABLE();
  GPIO_InitStructure.Pin = GYRO_INT1_PIN | GYRO_INT2_PIN;
  GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
  GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
  GPIO_InitStructure.Pull= GPIO_NOPULL;
  HAL_GPIO_Init(GYRO_INT_GPIO_PORT, &GPIO_InitStructure);
  
  SPIx_Init();
}
 
/**
  * @brief  Writes one byte to the GYROSCOPE.
  * @param  pBuffer pointer to the buffer  containing the data to be written to the GYROSCOPE.
  * @param  WriteAddr GYROSCOPE's internal address to write to.
  * @param  NumByteToWrite Number of bytes to write.
  * @retval None
  */
void GYRO_IO_Write(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite)
{
  /* Configure the MS bit: 
       - When 0, the address will remain unchanged in multiple read/write commands.
       - When 1, the address will be auto incremented in multiple read/write commands.
  */
  if(NumByteToWrite > 0x01)
  {
    WriteAddr |= (uint8_t)MULTIPLEBYTE_CMD;
  }
  /* Set chip select Low at the start of the transmission */
  GYRO_CS_LOW();
  
  /* Send the Address of the indexed register */
  SPIx_WriteRead(WriteAddr);
  
  /* Send the data that will be written into the device (MSB First) */
  while(NumByteToWrite >= 0x01)
  {
    SPIx_WriteRead(*pBuffer);
    NumByteToWrite--;
    pBuffer++;
  }
  
  /* Set chip select High at the end of the transmission */ 
  GYRO_CS_HIGH();
}
 
/**
  * @brief  Reads a block of data from the GYROSCOPE.
  * @param  pBuffer pointer to the buffer that receives the data read from the GYROSCOPE.
  * @param  ReadAddr GYROSCOPE's internal address to read from.
  * @param  NumByteToRead number of bytes to read from the GYROSCOPE.
  * @retval None
  */
void GYRO_IO_Read(uint8_t* pBuffer, uint8_t ReadAddr, uint16_t NumByteToRead)
{  
  if(NumByteToRead > 0x01)
  {
    ReadAddr |= (uint8_t)(READWRITE_CMD | MULTIPLEBYTE_CMD);
  }
  else
  {
    ReadAddr |= (uint8_t)READWRITE_CMD;
  }
  /* Set chip select Low at the start of the transmission */
  GYRO_CS_LOW();
  
  /* Send the Address of the indexed register */
  SPIx_WriteRead(ReadAddr);
  
  /* Receive the data that will be read from the device (MSB First) */
  while(NumByteToRead > 0x00)
  {
    /* Send dummy byte (0x00) to generate the SPI clock to GYROSCOPE (Slave device) */
    *pBuffer = SPIx_WriteRead(DUMMY_BYTE);
    NumByteToRead--;
    pBuffer++;
  }
  
  /* Set chip select High at the end of the transmission */ 
  GYRO_CS_HIGH();
}  
#endif /* HAL_SPI_MODULE_ENABLED */
 
#ifdef HAL_I2C_MODULE_ENABLED

-Eleon

Thanks for the reply, I added the code lines I needed also to define some functions manually (GYRO_IO_Read, SPIx_WriteRead, GYRO_IO_Init, SPIx_Init, SPIx_MspInit) but I am still unable to read the WHO_AM_I register!

Could you please tell me who I can tell the CubeMX to import me the necessary drivers (of L3GD20 and LSM303DLHC) while creating a new project?

Hi @Ala1980​ ,

you can for example check this tutorial to have an idea of how to import predefined libraries in a CubeMX project.

Moreover, I added STM32CubeMX tag for more help from this topic's experts.

-Eleon

Thanks for the reply,

my question was, since I am using Cube to generate my code, and since I asked Cube to set the default pin and peripheral settings, why Cube didn't imported all the necessary driver files to my project? (the following files for example: "stm32f3_discovery.c", "l3gd20.c", "stm32f3_discovery_accelerometer.c", "stm32f3_discovery_gyroscope.c")

What is the best way to let Cube import all these files in a clean and smooth way?

After manually adding the necessary driver files, I could compile the project and could at least call those functions: GYRO_IO_Init(), L3GD20_ReadID().

The problem is that I dont get the correct value of who_am_i from the function L3GD20_ReadID(), so I get 0x0F instead of 0xD4

It is like you are reading the register of the WHO_AM_I (0x0F) instead of the content... Can you check with reading the content of other registers, for example the CTRL_REG1 (20h) one? The content should be 0x07.

-Eleon

Thanks for the reply.

I dont know what was my mistake, but now I can read the correct value of the register (and not the address), also when I read the CTRL_REG1 then I get the correct value of 0x07.

My next problem is in reading the gyro values, so I am using the following block in main() inside main.c:

float AngleRates[3] = {0};

float AngleRateX, AngleRateY, AngleRateZ;

 while (1)

 {

L3GD20_ReadXYZAngRate(AngleRates);

AngleRateX = AngleRates[0];

AngleRateZ = AngleRates[1];

AngleRateY = AngleRates[2];

 }

I got always a fix reading in AngleRateX, AngleRateY, AngleRateZ (-0.49, -0.65, -1.33), and these readings dont change when I move/rotate the board!

Your futher help is highly appreciated.

Hi @Ala1980​ ,

for this purpose, I suggest you to check the C drivers on Github for the L3GD20 (see l3gd20h_read_data_polling.c)

In particular, before reading the output values you have to configure the device with the following procedure:

/* Restore default configuration */
  l3gd20h_dev_reset_set(&dev_ctx, PROPERTY_ENABLE);
  do {
    l3gd20h_dev_reset_get(&dev_ctx, &rst);
  } while (rst);
 
  /* Enable Block Data Update */
  l3gd20h_block_data_update_set(&dev_ctx, PROPERTY_ENABLE);
 
  /* Set full scale */
  l3gd20h_gy_full_scale_set(&dev_ctx, L3GD20H_2000dps);
 
  /* Set Output Data Rate / Power mode */
  l3gd20h_gy_data_rate_set(&dev_ctx, L3GD20H_50Hz);

-Eleon

Hi @Ala1980​ ,

please let me know in case of positive or negative feedback.

-Eleon