1.IIC的作用
芯片间总线(Inter Interface Circuit,IIC,现在基本采用荷兰飞利浦的IIC总线的技术规范。
带有IIC总线的接口的单片机都可直接与具有IIC总线接口的各种扩展器件(如存储器、I/O芯片、A/D、D/A、键盘、显示器)连接。这样就大大的简化了总线的数量。并且IIC通讯支持多主多从
总线上的任意模块都可以跳出来当主机,让IIC协议仲裁。
该通讯协议通过两根通讯线:双向的串行数据线SDA和串行时钟线SCL
特点:同步,半双工,带数据应答机制
2.IIC的硬件电路
所有I2C设备的SCL连在一起,SDA连在一起
设备的SCL和SDA均要配置成开漏输出模式(在一主多从的模式下也可以是推挽输出)
SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右(弱上拉杜绝电路短路现象)
任何时候,SCL时钟线都由主机掌握,SDA在主机接收字节和接受应答才由从机控制
3.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)
4.IIC完整时序
1.指定地址写
步骤:起始条件->主机发送一个字节时序(从机地址+读写位(1读,0写))->从机接收应答->指定设备内部寄存器地址->从机接收应答->主机发送内容->从机接收应答->结束条件。
2.当前地址读
步骤:起始条件->主机发送一个字节时序(从机地址+读写位(1读,0写))->从机接收应答->读取从机内容->从机发送应答->结束条件。
读取的数据被放在一个线性指针区域,每读取一位,指针往后移一字节(地址是没有指向性,很少用这种读取方式)
3.指定地址读
步骤:起始条件->主机发送一个字节时序(从机地址+读写位(1读,0写))->从机接收应答->指定设备内部寄存器地址->从机接收应答->SR(重复起始条件,前面相当于指定地址写,但还没写,因此后面加上读取内容,就是指定地址读)->主机发送(从机地址+1读)->从机接收应答->读取从机内容->从机发送应答->结束条件。
5.mpu6050的基本信息
1.简介
MPU6050是一个6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加速度、角速度参数,通过数据融合,可进一步得到姿态角,常应用于平衡车、飞行器等需要检测自身姿态的场景
3轴加速度计(Accelerometer):测量X、Y、Z轴的加速度
3轴陀螺仪传感器(Gyroscope):测量X、Y、Z轴的角速度
扩展:3轴磁力计和气压计(用于无人机的高度确定)
2.基本参数
16位ADC采集传感器的模拟信号,量化范围:-32768~32767
加速度计满量程选择:±2、±4、±8、±16(g)
陀螺仪满量程选择: ±250、±500、±1000、±2000(°/sec)
可配置的数字低通滤波器 可配置的时钟源
可配置的采样分频 I2C从机地址:1101000(AD0=0) 1101001(AD0=1)
6.IIC读取mpu6050(软件读取)
1.对IIC通讯引脚的封装:函数封装和宏定义
(为了更好移植和修改程序)
void MyI2C_W_SCL(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)BitValue);
Delay_us(10);
}
void MyI2C_W_SDA(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_9, (BitAction)BitValue);
Delay_us(10);
}
uint8_t MyI2C_R_SDA(void)
{
uint8_t BitValue;
BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_9);
Delay_us(10);
return BitValue;
}
//库函数定义,方便改动引脚和延迟时间
#define OLED_W_SCL(x) GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)(x))
#define OLED_W_SDA(x) GPIO_WriteBit(GPIOB, GPIO_Pin_9, (BitAction)(x))
//宏定义方式
2.IIC时序基本单元的编写
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_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB, GPIO_Pin_8 | GPIO_Pin_9);
}
//对GPIO口的初始化,用开漏输出
void MyI2C_Start(void)
{
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
MyI2C_W_SDA(0);
MyI2C_W_SCL(0);
}
//起始条件
void MyI2C_Stop(void)
{
MyI2C_W_SDA(0);
MyI2C_W_SCL(1);
MyI2C_W_SDA(1);
}
//终止条件
void MyI2C_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i ++)
{
MyI2C_W_SDA(Byte & (0x80 >> i));//可以从高位读取地址
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
}
//主机发送一个字节
uint8_t MyI2C_ReceiveByte(void)
{
uint8_t i, Byte = 0x00;
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;
}
//主机接收字节
void MyI2C_SendAck(uint8_t AckBit)
{
MyI2C_W_SDA(AckBit);
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
//从机发送应答
uint8_t MyI2C_ReceiveAck(void)
{
uint8_t AckBit;
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
AckBit = MyI2C_R_SDA();
MyI2C_W_SCL(0);
return AckBit;
}
//从机接收应答
3.根据需求初始化mpu6050
以及建立mpu6050地址库
4.通过指定地址读取数据
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
uint8_t Data;
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS);
MyI2C_ReceiveAck();
MyI2C_SendByte(RegAddress);
MyI2C_ReceiveAck();
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS | 0x01);
MyI2C_ReceiveAck();
Data = MyI2C_ReceiveByte();
MyI2C_SendAck(1);
MyI2C_Stop();
return Data;
}//指定地址读
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 = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);//高八位数据
DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);//低八位数据
*AccX = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
*AccY = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
*AccZ = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
*GyroX = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
*GyroY = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
*GyroZ = (DataH << 8) | DataL;
}//通过指针的形式返回多组数据