IIC介绍
I2C(Inter IC Bus)是由Philips公司开发的一种通用数据总线
- 两根通信线:SCL(Serial Clock)、SDA(Serial Data)
- 同步,半双工
- 带数据应答
- 支持总线挂载多设备(一主多从、多主多从)
硬件电路
- 所有I2C设备的SCL连在一起,SDA连在一起
- 设备的SCL和SDA均要配置成开漏输出模式
- SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右
IIC禁止所有设备输出强上拉的高电平,采用外置弱上拉电阻+开漏输出的电路结构
IIC时序基本单元
-
起始条件:SCL高电平期间,SDA从高电平切换到低电平
-
终止条件:SCL高电平期间,SDA从低电平切换到高电平
-
发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节
-
接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)
-
发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
-
接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)
IIC时序
指定地址写
对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)
起始位 + 高7位地址位1101000(MPU6050地址)+ 读写位0(写入)+ 接收从机的应答位0
+再一次 主机发送一字节(MPU6050内部寄存器地址)+从机应答:0
+再一次 主机发送一字节(写入的数据)+从机非应答:1+停止位
当前地址读
对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)
起始位 + 高7位地址位1101000(MPU6050地址)+ 读写位1(读出)+ 接收从机的应答位0
+再一次 主机接收一字节:从机在SCL低电平期间写入,主机在SCL高电平时读取+停止位
指定地址读
对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)
起始位 + 高7位地址位1101000(MPU6050地址)+ 读写位0(写入)+ 接收从机的应答位0
+再一次 主机发送一字节(MPU6050内部寄存器地址,寄存器指针指到此处)+从机应答
+Sr(Start Repeat)重复起始条件,用于重置读写位
+ 高7位地址位1101000(MPU6050地址)+ 读写位1(读出)+ 接收从机的应答位0
+再一次 主机接收一字节:从机在SCL低电平期间写入,主机在SCL高电平时读取+停止位
MPU6050简单介绍
MPU6050是一个6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加速度、角速度参数,通过数据融合,可进一步得到姿态角,常应用于平衡车、飞行器等需要检测自身姿态的场景
- 3轴加速度计(Accelerometer):测量X、Y、Z轴的加速度
- 3轴陀螺仪传感器(Gyroscope):测量X、Y、Z轴的角速度
加速度计具有静态稳定性,不具有动态稳定性;陀螺仪具有动态稳定性,不具有静态稳定性;二者进行互补滤波,则可得到静态和动态都相对稳定的姿态角
-
16位ADC采集传感器的模拟信号,量化范围:-32768~32767
-
加速度计满量程选择:±2、±4、±8、±16(g)
-
陀螺仪满量程选择: ±250、±500、±1000、±2000(°/sec)
-
可配置的数字低通滤波器
-
可配置的时钟源
-
可配置的采样分频
-
I2C从机地址:1101000(AD0=0) 1101001(AD0=1)
七位地址里的最低位取决于模块上AD0引脚接高电平或低电平,当AD0=0时,地址为1101000,当AD0=1时,地址为1101001
(16位表示时,有两种方式,①是把1101000转成16进制0x68,但是因为还有一位读写位,一般使用((0x68<<1)| 读写位)。②把0x68左移一位后的数据当作从机地址,也就是0xD0,再或上读写位。写就发送0xD0,读就发送0xD1。)
软件IIC实现代码
IIC.c
#include "stm32f10x.h" // Device header
#include "delay.h"
void MyIIC_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//开漏输出,弱上拉
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);
}
void MyIIC_W_SCL(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);
delay_us(10);
}
void MyIIC_W_SDA(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
delay_us(10);
}
uint8_t MyIIC_R_SDA(void)//读取当前位
{
uint8_t BitValue;
BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
delay_us(10);
return BitValue;
}
void MyIIC_Start(void)//起始信号
{
MyIIC_W_SDA(1);
MyIIC_W_SCL(1);
MyIIC_W_SDA(0);
MyIIC_W_SCL(0);
}
void MyIIC_Stop(void)//终止条件
{
MyIIC_W_SDA(0);
MyIIC_W_SCL(1);
MyIIC_W_SDA(1);
}
void MyIIC_SendByte(uint8_t Byte)//发送一字节
{
uint8_t i;
for(i=0; i<8; i++)
{
MyIIC_W_SDA(Byte & (0x80 >> i));
MyIIC_W_SCL(1);
MyIIC_W_SCL(0);
}
}
uint8_t MyIIC_ReceiveByte(void)//接收一字节
{
uint8_t i, Byte = 0x00;
MyIIC_W_SDA(1);
for(i=0; i<8; i++)
{
MyIIC_W_SCL(1);
if(MyIIC_R_SDA() == 1)//当SDA为1时,写入1;为0时,写入0
Byte |= (0x80 >> i);
MyIIC_W_SCL(0);
}
return Byte;
}
void MyIIC_SendAck(uint8_t AckBit)
{
MyIIC_W_SDA(AckBit);
MyIIC_W_SCL(1);
MyIIC_W_SCL(0);
}
uint8_t MyIIC_ReceiveAck(void)
{
uint8_t AckBit;
MyIIC_W_SDA(1);
MyIIC_W_SCL(1);
AckBit = MyIIC_R_SDA();//主机释放SDA后,从机发送应答
MyIIC_W_SCL(0);
return AckBit;
}
MPU6050.c
#include "stm32f10x.h" // Device header
#include "MyIIC.h"
#include "MPU6050_Reg.h"
#define MPU_6050_ADDRESS 0xD0
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
MyIIC_Start();
MyIIC_SendByte(MPU_6050_ADDRESS);
MyIIC_ReceiveAck();
MyIIC_SendByte(RegAddress);
MyIIC_ReceiveAck();
MyIIC_SendByte(Data);
MyIIC_ReceiveAck();
MyIIC_Stop();
}
uint8_t MPU_6050_ReadReg(uint8_t RegAddress)//指定地址读
{
uint8_t Data;
MyIIC_Start();
MyIIC_SendByte(MPU_6050_ADDRESS);
MyIIC_ReceiveAck();
MyIIC_SendByte(RegAddress);
MyIIC_ReceiveAck();
MyIIC_Start();
MyIIC_SendByte(MPU_6050_ADDRESS | 0x01);
MyIIC_ReceiveAck();
Data = MyIIC_ReceiveByte();
MyIIC_SendAck(1);
MyIIC_Stop();
return Data;
}
void MPU6050_Init(void)
{
MyIIC_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);
}
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
uint8_t DataH, DataL;
DataH = MPU_6050_ReadReg(MPU6050_ACCEL_XOUT_H);//分别读取高位和低位,并利用移位拼接到16位数据中
DataL = MPU_6050_ReadReg(MPU6050_ACCEL_XOUT_L);
*AccX = (DataH << 8) | DataL;
DataH = MPU_6050_ReadReg(MPU6050_ACCEL_YOUT_H);
DataL = MPU_6050_ReadReg(MPU6050_ACCEL_YOUT_L);
*AccY = (DataH << 8) | DataL;
DataH = MPU_6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
DataL = MPU_6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
*AccZ = (DataH << 8) | DataL;
DataH = MPU_6050_ReadReg(MPU6050_GYRO_XOUT_H);
DataL = MPU_6050_ReadReg(MPU6050_GYRO_XOUT_L);
*GyroX = (DataH << 8) | DataL;
DataH = MPU_6050_ReadReg(MPU6050_GYRO_YOUT_H);
DataL = MPU_6050_ReadReg(MPU6050_GYRO_YOUT_L);
*GyroY = (DataH << 8) | DataL;
DataH = MPU_6050_ReadReg(MPU6050_GYRO_ZOUT_H);
DataL = MPU_6050_ReadReg(MPU6050_GYRO_ZOUT_L);
*GyroZ = (DataH << 8) | DataL;
}
MPU6050_Reg.h
#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H
#define MPU6050_SMPLRT_DIV 0x19//采样率分频
#define MPU6050_CONFIG 0x1A//配置寄存器
#define MPU6050_GYRO_CONFIG 0x1B//陀螺仪配置寄存器
#define MPU6050_ACCEL_CONFIG 0x1C//加速度计配置寄存器
#define MPU6050_ACCEL_XOUT_H 0x3B//加速度寄存器X轴的高8位
#define MPU6050_ACCEL_XOUT_L 0x3C//加速度寄存器X轴的低8位
#define MPU6050_ACCEL_YOUT_H 0x3D
#define MPU6050_ACCEL_YOUT_L 0x3E
#define MPU6050_ACCEL_ZOUT_H 0x3F
#define MPU6050_ACCEL_ZOUT_L 0x40
#define MPU6050_TEMP_OUT_H 0x41
#define MPU6050_TEMP_OUT_L 0x42
#define MPU6050_GYRO_XOUT_H 0x43//陀螺仪的x轴
#define MPU6050_GYRO_XOUT_L 0x44
#define MPU6050_GYRO_YOUT_H 0x45
#define MPU6050_GYRO_YOUT_L 0x46
#define MPU6050_GYRO_ZOUT_H 0x47
#define MPU6050_GYRO_ZOUT_L 0x48
#define MPU6050_PWR_MGMT_1 0x6B//电源管理寄存器1,地址是0x6B
#define MPU6050_PWR_MGMT_2 0x6C//电源管理寄存器2,地址是0x6B
#define MPU6050_WHO_AM_I 0x75
#endif
main.c
#include "stm32f10x.h" // Device header
#include "delay.h"
#include "LED.h"
#include "KEY.h"
#include "OLED.h"
#include "MPU6050.h"
int main(void)
{
delay_init();
OLED_Init();
MPU6050_Init();
int16_t AX, AY, AZ, GX, GY, GZ;
while(1)
{
MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);
OLED_ShowSignedNum(1, 1, AX, 5);
OLED_ShowSignedNum(2, 1, AY, 5);
OLED_ShowSignedNum(3, 1, AZ, 5);
OLED_ShowSignedNum(1, 8, GX, 5);
OLED_ShowSignedNum(2, 8, GY, 5);
OLED_ShowSignedNum(3, 8, GZ, 5);
}
}