HW6 - ndm736/ME433_2020 GitHub Wiki

An IMU, or inertial measurement unit, is a sensor that is used to calculate position, velocity, or acceleration. Modern IMUs are MEMS devices, with built in analog to digital converters, and their own dedicated microcontroller to control and process the data (example from Digikey).

Common IMUs combine accelerometers, gyroscopes, magnetometers, and altimeters. Often they come packaged together in a single chip (although altimeters are still new and less common). The sensors typically communicate over I2C or SPI.

We will use the LSM6DS33 accelerometer and gyroscope chip made by ST, soldered onto a breakout board by Pololu.

This chip measures acceleration (accelerometer) and angular velocity (gyroscopic) data about the X, Y, and Z axes with 16 bit resolution, with a variety of sensitivities. It contains a temperature sensor so that it can self correct for temperature effects, and we can also access the temperature data as a 16 bit number.

The LSM6DS33 can communicate over I2C or SPI. We will use I2C to reduce the number of pins used from the PIC.

Pololu has made it easy to hook up the chip. Connect 3.3V to VIN, GND to GND, SDA1 to SDA, and SCL1 to SCL. The 10k pull up resistors are built onto the breakout board, so you don't have to add them. The Pololu PCB has a pull up resistor on the SA0 pin, so the 7 bit address of the chip is 0b1101011. The maximum baud is 400kHz (maybe we can overclock this to keep our display FPS high).

Initialize I2C like you did for the IO pin expander. The IMU has a register called WHO_AM_I, which contains the constant value 0b01101001 (decimal 105). Read from WHO_AM_I and check that you get the right value back, this will check that your I2C bus is working. If you don't get the right value back, go into an infinite loop and blink your LED so that you know you need a power reset. If you do get the right value back, start your LED heartbeat and move on.

Write to several registers to initialize the chip. Once initialized, you can read from the data registers to figure out the acceleration, angular velocity, and temperature of the chip.

The first challenge is to figure out which registers to write to to initialize the chip. This chip has many cool features, but we just want the bare bones ability to read the three acceleration, three angular velocity, and temperature data at any time. Out of the dozen registers, we only need to change three.

To turn on the accelerometer, write to the CTRL1_XL register. Set the sample rate to 1.66 kHz, with 2g sensitivity, and 100 Hz filter.

To turn on the gyroscope, write to the CTRL2_G register. Set the sample rate to 1.66 kHz, with 1000 dps sensitivity.

The last register to change is CTRL3_C, which contains the IF_INC bit. Making the IF_INC bit a 1 will enable the ability to read from multiple registers in a row without specifying every register location, saving us time when reading out all of the data.

With IF_INC set, we can read every register from OUT_TEMP_L to OUTZ_H_XL in a single read, because they are sequential in the IMU's memory.

Write a I2C_read_multiple() function. The prototype should look like:

void I2C_read_multiple(unsigned char address, unsigned char register, unsigned char * data, int length)

The I2C_read_multiple() function works just like reading from the IO pin expander, except the i2c_master_recv() function should be in a for loop, length times. After every recv, do a i2c_master_ack(0), to let the chip know you want to continue to read. The very last read should use a i2c_master_ack(1), to let the chip know you are finally done reading.

Once you have your unsigned char array, reconstruct the signed short value by shifting the high byte and ORing it with the low byte.

Call the function with the 7 bit write address of the chip (both the write and read address will be used, we'll add the 1 at the end to make the read address from the write address) and the OUT_TEMP_L register. Send the pointer to an array of unsigned chars as data, and the size of the array as length. There are 14 8 bit bytes to read, to recombine into 7 16 bit shorts containing the temperature, three axes of gyroscope data, and three axes of accelerometer data, so make the data array have 14 values, and send 14 as the length.

Also note, if you wanted to read only the accelerometer data, you could use the same function, sending the address, the OUTX_L_XL register, and an array with a length of 6.

Many I2C chips allow you to read data out like this, as long as the registers you need are sequential.

Print the 7 signed short values to your display and double check that they make sense. With a 2g sensitivity and Z facing up, the Z acceleration value should be about -16383, and the X and Y acceleration values should be about 0. When not moving, the X Y and Z gyro values should be about 0. The temperature value should increase if you touch the chip to warm it up, if you want you can do the math to display the temperature in degree C (a value of 0 is 25 deg C, a value of -32767 is -40 deg C, a value of 32767 is 85 deg C). All of the values should fluctuate a little, and have some slight bias. We will investigate these further later on.

The last thing to do is to make a graphic display to show how level the chip is (an inclinometer). Use the X and Y acceleration values to draw bars along a Cartesian map on your display. The larger the magnitude, the longer the bar, in the downwards direction. Read the IMU and update the display at least 20 times per second.

Put the code in a folder called HW6 in your repo, and make a short video demonstrating the inclinometer and upload to Canvas.