Top.Mail.Ru

STM32 and L3GD20. Three-axis digital output gyroscope.

Today I would like to talk with you about the L3GD20 digital gyroscope. We will discuss the main features of this sensor and create an example project for STM32 microcontroller.

L3GD20 – is a small chip, produced by the STMicroelectronics. It can measure the three-axis angular rate and provide it through a digital interface – I2C or SPI. This sensor is mounted on the STM32F3Discovery board, so I’ll use it for this project. L3GD20 is connected to the MCU via SPI interface:

L3GD20 gyroscope.

The serial interface interacts with the outside world through 4 wires: CS, SPC, SDI and SDO. Here is the data exchange protocol:

SPI frame format.

Chip Select (CS) signal goes low at the start of the transmission and goes high at the end. When the CS is high the master (MCU) should provide the Serial Port Clock signal (SPC). This is quite clear 🙂 Let’s proceed to the data format.

The first transmitted bit is R/W bit:

  • If it is set to 1, we will read the measured data from the sensor
  • If we’d like to write some data to the L3GD20, we should reset this bit.

The second bit is MS. When it is set to 1 the address will be auto-incremented in multiple read/write commands. For example, we’d like to transmit the write command consisting of the following parts:

  • The address value which equals 0x01
  • MS bit is set
  • Five data bytes

As a result of transmitting this command, the data will be written to the registers with addresses 0x01/0x02/0x03/0x04/0x05.

The next six bits in the data frame are the address bits of the indexed register. And the following bytes are the data that will be written/read to/from the device. So, the protocol is clear, let’s proceed to the creating of the project! Please note, that I’ll use Standard Peripheral Library (SPL) and CMSIS in this project. Thus, all the library files should be added to the project.

L3GD20 registers are shown in the following table:

Gyroscope registers.

In order to turn on the sensor we should write the byte 0x0F to the register CTRL_REG1. After that, let’s read the data from any register and check it's value. For instance, we can take WHO_AM_I register. It's value should be equal 0xD4.

Let’s create the new project and add library files:

/**************************************************************************************************/
#include "stm32f30x_gpio.h"
#include "stm32f30x_rcc.h"
#include "stm32f30x_spi.h"
#include "stm32f30x.h"


/**************************************************************************************************/

Then let’s declare all variables:

/**************************************************************************************************/
#define DUMMY                                                    0x00
#define TIMEOUT_TIME                                             0x1000

SPI_InitTypeDef spi;
GPIO_InitTypeDef gpio;
uint8_t sendData;
uint8_t receiveData[8];;
uint16_t timeout;
uint8_t tempByte;


/**************************************************************************************************/

The next step is to create the initialization function:

/**************************************************************************************************/
void initAll()
{
	// RCC settings
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

	// PA5, PA6 and PA7 are SPI pins
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_5);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_5);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_5);

	gpio.GPIO_Mode = GPIO_Mode_AF;
	gpio.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
	gpio.GPIO_OType = GPIO_OType_PP;
	gpio.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &gpio);

	// Chip Select pin
	gpio.GPIO_Mode = GPIO_Mode_OUT;
	gpio.GPIO_Pin = GPIO_Pin_3;
	gpio.GPIO_OType = GPIO_OType_PP;
	gpio.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOE, &gpio);

	// Configure and enable the SPI peripheral
	spi.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	spi.SPI_DataSize = SPI_DataSize_8b;
	spi.SPI_CPOL = SPI_CPOL_High;
	spi.SPI_CPHA = SPI_CPHA_2Edge;
	spi.SPI_NSS = SPI_NSS_Soft;
	spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
	spi.SPI_FirstBit = SPI_FirstBit_MSB;
	spi.SPI_CRCPolynomial = 7;
	spi.SPI_Mode = SPI_Mode_Master;
	SPI_Init(SPI1, &spi);

	SPI_Cmd(SPI1, ENABLE);

	SPI_RxFIFOThresholdConfig(SPI1, SPI_RxFIFOThreshold_QF);

	SPI_DataSizeConfig(SPI1, ENABLE);
}


/**************************************************************************************************/

Now, we need some functions in order to write and read gyroscope data:

/**************************************************************************************************/
uint8_t sendByte(uint8_t byteToSend)
{
	timeout = TIMEOUT_TIME;
	while ((SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) & (timeout != 0))
	{
		timeout--;
	}

	SPI_SendData8(SPI1, byteToSend);

	timeout = TIMEOUT_TIME; 
	while ((SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) & (timeout != 0))
	{
		timeout--;
	}

	return (uint8_t)SPI_ReceiveData8(SPI1);
}


/**************************************************************************************************/
void writeData(uint8_t address, uint8_t dataToWrite)
{
	GPIO_ResetBits(GPIOE, GPIO_Pin_3); 
	sendByte(address);
	sendByte(dataToWrite);
	GPIO_SetBits(GPIOE, GPIO_Pin_3);
}


/**************************************************************************************************/
uint8_t readData(uint8_t address)
{
	GPIO_ResetBits(GPIOE, GPIO_Pin_3); 
	sendByte(address);
	tempByte = sendByte(DUMMY);
	GPIO_SetBits(GPIOE, GPIO_Pin_3);
	return tempByte; 
}


/**************************************************************************************************/

The first function named sendByte() will be used both in the write and read functions. Please note the using of the extra timeout in the while() cycle. It is done in order to prevent the program freezing.

In the writeData() function we transmit the address byte (including bits R/W and MS) and the data byte. Don’t forget to set Chip Select low before transmission and to set it high after it.

The readData() function is a lot like the writeData() function, but there is one important difference. It’s not enough to send the address byte, the MCU should also provide the clock signal in order to read the data from the sensor. To do this we should send a dummy byte (0x00) to the sensor.

Now, it’s time to add the main() function to our project:

/**************************************************************************************************/
int main()
{
	initAll();
	writeData(0x20, 0x0F);

	while(1)
	{
		receiveData[0] = readData(0x8F);
	}
}


/**************************************************************************************************/

Firstly, we initialize the gyroscope and, secondly, read the data from it in the while() cycle. Let’s compile the project and program the MCU. After that, we can start the debug session and check the read data:

L3GD20 and STM32.

As you see the value is correct! We’ll continue testing the L3GD20 gyroscope in the future posts, so subscribe to the MicroTechnics and visit our site again! 🙂

Подписаться
Уведомить о
guest

1 Комментарий
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
duc
duc
4 лет назад

I do not understand where the address bit of L3GD20 is 7 bits but in the SPI frame the address is only 6 bits

1
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x