Lab 4


In this lab, you will be developing a driver for the ST LSM303DLHC 3-Axis Accelerometer and Magnetometer. This will initially involve bringing up the I2C communication interface connected to this device. One communication is established, data can be read from both the accelerometer and magnetometer which appear as separate I2C devices within the same package.

Drivers will be added for i2c (ds_i2c.c[h]), the accelerometer (ds_accel.c[h]), and the magnetometer (ds_mag.c[h]).

I2C (ds_i2c.c[h])

In this section, functions to initialize I2C1 will be created along with functions to read and write devices on the bus.

I2C Functions

Initialization: ds_i2c1_init();

I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_AnalogFilter = I2C_AnalogFilter_Enable;
I2C_InitStructure.I2C_DigitalFilter = 0x00;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_Timing = 0x00902025;
I2C_Init(I2C1, &I2C_InitStructure);
I2C_Cmd(I2C1, ENABLE);


The I2C involves the following steps. Many of the steps involve data being read or written programmatically. However, some steps such as acknowledgement and multiple byte handled automatically by the I2C peripheral hardware. A STM32 peripheral library function named I2C_TransferHandling will be used to manage the state of the I2C peripheral. This function manipulates to the I2C CR2 register.

This diagram shows the I2C bus transfers for the different types of transactions.

Read: void ds_i2c1_read(uint8_t device, uint8_t reg, uint8_t* buffer, uint16_t numbytes);

This function will read multiple bytes into buffer from the I2C slave specified by device starting at the register address specified by reg.

 while (I2C_GetFlagStatus(I2C1, I2C_ISR_BUSY) != RESET); 
I2C_TransferHandling(I2C1, device, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
while (I2C_GetFlagStatus(I2C1, I2C_ISR_TXIS) == RESET);
if (numbytes>1) {
  reg |=0x80;                                                                             
while (I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET); 
I2C_TransferHandling(I2C1, device, numbytes, I2C_AutoEnd_Mode, I2C_Generate_Start_Read);
while (numbytes--) {
  while(I2C_GetFlagStatus(I2C1, I2C_ISR_RXNE) == RESET);
  *buffer++ = I2C_ReceiveData(I2C1);
while (I2C_GetFlagStatus(I2C1, I2C_ISR_STOPF) == RESET); 
I2C_ClearFlag(I2C1, I2C_ICR_STOPCF);

Write: void ds_i2c1_write(uint8_t device, uint8_t reg, uint8_t* value);

This function will write a single byte to the device. It writes only a single byte because, it will be used to write one byte configuration registers within the the accel and mag.

while (I2C_GetFlagStatus(I2C1, I2C_ISR_BUSY) != RESET);
I2C_TransferHandling(I2C1, device, 1, I2C_Reload_Mode, I2C_Generate_Start_Write);
while (I2C_GetFlagStatus(I2C1, I2C_ISR_TXIS) == RESET);
while(I2C_GetFlagStatus(I2C1, I2C_ISR_TCR) == RESET);
I2C_TransferHandling(I2C1, device, 1, I2C_AutoEnd_Mode, I2C_No_StartStop);
while(I2C_GetFlagStatus(I2C1, I2C_ISR_TXIS) == RESET);
I2C_SendData(I2C1, *value);
while(I2C_GetFlagStatus(I2C1, I2C_ISR_STOPF) == RESET);
I2C_ClearFlag(I2C1, I2C_ICR_STOPCF);

This completes the initialization of the I2C peripheral and the creation of the bus read and write functions. These function will now be used to initialize and read data from the accelerometer and magnetometer.

Accelerometer Device 0×32 (ds_accel.c[h])

The accelerometer measures acceleration in the X, Y, and Z axis in G - where 1G is the resting force due to gravity.

Initialization: void ds_accel_init()

The initialization will write 3 control registers within the accelerometer.

Reading Data: void ds_accel_read(float *accel_data)

Similar to the gyro, the accelerometer store the data for each axis in two adjacent bytes. The data for the X axis starts at register 0×28. The 6 bytes will be read and converted into a floating point number based on the full-scale range of the device.

for (i=0; i<3; i++) {
  raw_data[i]=((int16_t)((uint16_t)buffer[2*i+1] << 8) + buffer[2*i])/(uint8_t)16;

Magnetometer Device 0×3C (ds_mag.c[h])

The magnetometer measures the magnetic field in the X, Y, and Z axis in Gauss. A typical use for this type of sensor is to measure the earth's magnetic field in a compass application.

Initialization: void ds_mag_init()

The initialization will write 3 control registers within the accelerometer.

Reading Data: void ds_mag_read(float *accel_data)

The magnetometer is similar to the gyro and accel in the way that it stores the axis data in 6 registers. However, they added a wrinkle and placed the Z axis between X and Y. Additionally, a different conversion factor is used for the Z axis.

ds_i2c1_read(0x3C, 0x03, buffer,2);   // Read X Axis
ds_i2c1_read(0x3C, 0x07, buffer+2,2); // Read Y Axis
ds_i2c1_read(0x3C, 0x05, buffer+4,2); // Read Z Axis (notice that Z is out of order in the chip).
for (i=0; i<2; i++) {
  mag_data[i]=(float)((int16_t)(((uint16_t)buffer[2*i] << 8) + buffer[2*i+1]))/230;
mag_data[2]=(float)((int16_t)(((uint16_t)buffer[4] << 8) + buffer[5]))/205;


What to Turn in:

This assignment is due by midnight on 2/7. 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.