cancel
Showing results for 
Search instead for 
Did you mean: 

Receiving data from L3GD20 gyroscope sensor via SPI

TLee.15
Associate II

I'm trying to understand how SPI works on STM32. I use a STM32L472-discovery kit which includes a L3GD20 gyroscope sensor. On the board, STM32L4 is connected to the sensor via SPI2. I configured the GPIO pins needed for SPI2 and provided clocks to the GPIO, SPI units. Below are some of my settings. Note that I'm not suing the HAL library, as it has too many, complex codes.

SPIx->CR1 |= SPI_CR1_CPOL | 
                 SPI_CR1_CPHA |
                 SPI_CR1_BR_1 |
                 SPI_CR1_SSM  |
                 SPI_CR1_SSI  |
                 SPI_CR1_MSTR; // STM32 is the master device.
    SPIx->CR1 &= ~SPI_CR1_LSBFIRST; // MSB first
 
    /* Data length is 8-bit*/ 
    SPIx->CR2 &= ~(0b1111 << SPI_CR2_DS_Pos); // reset the 4 bits  
    SPIx->CR2 |= 0b0111 << SPI_CR2_DS_Pos;  
 
    /* motorola mode */ 
    SPIx->CR2 &= ~SPI_CR2_FRF;
 
    /* no NSS bit  (See Figure 14 in the datasheet) */
    SPIx->CR2 &= ~SPI_CR2_NSSP;
 
    /* RXFIFO threshold : half of 8-bit */
    SPIx->CR2 |= SPI_CR2_FRXTH; 
 
    SPIx->CR1 |= SPI_CR1_SPE;

After initializing the MCU, I wanted to send 8-bit data to the sensor and then read the 8-bit received from the sensor. Next I tried to send 8-bit dummy data (0x27) to the sensor and read 8-bit received from the sensor. Below is the content of the Write_and_Read function that I'm using.

    uint8_t RW = 1;
    uint8_t MS = 0;
    uint8_t word = (RW << 7) | (MS << 6);
    uint8_t dummy[3] = {0,0,0};
    uint8_t dummy_2[10];
    uint8_t value[3] = {0,0,0};
    word = word + addr;  
  
    GPIOD->ODR &= ~(0b1 << 7);
    USART_Delay(30000);
    for (int k = 0; k < 10; k++) {
        dummy_2[k] = SPIx->DR;
    }
 
    int i = 0;
    int j = 0;
 
    while(!(SPI2->SR & SPI_SR_TXE));
    SPI2->DR = word;
    while(!(SPI2->SR & SPI_SR_TXE));
    while(SPI2->SR & SPI_SR_BSY);
    while(SPI2->SR & SPI_SR_RXNE){
        dummy[i] = SPI2->DR;
        i++;
    };
    while(SPI2->SR & SPI_SR_BSY);
 
    while(!(SPI2->SR & SPI_SR_TXE));
    SPI2->DR = word;
    while(!(SPI2->SR & SPI_SR_TXE));
    while(SPI2->SR & SPI_SR_BSY);
    while(SPI2->SR & SPI_SR_RXNE){
        value[i] = SPI2->DR;
        j++;
    }
    while(SPI2->SR & SPI_SR_BSY);
 
    /* copy the value to rBuffer */
    *rBuffer = value[i];
 
    USART_Delay(300);
    GPIOD->ODR |= (0b1 << 7);
 
    return 0;

When a 8-bit number 0x20 is sent, then right after the transmission I red 3 bytes from the RXFIFO. After sending 0x27, this function red 3 bytes again. Below are the sent data and received data. Note that the 0x27 is just dummy data for sending a SCLK signal to the sensor.

addr       value
0x20  -> 0xFF 0xFF 0x00 
0x27 -> 0x00 0x00 0xFF
0x0F -> 0xFF 0xD4 0x00 
0x27 -> 0x00 0x00 0xD4 
0x20 -> 0xFF 0x07 0x00 
0x27 -> 0x00 0x00 0x07 
0x0F -> 0xFF 0xD4 0x00
0x27 -> 0x00 0x00 0xD4 
0x20 -> 0xFF 0x07 0x00 
0x27 -> 0x00 0x00 0x07

I see a couple of problems.

(1) When a byte is sent, then 3 bytes are read from the RXFIFO buffer. The buffer is supposed to have only 1 byte, isn't it?

(The dummy_2 contains only zeros. So there was no data in RXFIFO before the data transmission.)

(2) When 0x0F is sent, then 0xD4 should be received. But this number appears two times among the 6 bytes received after sending 0x0F. When 0x20 is sent, I expect to receive 0x07. This number appears also two times among the next 6 bytes.

(3) For some reason, the first 3 bytes contain two 0xFF 0xFF bytes. This was reproduced many times. It happens once after each MCU reset.

Can someone suggest what might be wrong with my codes?

8 REPLIES 8
ssipa.1
Associate II

hi Tlee

MEMS sensor L3GD20 is included on STM32F429-Discovery board, there is a library to use it. This is 3-axis digital gyroscope, so it can measure rotation in X, Y and Z axis. L3GD20 sensor works with SPI communication, or I2C. On Discovery board is connected for SPI mode, SPI5 is used for communication.

L3GD20 can be set to work in 3 different full scales: 250, 500 and 2000 dps.

Sensor has also possibility to measure temperature, but this is not included in this library.

Hi ssipa.1,

I'm sorry that my initial post didn't have content. I was unfamiliar with the UI for this forum. Also it was impossible to delete the initial post. Now I have written my question.  

for me too its a bit hard to get a clear answer, however its a good to know.

TDK
Guru

> SPI2->DR = word;

This is a 32-bit access. If you want to only write a byte, then you need to use a cast to do so.

https://github.com/STMicroelectronics/STM32CubeL4/blob/f93a2f74f8e9912405dbf1a297b6df0c423eddf2/Drivers/STM32L4xx_HAL_Driver/Src/stm32l4xx_hal_spi.c#L930

*((__IO uint8_t *)&hspi->Instance->DR) = (*hspi->pTxBuffPtr);

> value[i] = SPI2->DR;

> j++;

Surely this should be value[j] and not value[i].

If you feel a post has answered your question, please click "Accept as Solution".

Thank you so much for your reply! Your comment explains why I received 3 bytes after SPI2->DR = word.

I changed line 19 and line 29 as below.

// line 19
 *((volatile uint8_t *)&SPI2->DR) = word;
 
// line 29 
 *((volatile uint8_t *)&SPI2->DR) = 0x27;
 

Also I fixed the type in 'value[i]'. Now the problems (1) and (2) have disappeared! I get only one byte from the sensor, after sending one byte to it.

TDK
Guru

Boil down problem 3 into a working example. You code doesn't include the logic that sends 0x20, for example, so hard to know where the bug is.

Hard to decipher at a glance what fixing (1) and (2) does to change the results you've posted.

If you feel a post has answered your question, please click "Accept as Solution".

Hi TDK,

here I post the changed codes and the main function, and the results.

First, I changed the codes as follows :

int read_Gyro_single_reg(SPI_TypeDef *SPIx, uint8_t addr, uint8_t *rBuffer){
    uint8_t RW = 1;
    uint8_t MS = 0;
    uint8_t word = (RW << 7) | (MS << 6);
    uint8_t dummy = 0;
    uint8_t value = 0;
    word = word + addr;
 
    Gyro_CS_on;
    USART_Delay(30000);
 
    while(!(SPI2->SR & SPI_SR_TXE));
    *((volatile uint8_t *)&SPI2->DR) = word;
    while(!(SPI2->SR & SPI_SR_TXE));
    while(SPI2->SR & SPI_SR_BSY);
    while(SPI2->SR & SPI_SR_RXNE){
        dummy = *((volatile uint8_t *)&SPI2->DR);
    }
    while(SPI2->SR & SPI_SR_BSY);
 
    while(!(SPI2->SR & SPI_SR_TXE));
    *((volatile uint8_t *)&SPI2->DR)= 0x00;
    while(!(SPI2->SR & SPI_SR_TXE));
    while(SPI2->SR & SPI_SR_BSY);
    while(SPI2->SR & SPI_SR_RXNE){
        value = *((volatile uint8_t *)&SPI2->DR);
    }
    while(SPI2->SR & SPI_SR_BSY);
 
    /* copy the value to rBuffer */
    *rBuffer = value;
 
    USART_Delay(300);
    Gyro_CS_off;
 
    return 0;
 
}

Then, in the main function I have these codes.

int main(void)
{
    SysClock_Init();
    GPIO_Init();
    UART2_Init();
    SPI2_Init();
 
    char buffer[100] = "abcde\n"; 
    uint8_t rBuffer = 0;
    uint8_t addresses[8] = {0x20, 0x0F, 0x20, 0x0F, 0x20, 0x0F, 0x20, 0x0F};
 
    while(1)
    {   
        if (GPIOA->IDR & (1U << 5)){ // BUtton PA5 pressed
            GPIOB->ODR |= 1U << 2; // PB2 LED on
            for (int i = 0; i < 8; i++) {
                read_Gyro_single_reg(SPI2, addresses[i], &rBuffer);
                snprintf(buffer, 35, "address : 0x%.2X, value : 0x%.2X \n", addresses[i], rBuffer);
                USART_Write_8bit(USART2, buffer, 35); 
            }   
        } else {
            GPIOB->ODR &= ~(1U << 2); 
        }   
 
        if (GPIOA->IDR & 1U << 2) {
            GPIOE->ODR |= 1U << 8;
        } else {
            GPIOE->ODR &= ~(1U << 8); 
        }   
    }   
}

UART2 is connected to my PC. When the program runs, and the button which is connected to PA5 is pressed, then the board sends the following strings to my PC. These results look normal except that the first value is 0xFF. The first value is supposed to be 0x07.

address : 0x20, value : 0xFF                                                                     
address : 0x0F, value : 0xD4                                                                     
address : 0x20, value : 0x07                                                                     
address : 0x0F, value : 0xD4                                                                     
address : 0x20, value : 0x07                                                                     
address : 0x0F, value : 0xD4                                                                     
address : 0x20, value : 0x07                                                                     
address : 0x0F, value : 0xD4

Interestingly, when I push the button again, then I see these results. There's one difference here. The first number received is not 0xFF but 0x07.

address : 0x20, value : 0x07                                                                     
address : 0x0F, value : 0xD4                                                                     
address : 0x20, value : 0x07                                                                     
address : 0x0F, value : 0xD4                                                                     
address : 0x20, value : 0x07                                                                     
address : 0x0F, value : 0xD4                                                                     
address : 0x20, value : 0x07                                                                     
address : 0x0F, value : 0xD4

I get 0xFF only once whenever I reset the discovery board.

TLee.15
Associate II

I changed the clock polarity and clock phase to zeros. Now the STM32 reads 0x07 instead of 0xFF as the first byte. This is the correct value corresponding to 0x20.

I think there are errors in the datasheet of L3GD20. In the document ST describes as if the correct clock polarity and phase are ones. But in fact those should be zeros. This is also reflected in their example codes included in STM32Cube. I'm reporting this issue to ST. But well it's a bit surprising. This component was released more than 5 years ago, so someone else might have reported it before.