目录
一、I2C时序
在学习51单片机时,已经接触过I2C基本时序了,简单回顾一下。
I2C的基本时序有:
- 起始信号、停止信号;
- 接收一个字节、发送一个字节;
- 接收应答、发送应答。
程序如下:
#include "stm32f10x.h" // Device header
#include "Delay.h"
/*
*函数名 :MyI2C_W_SCL(uint8_t Bitvalue)
*函数功能 :主机操作SCL线
*输入 :Bitvalue:高电平还是低电平,范围0或1
*输出 :无
*/
void MyI2C_W_SCL(uint8_t Bitvalue)
{
GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)Bitvalue);
Delay_us(10);
}
/*
*函数名 :MyI2C_R_SDA(void)
*函数功能 :读取SDA线上的数据
*输入 :无
*输出 :bit:高电平还是低电平,范围0或1
*/
uint8_t MyI2C_R_SDA(void)
{
uint8_t bit=0;
bit=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
Delay_us(10);
return bit;
}
/*
*函数名 :MyI2C_W_SDA(uint8_t Bitvalue)
*函数功能 :写数据到SDA线上
*输入 :Bitvalue:高电平还是低电平,范围0或1
*输出 :无
*/
void MyI2C_W_SDA(uint8_t Bitvalue)
{
GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)Bitvalue);
Delay_us(10);
}
/*
*函数名 :MyI2C_Init(void)
*函数功能 :I2C初始化
*输入 :无
*输出 :无
*/
void MyI2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//开漏输出模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/*
*函数名 :MyI2C_Start(void)
*函数功能 :I2C起始信号
*输入 :无
*输出 :无
*/
void MyI2C_Start(void)
{
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
MyI2C_W_SDA(0);
MyI2C_W_SCL(0);//保证每个时序单元操作完后,SCL为低电平
}
/*
*函数名 :MyI2C_Stop(void)
*函数功能 :I2C停止信号
*输入 :无
*输出 :无
*/
void MyI2C_Stop(void)
{
MyI2C_W_SDA(0);
MyI2C_W_SCL(1);
MyI2C_W_SDA(1);
}
/*
*函数名 :MyI2C_WriteByte(uint8_t Byte)
*函数功能 :主机写入一个字节数据
*输入 :Byte:写入的数据 范围:0x00~0xff
*输出 :无
*/
void MyI2C_WriteByte(uint8_t Byte)
{
uint8_t i=0;
for(i=0;i<8;i++)
{
MyI2C_W_SDA(Byte&(0x80)>>i);
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
}
/*
*函数名 :MyI2C_ReadByte(void)
*函数功能 :主机读取一个字节数据
*输入 :无
*输出 :Byte:读出的数据 范围:0x00~0xff
*/
uint8_t MyI2C_ReadByte(void)
{
uint8_t i,Byte=0;
MyI2C_W_SDA(1);//释放总线
for(i=0;i<8;i++)
{
MyI2C_W_SCL(1);
if(MyI2C_R_SDA()==1){Byte|=0x80>>i;}
MyI2C_W_SCL(0);
}
return Byte;
}
/*
*函数名 :MyI2C_SeedAck(uint8_t Ack)
*函数功能 :主机发送应答
*输入 :Ack:应答信号 可以是:0(应答)或1(非应答)
*输出 :无
*/
void MyI2C_SeedAck(uint8_t Ack)
{
MyI2C_W_SDA(Ack);
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
/*
*函数名 :MyI2C_ReciveAck(void)
*函数功能 :主机接收应答
*输入 :无
*输出 :Ack:应答信号 可以是:0(应答)或1(非应答)
*/
uint8_t MyI2C_ReciveAck(void)
{
uint8_t Ack=0;
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
Ack=MyI2C_R_SDA();
MyI2C_W_SCL(0);
return Ack;
}
1.1 指定地址写数据
- 指定地址写: 对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)。
1.2 指定地址读数据
- 指定地址读:对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)
- 可以读取连续地址中的数据。
二、MPU6050简介
- MPU6050是一个6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加速度、角速度参数,通过数据融合,可进一步得到姿态角,常应用于平衡车、飞行器等需要检测自身姿态的场景
- 3轴加速度计(Accelerometer):测量X、Y、Z轴的加速度
- 3轴陀螺仪传感器(Gyroscope):测量X、Y、Z轴的角速度
2.1 MPU6050的参数
- 16位ADC采集传感器的模拟信号,量化范围:-32768~32767
- 加速度计满量程选择:±2、±4、±8、±16(g)
- 陀螺仪满量程选择: ±250、±500、±1000、±2000(°/sec)
- 可配置的数字低通滤波器
- 可配置的时钟源
- 可配置的采样分频 I2C从机地址:1101000(AD0=0) 1101001(AD0=1)
2.2 MPU6050的硬件电路
- VCC、GND:电源
- SCL、 SDA :I2C通信引脚
- XCL、XDA:主机I2C通信引脚
- AD0:从机地址最低位,可配置从机地址
- INT:中断信号输出
2.3 MPU6050框图
- 在使用MPU6050前要先初始化,包括:解除sleep状态、取消各个轴的待机、分频、低通滤波配置、加速度计配置、陀螺仪配置。这些都是通过写入寄存器数据来实现的。
- MPU6050内置了AD转换器,自动将测量的模拟量转换成数字量,存放在对应的寄存器中,只需要对各个寄存器进行读操作,就可以把数据读取出来。
程序如下:
#include "stm32f10x.h" // Device header
#include "I2C.h"
#include "MPU6050_Reg.h"
#define MPU6050_address 0xD0
/*
*函数名 :MPU6050_WriteReg(uint8_t regaddress,uint8_t Data)
*函数功能 :指定寄存器地址,往MPU6050的寄存器中写入数据
*输入 :regaddress:寄存器的地址 @ref MPU6050_Reg.h
Data :写入的数据
*输出 :无
*/
void MPU6050_WriteReg(uint8_t regaddress,uint8_t Data)
{
MyI2C_Start();
MyI2C_WriteByte(MPU6050_address);
MyI2C_ReciveAck();
MyI2C_WriteByte(regaddress);
MyI2C_ReciveAck();
MyI2C_WriteByte(Data);
MyI2C_ReciveAck();
MyI2C_Stop();
}
/*
*函数名 :MPU6050_Init(void)
*函数功能 :MPU6050初始化
*输入 :无
*输出 :无
*/
void MPU6050_Init(void)
{
MyI2C_Init();
MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x01);
MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0x00);
MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09);
MPU6050_WriteReg(MPU6050_CONFIG,0x06);
MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0x18);
MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x18);
}
/*
*函数名 :MPU6050_ReadReg(uint8_t regaddress)
*函数功能 :指定寄存器地址,读取MPU6050寄存器中的数据
*输入 :regaddress:寄存器的地址 @ref MPU6050_Reg.h
*输出 :Data:读取的数据
*/
uint8_t MPU6050_ReadReg(uint8_t regaddress)
{
uint8_t Data=0;
MyI2C_Start();
MyI2C_WriteByte(MPU6050_address);
MyI2C_ReciveAck();
MyI2C_WriteByte(regaddress);
MyI2C_ReciveAck();
MyI2C_Start();
MyI2C_WriteByte(MPU6050_address+1);
MyI2C_ReciveAck();
Data=MyI2C_ReadByte();
MyI2C_SeedAck(1);
MyI2C_Stop();
return Data;
}
/*
*函数名 :MPU6050_ReadRegData(uint16_t*Ax,uint16_t*Ay,uint16_t*Az,
uint16_t*Gx,uint16_t*Gy,uint16_t*Gz)
*函数功能 :连续读取MPU6050寄存器中的数据(数字量)
*输入 :Ax:x轴的加速度 0-32768
Ay:y轴的加速度 0-32768
Az:z轴的加速度 0-32768
Gx:x轴的角速度 0-32768
Gy:y轴的角速度 0-32768
Gz:z轴的角速度 0-32768
*输出 :无
*/
void MPU6050_ReadRegData(uint16_t*Ax,uint16_t*Ay,uint16_t*Az,
uint16_t*Gx,uint16_t*Gy,uint16_t*Gz)
{
int16_t Data_H,Data_L=0;
MyI2C_Start();
MyI2C_WriteByte(MPU6050_address);
MyI2C_ReciveAck();
MyI2C_WriteByte(MPU6050_ACCEL_XOUT_H);
MyI2C_ReciveAck();
MyI2C_Start();
MyI2C_WriteByte(MPU6050_address+1);
MyI2C_ReciveAck();
Data_H=MyI2C_ReadByte();
MyI2C_SeedAck(0);
Data_L=MyI2C_ReadByte();
MyI2C_SeedAck(0);
*Ax=Data_H<<8|Data_L;
Data_H=MyI2C_ReadByte();
MyI2C_SeedAck(0);
Data_L=MyI2C_ReadByte();
MyI2C_SeedAck(0);
*Ay=Data_H<<8|Data_L;
Data_H=MyI2C_ReadByte();
MyI2C_SeedAck(0);
Data_L=MyI2C_ReadByte();
MyI2C_SeedAck(0);
*Az=Data_H<<8|Data_L;
Data_H=MyI2C_ReadByte();
MyI2C_SeedAck(0);
Data_L=MyI2C_ReadByte();
MyI2C_SeedAck(0);
Data_H=MyI2C_ReadByte();
MyI2C_SeedAck(0);
Data_L=MyI2C_ReadByte();
MyI2C_SeedAck(0);
Data_H=MyI2C_ReadByte();
MyI2C_SeedAck(0);
Data_L=MyI2C_ReadByte();
MyI2C_SeedAck(0);
*Gx=Data_H<<8|Data_L;
Data_H=MyI2C_ReadByte();
MyI2C_SeedAck(0);
Data_L=MyI2C_ReadByte();
*Gy=Data_H<<8|Data_L;
MyI2C_SeedAck(1);
MyI2C_Stop();
Data_H=MPU6050_ReadReg(0x47);
Data_L=MPU6050_ReadReg(0x48);
*Gz=Data_H<<8|Data_L;
}