I2C通信
名称 | 引脚 | 双工 | 时钟 | 电平 | 设备 |
---|---|---|---|---|---|
I2C |
SCL、SDA | 半双工 | 同步 | 单端 | 多设备 |
I2C(Inter IC Bus)
是由Philips公司开发的一种通用数据总线两根通信线:
SCL
(Serial Clock)、SDA
(Serial Data)同步,半双工
带数据应答
支持总线挂载多设备(一主多从、多主多从)
Ⅰ、硬件电路
所有I2C设备的SCL连在一起,SDA连在一起
设备的SCL和SDA均要配置成开漏输出模式
SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右,高电平驱动能力弱
Ⅱ、IIC时序基本单元
① 起始条件
- SCL高电平期间,SDA从高电平切换到低电平
② 终止条件
- SCL高电平期间,SDA从低电平切换到高电平
③ 发送一个字节
- SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节
发送期间SCL与SDA只能由主机(STM32)控制
- 最后一位为读写标志位(0:写;1:读)
④ 接收一个字节
- SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)
⑤ 发送应答
- 主机在接收完一个字节之后,在下一个时钟发送一位数据,(SDA)数据0表示应答,数据1表示非应答
⑥ 接收应答
- 主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)
Ⅲ、IIC时序
指定地址写单字节
指定地址写多字节
指定地址读单字节
指定地址读多字节
① 指定地址写
- 对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)
② 当前地址读
- 对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)
- 每一次的读写数据都会造成地址指针+1
③ 指定地址读
- 对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)
Sr
:重复起始条件(另启时序)
Ⅳ、MPU6050—6轴姿态传感器(软件I2C)
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
)
1、模块内部电路
引脚 功能 VCC、GND 电源 SCL、SDA I2C通信引脚 XCL、XDA 主机I2C通信引脚 (与扩展设备通信) AD0 从机地址最低位 INT 中断信号输出
2、寄存器地址
3、软件模拟IIC
可任意指定两个引脚,分别作为
IIC
的SCL
和SDA
,本示例中使用的引脚为PB10、PB11
- SCL:PB10
- SDA:PB11
MyI2C.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
/*
SCL:>PB10
SDA:>PB11
*/
void MyI2C_W_SCL(char BitValue) {
GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);
Delay_us(10);
}
void MyI2C_W_SDA(char BitValue) {
GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
Delay_us(10);
}
uint8_t MyI2C_R_SDA(void) {
uint8_t BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
Delay_us(10);
return BitValue;
}
//初始化IIC
void MyI2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;//配置成开漏输出模式
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);//初始化为高电平(释放总线)
}
//I2C开始函数(SCL高电平期间,SDA从高电平切换到低电平,最后拉低SCL)
//为了兼容重复起始条件,应将SDA置高放在SCL置高之前
void MyI2C_Start(void)
{
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
MyI2C_W_SDA(0);
MyI2C_W_SCL(0);
}
//I2C终止函数(SCL高电平期间,SDA从低电平切换到高电平)
void MyI2C_Stop(void)
{
MyI2C_W_SDA(0);
MyI2C_W_SCL(1);
MyI2C_W_SDA(1);
}
//I2C发送一个字节
void MyI2C_SendByte(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);
}
}
//I2C接收一个字节
uint8_t MyI2C_ReceiveByte(void)
{
uint8_t Byte = 0x00;
uint8_t i = 0;
MyI2C_W_SDA(1);//主机释放SDA
for(i = 0;i < 8;i++) {
MyI2C_W_SCL(1);
if(MyI2C_R_SDA() == 1) {
//主机读取SDA
Byte |= (0x80 >> i);
}
MyI2C_W_SCL(0);
}
return Byte;
}
//发送应答
void MyI2C_SendAck(char AckBit)
{
MyI2C_W_SDA(AckBit);
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
//接收应答
uint8_t MyI2C_ReceiveAck(void)
{
MyI2C_W_SDA(1);//松手
MyI2C_W_SCL(1