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]).
In this section, functions to initialize I2C1 will be created along with functions to read and write devices on the bus.
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOX, ENABLE);
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.
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;
}
I2C_SendData(I2C1,reg);
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);
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);
I2C_SendData(I2C1,reg);
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.
The accelerometer measures acceleration in the X, Y, and Z axis in G - where 1G is the resting force due to gravity.
The initialization will write 3 control registers within the accelerometer.
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;
accel_data[i]=(float)raw_data[i]/1000.0;
}
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.
The initialization will write 3 control registers within the accelerometer.
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;
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.