Lab 3


In this lab, you will be developing a driver for the ST L3GD20 3-Axis Gyroscope. To communicate with this device, you will be using serial peripheral interface (SPI). Once communication is established, you will develop initialization code so that data from each axis can be read.


Performing the initialization on SPI1 follows the pattern that was used in lab1 for the UART. In this section of the lab, you will create void ds_gyro_interface_init(void) which will be called from void ds_gyro_init(void).

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 , ENABLE);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_RxFIFOThresholdConfig(SPI1, SPI_RxFIFOThreshold_QF);

SPI Primatives

There are several base functions that you will need to create to allow basic reading and writing of the gyro. The functions are the as follows:


You will need to control the chip selects directly. To assist in making your code more readable, create macros to set the chip select high and low using GPIO_ResetBits and GPIO_SetBits.

ds_gyro_sendbyte(uint8_t byte);

Similar to the functionality of the UART from lab1, you will need to poll the transmitter status prior to writing the byte to send to the transmitter register.

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); 
SPI_SendData8(SPI1, byte);

One Key difference between the UART and SPI, is that a byte is received for each one that is sent. You will need to poll to confirm that receiver has fully received the character prior to reading it.

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)
return (uint8_t)SPI_ReceiveData8(SPI1);

ds_gyro_read(uint8_t* pBuffer, uint8_t ReadAddr, uint16_t NumByteToRead);

This function needs to be able to handle the reading of multiple bytes from the interface. When we are talking to the gyro, we need to indicate the following related to the transaction.

if (NumByteToRead > 0x01) {
  ReadAddr |= (uint8_t)(0x80 | 0x40); // If sending more that one byte set multibyte commands
else {
  ReadAddr |= (uint8_t) (0x80); // Else just set the read mode 

The address now carries information about where to start the read and whether the gyro should expect to see addition bytes being read.

To start the transaction, the chip select goes low and the address byte is sent.


The gyro will now return the data from the previous transaction as each byte is clocked. We will clock out the dummy data just to cause the SPI to clock the SCLK pins.

while(NumByteToRead > 0x00) {
  *pBuffer = ds_gyro_sendbyte(((uint8_t)0x00));

To complete the transaction, we need to set the chip select high.


void ds_gyro_write(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite)

Similar to the read function, this function has the capability to handle multiple bytes. Below is the pseudocode for the function.

Gyro Initialization

The gyro has registers that can be written to setup the device. The device parameters are encoded within each control register. You will write a function that calls your interface initialization function from above and then write control register 1 and control register 4.

// CTRL1 Register 
// Bit 7:6 Data Rate: Datarate 0
// Bit 5:4 Bandwidth: Bandwidth 3
// Bit 3: Power Mode: Active
// Bit 2:0 Axes Enable: X,Y,Z enabled
ctrl1 |= (uint8_t) (((uint8_t)0x00) |\
                   ((uint8_t)0x30) |\
		   ((uint8_t)0x08) |\
// CTRL4 Register 
// Bit 7 Block Update: Continuous */
// Bit 6 Endianess: LSB first  */
// Bit 5:4 Full Scale: 500 dps */
ctrl4 |= (uint8_t) (((uint8_t)0x00) |\
                    ((uint8_t)0x00) |\

Getting Data from the Gyro: void ds_gyro_getdata(float *pfData)

The data for each axis is encoded in two bytes. These bytes are encoded in 6 successive bytes starting at address 0×28. The data can be read using:


The data needs to be combined into 16 bit words which can be accomplished using:

for(i=0; i<3; i++) {
  RawData[i]=(int16_t)(((uint16_t)tmpbuffer[2*i+1] << 8) + tmpbuffer[2*i]);

The raw data needs to be divided by the sensitivity value for the 500 dps setting that we applied when we initialized the gyro.

#define GYRO_Sensitivity_500dps   (float) 57.1429f
for(i=0; i<3; i++) {


Develop a system that allows a user to select a measurement axis using the user button. The system should output the current axis on the console along with the measured data. Additionally, each sample should be displayed on the LED display. Start by lighting the north led for values greater than 5 dgps and then lighting additional leds proportional to the rate with south led being the maximum rate. The idea is that the leds will act as a type of bar graph. Positive values should light the western side of the led circle and negative values should light the eastern side.

Along with being able to cycle through the X, Y, and Z axis with the user button, the user should also be able to type the letter of the axis into the console to select it. If the user then elects to use the button, the cycle should start at the current axis.

What to Turn in:

This assignment is due by midnight on 1/31. At midnight, your AI will pull the current state of your repository to review your code. In the following lab session, you will be asked to demonstrate your solution. Independently, you will also write a lab report describing in approximately 300 words or less the following:

You will turn in this lab report in the oncourse assignments section.