1 I2C协议
1.1 I2C协议
一种用于连接微控制器和外围设备的两线串行总线协议,适用于短距离低速设备间通信,通信方式有同步、串行和半双工等。
1.2 两线传输
SCL:时钟线,用于同步数据传输。
SDA:数据线,用于传输数据。
1.3 主从设备
在总线上,可以有多个主设备和多个从设备,但在通信时只能有一个主设备控制总线(主设备的选择由仲裁机制决定),总线上每个设备都有一个唯一的地址(7位或10位)。
1.4 仲裁机制
1.4.1 触发条件
两个或多个主设备同时发起传输。
1.4.2 判断条件
主设备在发送高低电平时同时检查总线状态,当主设备输出高电平时检测到有其它设备在拉低电平,则触发机制。
1.4.3 仲裁原理
“线与”逻辑:
1. 任一设备输出低电平(拉低SDA),总线即为低电平。
2. 所有设备输出高电平(释放SDA),总线才为高电平。
示例:
主设备1发送10101010
主设备2发送10101011
前7位“1010101”两个设备发送数据相同;到第8位时主设备1输出低电平拉低SDA,主设备2输出高电平拉高SDA但检测到总线状态与自身冲突,则主设备2退出,主设备1继续传输。
1.5 上拉电阻的作用
1. I²C设备的数据线SDA和时钟线SCL均为开漏输出;只能主动拉低,无法主动输出高电平,当所有设备释放总线(不拉低)时,可以通过上拉电阻将总线拉至高电平。
2. 防止上升沿时间过长导致时序错误。
1.6 传输速率
标准模式(最高100kHz)、快速模式(最高400kHz)和高速模式(最高3.4MHz需设备支持)。
2 I2C协议的基本操作
2.1 数据传输
数据以字节为单位传输,主机每写一个字节后都要等待从机的应答信号ACK或非应答信号NACK,主机每读一个字节后都要向从机发送应答信号ACK或非应答信号NACK;只有在接收到应答信号后,才会继续读/写字节。
2.1.1 读数据
主机通过从机地址向指定从机发送读数据操作,指定从机向主机返回应答信号,主机收到应答信号后,从机主动控制SDA向主机返回8个位的数据;主机在第9个时钟周期向从机发送应答信号,从机继续控制SDA向主机返回8个位的数据,直到主机向从机发送非应答信号时从机便停止数据传输。
2.1.2 写数据
主机通过从机地址向指定从机发送写数据操作,指定从机向主机返回应答信号,主机收到应答信号后,主机控制SDA向从机传输8个位的数据;从机在第9个时钟周期向主机返回应答信号,主机继续控制SDA向从机传输8个位的数据,直到从机向主机返回非应答信号时主机便停止数据传输。
2.2 起始/停止信号(含时序图)
起始信号:SCL为高电平期间,SDA从高电平跳变至低电平。
停止信号:SCL为高电平期间,SDA从低电平跳变至高电平。
2.3 数据的有效性(含时序图)
当SCL为高电平时,SDA数据有效(SDA为高电平时数据为1,低电平时数据为0);当SCL为低电平时,SDA跳变为下一次传输的数据。
2.4 应答/非应答信号(含时序图)
以发送0100 1101为例,时序图如下所示:
3 软/硬件I2C
3.1 软件I2C
GPIO模拟时序,通过代码控制SCL和SDA电平变化来实现通信,可以任意配置引脚(具体需查看MCU引脚分配)。
优点:灵活性高、成本低,可用于非I2C标准设备。
缺点:占用CPU时间CPU持续工作、速度较低通常不高于100KHZ、时序精度低依赖软件延时、需要手动实现仲裁、功耗高。
3.2 硬件I2C
使用单片机内置的I2C外设、硬件自动生成时序、占用硬件资源。
优点:解放CPU、速度快、时序精度稳定、硬件处理仲裁、直接调用库函数简化开发、功耗低。
缺点:成本高、引脚固定。
3.3 I2C初始化
3.3.1 软件I2C初始化
void Model_I2C_Init(void){
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
/*GPIO初始化*/
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);
}
3.3.2 硬件I2C初始化
void Model_I2C_Init(void){
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/*I2C初始化*/
I2C_InitTypeDef I2C_InitStructure;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_ClockSpeed = 100000;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_Init(I2C2, &I2C_InitStructure);
/*I2C使能*/
I2C_Cmd(I2C2, ENABLE);
}
3.3.2.1 模式选择
I2C_Mode
I2C_Mode_I2C:标准I2C模式,适用于主/从设备通信。
I2C_Mode_SMBusDevice/I2C_Mode_SMBusHost:仅需在兼容SMBus协议时使用。
3.3.2.2 时钟速度
I2C_ClockSpeed
时钟速度可自定义,但需低于400kHz。
3.3.2.3 时钟占空比
I2C_DutyCycle
I2C_DutyCycle_2:低电平时间是高电平的2倍(2/1)。
I2C_DutyCycle_16_9:高速模式下可选,低电平时间是高电平的1.78倍(16/9)。
3.3.2.4 应答使能
I2C_Ack
I2C_Ack_Enable:主模式使能用于接收从设备ACK。
I2C_Ack_Disable:特殊场景(如广播地址)可能禁用。
3.3.2.5 地址模式
I2C_AcknowledgedAddress
I2C_AcknowledgedAddress_7bit:7位地址。
I2C_AcknowledgedAddress_10bit:10位地址。
3.3.2.6 自身地址
I2C_OwnAddress1
主模式:设为0x00(无效)。
从模式:需配置唯一地址
3.4 起始/停止信号
3.4.1 软件I2C
#define Model_I2C_W_SCL(x) GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)(x))
#define Model_I2C_W_SDA(x) GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)(x))
/*起始信号*/
void Model_I2C_Start(void){
Model_W_SDA(1); //SDA为高电平
Model_I2C_W_SCL(1); //SCL为高电平
Model_I2C_W_SDA(0); //在SCL高电平期间,拉低SDA
Model_I2C_W_SCL(0); //起始后把SCL也拉低,占用总线
}
/*停止信号*/
void Model_I2C_Stop(void){
Model_I2C_W_SDA(0); //拉低SDA为低电平
Model_I2C_W_SCL(1); //拉高SCL为高电平
Model_I2CW_SDA(1); //在SCL高电平期间,拉高SDA
}
3.4.2 硬件I2C
/*起始信号*/
I2C_GenerateSTART(I2C1, ENABLE);
/*停止信号*/
I2C_GenerateSTOP(I2C1, ENABLE);
3.5 发送一个字节
3.5.1 软件I2C
void Model_I2C_SendByte(uint8_t Byte){
uint8_t i;
for (i = 0; i < 8; i ++){
Model_I2C_W_SDA(Byte & (0x80 >> i));
Model_I2C_W_SCL(1);
Model_I2C_W_SCL(0);
}
}
3.5.2 硬件I2C
I2C_SendData(I2C2, Byte);
3.6 接收一个字节
3.6.1 软件I2C
uint8_t Model_I2C_ReceiveByte(void){
uint8_t i, Byte = 0x00;
Model_I2C_W_SDA(1)
for (i = 0; i < 8; i ++) {
Model_I2C_W_SCL(1);
if (Model_I2C_R_SDA()){
Byte |= (0x80 >> i);
}
Model_I2C_W_SCL(0);
}
return Byte;
}
3.6.2 硬件I2C
I2C_ReceiveData(I2C2);
3.7 发送应答位
3.7.1 软件I2C
void Model_I2C_SendAck(uint8_t Ack){
Model_I2C_W_SDA(Ack);
Model_I2C_W_SCL(1);
Model_I2C_W_SCL(0);
}
3.7.2 硬件I2C
I2C_AcknowledgeConfig(I2C2, ENABLE);// 应答:ENABLE 非应答:DISABLE
3.8 接收应答位
3.8.1 软件I2C
uint8_t Model_I2C_ReceiveAck(void){
uint8_t Ack;
Model_I2C_W_SDA(1);
Model_I2C_W_SCL(1);
Ack = Model_I2C_R_SDA();
Model_I2C_W_SCL(0);
return Ack;
}
3.8.2 硬件I2C
I2C_CheckEvent(I2C2, uint32_t I2C_EVENT);//I2C_EVENT为EVx事件
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/2502_91794894/article/details/148513610
|