1、什么是I2C通信协议,为什么要设计I2C通信协议?
(1)I2C(Inter - Integrated Circuit)通信协议是一种 同步、半双工(对话式)、多模块、有应答机制 的串行通信协议,主要用于连接低速设备,如微控制器、传感器、EEPROM等;
(2)设计目的:为了简化硬件连接、支持多设备通信、低功耗、可软件实现。
2、采用I2C协议的模块:MPU6050模块、OLED模块、AT24C02存储器模块、DS3231实时时钟模块;
3、两根通信线:SCL时钟线 + SDA数据线
4、I2C硬件电路:

硬件要求:
① 从机不允许控制SCL总线
② 从机不能主动发起控制SDA总线
(只能在主机发送读取命令后或者从机应答时,控制SDA总线)
③ I2C设备从机地址一般是7位/10位
5、I2C时序基本单元
(1)起始条件
![]()
(2)终止条件
![]()

(3)发送一个字节
① 主机拉低SCL,SCL从1 --->0;
② SCL=0, 主机写SDA;
③ 主机释放SCL,SCL从0 --->1;
④ SCL=1,从机读SDA;
(4)接收一个字节
主机释放SDA控制权
① 主机拉低SCL,SCL从1 --->0;
② SCL=0,从机写SDA;
③ 主机释放SCL,SCL从0--->1;
④ SCL=1,主机读SDA;
(5)发送应答
主机接收到数据后,发送0表示主机应答,发送1表示主机非应答
(6)接收应答
主机发送数据后,释放SDA控制权
接收到0表示从机应答,接收到1表示从机非应答
6、I2C读写时序
(1)指定地址写数据
起始条件 + 从机地址(7位)+ 读写位(0写)+ 从机应答 + 寄存器地址/指令控制字/存储器地址 + 从机应答(0) + 数据 + 终止条件
(2)当前地址读数据
起始条件 + 从机地址(7位)+ 读写位(1读)+ 从机应答 + 主机接收数据 + 主机应答(0) + 终止条件
(3)指定地址读数据
起始条件 + 从机地址(7位)+ 读写位(0写)+ 从机应答 + 寄存器地址/指令控制字/存储器地址 + 从机应答(0)+
重复起始条件 + 主机接收数据 + 主机应答(0)+ 终止条件
7、I2C点名MPU6050实验(MPU6050作为从机的地址是0xD0或者0xD1,取决于AD0引脚)

MyI2C.c
#include "stm32f10x.h"
#include "Delay.h"
void MyI2C_W_SCL(uint8_t uiValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)uiValue);
Delay_us(10);
}
void MyI2C_W_SDA(uint8_t uiValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)uiValue);
Delay_us(10);
}
uint8_t MyI2C_R_SDA(void)
{
uint8_t uiRet = 0;
uiRet = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
Delay_us(10);
return uiRet;
}
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 uiByte)
{
uint8_t i = 0;
for(i=0; i<8; i++)
{
MyI2C_W_SDA(uiByte & (0x80 >> i));
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
}
void MyI2C_SendAck(uint8_t uiAckBit)
{
MyI2C_W_SDA(uiAckBit);
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
uint8_t MyI2C_ReceiveByte(void)
{
uint8_t uiLoop = 0;
uint8_t uiByte = 0;
MyI2C_W_SDA(1);
for(uiLoop=0; uiLoop<8; uiLoop++)
{
MyI2C_W_SCL(1);
if( Bit_SET == MyI2C_R_SDA() )
{
uiByte |= (0x80 >> uiLoop);
}
MyI2C_W_SCL(0);
}
return uiByte;
}
uint8_t MyI2C_ReceiveAck(void)
{
uint8_t uiAckBit = 0;
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
uiAckBit = MyI2C_R_SDA();
MyI2C_W_SCL(0);
return uiAckBit;
}
void MyI2C_Init(void)
{
/* 1、开启GPIOB时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
/* 2、GPIO引脚初始化,SCL和SDA引脚分别是PB10和PB11引脚 */
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);
/* 3、空闲状态将引脚初始化为高电平 */
GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);
}
main.c
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "MyI2C.h"
int main(void)
{
uint8_t uiAck = 0;
OLED_Init();
MyI2C_Init();
MyI2C_Start();
MyI2C_SendByte(0xD2); // AD0引脚接高电平从机地址为0xD1, AD0引脚接低电平从机地址为0xD0。
uiAck = MyI2C_ReceiveAck();
MyI2C_Stop();
OLED_ShowNum(1, 1, uiAck,5);
while(1)
{
}
}
7、I2C读写MPU6050寄存器实验
MPU6050.C
#include "stm32f10x.h"
#include "MyI2C.h"
#define MPU6050_ADDRESS 0xD0 //MPU6050设备地址
void MPU6050_WriteReg(uint8_t uiAddr, uint8_t uiData)
{
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS);
MyI2C_ReceiveAck();
MyI2C_SendByte(uiAddr);
MyI2C_ReceiveAck();
MyI2C_SendByte(uiData);
MyI2C_ReceiveAck();
MyI2C_Stop();
}
uint8_t MPU6050_ReadReg(uint8_t uiAddr)
{
uint8_t uiData = 0x00;
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS);
MyI2C_ReceiveAck();
MyI2C_SendByte(uiAddr);
MyI2C_ReceiveAck();
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS | 0x01);
MyI2C_ReceiveAck();
uiData = MyI2C_ReceiveByte();
MyI2C_SendAck(1);
MyI2C_Stop();
return uiData;
}
void MPU6050_Init(void)
{
MyI2C_Init();
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"
int main(void)
{
uint8_t uiID = 0x00;
/* 模块初始化 */
OLED_Init();
MPU6050_Init();
MPU6050_WriteReg(0x6B, 0x02);
/* 读取寄存器0x6B */
uiID = MPU6050_ReadReg(0x6B);
OLED_ShowHexNum(1, 1, uiID, 2);
Delay_s(2);
/* 读取寄存器0x75 */
uiID = MPU6050_ReadReg(0x75);
OLED_ShowHexNum(1, 1, uiID, 2);
while(1)
{
}
}
同步半双工
浙公网安备 33010602011771号