-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Created cpp and header files for the ICM42688. Code has been structured from the tested code found at https://github.com/UWARG/efs-kitchen-sinks/blob/main/icm42688.zip IMU Driver has been restructured with a virtual class header file, actual driver header file, and driver code. - Implemented a constructor to take in SPI_HANDLE, GPIO PORT, and CS_PIN - Implemented code to calibrate, read, and process data from the 6-axis IMU - Altered cmakelist to support compilation
- Loading branch information
1 parent
09a7bea
commit 10d952b
Showing
5 changed files
with
330 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
// icm42688.hpp | ||
|
||
#ifndef ICM42688_HPP | ||
#define ICM42688_HPP | ||
|
||
#include "imu_driver.hpp" | ||
#include <stdint.h> | ||
#include "stm32l5xx_hal.h" | ||
#include "stm32l5xx_hal_gpio.h" | ||
#include <stm32l552xx.h> | ||
|
||
#define GYRO_CALIBRATION_DATA_BUFFER_SIZE 14 //Size of buffer to store raw data from IMU, as there are 14 registers to be read from | ||
#define RAW_MEAS_BUFFER_SIZE 7 //Size of buffer to store raw measurements from IMU (3 accel, 3 gyrop, 1 temp) | ||
|
||
class ICM42688 : public IMUDriver { | ||
public: | ||
ICM42688(SPI_HandleTypeDef * spi_handle, GPIO_TypeDef * cs_gpio_port, uint16_t cs_pin); | ||
|
||
/** | ||
* Sets up the IMU's initial starting conditions by setting | ||
* the cs pin and calling reset(), setLowNoiseMode(), and calibrate() | ||
* | ||
* @return none | ||
*/ | ||
void beginMeasuring() override; | ||
|
||
/** | ||
* Calibrates the gyroscope. Algorithm adapted from https://github.com/finani/ICM42688/tree/master/src | ||
* | ||
* @return none | ||
*/ | ||
void calibrate() override; | ||
|
||
/** | ||
* Retrieve and process IMU accelerometer and gyroscope data into m/s and deg/s | ||
* | ||
* @param data_buffer -> array used to store and process raw data from IMU | ||
* | ||
* @return IMUData_t structure consisting of accelerometer and gyroscope data in m/s and deg/s | ||
*/ | ||
IMUData_t getResult(uint8_t * data_buffer) override; | ||
|
||
private: | ||
/** | ||
* Communicate with IMU via SPI to read raw data | ||
* | ||
* @param sub_address -> memory address of starting byte to be retrieved | ||
* @param num_bytes_to_retrieve -> number of bytes to be retrieved | ||
* @param destination -> array to be populated with raw data | ||
* | ||
* @return none | ||
*/ | ||
void readRegister(uint8_t sub_address, uint8_t num_bytes_to_retrieve, uint8_t * destination); | ||
|
||
/** | ||
* Communicate with IMU via SPI to write data onto IMU | ||
* | ||
* @param sub_address -> memory address of byte to be written on | ||
* @param data_to_imu -> data to be written onto IMU | ||
* | ||
* @return none | ||
*/ | ||
void writeRegister(uint8_t sub_address, uint8_t data_to_imu); | ||
|
||
/** | ||
* Resets the IMU's device configurations. Necessary to initilize IMU | ||
* | ||
* @return none | ||
*/ | ||
void reset(); | ||
|
||
/** | ||
* Sets the IMU's power management settings to low noise mode. Necessary to initilaize IMU | ||
* | ||
* @return none | ||
*/ | ||
void setLowNoiseMode(); | ||
|
||
/** | ||
* Sets the IMU register address bank to a value of 0 - 4 | ||
* | ||
* @param bank -> Bank number (0 - 4) | ||
* | ||
* @return none | ||
*/ | ||
void setBank(uint8_t bank); | ||
|
||
/** | ||
* Configures the full-scale range of the gyroscope on the IMU. Adapted from https://github.com/finani/ICM42688/tree/master/src | ||
* | ||
* @param fs_sel -> full-scale selection for the gyro | ||
* | ||
* @return none | ||
*/ | ||
void setGyroFS(uint8_t fs_sel); | ||
|
||
//Variables used in gyro calibration | ||
float gyroScale_ = 0; //gyro scale factor | ||
uint8_t currentFSSel_ = 0; //current full-scale selection for the gyro | ||
uint8_t gyroFS_ = 0; | ||
float gyroBD_[3] = {0, 0, 0}; | ||
float gyrB_[3] = {0, 0, 0}; | ||
float gyr_[3] = {0, 0, 0}; | ||
uint8_t gyro_buffer_[GYRO_CALIBRATION_DATA_BUFFER_SIZE]; | ||
int16_t raw_meas_gyro_[RAW_MEAS_BUFFER_SIZE]; | ||
|
||
//Used to hold raw IMU data | ||
int16_t raw_meas_[RAW_MEAS_BUFFER_SIZE]; | ||
|
||
SPI_HandleTypeDef * spiHandle_; | ||
GPIO_TypeDef * csGpioPort_; | ||
uint16_t csPin_; | ||
}; | ||
|
||
#endif //ICM42688_HPP |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// imu_driver.hpp | ||
|
||
#ifndef IMU_DRIVER_HPP | ||
#define IMU_DRIVER_HPP | ||
|
||
#include <stdint.h> | ||
|
||
struct IMUData_t { | ||
float gyrx, gyry, gyrz; | ||
float accx, accy, accz; | ||
}; | ||
|
||
class IMUDriver { | ||
public: | ||
/** | ||
* Initializes the IMU for sampling | ||
*/ | ||
virtual void beginMeasuring() = 0; | ||
|
||
/** | ||
* Calibrates IMU gyro sensor | ||
*/ | ||
virtual void calibrate() = 0; | ||
|
||
/** | ||
* Retrieves newest data stored by IMU | ||
*/ | ||
virtual IMUData_t getResult(uint8_t * data_buffer) = 0; | ||
|
||
virtual ~IMUDriver() {} | ||
}; | ||
|
||
#endif //IMU_DRIVER_HPP |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
// icm42688.cpp | ||
|
||
#include "icm42688.hpp" | ||
#include "stm32l5xx_hal.h" | ||
#include "stm32l5xx_hal_gpio.h" | ||
#include "stm32l5xx_hal_spi.h" | ||
#include "spi.h" | ||
#include "gpio.h" | ||
#include <string.h> | ||
#include <stdio.h> | ||
|
||
//Register Addresses | ||
#define REG_BANK_SEL 0x76 | ||
#define UB0_REG_DEVICE_CONFIG 0x11 | ||
#define UB0_REG_PWR_MGMT0 0x4E | ||
#define UB0_REG_TEMP_DATA1 0x1D | ||
|
||
#define BIT_READ 0x80 //Read bit mask | ||
|
||
#define NUM_GYRO_SAMPLES 1000 //Number of samples to take for calibration | ||
|
||
//Scale Factors (refer to page 11-12 of https://product.tdk.com/system/files/dam/doc/product/sensor/mortion-inertial/imu/data_sheet/ds-000347-icm-42688-p-v1.6.pdf) | ||
#define GYRO_SENSITIVITY_2000DPS 16.4 //Currently in Primary Use | ||
#define GYRO_SENSITIVITY_1000DPS 32.8 | ||
#define GYRO_SENSITIVITY_500DPS 65.5 | ||
#define GYRO_SENSITIVITY_250DPS 131 | ||
#define GYRO_SENSITIVITY_125DPS 262 | ||
#define GYRO_SENSITIVITY_62_5DPS 524.3 | ||
#define GYRO_SENSITIVITY_31_25DPS 1048.6 | ||
#define GYRO_SENSITIVITY_15_625PS 2097.2 | ||
|
||
#define ACCEL_SENSITIVITY_2G 16384 | ||
#define ACCEL_SENSITIVITY_4G 8192 | ||
#define ACCEL_SENSITIVITY_8G 4096 | ||
#define ACCEL_SENSITIVITY_16G 2048 //Currently in Primary Use | ||
|
||
ICM42688::ICM42688(SPI_HandleTypeDef * spi_handle, GPIO_TypeDef * cs_gpio_port, uint16_t cs_pin) { | ||
spiHandle_ = spi_handle; | ||
csGpioPort_ = cs_gpio_port; | ||
csPin_ = cs_pin; | ||
} | ||
|
||
void ICM42688::readRegister(uint8_t sub_address, uint8_t num_bytes_to_retrieve, uint8_t * destination) { | ||
//Set read bit for register address | ||
uint8_t tx = sub_address | BIT_READ; | ||
|
||
//Dummy transmit and receive buffers | ||
uint8_t dummy_tx[num_bytes_to_retrieve]; | ||
uint8_t dummy_rx; | ||
|
||
//Initialize values to 0 | ||
memset(dummy_tx, 0, num_bytes_to_retrieve * sizeof(dummy_tx[0])); | ||
|
||
HAL_GPIO_WritePin(csGpioPort_, csPin_, GPIO_PIN_RESET); | ||
|
||
HAL_SPI_TransmitReceive(spiHandle_, &tx, &dummy_rx, 1, HAL_MAX_DELAY); | ||
HAL_SPI_TransmitReceive(spiHandle_, dummy_tx, destination, num_bytes_to_retrieve, HAL_MAX_DELAY); | ||
|
||
HAL_GPIO_WritePin(csGpioPort_, csPin_, GPIO_PIN_SET); | ||
} | ||
|
||
void ICM42688::writeRegister(uint8_t sub_address, uint8_t data_to_imu) { | ||
//Prepare transmit buffer | ||
uint8_t tx_buf[2] = {sub_address, data_to_imu}; | ||
|
||
HAL_GPIO_WritePin(csGpioPort_, csPin_, GPIO_PIN_RESET); | ||
|
||
HAL_SPI_Transmit(spiHandle_, tx_buf, sizeof(tx_buf), HAL_MAX_DELAY); | ||
|
||
HAL_GPIO_WritePin(csGpioPort_, csPin_, GPIO_PIN_SET); | ||
} | ||
|
||
void ICM42688::beginMeasuring() { | ||
HAL_GPIO_WritePin(csGpioPort_, csPin_, GPIO_PIN_SET); | ||
reset(); | ||
setLowNoiseMode(); | ||
calibrate(); | ||
} | ||
|
||
void ICM42688::setBank(uint8_t bank) { | ||
writeRegister(REG_BANK_SEL, bank); //bank should always be 0 for acc and gyr data | ||
} | ||
|
||
void ICM42688::reset() { | ||
setBank(0); | ||
writeRegister(UB0_REG_DEVICE_CONFIG, 0x01); | ||
HAL_Delay(1); | ||
} | ||
|
||
void ICM42688::setLowNoiseMode() { | ||
writeRegister(UB0_REG_PWR_MGMT0, 0x0F); | ||
} | ||
|
||
void ICM42688::setGyroFS(uint8_t fs_sel) { | ||
uint8_t reg; | ||
|
||
setBank(0); | ||
|
||
//Read current register value | ||
readRegister(0x4F, 1, ®); | ||
|
||
//Only change FS_SEL in reg | ||
reg = (fs_sel << 5) | (reg & 0x1F); | ||
|
||
writeRegister(0x4F, reg); | ||
|
||
gyroScale_ = (2000.0f / (float)(1 << fs_sel)) / 32768.0f; | ||
gyroFS_ = fs_sel; | ||
} | ||
|
||
void ICM42688::calibrate() { | ||
//Set at a lower range (more resolution since IMU not moving) | ||
const uint8_t currentFSSel_ = gyroFS_; | ||
setGyroFS(GYRO_SENSITIVITY_250DPS); //Set to 250 dps | ||
|
||
//Take samples and find bias | ||
gyroBD_[0] = 0; | ||
gyroBD_[1] = 0; | ||
gyroBD_[2] = 0; | ||
|
||
for (size_t i = 0; i < NUM_GYRO_SAMPLES; i++) { | ||
readRegister(UB0_REG_TEMP_DATA1, 14, gyro_buffer_); | ||
|
||
//Combine raw bytes into 16-bit values | ||
for (size_t j = 0; j < 7; j++) { | ||
raw_meas_gyro_[j] = ((int16_t)gyro_buffer_[j * 2] << 8) | gyro_buffer_[j * 2 + 1]; | ||
} | ||
|
||
//Process gyro data | ||
for (size_t k = 0; k < 3; k++) { | ||
gyr_[k] = (float)raw_meas_gyro_[k + 3] / GYRO_SENSITIVITY_250DPS; | ||
} | ||
|
||
/* | ||
Calculate bias by collecting samples and considering pre-existing bias | ||
For each iteration, add the existing bias with the new bias and divide by the sample size | ||
to get an average bias over a specified number of gyro samples | ||
*/ | ||
gyroBD_[0] += (gyr_[0] + gyrB_[0]) / NUM_GYRO_SAMPLES; | ||
gyroBD_[1] += (gyr_[1] + gyrB_[1]) / NUM_GYRO_SAMPLES; | ||
gyroBD_[2] += (gyr_[2] + gyrB_[2]) / NUM_GYRO_SAMPLES; | ||
|
||
HAL_Delay(1); | ||
} | ||
|
||
gyrB_[0] = gyroBD_[0]; | ||
gyrB_[1] = gyroBD_[1]; | ||
gyrB_[2] = gyroBD_[2]; | ||
|
||
//Recover the full scale setting | ||
setGyroFS(currentFSSel_); | ||
} | ||
|
||
IMUData_t ICM42688::getResult(uint8_t * data_buffer) { | ||
//Collect Data | ||
readRegister(UB0_REG_TEMP_DATA1, 14, data_buffer); | ||
|
||
//Formatting raw data | ||
for (size_t i = 0; i < 3; i++) { | ||
raw_meas_[i] = ((int16_t)data_buffer[i * 2] << 8) | data_buffer[i * 2 + 1]; | ||
} | ||
|
||
IMUData_t IMUData {}; | ||
|
||
IMUData.accx = (float)raw_meas_[1] / ACCEL_SENSITIVITY_16G; | ||
IMUData.accy = (float)raw_meas_[2] / ACCEL_SENSITIVITY_16G; | ||
IMUData.accz = (float)raw_meas_[3] / ACCEL_SENSITIVITY_16G; | ||
|
||
IMUData.gyrx = (float)raw_meas_[4] / GYRO_SENSITIVITY_2000DPS; | ||
IMUData.gyry = (float)raw_meas_[5] / GYRO_SENSITIVITY_2000DPS; | ||
IMUData.gyrz = (float)raw_meas_[6] / GYRO_SENSITIVITY_2000DPS; | ||
|
||
return IMUData; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters