stm32与mpu6050通讯
摘要:本例子是采用stm32f103rbt6和mpu6050的通讯实验,mpu6050是直接淘宝上买的GY-521模块。
本例子是库函数版本。
结构:
调用ISys.c里的MPU6050_Configuration();函数来初始化以下所有,包含iic和mpu6050。
mpu6050.c 初始化mpu6050的各个参数,并且给出输出加速度和陀螺仪数据的函数
iic_analog.c iic的模拟,初始化iic
mpuiic.c iic所用引脚的gpio初始化,以及各种操作函数
Kalman_Filter.c 卡尔曼滤波器
ISys.c 调用以上各个函数完成初始化
代码如下:
mpu6050.h
#ifndef _MPU6050_h_
#define _MPU6050_h_
#define SMPLRT_DIV 0x19 //陀螺仪采样率,典型值:0x07(125Hz)
#define CONFIG 0x1A //低通滤波频率,典型值:0x06(5Hz)
#define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
#define ACCEL_CONFIG 0x1C //加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)
#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 PWR_MGMT_1 0x6B //电源管理,典型值:0x00(正常启用)
#define WHO_AM_I 0x75 //IIC地址寄存器(默认数值0x68,只读)
#define SLAVEADRESS 0xD0 //IIC写入时的地址字节数据,+1为读取
//初始化MPU6050
extern void MPU6050_Inital(void);
//获取加速度计的值
extern short getAccX(void);
extern short getAccY(void);
extern short getAccZ(void);
//获取陀螺仪的值
extern short getGyroX(void);
extern short getGyroY(void);
extern short getGyroZ(void);
//获取温度
extern short getTemperature(void);
#endif
mpu6050.c
#include "stm32f10x.h"
#include "MPU6050.h"
#include "iic_analog.h"
#include "delay.h"
void delay_IIC( int ms );
void MPU6050_Inital(void)
{
delay_IIC( 100 );
Single_Write_IIC( SLAVEADRESS , PWR_MGMT_1 , 0x00 ); //解除休眠
delay_ms(1000); //延时,保证初始化成功
Single_Write_IIC( SLAVEADRESS , PWR_MGMT_1 , 0x03 ); //选时钟
Single_Write_IIC( SLAVEADRESS , SMPLRT_DIV , 0x00 ); //陀螺仪采样率,1khz貌似不错
Single_Write_IIC( SLAVEADRESS , CONFIG , 0x03 ); //加速度44hz滤波,陀螺仪42hz滤波
Single_Write_IIC( SLAVEADRESS , GYRO_CONFIG , 0x18 ); //陀螺仪最大量程-+2000度每秒 灵敏度:16.4 LSB/(o/s)
Single_Write_IIC( SLAVEADRESS , ACCEL_CONFIG , 0x08 ); //加速度最大量程-+4g 灵敏度: 8192 LSB/g
delay_IIC( 100 );
}
short getAccX(void)
{
short AccX = 0;
char AccXH = 0, AccXL = 0;
AccXH = Single_Read_IIC( SLAVEADRESS , ACCEL_XOUT_H );
AccXL = Single_Read_IIC( SLAVEADRESS , ACCEL_XOUT_L );
AccX = (AccXH<<8)|AccXL;
return AccX;
}
short getAccY(void)
{
short AccY = 0;
char AccYH = 0 , AccYL = 0;
AccYH = Single_Read_IIC( SLAVEADRESS , ACCEL_YOUT_H );
AccYL = Single_Read_IIC( SLAVEADRESS , ACCEL_YOUT_L );
AccY = (AccYH<<8)|AccYL;
return AccY;
}
short getAccZ(void)
{
short AccZ = 0;
char AccZH = 0 , AccZL = 0;
AccZH = Single_Read_IIC( SLAVEADRESS , ACCEL_ZOUT_H );
AccZL = Single_Read_IIC( SLAVEADRESS , ACCEL_ZOUT_L );
AccZ = (AccZH<<8)|AccZL;
return AccZ;
}
short getGyroX(void)
{
short GyroX = 0;
char GyroXH = 0 , GyroXL = 0;
GyroXH = Single_Read_IIC( SLAVEADRESS , GYRO_XOUT_H );
GyroXL = Single_Read_IIC( SLAVEADRESS , GYRO_XOUT_H );
GyroX = (GyroXH<<8)|GyroXL;
return GyroX;
}
short getGyroY(void)
{
short GyroY = 0;
char GyroYH = 0 , GyroYL = 0;
GyroYH = Single_Read_IIC( SLAVEADRESS , GYRO_YOUT_H );
GyroYL = Single_Read_IIC( SLAVEADRESS , GYRO_YOUT_H );
GyroY = (GyroYH<<8)|GyroYL;
return GyroY;
}
short getGyroZ(void)
{
short GyroZ = 0;
char GyroZH = 0 , GyroZL = 0;
GyroZH = Single_Read_IIC( SLAVEADRESS , GYRO_ZOUT_H );
GyroZL = Single_Read_IIC( SLAVEADRESS , GYRO_ZOUT_H );
GyroZ = (GyroZH<<8)|GyroZL;
return GyroZ;
}
short getTemperature(void)
{
short temperature = 0;
char temperatureH = 0 , temperatureL = 0;
temperatureH = Single_Read_IIC( SLAVEADRESS , TEMP_OUT_H );
temperatureL = Single_Read_IIC( SLAVEADRESS , TEMP_OUT_L );
temperature = (temperatureH<<8)|temperatureL;
return temperature;
}
void delay_IIC( int ms )
{
int i,j;
for( i = 0 ; i < ms ; i++ )
{
for( j = 0 ; j < 30000 ; j++ );
}
}
因使用IIC通讯,所以要初始化IIC。
mpuiic.h
#ifndef __MPUIIC_H
#define __MPUIIC_H
#include "sys.h"
//IO方向设置
#define MPU_SDA_IN() {GPIOB->CRH&=0XFFFF0FFF;GPIOB->CRH|=8<<12;}
#define MPU_SDA_OUT() {GPIOB->CRH&=0XFFFF0FFF;GPIOB->CRH|=3<<12;}
//IO操作函数
#define MPU_IIC_SCL PBout(10) //SCL
#define MPU_IIC_SDA PBout(11) //SDA
#define MPU_READ_SDA PBin(11) //输入SDA
//IIC所有操作函数
void MPU_IIC_Delay(void); //MPU IIC延时函数
void MPU_IIC_Init(void); //初始化IIC的IO口
void MPU_IIC_Start(void); //发送IIC开始信号
void MPU_IIC_Stop(void); //发送IIC停止信号
void MPU_IIC_Send_Byte(u8 txd); //IIC发送一个字节
u8 MPU_IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 MPU_IIC_Wait_Ack(void); //IIC等待ACK信号
void MPU_IIC_Ack(void); //IIC发送ACK信号
void MPU_IIC_NAck(void); //IIC不发送ACK信号
void IMPU_IC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 MPU_IIC_Read_One_Byte(u8 daddr,u8 addr);
#endif
mpuiic.c
#include "mpuiic.h"
#include "delay.h"
//MPU IIC 延时函数
void MPU_IIC_Delay(void)
{
delay_us(2);
}
//初始化IIC
void MPU_IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//先使能外设IO PORTB时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIO
GPIO_SetBits(GPIOB,GPIO_Pin_10|GPIO_Pin_11); //PB10,PB11 输出高
}
//产生IIC起始信号
void MPU_IIC_Start(void)
{
MPU_SDA_OUT();//sda线输出
MPU_IIC_SDA=1;
MPU_IIC_SCL=1;
MPU_IIC_Delay();
MPU_IIC_SDA=0;//START:when CLK is high,DATA change form high to low
MPU_IIC_Delay();
MPU_IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void MPU_IIC_Stop(void)
{
MPU_SDA_OUT();//sda线输出
MPU_IIC_SCL=0;
MPU_IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
MPU_IIC_Delay();
MPU_IIC_SCL=1;
MPU_IIC_SDA=1;//发送I2C总线结束信号
MPU_IIC_Delay();
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
u8 MPU_IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
MPU_SDA_IN(); //SDA设置为输入
MPU_IIC_SDA=1;MPU_IIC_Delay();
MPU_IIC_SCL=1;MPU_IIC_Delay();
while(MPU_READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
MPU_IIC_Stop();
return 1;
}
}
MPU_IIC_SCL=0;//时钟输出0
return 0;
}
//产生ACK应答
void MPU_IIC_Ack(void)
{
MPU_IIC_SCL=0;
MPU_SDA_OUT();
MPU_IIC_SDA=0;
MPU_IIC_Delay();
MPU_IIC_SCL=1;
MPU_IIC_Delay();
MPU_IIC_SCL=0;
}
//不产生ACK应答
void MPU_IIC_NAck(void)
{
MPU_IIC_SCL=0;
MPU_SDA_OUT();
MPU_IIC_SDA=1;
MPU_IIC_Delay();
MPU_IIC_SCL=1;
MPU_IIC_Delay();
MPU_IIC_SCL=0;
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void MPU_IIC_Send_Byte(u8 txd)
{
u8 t;
MPU_SDA_OUT();
MPU_IIC_SCL=0;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
MPU_IIC_SDA=(txd&0x80)>>7;
txd<<=1;
MPU_IIC_SCL=1;
MPU_IIC_Delay();
MPU_IIC_SCL=0;
MPU_IIC_Delay();
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 MPU_IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
MPU_SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
MPU_IIC_SCL=0;
MPU_IIC_Delay();
MPU_IIC_SCL=1;
receive<<=1;
if(MPU_READ_SDA)receive++;
MPU_IIC_Delay();
}
if (!ack)
MPU_IIC_NAck();//发送nACK
else
MPU_IIC_Ack(); //发送ACK
return receive;
}
iic_analog.h
#ifndef _iic_analog_h_
#define _iic_analog_h_
#include "stm32f10x.h"
#include "sys.h"
/*********************************************************************************/
/*修改模拟IIC的读取引脚以及引脚的端口号 */
/*这些宏定义定义好了以后引脚初始化函数会自行初始化时钟使能等必要的参数 */
/*********************************************************************************/
#define IIC_GPIO (GPIOB)
#define IIC_GOIO_SDA (GPIOB)
#define IIC_GPIO_SCL (GPIOB)
#define IIC_SDA (GPIO_Pin_7)
#define IIC_SCL (GPIO_Pin_6)
/*********************************************************************************/
/************************************************************************************/
/*使用此函数 初始化 模拟IIC 其中参数 在以上宏定义中 有定义 使用时只需要修改宏定义即可 */
/* 此函数调用时 请复制以下代码
IIC_GPIO_Configuration( IIC_GOIO_SDA , IIC_SDA , IIC_GPIO_SCL , IIC_SCL ); */
/************************************************************************************/
//IIC 引脚配置
extern void IIC_GPIO_Configuration( GPIO_TypeDef * GPIOx_SDA , uint16_t SDA_Pin , GPIO_TypeDef * GPIOx_SCL , uint16_t SCL_Pin );
/*********************************************************************************/
/*使用以下代码时请勿修改 */
/* */
/*********************************************************************************/
//使用软件模拟I2C
#define SET_SDA { GPIO_SetBits( IIC_GPIO , IIC_SDA ); }
#define RESET_SDA { GPIO_ResetBits( IIC_GPIO , IIC_SDA );}
#define SET_SCL { GPIO_SetBits( IIC_GPIO , IIC_SCL ); }
#define RESET_SCL { GPIO_ResetBits( IIC_GPIO , IIC_SCL); }
#define IIC_SDA_STATE (IIC_GPIO->IDR&IIC_SDA)
#define IIC_SCL_STATE (IIC_GPIO->IDR&IIC_SDA)
#define IIC_DELAY { IIC_Delay(); }
enum IIC_REPLAY_ENUM
{
IIC_NACK = 0,
IIC_ACK = 1
};
enum IIC_BUS_STATE_ENUM
{
IIC_BUS_READY = 0,
IIC_BUS_BUSY=1,
IIC_BUS_ERROR=2
};
//IIC 延时
extern void IIC_Delay(void);
//IIC 启动函数
extern u8 IIC_Start(void);
//IIC 停止函数
extern void IIC_Stop(void);
//IIC 发送动作
extern void IIC_SendACK(void);
//IIC 停止动作
extern void IIC_SendNACK(void);
//IIC 发送单字节
extern u8 IIC_SendByte(u8 Data);
//IIC 接收单字节
extern u8 IIC_RecvByte(void);
//IIC 写入单字节
extern void Single_Write_IIC(u8 SlaveAddress,u8 REG_Address,u8 REG_data);
//IIC 读取单字节
extern u8 Single_Read_IIC(u8 SlaveAddress, u8 REG_Address);
//GPIO 过滤器
extern uint16_t GPIO_Filter( GPIO_TypeDef * GPIOx );
#endif
iic_analog.c
#include "stm32f10x.h"
#include "iic_analog.h"
/************************************************************/
/*模拟IIC引脚初始化函数*/
/************************************************************/
void IIC_GPIO_Configuration( GPIO_TypeDef * GPIOx_SDA , uint16_t SDA_Pin , GPIO_TypeDef * GPIOx_SCL , uint16_t SCL_Pin )
{
GPIO_InitTypeDef GPIO_InitStructure;
uint32_t RCC_GPIOx_SDA = 0;
uint32_t RCC_GPIOx_SCL = 0;
//得到滤波后的引脚端口
RCC_GPIOx_SDA = GPIO_Filter( GPIOx_SDA );
RCC_GPIOx_SCL = GPIO_Filter( GPIOx_SCL );
//使能时钟
RCC_APB2PeriphClockCmd(RCC_GPIOx_SDA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_GPIOx_SCL,ENABLE);
//配置引脚
GPIO_InitStructure.GPIO_Pin = SDA_Pin;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;
GPIO_Init(GPIOx_SDA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = SCL_Pin;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;
GPIO_Init(GPIOx_SCL, &GPIO_InitStructure);
//初始化ICC的模式
SET_SDA;
SET_SCL;
}
/************************************************************/
/************************************************************/
void IIC_Delay(void)
{
u32 i = 5;
while( i-- );
}
/*******************************************************************
TWI_START
发送启动数据
*******************************************************************/
u8 IIC_Start(void)
{
SET_SDA;
IIC_DELAY;
SET_SCL;
IIC_DELAY;
if( IIC_SDA_STATE == RESET )
{
return IIC_BUS_BUSY;
}
RESET_SDA;
IIC_DELAY;
RESET_SCL;
IIC_DELAY;
if( IIC_SDA_STATE == SET )
{
return IIC_BUS_ERROR;
}
return IIC_BUS_READY;
}
/*******************************************************************
TWI_STOP
发送停止数据
*******************************************************************/
void IIC_Stop(void)
{
RESET_SDA;
IIC_DELAY;
SET_SCL;
IIC_DELAY;
SET_SDA;
IIC_DELAY;
}
/*******************************************************************************
* 函数名称:TWI_SendNACK
* 描 述:收到数据,发送NACK
*******************************************************************************/
void IIC_SendNACK(void)
{
RESET_SDA;
IIC_DELAY;
SET_SCL;
IIC_DELAY;
RESET_SCL;
IIC_DELAY;
}
/*******************************************************************************
* 函数名称:TWI_SendACK
* 描 述:收到数据,发送ACK
*******************************************************************************/
void IIC_SendACK(void)
{
SET_SDA;
IIC_DELAY;
SET_SCL;
IIC_DELAY;
RESET_SCL;
IIC_DELAY;
}
/*******************************************************************************
* 函数名称:TWI_SendByte
* 描 述:发送一个字节
*******************************************************************************/
u8 IIC_SendByte(u8 Data)
{
u8 i;
RESET_SCL;
for(i=0;i<8;i++)
{
//---------数据建立----------
if(Data&0x80)
{
SET_SDA;
}
else
{
RESET_SDA;
}
Data<<=1;
IIC_DELAY;
//---数据建立保持一定延时----
//----产生一个上升沿[正脉冲]
SET_SCL;
IIC_DELAY;
RESET_SCL;
IIC_DELAY;//延时,防止SCL还没变成低时改变SDA,从而产生START/STOP信号
//---------------------------
}
//接收从机的应答
SET_SDA;
IIC_DELAY;
SET_SCL;
IIC_DELAY;
if(IIC_SDA_STATE)
{
RESET_SCL;
return IIC_NACK;
}
else
{
RESET_SCL;
return IIC_ACK;
}
}
/*******************************************************************************
* 函数名称:TWI_ReceiveByte
* 描 述:接收一个字节
*******************************************************************************/
u8 IIC_RecvByte(void)
{
u8 i,Dat = 0;
SET_SDA;
RESET_SCL;
Dat=0;
for(i=0;i<8;i++)
{
SET_SCL;//产生时钟上升沿[正脉冲],让从机准备好数据
IIC_DELAY;
Dat<<=1;
if(IIC_SDA_STATE) //读引脚状态
{
Dat|=0x01;
}
RESET_SCL;//准备好再次接收数据
IIC_DELAY;//等待数据准备好
}
return Dat;
}
/******单字节写入*******************************************/
void Single_Write_IIC(u8 SlaveAddress,u8 REG_Address,u8 REG_data)
{
IIC_Start(); //起始信号
IIC_SendByte(SlaveAddress); //发送设备地址+写信号
IIC_SendByte(REG_Address); //内部寄存器地址, //请参考中文pdf22页
IIC_SendByte(REG_data); //内部寄存器数据, //请参考中文pdf22页
IIC_Stop(); //发送停止信号
}
/********单字节读取*****************************************/
u8 Single_Read_IIC(u8 SlaveAddress, u8 REG_Address)
{
u8 REG_data;
IIC_Start(); //起始信号
IIC_SendByte(SlaveAddress); //发送设备地址+写信号
IIC_SendByte(REG_Address); //发送存储单元地址,//从0开始
IIC_Start(); //起始信号
IIC_SendByte(SlaveAddress+1); //发送设备地址+读信号
REG_data = IIC_RecvByte(); //读出寄存器数据
IIC_SendACK();
IIC_Stop(); //停止信号
return REG_data;
}
/*******************************************************************
引脚端口过滤器 返回值为 引脚端口的时钟编号
*******************************************************************/
uint16_t GPIO_Filter( GPIO_TypeDef * GPIOx )
{
uint32_t RCC_GPIOx = 0;
if( GPIOx == GPIOA )
{
RCC_GPIOx = RCC_APB2Periph_GPIOA;
}
else if( GPIOx == GPIOA )
{
RCC_GPIOx = RCC_APB2Periph_GPIOA;
}
else if( GPIOx == GPIOB )
{
RCC_GPIOx = RCC_APB2Periph_GPIOB;
}
else if( GPIOx == GPIOC )
{
RCC_GPIOx = RCC_APB2Periph_GPIOC;
}
else if( GPIOx == GPIOD )
{
RCC_GPIOx = RCC_APB2Periph_GPIOD;
}
else if( GPIOx == GPIOE )
{
RCC_GPIOx = RCC_APB2Periph_GPIOE;
}
else if( GPIOx == GPIOF )
{
RCC_GPIOx = RCC_APB2Periph_GPIOF;
}
else if( GPIOx == GPIOG )
{
RCC_GPIOx = RCC_APB2Periph_GPIOG;
}
return RCC_GPIOx;
}
Kalman_Filter.h
#ifndef _KALMAN_FILTER_H_
#define _KALMAN_FILTER_H_
float Kalman_Filter(float Accel,float Gyro);
#endif
Kalman_Filter.c
#include "Kalman_Filter.h"
float Angle = 0;
//******卡尔曼参数************
const float Q_angle=0.001;
const float Q_gyro=0.003;
const float R_angle=0.5;
const float dt=0.01; //dt为kalman滤波器采样时间;
const char C_0 = 1;
float Q_bias, Angle_err;
float PCt_0, PCt_1, E;
float K_0, K_1, t_0, t_1;
float Pdot[4] ={0,0,0,0};
float PP[2][2] = { { 1, 0 },{ 0, 1 } };
float Kalman_Filter(float Accel,float Gyro)
{
Angle+=(Gyro - Q_bias) * dt; //先验估计
Pdot[0]=Q_angle - PP[0][1] - PP[1][0]; // Pk-先验估计误差协方差的微分
Pdot[1]= -PP[1][1];
Pdot[2]= -PP[1][1];
Pdot[3]=Q_gyro;
PP[0][0] += Pdot[0] * dt; // Pk-先验估计误差协方差微分的积分
PP[0][1] += Pdot[1] * dt; // =先验估计误差协方差
PP[1][0] += Pdot[2] * dt;
PP[1][1] += Pdot[3] * dt;
Angle_err = Accel - Angle; //zk-先验估计
PCt_0 = C_0 * PP[0][0];
PCt_1 = C_0 * PP[1][0];
E = R_angle + C_0 * PCt_0;
K_0 = PCt_0 / E;
K_1 = PCt_1 / E;
t_0 = PCt_0;
t_1 = C_0 * PP[0][1];
PP[0][0] -= K_0 * t_0; //后验估计误差协方差
PP[0][1] -= K_0 * t_1;
PP[1][0] -= K_1 * t_0;
PP[1][1] -= K_1 * t_1;
Angle += K_0 * Angle_err; //后验估计
Q_bias += K_1 * Angle_err; //后验估计
Gyro = Gyro - Q_bias; //输出值(后验估计)的微分=角速度
return Angle;
}
ISys.h
#ifndef _ISys_h_
#define _ISys_h_
extern void MPU6050_Configuration(void);
#endif
ISys.c
#include "stm32f10x.h"
#include "ISys.h"
#include "iic_analog.h"
#include "MPU6050.h"
#include "delay.h"
void MPU6050_Configuration(void)
{
IIC_GPIO_Configuration( IIC_GOIO_SDA , IIC_SDA , IIC_GPIO_SCL , IIC_SCL );
delay_ms(300);
MPU6050_Inital();
}