通过MPU9250中断唤醒控制led1亮灭。注1.GPIO中断输入开始GPIO_Init未配置也能正常使用原因为芯片上电默认就是浮空输入刚好外部MPU9250中断配置的是推挽输出所以可以正常使用2.如果外部触发中断是推挽输出的STM32侧中断输入配置模式可以为GPIO_Mode_IPD下拉输入、GPIO_Mode_IPU上拉输入、GPIO_Mode_IN_FLOATING浮空输入均可3.如果为GPIO_Mode_IPD下拉输入对方断电悬空时引脚固定为低电平4.如果为GPIO_Mode_IPU上拉输入对方断电悬空时引脚固定为高电平5.如果为GPIO_Mode_IN_FLOATING浮空输入对方断电悬空时引脚电平随机容易误触发中断不推荐使用一、硬件参考篇《STM32串口打印以及OLED显示MPU-9250九轴传感器实时数据》MPU9250-INT中断管脚 ---- 接开发板PC4MPU9260-INT配置为推挽输出高电平有效用户需要主动读取MPU9250寄存器INT中断管脚才能被拉低。二、寄存器配置寄存器具体配置如下重要void MPU9250_IT_CONFIG_Init(u8 threshold) { Single_Write(ACCEL_ADDRESS, PWR_MGMT_1, 0x00); //解除休眠状态,退出睡眠,MPU9250_Init中也会配置 Single_Write(ACCEL_ADDRESS, USER_CTRL, 0x00); //不使用 FIFO,不使用 DMP数字运动处理器后续再看这里配置会不会影响 Single_Write(ACCEL_ADDRESS, ACCEL_CONFIG_2, 0x05); //启用滤波器 Single_Write(ACCEL_ADDRESS, MOT_DETECT_CTRL, 0xC0); // 使能运动唤醒中断 Single_Write(ACCEL_ADDRESS, INT_ENABLE, 0x40); //只使能WOM运动中断禁用溢出等中断 Single_Write(ACCEL_ADDRESS, INT_PIN_CFG, 0x10); //INT高电平有效推挽输出中断引脚电平保持任意 I2C 读操作即可清除中断 //Single_Write(ACCEL_ADDRESS, INT_PIN_CFG, 0x20); //INT高电平有效推挽输出中断引脚电平保持直至中断状态被清除 if((threshold 0) (threshold 0xFF)) //1~255 { Single_Write(ACCEL_ADDRESS, WOM_THR, threshold); //0-255对应INT唤醒阈值4mg-1020mg这里配置由参数决定 } else { Single_Write(ACCEL_ADDRESS, WOM_THR, 0xFA); //0-255对应INT唤醒阈值4mg-1020mg这里配置为1000mg 1g } }三、工程实现mpu9250.h#ifndef __MPU9250_H #define __MPU9250_H #include system.h //可以使用位带操作 #include math.h #include stdbool.h //使用true/false //定义MPU9250内部地址 #define ACCEL_ADDRESS 0xD0 //加速度地址 #define GYRO_ADDRESS 0xD0 //陀螺地址 //MPU9250内置磁力计AK8963的I2C地址通常是 0x0C7位地址写地址应该是 0x180x0C 1。 #define MAG_ADDRESS 0x18 //磁场地址 #define WHO_AM_I 0x75 //IIC地址寄存器(默认数值0x68只读) #define PWR_MGMT_1 0x6B //电源管理典型值0x00(正常启用) #define SMPLRT_DIV 0x19 //陀螺仪采样率典型值0x07(125Hz) #define CONFIG 0x1A //低通滤波频率典型值0x06(5Hz) #define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围典型值0x18(不自检-2000deg/s) #define ACCEL_CONFIG 0x1C //加速计自检、测量范围及高通滤波频率典型值0x01(不自检2G5Hz) /* 加速度XYZ三轴数据读取寄存器每轴对应着高8位和低8位 */ #define ACCEL_XOUT_H 0x3B #define ACCEL_XOUT_L 0x3C #define ACCEL_YOUT_H 0x3D #define ACCEL_YOUT_L 0x3E #define ACCEL_ZOUT_H 0x3F #define ACCEL_ZOUT_L 0x40 /* 温度数据读取寄存器 */ #define TEMP_OUT_H 0x41 #define TEMP_OUT_L 0x42 /* 陀螺仪数据读取寄存器 */ #define GYRO_XOUT_H 0x43 #define GYRO_XOUT_L 0x44 #define GYRO_YOUT_H 0x45 #define GYRO_YOUT_L 0x46 #define GYRO_ZOUT_H 0x47 #define GYRO_ZOUT_L 0x48 /* 指南针数据读取寄存器 */ #define MAG_XOUT_L 0x03 #define MAG_XOUT_H 0x04 #define MAG_YOUT_L 0x05 #define MAG_YOUT_H 0x06 #define MAG_ZOUT_L 0x07 #define MAG_ZOUT_H 0x08 /* 中断配置相关具体说明看C文件 */ //#define PWR_MGMT_1 0x6B //电源管理典型值0x00(正常启用) 上面已经定义了 #define ACCEL_CONFIG_2 0x1D #define MOT_DETECT_CTRL 0x69 #define INT_ENABLE 0x38 #define INT_PIN_CFG 0x37 #define USER_CTRL 0x6A #define WOM_THR 0x1F /*更换管脚修改如下即可*/ /* IIC_SCL时钟端口、引脚定义 */ #define IIC_SCL_PORT GPIOB #define IIC_SCL_PIN (GPIO_Pin_10) #define IIC_SCL_PORT_RCC RCC_APB2Periph_GPIOB //为了使能端口时钟 /* IIC_SDA时钟端口、引脚定义 */ #define IIC_SDA_PORT GPIOB #define IIC_SDA_PIN (GPIO_Pin_11) #define IIC_SDA_PORT_RCC RCC_APB2Periph_GPIOB #define SCL_H GPIOB-BSRR GPIO_Pin_10 #define SCL_L GPIOB-BRR GPIO_Pin_10 #define SDA_H GPIOB-BSRR GPIO_Pin_11 #define SDA_L GPIOB-BRR GPIO_Pin_11 #define READ_SDA GPIOB-IDR GPIO_Pin_11 #define READ_MPU9250_INT PCin(4) /**/ /* 配置读9轴数据调用 1.MPU9250_IIC_Init 2.MPU9250_CONFIG_Init 3.调用函数读取数据READ_MPU9250_Accel/READ_MPU9250_Gyro/READ_MPU9250_Temp/READ_MPU9250_Mag */ /* 配置中断唤醒调用flow 1.MPU9250_IIC_Init 2.MPU9250_IT_CONFIG_Init 3.MPU9250_TIM4_1MS_Init 4.MPU9250_EXIT_Init */ //COMMON公共使用 void MPU9250_IIC_Init(void); //初始化IIC的IO口 SDL SDA unsigned char Single_Read(unsigned char SlaveAddress, unsigned char REG_Address); //用于读取固定寄存器值判断模块及I2C通信是否正常 bool Single_Write(unsigned char SlaveAddress, unsigned char REG_Address, unsigned char REG_data); //写寄存器接口 //普通读取9轴数据及温度需要调用 void MPU9250_CONFIG_Init(void); //配置MPU9250 相关寄存器如量程等读9轴数据时需要先配置 void READ_MPU9250_Accel(float *pA_x_g, float *pA_y_g, float *pA_z_g); void READ_MPU9250_Gyro(short *pG_x, short *pG_y, short *pG_z); void READ_MPU9250_Temp(float *pT_temp); void READ_MPU9250_Mag(short *pM_x, short *pM_y, short *pM_z); //MPU9250中断测试需要调用 void MPU9250_TIM4_1MS_Init(void); //定时器中断1MS,用于GPIO中断中计时消除抖动 void MPU9250_EXIT_Init(void); //配置GPIO中断 void MPU9250_IT_CONFIG_Init(u8 threshold); //MPU9250相关中断配置 #endifmup9250.c#include mpu9250.h #include SysTick.h #include led.h //使用led1位带操作 //不需要外部文件访问 static uint32_t time4_count 0; static uint32_t last_count 0; //不需要外部文件访问 static void I2C_delay(void); static void delay5ms(void); static void Delay(vu32 nCount); static void Delayms(vu32 m); static bool I2C_Start(void); static void I2C_Stop(void); static void I2C_Ack(void); static bool I2C_WaitAck(void); static void I2C_NoAck(void); static void I2C_SendByte(u8 SendByte); static unsigned char I2C_RadeByte(void); void I2C_delay(void) { u8 i 30; //这里可以优化速度经测试最低到5还能写入 while(i) { i--; } } void delay5ms(void) { int i 5000; while(i) { i--; } } void Delay(vu32 nCount) { for(; nCount ! 0; nCount--); } void Delayms(vu32 m) { u32 i; for(; m ! 0; m--) { for(i 0; i 50000; i); } } /* 加速度数据读取读取数据位有符号浮点单位g */ //静态姿态由该函数读取 void READ_MPU9250_Accel(float *pA_x_g, float *pA_y_g, float *pA_z_g) { unsigned char buf[2]; short raw_x, raw_y, raw_z; const float sensitivity 16384.0; // ±2g量程的灵敏度 buf[0] Single_Read(ACCEL_ADDRESS, ACCEL_XOUT_L); buf[1] Single_Read(ACCEL_ADDRESS, ACCEL_XOUT_H); raw_x (buf[1] 8) | buf[0]; *pA_x_g (float)raw_x / sensitivity; buf[0] Single_Read(ACCEL_ADDRESS, ACCEL_YOUT_L); buf[1] Single_Read(ACCEL_ADDRESS, ACCEL_YOUT_H); raw_y (buf[1] 8) | buf[0]; *pA_y_g (float)raw_y / sensitivity; buf[0] Single_Read(ACCEL_ADDRESS, ACCEL_ZOUT_L); buf[1] Single_Read(ACCEL_ADDRESS, ACCEL_ZOUT_H); raw_z (buf[1] 8) | buf[0]; *pA_z_g (float)raw_z / sensitivity; } //读取动态角速度 //读取的RAW数据是16位有符号整型范围-32768 到 32767 //输出转化为short型后续也可以改成浮点型-2000 void READ_MPU9250_Gyro(short *pG_x, short *pG_y, short *pG_z) { unsigned char buf[2]; buf[0] Single_Read(GYRO_ADDRESS, GYRO_XOUT_L); buf[1] Single_Read(GYRO_ADDRESS, GYRO_XOUT_H); *pG_x (buf[1] 8) | buf[0]; *pG_x / 16.4; buf[0] Single_Read(GYRO_ADDRESS, GYRO_YOUT_L); buf[1] Single_Read(GYRO_ADDRESS, GYRO_YOUT_H); *pG_y (buf[1] 8) | buf[0]; *pG_y / 16.4; buf[0] Single_Read(GYRO_ADDRESS, GYRO_ZOUT_L); buf[1] Single_Read(GYRO_ADDRESS, GYRO_ZOUT_H); *pG_z (buf[1] 8) | buf[0]; *pG_z / 16.4; } void READ_MPU9250_Temp(float *pT_temp) { unsigned char buf[2]; short raw_temp; buf[0] Single_Read(GYRO_ADDRESS, TEMP_OUT_L); buf[1] Single_Read(GYRO_ADDRESS, TEMP_OUT_H); raw_temp (buf[1] 8) | buf[0]; // 标准公式温度(°C) raw_temp / 333.87 21 *pT_temp (float)raw_temp / 333.87f 21.0f; } //磁轴传感器读取RAW数据数据未处理,寄存器精度配置为14bit void READ_MPU9250_Mag(short *pM_x, short *pM_y, short *pM_z) { unsigned char buf[2]; //这里的设置和Delayms后续再优化 Single_Write(GYRO_ADDRESS, 0x37, 0x02); //turn on Bypass Mode Delayms(10); Single_Write(MAG_ADDRESS, 0x0A, 0x01); //ADC配置为14位 0.6 μT/LSB -8192 ~ 8191 配置为16位需要写0x11 Delayms(10); buf[0] Single_Read(MAG_ADDRESS, MAG_XOUT_L); buf[1] Single_Read(MAG_ADDRESS, MAG_XOUT_H); *pM_x (buf[1] 8) | buf[0]; buf[0] Single_Read(MAG_ADDRESS, MAG_YOUT_L); buf[1] Single_Read(MAG_ADDRESS, MAG_YOUT_H); *pM_y (buf[1] 8) | buf[0]; buf[0] Single_Read(MAG_ADDRESS, MAG_ZOUT_L); buf[1] Single_Read(MAG_ADDRESS, MAG_ZOUT_H); *pM_z (buf[1] 8) | buf[0]; } void MPU9250_IIC_Init(void) //初始化IIC的IO口 { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(IIC_SCL_PORT_RCC|IIC_SDA_PORT_RCC, ENABLE); //使能端口时钟 GPIO_InitStructure.GPIO_Pin IIC_SCL_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_OD; //配置为开漏输出EEPRM配置为推挽也可以后面再研究 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(IIC_SCL_PORT, GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin IIC_SDA_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_OD; //配置为开漏输出EEPRM配置为推挽也可以后面再研究 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(IIC_SDA_PORT, GPIO_InitStructure); //总线开始为空闲状态 SDA_H; SCL_H; } void MPU9250_CONFIG_Init(void) { delay_ms(10); // Single_Write(GYRO_ADDRESS,PWR_M, 0x80); // // Single_Write(GYRO_ADDRESS,SMPL, 0x07); // // Single_Write(GYRO_ADDRESS,DLPF, 0x1E); //±2000° // Single_Write(GYRO_ADDRESS,INT_C, 0x00 ); // // Single_Write(GYRO_ADDRESS,PWR_M, 0x00); // Single_Write(GYRO_ADDRESS, PWR_MGMT_1, 0x00); //解除休眠状态 Single_Write(GYRO_ADDRESS, SMPLRT_DIV, 0x07); Single_Write(GYRO_ADDRESS, CONFIG, 0x06); Single_Write(GYRO_ADDRESS, GYRO_CONFIG, 0x18); //配置的量程为±2000 dps度/秒 ±2000 dps16.4 LSB/(°/s) Single_Write(GYRO_ADDRESS, ACCEL_CONFIG, 0x01); //配置加速度量程为±2g // Single_Write(GYRO_ADDRESS,0x6A,0x00); //close Master Mode } /***************************************************************************/ //1MS产生一次中断 void MPU9250_TIM4_1MS_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; NVIC_InitTypeDef NVIC_InitStructure; //TIM4时钟使能 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //初始化NVIC,这里未配置中断优先级 NVIC_InitStructure.NVIC_IRQChannel TIM4_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); TIM_TimeBaseInitStructure.TIM_Period 1000-1; //1ms TIM_TimeBaseInitStructure.TIM_Prescaler 72-1; //72分频 1MHz TIM_TimeBaseInitStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseInit(TIM4, TIM_TimeBaseInitStructure); //开启定时器中断即使用定时器4的更新中断作为NVIC的中断源如计数器到达设定自动重装载寄存器值时触发更新中断 TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); TIM_Cmd(TIM4, ENABLE); } //定时器中断服务函数只做一件事计数器加一 void TIM4_IRQHandler(void) { if(TIM_GetITStatus(TIM4,TIM_IT_Update) SET) //溢出中断 { time4_count; } TIM_ClearITPendingBit(TIM4,TIM_IT_Update); //清除中断标志位 } /***************************************************************************/ void MPU9250_EXIT_Init(void) { GPIO_InitTypeDef MY_GPIO_Init; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //在LED初始化中已经使能过了重复使能不影响 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启GPIO复用功能时钟 MY_GPIO_Init.GPIO_Pin GPIO_Pin_4; MY_GPIO_Init.GPIO_Mode GPIO_Mode_IPD; //设置为输入下拉GPIO_Mode_IN_FLOATING浮空输入也可以不推荐 MY_GPIO_Init.GPIO_Speed GPIO_Speed_50MHz; /* 1.开始GPIO_Init未配置也能正常使用原因为芯片上电默认就是浮空输入刚好外部MPU9250中断配置的是推挽输出所以可以正常使用 2.如果外部触发中断是推挽输出的STM32侧中断输入配置模式可以为GPIO_Mode_IPD下拉输入、GPIO_Mode_IPU上拉输入、GPIO_Mode_IN_FLOATING浮空输入均可 3.如果为GPIO_Mode_IPD下拉输入对方断电悬空时引脚固定为低电平 4.如果为GPIO_Mode_IPU上拉输入对方断电悬空时引脚固定为高电平 5.如果为GPIO_Mode_IN_FLOATING浮空输入对方断电悬空时引脚电平随机容易误触发不推荐 */ GPIO_Init(GPIOC, MY_GPIO_Init); GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource4); //选择GPIO管脚作为外部中断线路 EXTI_InitStructure.EXTI_Line EXTI_Line4; EXTI_InitStructure.EXTI_Mode EXTI_Mode_Interrupt; //mode为外部中断 EXTI_InitStructure.EXTI_Trigger EXTI_Trigger_Rising; //选择上升边沿触发 EXTI_InitStructure.EXTI_LineCmd ENABLE; //使能 EXTI_Init(EXTI_InitStructure); NVIC_InitStructure.NVIC_IRQChannel EXTI4_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 2; //抢占优先级设为2 NVIC_InitStructure.NVIC_IRQChannelSubPriority 3; //响应优先级设为3 NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); } void EXTI4_IRQHandler(void) { uint32_t now 0; if(EXTI_GetITStatus(EXTI_Line4) 1) { now time4_count; if((now - last_count) 80) //消除抖动延时80ms { EXTI_ClearITPendingBit(EXTI_Line4); return; } last_count now; if(READ_MPU9250_INT 1) //再次判断中断有没有被拉起来 { printf(1.READ_MPU9250_INT %d\r\n, READ_MPU9250_INT); Single_Read(ACCEL_ADDRESS, WHO_AM_I); //读MPU9250任意寄存器,即可清除中断 //delay_ms(5); //实际测试不需要延时只要Single_Read后INT管脚立即被拉低 printf(2.READ_MPU9250_INT %d\r\n, READ_MPU9250_INT); led1 ~led1; } } EXTI_ClearITPendingBit(EXTI_Line4); } void MPU9250_IT_CONFIG_Init(u8 threshold) { Single_Write(ACCEL_ADDRESS, PWR_MGMT_1, 0x00); //解除休眠状态,退出睡眠,MPU9250_Init中也会配置 Single_Write(ACCEL_ADDRESS, USER_CTRL, 0x00); //不使用 FIFO,不使用 DMP数字运动处理器后续再看这里配置会不会影响 Single_Write(ACCEL_ADDRESS, ACCEL_CONFIG_2, 0x05); //启用滤波器 Single_Write(ACCEL_ADDRESS, MOT_DETECT_CTRL, 0xC0); // 使能运动唤醒中断 Single_Write(ACCEL_ADDRESS, INT_ENABLE, 0x40); //只使能WOM运动中断禁用溢出等中断 Single_Write(ACCEL_ADDRESS, INT_PIN_CFG, 0x10); //INT高电平有效推挽输出中断引脚电平保持任意 I2C 读操作即可清除中断 //Single_Write(ACCEL_ADDRESS, INT_PIN_CFG, 0x20); //INT高电平有效推挽输出中断引脚电平保持直至中断状态被清除 if((threshold 0) (threshold 0xFF)) //1~255 { Single_Write(ACCEL_ADDRESS, WOM_THR, threshold); //0-255对应INT唤醒阈值4mg-1020mg这里配置由参数决定 } else { Single_Write(ACCEL_ADDRESS, WOM_THR, 0xFA); //0-255对应INT唤醒阈值4mg-1020mg这里配置为1000mg 1g } } /******************************************************************************* * Function Name : I2C_Start * Description : Master Start Simulation IIC Communication * Input : None * Output : None * Return : Wheather Start ****************************************************************************** */ bool I2C_Start(void) { SDA_H; SCL_H; I2C_delay(); if(!READ_SDA) { return false; //SDA线为低电平则总线忙,退出 } SDA_L; I2C_delay(); if(READ_SDA) { return false; //SDA线为高电平则总线出错,退出 } SDA_L; I2C_delay(); return true; } /******************************************************************************* * Function Name : I2C_Stop * Description : Master Stop Simulation IIC Communication * Input : None * Output : None * Return : None ****************************************************************************** */ void I2C_Stop(void) { SCL_L; I2C_delay(); SDA_L; I2C_delay(); SCL_H; I2C_delay(); SDA_H; I2C_delay(); } /******************************************************************************* * Function Name : I2C_Ack * Description : Master Send Acknowledge Single * Input : None * Output : None * Return : None ****************************************************************************** */ void I2C_Ack(void) { SCL_L; I2C_delay(); SDA_L; I2C_delay(); SCL_H; I2C_delay(); SCL_L; I2C_delay(); } /******************************************************************************* * Function Name : I2C_NoAck * Description : Master Send No Acknowledge Single * Input : None * Output : None * Return : None ****************************************************************************** */ void I2C_NoAck(void) { SCL_L; I2C_delay(); SDA_H; I2C_delay(); SCL_H; I2C_delay(); SCL_L; I2C_delay(); } /******************************************************************************* * Function Name : I2C_WaitAck * Description : Master Reserive Slave Acknowledge Single * Input : None * Output : None * Return : Wheather Reserive Slave Acknowledge Single ****************************************************************************** */ bool I2C_WaitAck(void) //返回为:1有ACK,0无ACK { SCL_L; I2C_delay(); SDA_H; I2C_delay(); SCL_H; I2C_delay(); if(READ_SDA) { SCL_L; I2C_delay(); return false; } SCL_L; I2C_delay(); return true; } /******************************************************************************* * Function Name : I2C_SendByte * Description : Master Send a Byte to Slave * Input : Will Send Date * Output : None * Return : None ****************************************************************************** */ void I2C_SendByte(u8 SendByte) //数据从高位到低位 { u8 i 8; while(i--) { SCL_L; I2C_delay(); if(SendByte 0x80) { SDA_H; } else { SDA_L; } SendByte 1; I2C_delay(); SCL_H; I2C_delay(); } SCL_L; } /******************************************************************************* * Function Name : I2C_RadeByte * Description : Master Reserive a Byte From Slave * Input : None * Output : None * Return : Date From Slave ****************************************************************************** */ unsigned char I2C_RadeByte(void) //数据从高位到低位 { u8 i 8; u8 ReceiveByte 0; SDA_H; while(i--) { ReceiveByte 1; SCL_L; I2C_delay(); SCL_H; I2C_delay(); if(READ_SDA) { ReceiveByte | 0x01; } } SCL_L; return ReceiveByte; } //单字节写入 bool Single_Write(unsigned char SlaveAddress, unsigned char REG_Address, unsigned char REG_data) { if(!I2C_Start()) { return false; } I2C_SendByte(SlaveAddress); //发送设备地址写信号 //I2C_SendByte(((REG_Address 0x0700) 7) | SlaveAddress 0xFFFE);//设置高起始地址器件地址 if(!I2C_WaitAck()) { I2C_Stop(); return false; } I2C_SendByte(REG_Address); //设置低起始地址 I2C_WaitAck(); I2C_SendByte(REG_data); I2C_WaitAck(); I2C_Stop(); delay5ms(); return true; } //单字节读取 unsigned char Single_Read(unsigned char SlaveAddress, unsigned char REG_Address) { unsigned char REG_data; if(!I2C_Start()) { return false; } I2C_SendByte(SlaveAddress); //I2C_SendByte(((REG_Address 0x0700) 7) | REG_Address 0xFFFE); //设置高起始地址器件地址 if(!I2C_WaitAck()) { I2C_Stop(); return false; } I2C_SendByte((u8)REG_Address); //设置低起始地址 I2C_WaitAck(); I2C_Start(); I2C_SendByte(SlaveAddress 1); I2C_WaitAck(); REG_data I2C_RadeByte(); I2C_NoAck(); I2C_Stop(); return REG_data; }main.c#include system.h //已经包含了stm32f10x.h 以及定义了位带操作的宏后续只包含该头文件就可以了 #include SysTick.h //使用delay_ms和delay_us #include mpu9250.h //包含了math.h #include oled.h //oled显示 #include bmp.h //存储要显示的图片数据 #include usart.h //使用printf #include led.h //使用led1 //同时只能使能一个 #define TEST_Accel 0 //测试加速度 #define TEST_Gyro_Temp 0 //测试陀螺仪动态角速度和温度 #define TEST_Mag 0 //测试磁场传感器指南针 #define TEST_INT 1 //测试中断触发 // 直接调用这个函数返回0-360度指南针效果 float GetCompassAngle(short mx, short my, short mz) { float angle; angle atan2((float)my, (float)mx) * 180.0 / 3.14159; if(angle 0) angle 360; return angle; } int main() { float threshold 1.0; float data_x 0, data_y 0, data_z 0, data_temp 0; short gyro_x, gyro_y, gyro_z, mag_x, mag_y, mag_z; float gyro_pitch, gyro_roll; u8 id 0; SysTick_Init(72); //72为SYSCLK delay_ms延时函数需要使用 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断分组2:2 在使用中断回调函数时都需要调用, 这里虽然没有使用中断但还是留着 USART1_Init(9600); OLED_Init(); OLED_ColorTurn(0); //0正常显示1 反色显示 OLED_DisplayTurn(0); //0正常显示 1 屏幕翻转显示 #if (TEST_INT 1) Led_Init(); MPU9250_IIC_Init(); MPU9250_IT_CONFIG_Init(128); //设置触发阈值128 * 4 512mg加速度触发 效果最好 //MPU9250_IT_CONFIG_Init(16); //设置触发阈值16 * 4 64mg加速度触发 拍桌子可触发 MPU9250_TIM4_1MS_Init(); MPU9250_EXIT_Init(); #else MPU9250_IIC_Init(); MPU9250_CONFIG_Init(); //测试中断不需要初始化MPU9250 #endif id Single_Read(ACCEL_ADDRESS, WHO_AM_I); //读到 0x71 通信成功 printf(MPU9250 ID 0x%02X \r\n, id); if(id ! 0x71) { printf(MPU9250 I2C failed!\r\n); } else { printf(MPU9250 ready.\r\n); } while(1) { #if TEST_INT //while循环什么也不干 #endif //1.测试三轴加速度单位g,量程-2g //2.俯仰角横滚角都是通过三轴加速度得到的 #if TEST_Accel READ_MPU9250_Accel(data_x, data_y, data_z); /* 如果需要检测碰撞加速度把这3行屏蔽即可 */ gyro_pitch atan2(-data_x, sqrt(data_y*data_y data_z*data_z)) * 180 / 3.14159; //俯仰角-90 gyro_roll atan2(data_y, data_z) * 180 / 3.14159; //横滚角-180 printf(gyro_pitch%.2f, gyro_roll%.2f\r\n, gyro_pitch, gyro_roll); // 检测是否有超过阈值的加速度变化 if(abs(data_x) threshold || abs(data_y) threshold || abs(data_z) threshold) { printf(Knock detected! Accel: X%.2f, Y%.2f, Z%.2f\r\n, data_x, data_y, data_z); // 在OLED上显示敲击提示 OLED_ShowString(0,0,Knock!,16,1); OLED_Refresh(); delay_ms(200); OLED_Clear(); } else { // 正常显示加速度值 OLED_ShowString(0,0,Unit(-2g):g,16,1); OLED_ShowString(0,16,X-Val:,16,1); OLED_ShowString(0,32,Y-Val:,16,1); OLED_ShowString(0,48,Z-Val:,16,1); OLED_ShowFloat(60,16,data_x,1,3,16,1); OLED_ShowFloat(60,32,data_y,1,3,16,1); OLED_ShowFloat(60,48,data_z,1,3,16,1); OLED_Refresh(); } delay_ms(50); // 敲击检测用50ms足够 OLED_Clear(); #endif //1.测试陀螺仪及温度这里只能测试角速度不能检测静态姿态 //2.这里温度测试正常浮点 #if TEST_Gyro_Temp READ_MPU9250_Gyro(gyro_x, gyro_y, gyro_z); //这里读取的是-2000但是有静态漂移实际应用中需要减去静态漂移 READ_MPU9250_Temp(data_temp); // 串口输出 printf(Gyro/Temp: X%d Y%d Z%d Temp%f\r\n, gyro_x, gyro_y, gyro_z, data_temp); // OLED显示 OLED_ShowString(0,0,Temp:,16,1); OLED_ShowString(0,16,X:,16,1); OLED_ShowString(0,32,Y:,16,1); OLED_ShowString(0,48,Z:,16,1); OLED_ShowFloat(60,0,data_temp,2,3,16,1); OLED_ShowIntNum(60,16,gyro_x,5,16,1); OLED_ShowIntNum(60,32,gyro_y,5,16,1); OLED_ShowIntNum(60,48,gyro_z,5,16,1); OLED_Refresh(); delay_ms(200); OLED_Clear(); #endif //测试磁轴传感器显示指南针效果 #if TEST_Mag READ_MPU9250_Mag(mag_x, mag_y, mag_z); // OLED显示RAW值 OLED_ShowString(0,0,Mag(s16),16,1); OLED_ShowString(0,16,X:,16,1); OLED_ShowString(0,32,Y:,16,1); OLED_ShowString(0,48,Z:,16,1); OLED_ShowIntNum(60,16,mag_x,5,16,1); OLED_ShowIntNum(60,32,mag_y,5,16,1); OLED_ShowIntNum(60,48,mag_z,5,16,1); printf(角度: %.0f°\r\n, GetCompassAngle(mag_x, mag_y, mag_z)); //串口显示指南针效果 OLED_Refresh(); delay_ms(200); OLED_Clear(); #endif } }四、效果演示开机后触碰唤醒触发INT前led1灭触发后打开led1再次触发关闭led1: