Skip to main content
Visitor II
September 25, 2020
Solved

Reading data from L3GD20 (SPI)

  • September 25, 2020
  • 3 replies
  • 7802 views

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)

    This topic has been closed for replies.
    Best answer by Eleon BORLINI

    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

    3 replies

    ST Employee
    September 25, 2020

    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

    Ala1980Author
    Visitor II
    September 27, 2020

    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?

    ST Employee
    September 28, 2020

    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

    Ala1980Author
    Visitor II
    October 4, 2020

    I added the following lines for initializing the Gyro, but got also unexpected readings:

    ctrl1 = (uint8_t) (L3GD20_MODE_ACTIVE | L3GD20_OUTPUT_DATARATE_4 |

    L3GD20_AXES_ENABLE | L3GD20_BANDWIDTH_4);

    ctrl4 = (uint8_t) (L3GD20_BlockDataUpdate_Single | L3GD20_BLE_LSB | L3GD20_FULLSCALE_250);

    GYRO_IO_Write(&ctrl1, L3GD20_CTRL_REG1_ADDR, 1);

    GYRO_IO_Write(&ctrl4, L3GD20_CTRL_REG4_ADDR, 1);

    The problem is now, I read varying values on the axes even if the board is completely stable (and the readings vary in a very big range (-300 -> +300).

    Is there any declaration for this?

    Thanks in advance

    ST Employee
    October 8, 2020

    Hi @Ala1980​ ,

    glad to hear you did some progress.

    Are you correctly interpreting the received data? The following one is the conversion formula (see l3gd20h_reg.c:(

    float_t l3gd20h_from_fs245_to_mdps(int16_t lsb)
    {
     return ((float_t)lsb * 8.75f);
    }

    Let me please know.

    -Eleon

    Ala1980Author
    Visitor II
    October 8, 2020

    But multiplying the readings by 8 will increase the problem!

    So, my problem was that as if the sensor is too much sensitive.

    Even when the board is stable on a table, I see that the reading is varying too much (between -300 -> +300).

    @Eleon BORLINI​ Could you please explain why you want me to multiply the reading by 8.75?

    Ala1980Author
    Visitor II
    November 16, 2020

    I understand that the gyroscope measures the change in angle on each axis, in other words it measures the angular speed in deg/sec (or in other scale).

    My question is, in case the gyroscope was rotating with 5 deg/sec on T0 and then with 3 deg/sec on T1 and then stopped rotating (i.e. on T2 was is rotating 0 deg/sec).

    So in my application if my sampling rate is equal to T2-T0 and if my first measurement was at time T0 = 5 deg/sec then what would I read at next sample (on T2), would I read 0?

    Acutally this is not so drastic if I average the speed values.

    More drastic if the rotation is only between my samples, so in the example above if it was rotating 0 deg/sec on T0, 5 deg/sec on T1 and 0 deg/sec on T2, then the readings will tell me that it is stationary but it rotates actually!!

    Is there any explanation pleas? Or maybe I don't understand the whole issue!!

    ST Employee
    November 17, 2020

    Hi @Ala1980​ ,

    To obtain degrees from deg/sec you correctly have to integrate the angular speed on time. It is suggested that T1-T0 = 1/ODR.

    How to do this, it depend on a number of condition. The simplest way could be -for example- integrating 5 deg/s from T0 to T1 (so you calculate the area of the rectangle with sides T1-T0 and 5deg/s), and then you sum all the ; or you can linearly interpolate and average the T0 and T1 angular speed value, in this case 4 deg/s, and again integrate it in the T1-T0 time. For the sign, if you are not changing the rotational direction (i.e. you are not crossing the line of 0deg/s), you have to sum the areas with the same sign, the one of the output.

    As important side note, you should also run periodical calibration of the gyroscope, and for this scope you could use the MotionGC library described here.

    -Eleon

    Ala1980Author
    Visitor II
    November 17, 2020

    Thanks for the reply, I understand that I need to integrate the speed in order to get the angular position (degree).

    From your reply I can understand that the gyroscope delivers really the current angular speed (exactly on the moment of reading), and it doesn't care whether the previous angular speed has been read or not!

    Maybe I should enlarge the time span in order to convey my idea: So if my sampling rate is 1 minute, and my initial degree is 90°. So I read the gyro once at 13:05:00 (HH:MM:SS) and once at 13:06:00 but the object was only rotating in the time range 13:05:10 till 13:05:50, then, according to my understanding my both readings will give me 0 deg/sec, and as a result I will integrate the 0 value, consequently my calculation will tell me that the object is still on 90° (which is incorrect because I missed the rotation between 13:05:10 till 13:05:50.

    Is the goal of MotionGC just to overcome the bias problem of gyro or it does also fusing the sensor data of Acc + Gyro? (Something like Kalman filter)?

    Is there any alternative for MotionGC for STM32F3 Discovery? Or at least another software/way for continuously reading of gyro data into my PC?