STM32的硬件IIC和软件模拟IIC在引脚配置上有根本性的区别。
下面是两者的核心区别对比:
特性 | 硬件IIC | 软件模拟IIC (Software IIC) |
---|---|---|
引脚要求 | 必须 使用芯片指定的、具有I2C外设功能复用的特定引脚。 | 几乎任意通用输入输出(GPIO)引脚都可以。 |
配置模式 | 将GPIO模式设置为 GPIO_Mode_AF_OD (复用开漏输出)。 |
将GPIO模式设置为 GPIO_Mode_Out_OD (通用开漏输出)或使用推挽输出加外部上拉。 |
初始化内容 | 需要初始化两个部分 : 1. GPIO引脚 2. I2C外设本身(如I2C1) | 只需要初始化GPIO引脚。所有时序都由软件代码控制。 |
内部上拉电阻 | 通常禁用 内部上拉,依赖外部上拉电阻(4.7kΩ),以确保总线兼容性和可靠性。 | 强烈建议 使用外部上拉电阻(4.7kΩ),禁用内部上拉。 |
灵活性 | 低 。引脚由芯片数据手册(Datasheet)的引脚定义表固定死,不可更改。 | 极高。可以任意更换到其他GPIO引脚,无需修改硬件电路。 |
性能 | 高。由硬件处理,占用CPU资源少,速度稳定且快。 | 低。由CPU循环模拟,占用大量CPU资源,速度慢且受其他中断影响。 |
可靠性 | 高。硬件处理时序精确,抗干扰能力强。 | 较低。时序由软件延时保证,在复杂中断环境中容易出错。 |
详细配置代码对比
假设我们使用STM32F103系列芯片,操作I2C1,其硬件I2C引脚通常是PB6(SCL) 和PB7(SDA)。
1. 硬件IIC配置
硬件IIC的配置分为两大步:配置GPIO 和配置I2C外设。
c
/* 硬件IIC引脚配置函数 */
void HW_I2C_GPIO_Config(void) {
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
// 配置I2C1 SCL (PB6) 和 SDA (PB7)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
// 关键区别:模式必须设置为 **复用开漏输出**
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 速度建议设置高一些
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/* 硬件IIC外设模式配置函数 */
void HW_I2C_Mode_Config(void) {
I2C_InitTypeDef I2C_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); // I2C1时钟位于APB1总线
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = 0x00; // 作为主设备,此地址可任意,不冲突即可
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 400000; // 400kHz
I2C_Init(I2C1, &I2C_InitStructure);
I2C_Cmd(I2C1, ENABLE); // 使能I2C1
}
// 在主函数中,需要调用以上两个函数来完成完整初始化
void main() {
...
HW_I2C_GPIO_Config();
HW_I2C_Mode_Config();
...
}
2. 软件模拟IIC (Software IIC) 配置
软件IIC只需要配置GPIO,非常灵活。这里我们仍然使用PB6和PB7,但你完全可以换成PC0和PC1等其他引脚。
c
// 首先进行宏定义,方便后续编程和修改引脚
#define SOFT_I2C_SDA_PORT GPIOB
#define SOFT_I2C_SDA_PIN GPIO_Pin_7
#define SOFT_I2C_SCL_PORT GPIOB
#define SOFT_I2C_SCL_PIN GPIO_Pin_6
#define SOFT_I2C_SDA_HIGH() GPIO_SetBits(SOFT_I2C_SDA_PORT, SOFT_I2C_SDA_PIN)
#define SOFT_I2C_SDA_LOW() GPIO_ResetBits(SOFT_I2C_SDA_PORT, SOFT_I2C_SDA_PIN)
#define SOFT_I2C_SCL_HIGH() GPIO_SetBits(SOFT_I2C_SCL_PORT, SOFT_I2C_SCL_PIN)
#define SOFT_I2C_SCL_LOW() GPIO_ResetBits(SOFT_I2C_SCL_PORT, SOFT_I2C_SCL_PIN)
#define SOFT_I2C_SDA_READ() GPIO_ReadInputDataBit(SOFT_I2C_SDA_PORT, SOFT_I2C_SDA_PIN)
/* 软件IIC GPIO配置函数 */
void SOFT_I2C_GPIO_Config(void) {
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// 配置SCL引脚为**通用开漏输出**
GPIO_InitStructure.GPIO_Pin = SOFT_I2C_SCL_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 关键区别:通用开漏输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SOFT_I2C_SCL_PORT, &GPIO_InitStructure);
// 配置SDA引脚,初始化为开漏输出。在读取时会临时切换为输入模式。
GPIO_InitStructure.GPIO_Pin = SOFT_I2C_SDA_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 关键区别:通用开漏输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SOFT_I2C_SDA_PORT, &GPIO_InitStructure);
// 将总线置于空闲状态(高电平)
SOFT_I2C_SCL_HIGH();
SOFT_I2C_SDA_HIGH();
}
// 之后,你需要用软件代码模拟I2C的时序,例如:
void SOFT_I2C_Start(void) {
SOFT_I2C_SDA_HIGH();
SOFT_I2C_SCL_HIGH();
Delay_us(5);
SOFT_I2C_SDA_LOW(); // Start condition: SDA falls while SCL is high
Delay_us(5);
SOFT_I2C_SCL_LOW();
}
// ... 还需要实现Stop、SendByte、ReceiveByte等函数
总结与选择建议
场景 | 推荐方案 | 理由 |
---|---|---|
高速、高可靠性、多从机通信 | 硬件IIC | 硬件处理效率高,不占用CPU,时序精确稳定。 |
引脚冲突,硬件IIC引脚被占用 | 软件IIC | 可以灵活更换到其他任意引脚。 |
学习、调试、快速验证 | 软件IIC | 配置简单,便于理解I2C协议本质,调试时可用逻辑分析仪抓波形。 |
项目初期,硬件设计未定型 | 软件IIC | 即使后期硬件引脚变更,软件也只需修改宏定义,无需大改。 |
需要同时使用多个I2C总线 | 组合使用 | STM32通常只有1-2个硬件I2C,可用硬件I2C操作主要设备,软件I2C操作次要设备。 |
核心记住一点:硬件IIC的引脚是芯片固定的,而软件IIC的引脚是你自己任选的。 硬件IIC需要配置外设模式,软件IIC只需要配置GPIO模式并为它们编写模拟时序的函数。无论是软件IIC抑或是硬件IIC都依赖于外部上拉电阻来确保线兼容性和可靠性,这个电阻通常可以取4.7K。