ICM-20602 是 日本TDKTDK公司生产的六轴传感器。
ICM-20602的DataSheet可在其官网下载: ICM20602 或者参考链接: 芯片翻译之ICM20602.
IIC软件模拟时序,请参考链接: IIC软件模拟时序-stm32F系列平台通用
下面为MCU通过IIC与ICM20602通信的读写函数
/*-------------- 读写从机的内部寄存器reg,读写1个字节数据 ----------------*/
/**
* @brief IIC写一个字节
* @param reg 要写的寄存器地址
* buf 写入到的数据存储区
* @retval 1:失败
* 0:成功
*/
u8 ICM_Write_Byte(u8 reg, u8 data)
{
IIC_Start();
IIC_Send_Byte((ICM20602_ADDR << 1) | 0); //发送器件地址+写命令
if (IIC_Wait_Ack()) //等待应答
{
IIC_Stop();
return 1;
}
IIC_Send_Byte(reg); //写寄存器地址
IIC_Wait_Ack(); //等待应答
IIC_Send_Byte(data); //发送数据
if (IIC_Wait_Ack()) //等待ACK
{
IIC_Stop();
return 1;
}
IIC_Stop();
return 0;
}
/**
* @brief IIC读一个字节
* @param reg 要写的寄存器地址
* @retval 读到的数据
*/
u8 ICM_Read_Byte(u8 reg)
{
u8 res;
IIC_Start();
IIC_Send_Byte((ICM20602_ADDR << 1) | 0); //发送器件地址+写命令
IIC_Wait_Ack(); //等待应答
IIC_Send_Byte(reg); //写寄存器地址
IIC_Wait_Ack(); //等待应答
IIC_Start();
IIC_Send_Byte((ICM20602_ADDR << 1) | 1); //发送器件地址+读命令
IIC_Wait_Ack(); //等待应答
res = IIC_Read_Byte(0); //读取数据,发送nACK
IIC_Stop(); //产生一个停止条件
return res;
}
/*----------------------- ICM20602 连续读写数据 ----------------------*/
/**
* @brief IIC发送数据
* @param
* @retval 返回从机有无应答
* 1:有应答
* 0:无应答
*/
u8 ICM_Write_Len(u8 addr, u8 reg, u8* buf, u8 len)
{
u8 i;
IIC_Start();
IIC_Send_Byte((addr << 1) | 0); //发送器件地址+写命令
if (IIC_Wait_Ack()) //等待应答
{
IIC_Stop();
return 1;
}
IIC_Send_Byte(reg); //写寄存器地址
IIC_Wait_Ack(); //等待应答
for (i = 0; i < len; i++) {
IIC_Send_Byte(buf[i]); //发送数据
if (IIC_Wait_Ack()) //等待ACK
{
IIC_Stop();
return 1;
}
}
IIC_Stop();
return 0;
}
/**
* @brief IIC读取数据
* @param addr 器件地址
* reg 要读取的寄存器地址
* len 要读取的长度
* buf 读取到的数据存储区
* @retval 1:失败
* 0:成功
*/
u8 ICM_Read_Len(u8 addr, u8 reg, u8* buf, u8 len)
{
IIC_Start();
IIC_Send_Byte((addr << 1) | 0); //发送器件地址+写命令
if (IIC_Wait_Ack()) //等待应答
{
IIC_Stop();
return 1;
}
IIC_Send_Byte(reg); //写寄存器地址
IIC_Wait_Ack(); //等待应答
IIC_Start();
IIC_Send_Byte((addr << 1) | 1); //发送器件地址+读命令
IIC_Wait_Ack(); //等待应答
while (len) {
if (len == 1)
*buf = IIC_Read_Byte(0); //读数据,发送nACK
else
*buf = IIC_Read_Byte(1); //读数据,发送ACK
len--;
buf++;
}
IIC_Stop(); //产生一个停止条件
return 0;
}
在初始化配置IMU传感器时,一定要先配置成功能正常读取IMU传感器的内部标识 id
/* ------------- ICM20602 六轴传感器初始化 ---------------- */
u8 ICM20602_sensor_init(void)
{
u8 ICM_id;
/* (1)IIC初始化 */
IIC_Dev_Init();
#if 1 // 此部分为ICM20602的INT引脚产生中断的相关配置
/*- ICM20602引脚INT产生中断时,MCU引脚PE0配置外部中断和NVIC优先级 -*/
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能GPIOE、AFIO 端口时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE);
/* 配置ICM20602-PE0中断线 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOE, &GPIO_InitStructure);
/* 配置中断线和 GPIO-PE0 映射关系 */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource0);
/* 配置外部中断EXTI */
myEXTI_Config(EXTI_Line0, EXTI_Mode_Interrupt, EXTI_Trigger_Falling);
/* 配置NVIC中断优先级并使能中断 */
myNVIC_Config(EXTI0_IRQn, 0, 0); // 外部中断线0
#endif
/* (2)复位ICM20602 */
ICM_Write_Byte(ICM20602_PWR_MGMT1_REG, 0x80);
timer_delay(100); // 利用定时器配置延迟100ms
ICM_Write_Byte(ICM20602_PWR_MGMT1_REG, 0x00); //唤醒ICM20602
/* 复位重置传感器各个通道的寄存器 */
ICM_Write_Byte(ICM20602_USER_CTRL, 0x01);
timer_delay(100);
ICM_Write_Byte(ICM20602_USER_CTRL, 0x00);
/* (3)配置陀螺仪满量程范围:-2g ~ +2g;加速度计满量程范围:-2000dps ~ +2000dps */
ICM_Set_Gyro_Fsr(0x3);
ICM_Set_Accel_Fsr(0);
/* (4)设置采样频率50HZ */
ICM_Set_Rate(50);
/* (5)禁止中断,关闭硬件 */
ICM_Write_Byte(ICM20602_INT_ENABLE_REG, 0x00); // 关闭所有中断
ICM_Write_Byte(ICM20602_FIFO_EN, 0x00); // 关闭陀螺仪和加速度计的FIFO数据缓冲区
ICM_Write_Byte(ICM20602_INT_PIN_CFG, 0x80); // 设置INT引脚为低电平有效,开漏输出
/* (6)判断器件ID = 0x12 */
ICM_id = ICM_Read_Byte(ICM20602_WHO_AM_I);
printf("ICM20602_ID = 0x%x .......\r\n", ICM_id);
if (ICM_id == 0x12) {
/* 设置时钟源 */
ICM_Write_Byte(ICM20602_PWR_MGMT1_REG, 0x01);
/* 设置accelerate阈值 */
ICM_Write_Byte(ICM20602_LACCEL_WOM_X_THR, 0x7F);
ICM_Write_Byte(ICM20602_LACCEL_WOM_Y_THR, 0x7F);
ICM_Write_Byte(ICM20602_LACCEL_WOM_Z_THR, 0x7F);
/* 开启加速度计的阈值中断 */
ICM_Write_Byte(ICM20602_ACCEL_INTEL_CTRL, 0x80);
timer_delay(1);
/* 使能中断 */
ICM_Write_Byte(ICM20602_INT_ENABLE_REG, 0xE0);
timer_delay(1);
/* 开启加速度与陀螺仪都工作 */
ICM_Write_Byte(ICM20602_PWR_MGMT2_REG, 0x00);
timer_delay(1);
printf("ICM20602 IMU initalize success ............\r\n");
} else {
printf("ICM20602 initalize fail ...........\r\n");
return 1;
}
return 0;
}
/*---------- 设置运动检测(WAKE-ON MOTION) accelerate阈值 ----------*/
int Set_Accl_Thresh(u8 threshold)
{
u8 accl_val;
/* Threshold按十六进制传参不能超过值:(十进制0D)100 = 0x64 */
if(threshold > 100){
return 1;
}
u16 val_1= threshold * 0xFF;
u16 val_2 = (u8)(val_1 / 100);
accl_val = (u8)val_2;
_debug(SYS_INFO, "threshold = %d%%, val_1 = 0x%x, val_2 = 0x%x, set accl_val = 0x%x ...\n", \
threshold, val_1, val_2, accl_val);
ICM_Write_Byte(ICM20602_LACCEL_WOM_X_THR, accl_val);
ICM_Write_Byte(ICM20602_LACCEL_WOM_Y_THR, accl_val);
ICM_Write_Byte(ICM20602_LACCEL_WOM_Z_THR, accl_val);
return 0;
}
/* 外部中断配置EXTI寄存器 */
void myEXTI_Config(u32 myEXTI_Line, EXTIMode_TypeDef myEXTI_Mode, EXTITrigger_TypeDef myEXTI_Trigger)
{
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = myEXTI_Line;
EXTI_InitStructure.EXTI_Mode = myEXTI_Mode;
EXTI_InitStructure.EXTI_Trigger = myEXTI_Trigger;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
/* NVIC中断配置函数 */
void myNVIC_Config(u8 myNVIC_IRQChannel, u8 PreEmptionPriority, u8 SubPriority)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = myNVIC_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = PreEmptionPriority;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = SubPriority;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/* EXTI0 中断处理函数 */
void EXTI0_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line0)) {
/* ICM20602六轴传感器INT引脚 接MCU的PE0引脚 */
if (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_0) == Bit_RESET) {
ICM20602_Get_Data();
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
此部分参考ICM20602的数据手册,
/*-------------------------- 设置满量程范围 ---------------------*/
/* 设置GYRO陀螺仪的满量程范围 */
u8 ICM_Set_Gyro_Fsr(u8 fsr)
{
return ICM_Write_Byte(ICM20602_GYRO_CONFIG_REG, fsr << 3);
}
/* 设置ACCEL加速度计的满量程范围 */
u8 ICM_Set_Accel_Fsr(u8 fsr)
{
return ICM_Write_Byte(ICM20602_ACCEL_CONFIG1_REG, fsr << 3);
}
/*------------------------- 设置采样频率 -------------------------*/
/**
* 设置ICM20602的采样频率(假定Fs=1KHz)
* INTERNAL_SAMPLE_RATE = 1 kHz
* SAMPLE_RATE = INTERNAL_SAMPLE_RATE / (1 + SMPLRT_DIV);
* rate:4 ~ 1000(Hz)
* 返回值:0,设置成功;其他,设置失败
*/
u8 ICM_Set_Rate(u16 rate)
{
u8 div;
if (rate > 1000) {
rate = 1000;
} else if (rate < 4) {
rate = 4;
}
/* 设置预分频值DIV */
div = 1000 / rate - 1;
ICM_Write_Byte(ICM20602_SMPLRT_DIV, div);
/* 配置低通滤波器为采样率的1/2 */
return ICM_Set_DLPF(rate / 2);
}
/**
* 设置ICM20602的数字低通滤波器(假定Fs=1KHz)
* 返回值:0,设置成功;其他,设置失败
*/
u8 ICM_Set_DLPF(u16 dlpf)
{
u8 data;
if (dlpf >= 250) {
data = 0;
} else if (dlpf >= 176) {
data = 1;
} else if (dlpf >= 92) {
data = 2;
} else if (dlpf >= 41) {
data = 3;
} else if (dlpf >= 20) {
data = 4;
} else if (dlpf >= 10) {
data = 5;
} else {
data = 6;
}
return ICM_Write_Byte(ICM20602_CONFIG_REG, data);
}
/* ---------------------- 读取传感器采集到的数据 -------------------------- */
/**
* 读取陀螺仪值(原始值)
* gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)
* 返回值:0,成功; 其他,错误代码
*/
u8 ICM_Get_Gyroscope(short* gx, short* gy, short* gz)
{
u8 buf[6], res;
res = ICM_Read_Len(ICM20602_ADDR, ICM20602_GYRO_XOUT_H, buf, 6);
if (res == 0) {
*gx = ((u16)buf[0] << 8) | buf[1];
*gy = ((u16)buf[2] << 8) | buf[3];
*gz = ((u16)buf[4] << 8) | buf[5];
}
return res;
}
/**
* 读取加速度计值(原始值)
* ax,ay,az:加速度计x,y,z轴的原始读数(带符号)
* 返回值:0,成功; 其他,错误代码
*/
u8 ICM_Get_Accelerometer(short* ax, short* ay, short* az)
{
u8 buf[6], res;
res = ICM_Read_Len(ICM20602_ADDR, ICM20602_ACCEL_XOUT_H, buf, 6);
if (res == 0) {
*ax = ((u16)buf[0] << 8) | buf[1];
*ay = ((u16)buf[2] << 8) | buf[3];
*az = ((u16)buf[4] << 8) | buf[5];
}
return res;
}
/**
* 读取温度值(原始值)
* 返回值:温度值(扩大了100倍)
*/
short ICM_Get_Temperature(void)
{
u8 buf[2];
short raw;
float temp;
ICM_Read_Len(ICM20602_ADDR, ICM20602_TEMP_OUT_H, buf, 2);
raw = ((u16)buf[0] << 8) | buf[1];
temp = 36.53 + ((double)raw) / 340;
return temp * 100;
}
icm20602_iic.h 头文件
/* ------------------ Register map for ICM20602 ---------------------- */
#define ICM20602_SELF_TEST_X_ACCEL 0x0D // X轴加速度计自检寄存器
#define ICM20602_SELF_TEST_Y_ACCEL 0x0E // Y轴加速度计自检寄存器
#define ICM20602_SELF_TEST_Z_ACCEL 0x0F // Z轴加速度计自检寄存器
#define ICM20602_SMPLRT_DIV 0x19 // 采样频率分频器
#define ICM20602_CONFIG_REG 0x1A // 配置:fifo MODE, Enables the FSYNC and DLPF低通滤波器
#define ICM20602_GYRO_CONFIG_REG 0x1B // (4:3 bit)gyroscope configuration
#define ICM20602_ACCEL_CONFIG1_REG 0x1C // accelerometer configuration
#define ICM20602_ACCEL_CONFIG2_REG 0x1D // accelerometer configuration
#define ICM20602_LP_MODE_CFG 0x1E // gyroscope low-power mode or 6-axis low-power mode
#define ICM20602_LACCEL_WOM_X_THR 0x20 // 加速度计X轴阈值
#define ICM20602_LACCEL_WOM_Y_THR 0x21 // 加速度计Y轴阈值
#define ICM20602_LACCEL_WOM_Z_THR 0x22 // 加速度计Z轴阈值
#define ICM20602_FIFO_EN 0x23 // GYRO/ACCEL _FIFO_EN
#define ICM20602_FSYNC_INT 0x36
#define ICM20602_INT_PIN_CFG 0x37 // 中断引脚配置
#define ICM20602_INT_ENABLE_REG 0x38 // interrupt enable
#define ICM20602_INT_STATUS 0x3A // INTERRUPT STATUS
#define ICM20602_ACCEL_XOUT_H 0x3B // accelerometer measurements
#define ICM20602_ACCEL_XOUT_L 0x3C
#define ICM20602_ACCEL_YOUT_H 0x3D
#define ICM20602_ACCEL_YOUT_L 0x3E
#define ICM20602_ACCEL_ZOUT_H 0x3F
#define ICM20602_ACCEL_ZOUT_L 0x40
#define ICM20602_TEMP_OUT_H 0x41 // TEMPERATURE MEASUREMENT,Temperature = 36.53 + regval/340
#define ICM20602_TEMP_OUT_L 0x42
#define ICM20602_GYRO_XOUT_H 0x43 // gyroscope measurements
#define ICM20602_GYRO_XOUT_L 0x44
#define ICM20602_GYRO_YOUT_H 0x45
#define ICM20602_GYRO_YOUT_L 0x46
#define ICM20602_GYRO_ZOUT_H 0x47
#define ICM20602_GYRO_ZOUT_L 0x48
#define ICM20602_SELF_TEST_X_GYRO 0x50
#define ICM20602_SELF_TEST_Y_GYRO 0x51
#define ICM20602_SELF_TEST_Z_GYRO 0x52
#define ICM20602_SIGNAL_PATH_RESET 0x68 // ACCEL_RST,TEMP_RST
#define ICM20602_ACCEL_INTEL_CTRL 0x69
#define ICM20602_USER_CTRL 0x6A // FIFO_EN,FIFO_RST,SIG_COND_RST
#define ICM20602_PWR_MGMT1_REG 0x6B // DEVICE_RESET,SLEEP,CYCLE,GYRO_STANDBY,TEMP_DIS,CLKSEL
#define ICM20602_PWR_MGMT2_REG 0x6C // STBY_XA - STBY_ZG
#define ICM20602_I2C_IF 0x70 //1 – Disable I2C Slave module and put the serial interface in SPI mode only.
#define ICM20602_FIFO_COUNTH 0x72
#define ICM20602_FIFO_R_W 0x74 //FIFO READ WRITE
#define ICM20602_WHO_AM_I 0x75 //RO, default value ID = 0x12;
#define ICM20602_CONFIG 0x80
#define ICM20602_ADDR 0x69 // Slave address, ad0 set 0
/*------------------------- 初始化ICM20602 -----------------------------*/
u8 ICM20602_sensor_init(void);
int Set_Accl_Thresh(u8 threshold);
/*------------------------ 设置加速度计和陀螺仪 -------------------------*/
u8 ICM_Set_Gyro_Fsr(u8 fsr);
u8 ICM_Set_Accel_Fsr(u8 fsr);
u8 ICM_Set_DLPF(u16 lpf);
u8 ICM_Set_Rate(u16 rate);
/*---------------------- 获取 加速度计、陀螺仪和温度 数据 ----------------*/
short ICM_Get_Temperature(void);
u8 ICM_Get_Gyroscope(short* gx, short* gy, short* gz);
u8 ICM_Get_Accelerometer(short* ax, short* ay, short* az);
从下面的数据手册节选可以看出:SA0引脚决定了从机地址的值,从下面的我的ICM20602原理图可以看出,我的SA0引脚接VCC,故始终为高电平,因此:ICM20602_ADDR = 0x69;又CS片选始终也为高电平,即为IIC通信模式。具体的使用要看原理图,硬件工程师将SA0和CS引脚是怎么接的,是接地还是接的VCC。