打印
[信息]

STM32/软件I2C和硬件I2C

[复制链接]
23|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
磨砂|  楼主 | 2025-6-16 21:39 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
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

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

106

主题

4289

帖子

2

粉丝