1. IIC概述
IIC(Inter-Integrated Circuit,集成电路间总线)是一种由飞利浦公司(现恩智浦半导体)开发的两线式串行通信总线,广泛应用于微控制器与各类外设(如 EEPROM、传感器、RTC 时钟芯片等)之间的低速通信。
IIC总线仅需两根信号线即可完成数据传输:
- SDA(Serial Data Line,串行数据线):用于传输数据,双向传输,数据在 SCL 高电平期间必须保持稳定。
- SCL(Serial Clock Line,串行时钟线):用于传输时钟信号,由主机产生,控制数据传输的节奏。
IIC总线具有以下显著特点:
- 两线制:仅需 SDA 和 SCL 两根线,节省引脚资源。
- 多主机多从机:总线上可挂载多个设备,每个设备具有唯一的 7 位或 10 位地址。
- 半双工通信:同一时刻只能有一个方向的数据传输。
- 速率适中:标准模式 100kbit/s,快速模式 400kbit/s,高速模式 3.4Mbit/s。
- 开漏输出:SDA 和 SCL 均为开漏结构,需外接上拉电阻。
在 STM32 中,IIC 外设通常集成在芯片内部,支持硬件 IIC 和软件模拟 IIC 两种实现方式。硬件 IIC 由芯片内部外设自动处理时序,软件模拟 IIC 则通过 GPIO 翻转电平实现,两者各有优劣。
2. IIC协议
2.1 总线空闲状态
当总线处于空闲状态时,SDA 和 SCL 均为高电平。此时总线上没有任何设备占用通信,主机可以发起通信。
2.2 起始条件(START)
起始条件是 IIC 通信的启动标志,由主机产生。其时序要求为:在 SCL 保持高电平期间,SDA 由高电平跳变为低电平。
起始条件产生后,总线进入忙碌状态,其他设备不能再发起通信。
2.3 停止条件(STOP)
停止条件是 IIC 通信的结束标志,同样由主机产生。其时序要求为:在 SCL 保持高电平期间,SDA 由低电平跳变为高电平。
停止条件产生后,总线恢复空闲状态,释放总线控制权。
2.4 数据有效性
IIC 协议规定,数据在传输过程中必须满足以下时序要求:
- 数据稳定期:在 SCL 高电平期间,SDA 上的数据必须保持稳定,不允许发生变化。
- 数据变化期:在 SCL 低电平期间,SDA 上的数据才允许发生变化。
这一规定确保了接收方在 SCL 高电平采样时,读取到的数据是稳定可靠的。
2.5 应答机制(ACK/NACK)
IIC协议采用应答机制来确认数据接收是否成功。每传输一个字节(8位数据)后,接收方需要返回一个应答位:
- ACK(应答):接收方在第9个时钟周期将SDA拉低,表示成功接收数据,可以继续传输。
- NACK(非应答):接收方在第9个时钟周期保持SDA为高电平,表示未成功接收或不再接收数据。
主机在发送数据后,需要释放SDA并等待从机的应答信号;从机在接收数据后,需要主动拉低SDA以产生应答。
2.6 完整数据传输时序
一次完整的IIC数据传输过程如下:
2.7 时序图说明
IIC时序图是理解协议的关键工具,下面用文字描述标准时序图的各个阶段:
起始阶段:SCL为高,SDA从高跳变为低,产生START信号。
地址传输阶段:主机在SCL的驱动下,逐位发送从机地址,先发送最高位(MSB),共7位地址,随后发送1位读写标志(0表示写,1表示读)。
应答阶段:第9个时钟周期,从机拉低SDA,产生ACK应答。
数据传输阶段:主机继续发送数据字节,每个字节8位,每字节后跟一个ACK应答位。数据在SCL高电平期间保持稳定,在SCL低电平期间变化。
停止阶段:SCL为高,SDA从低跳变为高,产生STOP信号,总线释放。
2.8 读写操作示例
写操作流程:主机发送START → 发送从机地址+写标志(0)→ 等待ACK → 发送寄存器地址 → 等待ACK → 发送数据 → 等待ACK → 发送STOP。
读操作流程:主机发送START → 发送从机地址+写标志(0)→ 等待ACK → 发送寄存器地址 → 等待ACK → 重新发送START(重复起始条件)→ 发送从机地址+读标志(1)→ 等待ACK → 读取数据 → 主机发送NACK → 发送STOP。
3. STM32中的IIC实现
3.1 硬件IIC
STM32系列芯片内置IIC外设,支持主机模式和从机模式,具备中断和DMA传输能力。硬件IIC的优点是CPU占用低、时序精确,但配置相对复杂,且部分型号的硬件IIC存在兼容性问题。
3.2 软件模拟IIC
软件模拟IIC通过GPIO引脚手动翻转电平,按照IIC时序要求逐位传输数据。其优点是灵活、兼容性好、不受芯片型号限制,缺点是占用CPU资源较多。
3.3 上拉电阻
由于IIC总线采用开漏输出结构,SDA和SCL必须外接上拉电阻。典型上拉电阻值为4.7kΩ(标准模式)或2.2kΩ(快速模式),具体取值需根据总线电容和通信速率确定。
4. FM24CL04 EEPROM 详解
FM24CL04 是 Cypress(现 Infineon)推出的一款4Kbit 铁电存储器(FRAM),采用 IIC 接口与主机通信。与传统的 EEPROM 相比,FRAM 具有写入速度快、擦写寿命长、功耗低等显著优势,非常适合需要频繁读写数据的应用场景。
4.1 主要特性
- 存储容量:4Kbit(512 × 8 位),组织为两个 256 字节的存储区。
- 接口类型:标准 IIC 接口,支持 100kHz 标准模式和 400kHz 快速模式。
- 写入速度:无需等待写入周期,写入后立即可读,写入速度远快于传统 EEPROM。
- 擦写寿命:高达 10 亿次读写循环,远超普通 EEPROM 的 100 万次。
- 数据保持:掉电后数据可保持 10 年以上。
- 工作电压:2.7V ~ 3.6V,兼容 3.3V 系统。
- 封装形式:常见 SOP-8 封装,引脚与标准 24C04 EEPROM 兼容。
4.2 器件地址与存储区映射
FM24CL04 的从机地址为 7 位,格式为 1010 A2 A1 A0,其中 A2、A1 为硬件地址引脚,A0 为存储区选择位。由于芯片内部划分为两个 256 字节的存储区,A0 位用于选择访问哪个存储区:
- A0 = 0:访问存储区 0(地址 0x00 ~ 0xFF)。
- A0 = 1:访问存储区 1(地址 0x00 ~ 0xFF)。
因此,FM24CL04 的完整从机地址为 0xA0(写)/ 0xA1(读)(对应存储区 0),或 0xA2(写)/ 0xA3(读)(对应存储区 1),具体取决于 A2、A1 引脚的接法。
4.3 写操作时序
FM24CL04 的写操作与标准 IIC EEPROM 类似,但无需等待内部写周期。具体流程如下:
由于 FRAM 无需写周期,主机可以在发送 STOP 后立即发起下一次读写操作,无需像传统 EEPROM 那样轮询 ACK 或等待延时。
4.4 读操作时序
FM24CL04 支持两种读操作方式:当前地址读和随机地址读。
随机地址读流程:
4.5 与 STM32 的硬件连接
FM24CL04 与 STM32 的连接非常简单,只需两根 IIC 信号线加上拉电阻:
- SDA:连接到 STM32 的 IIC 数据引脚(如 PB7)。
- SCL:连接到 STM32 的 IIC 时钟引脚(如 PB6)。
- VCC:连接到 3.3V 电源。
- GND:连接到地。
- WP:写保护引脚,接地表示允许写入,接高电平表示写保护。
- A2、A1:地址引脚,通常接地。
SDA 和 SCL 需要外接上拉电阻(典型值 4.7kΩ),与 STM32 的 IIC 外设或软件模拟 IIC 配合使用。
4.6 应用场景
FM24CL04 凭借其高速写入和超长寿命,广泛应用于以下场景:
- 数据采集系统:频繁记录传感器数据,无需担心擦写寿命。
- 参数存储:保存系统配置参数、校准数据等。
- 掉电保护:在系统掉电瞬间快速保存关键状态。
- 日志记录:记录运行日志、故障信息等。
5. STM32F4xx I2C控制器
STM32F4xx系列微控制器内置了功能完善的I2C外设控制器,支持主机模式和从机模式,具备中断、DMA传输以及多主机仲裁等高级特性。本节将详细介绍STM32F4xx I2C控制器的内部结构、寄存器配置以及实际应用方法。
5.1 I2C控制器内部结构
STM32F4xx的I2C外设主要由以下功能模块组成:
- 时钟控制单元:负责产生SCL时钟信号,支持标准模式(100kHz)、快速模式(400kHz)以及快速模式+(1MHz)。
- 数据移位寄存器:实现SDA数据的串行发送与接收,支持7位和10位地址模式。
- 地址比较器:在从机模式下自动比较接收到的地址与自身地址,匹配时产生中断。
- 状态寄存器:记录通信过程中的各种状态标志,如起始条件、停止条件、应答状态等。
- 控制寄存器:配置I2C的工作模式、中断使能、DMA请求等参数。
- 数据寄存器:用于读写待发送或已接收的数据字节。
5.2 I2C引脚配置
STM32F4xx的I2C外设通常可以映射到多个GPIO引脚,以I2C1为例,其引脚映射如下:
- I2C1_SCL:可映射到 PB6、PB8 等引脚。
- I2C1_SDA:可映射到 PB7、PB9 等引脚。
- I2C2_SCL:可映射到 PB10、PF1 等引脚。
- I2C2_SDA:可映射到 PB11、PF0 等引脚。
- I2C3_SCL:可映射到 PA8、PH7 等引脚。
- I2C3_SDA:可映射到 PC9、PH8 等引脚。
在使用I2C外设前,需要将对应的GPIO引脚配置为复用功能(AF)模式,并设置为开漏输出、带上拉电阻。以I2C1使用PB6和PB7为例,GPIO配置代码如下:
// 使能GPIOB和I2C1时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
// 配置PB6为SCL,PB7为SDA,复用功能AF4
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OpenDrain;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 将PB6、PB7连接到I2C1的复用功能
GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1);
5.3 I2C初始化配置
完成GPIO配置后,需要对I2C外设进行初始化,设置通信速率、地址模式等参数。使用标准外设库的初始化代码如下:
I2C_InitTypeDef I2C_InitStructure;
// I2C1配置为快速模式,400kHz
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress = 0x00; // 主机模式,自身地址可设为0
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);
在配置I2C时钟频率时,需要注意APB1总线的时钟频率。I2C的时钟由APB1时钟分频得到,计算公式为:
// I2C时钟频率 = APB1时钟频率 / (2 × (CCR + 1)),快速模式
// 或 I2C时钟频率 = APB1时钟频率 / (2 × CCR),标准模式
5.4 主机发送数据
作为主机发送数据时,需要依次完成起始条件、发送从机地址、发送数据、停止条件等步骤。以下是一个向FM24CL04写入数据的完整示例:
void I2C_WriteByte(uint8_t slaveAddr, uint8_t regAddr, uint8_t data)
{
// 发送起始条件
I2C_GenerateSTART(I2C1, ENABLE);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
// 发送从机地址 + 写标志
I2C_Send7bitAddress(I2C1, slaveAddr, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
// 发送寄存器地址
I2C_SendData(I2C1, regAddr);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
// 发送数据
I2C_SendData(I2C1, data);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
// 发送停止条件
I2C_GenerateSTOP(I2C1, ENABLE);
}
5.5 主机接收数据
主机接收数据时,需要先发送从机地址和寄存器地址,然后重新发送起始条件并切换为接收模式。以下是从FM24CL04读取一个字节的示例:
uint8_t I2C_ReadByte(uint8_t slaveAddr, uint8_t regAddr)
{
uint8_t data = 0;
// 发送起始条件
I2C_GenerateSTART(I2C1, ENABLE);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
// 发送从机地址 + 写标志(先写寄存器地址)
I2C_Send7bitAddress(I2C1, slaveAddr, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
// 发送寄存器地址
I2C_SendData(I2C1, regAddr);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
// 重新发送起始条件(重复起始)
I2C_GenerateSTART(I2C1, ENABLE);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
// 发送从机地址 + 读标志
I2C_Send7bitAddress(I2C1, slaveAddr, I2C_Direction_Receiver);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
// 读取数据,发送NACK后停止
I2C_AcknowledgeConfig(I2C1, DISABLE);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));
data = I2C_ReceiveData(I2C1);
I2C_GenerateSTOP(I2C1, ENABLE);
I2C_AcknowledgeConfig(I2C1, ENABLE);
return data;
}
5.6 使用DMA进行批量传输
对于大量数据的传输,使用DMA可以显著降低CPU负载。STM32F4xx的I2C外设支持DMA请求,配置DMA传输的步骤如下:
以下是一个使用DMA向FM24CL04写入多个字节的示例:
void I2C_DMA_Write(uint8_t slaveAddr, uint8_t regAddr, uint8_t *buffer, uint16_t len)
{
// 配置DMA1通道6用于I2C1发送
DMA_InitTypeDef DMA_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&I2C1->DR;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)buffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStructure.DMA_BufferSize = len;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_Init(DMA1_Stream6, &DMA_InitStructure);
// 发送起始条件和从机地址
I2C_GenerateSTART(I2C1, ENABLE);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1, slaveAddr, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
// 发送寄存器地址
I2C_SendData(I2C1, regAddr);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
// 使能DMA请求并启动传输
I2C_DMACmd(I2C1, ENABLE);
DMA_Cmd(DMA1_Stream6, ENABLE);
// 等待传输完成
while (!DMA_GetFlagStatus(DMA1_Stream6, DMA_FLAG_TCIF6));
DMA_Cmd(DMA1_Stream6, DISABLE);
I2C_DMACmd(I2C1, DISABLE);
// 发送停止条件
I2C_GenerateSTOP(I2C1, ENABLE);
}
5.7 状态标志函数
在标准外设库中,I2C_GetFlagStatus 和 I2C_ClearFlag 是用于查询和清除 I2C 外设状态标志的两个重要函数,常用于轮询方式判断通信状态或处理异常情况。
I2C_GetFlagStatus 用于读取 I2C 外设的指定状态标志位,其函数原型如下:
FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);
该函数返回 SET 或 RESET,用于判断对应标志是否置位。常用标志包括:
- I2C_FLAG_BUSY:总线忙标志,置位表示总线正在通信。
- I2C_FLAG_AF:应答失败标志,置位表示从机未应答。
- I2C_FLAG_BTF:字节发送完成标志,置位表示数据寄存器已空且移位寄存器已发送完毕。
- I2C_FLAG_TXE:数据寄存器空标志,置位表示可以写入下一个数据。
- I2C_FLAG_RXNE:数据寄存器非空标志,置位表示已接收到数据。
使用示例:
// 等待数据发送完成
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BTF) == RESET);
// 检查从机是否应答
if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == SET)
{
// 从机无应答,处理错误
}
I2C_ClearFlag 用于手动清除指定的状态标志位,其函数原型如下:
void I2C_ClearFlag(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);
当通信异常导致某些标志(如应答失败标志 AF、总线错误标志 BERR)一直保持置位时,需要调用该函数清除标志,以便恢复正常通信。使用示例:
// 清除应答失败标志
I2C_ClearFlag(I2C1, I2C_FLAG_AF);
// 清除总线错误标志
I2C_ClearFlag(I2C1, I2C_FLAG_BERR);
在实际开发中,建议在每次通信开始前先清除相关标志,避免上一次通信遗留的标志影响本次判断;在通信过程中则通过 I2C_GetFlagStatus 轮询关键标志,确保每一步操作都正确完成。
5.8 常见问题与调试技巧
在使用STM32F4xx I2C控制器时,可能会遇到以下常见问题:
- 总线忙(BUSY)标志无法清除:当通信异常中断时,I2C总线的BUSY标志可能一直保持置位。解决方法是将I2C外设复位(I2C_SoftwareResetCmd),或切换GPIO引脚电平强制释放总线。
- SCL时钟频率不准确:检查APB1时钟频率配置是否正确,以及CCR寄存器的分频值是否计算准确。
- 从机无应答(NACK):检查从机地址是否正确、上拉电阻是否接好、从机是否处于正常工作状态。
- DMA传输数据错位:确保DMA配置的数据宽度与I2C数据寄存器一致(均为8位),并正确设置传输方向。
- 中断优先级冲突:当I2C中断与DMA中断同时使用时,需要合理设置NVIC优先级,避免数据丢失。
调试I2C通信时,建议使用逻辑分析仪或示波器观察SDA和SCL的波形,确认起始条件、地址、数据和停止条件是否符合协议要求。
5.9 软件模拟 IIC 示例(GPIO 位操作)
除了使用 STM32 硬件 I2C 外设外,也可以通过 GPIO 引脚手动翻转电平来软件模拟 IIC 时序。这种方式不依赖芯片内部 I2C 外设,兼容性更好,常用于调试或对时序要求不高的场景。下面给出一个完整的软件模拟 IIC 读写示例。
首先定义 SCL 和 SDA 引脚,其中 SDA 需要同时支持输入和输出:
#define SCL PBout(8)
#define SDA_R PBin(9)
#define SDA_W PBout(9)
起始条件:在 SCL 保持高电平期间,SDA 由高电平跳变为低电平。建立时间 Tsu 至少为 4.7us,保持时间 Thd 至少为 4.0us,因此延时取 4us 以上:
void i2c_start(void)
{
SCL = 1;
SDA_W = 1;
delay_us(4);
SDA_W = 0;
delay_us(4);
}
停止条件:在 SCL 保持高电平期间,SDA 由低电平跳变为高电平:
void i2c_stop(void)
{
SCL = 0;
SDA_W = 0;
delay_us(4);
SCL = 1;
SDA_W = 1;
delay_us(4);
}
主机发送一个字节:数据在 SCL 低电平期间变化,在 SCL 高电平期间保持稳定,从高位(MSB)开始逐位发送:
void i2c_send_byte(uint8_t c)
{
int i;
SCL = 0; // 拉低时钟线,允许 SDA 改变
delay_us(2);
for (i = 0; i < 8; i++)
{
SDA_W = !!(c & (1 << (7 – i))); // 先发高位
delay_us(2);
SCL = 1; // 锁存数据,让从机可以识别
delay_us(2);
SCL = 0;
delay_us(2);
}
SDA_W = 1; // 一个字节发送完毕,释放数据线,等待对方回 ACK
delay_us(2);
}
主机接收一个字节:同样在 SCL 低电平期间允许 SDA 变化,在高电平期间采样读取,从高位开始逐位接收:
uint8_t i2c_recv_byte(void)
{
uint8_t recv = 0;
int i;
for (i = 0; i < 8; i++)
{
SCL = 0; // 只有在时钟线为低电平时,SDA 才能改变
delay_us(2);
SCL = 1; // 时钟线为高电平,让 SDA 保持稳定
delay_us(2);
recv |= SDA_R << (7 – i); // 先接收高位(大端式)
}
SCL = 0; // 拉低时钟线,允许对方继续发数据
return recv;
}
主机发送应答位(ACK/NACK):在 SCL 低电平期间设置 SDA 电平,高电平期间锁存给从机识别:
void i2c_ack(uint8_t ack) // 回 ACK
{
SCL = 0;
delay_us(2);
SDA_W = ack;
delay_us(2);
SCL = 1;
delay_us(2);
SCL = 0; // 拉低时钟线,允许对方继续发数据
}
主机等待从机应答:释放 SDA 后拉高 SCL,检测 SDA 是否被从机拉低(ACK)。若超时未应答,则发送停止条件并返回错误:
uint8_t i2c_wait_ack(void)
{
uint8_t timeout = 0;
SCL = 1; // 锁存数据,使 SDA 保持稳定
delay_us(2);
while (SDA_R) // 等待 SDA_R 变为 0
{
timeout++;
if (timeout > 250)
{
i2c_stop();
return 1; // 超时,从机无应答
}
}
SCL = 0; // 拉低时钟线,允许对方继续发数据
return 0;
}
以从机地址 0b0101110(7 位地址)为例,读取一个字节时,主机先发送从机地址 + 读标志(1),即 0b0101110 1,随后等待从机应答,再逐位读取数据。上述代码中的延时参数(4us、2us)已满足标准模式 100kHz 下建立时间 Tsu ≥ 4.7us、保持时间 Thd ≥ 4.0us、数据有效时间 ≥ 3.4us 的时序要求。
6. 总结
本文围绕 STM32 的 IIC 通信展开,从协议基础到实际工程应用进行了系统梳理。核心要点可以归纳为以下几个方面:
- 协议层面:IIC 是两线制、半双工的串行通信总线,仅需 SDA 和 SCL 两根信号线。通信过程由起始条件(START)、从机地址、应答位(ACK/NACK)、数据字节和停止条件(STOP)组成,数据在 SCL 高电平期间保持稳定、低电平期间变化。
- 实现方式:STM32 支持硬件 IIC 和软件模拟 IIC 两种方案。硬件 IIC 由芯片外设自动处理时序,CPU 占用低、时序精确,但配置复杂且部分型号存在兼容性问题;软件模拟 IIC 通过 GPIO 翻转电平实现,灵活通用,但占用 CPU 资源较多。
- 典型器件:以 FM24CL04 铁电存储器(FRAM)为例,其采用 IIC 接口,写入无需等待写周期、擦写寿命高达 10 亿次,非常适合频繁读写和掉电保护等场景。器件地址为 1010 A2 A1 A0,通过 A0 位选择两个 256 字节存储区。
- 工程实践:STM32F4xx 的 I2C 控制器支持主机/从机模式、中断和 DMA 传输。开发时需正确配置 GPIO 复用功能、初始化 I2C 外设,并通过 I2C_GetFlagStatus 和 I2C_ClearFlag 轮询与清除状态标志,确保通信可靠。
- 调试要点:遇到总线忙、从机无应答、DMA 数据错位等问题时,应优先检查地址、上拉电阻、时钟分频和中断优先级,并借助逻辑分析仪或示波器观察 SDA/SCL 波形定位问题。
总体而言,IIC 协议本身并不复杂,难点在于时序控制和异常处理。建议读者先通过软件模拟 IIC 理解协议细节,再切换到硬件 IIC 提升效率,并结合实际器件(如 FM24CL04)完成读写验证,逐步掌握 STM32 的 IIC 开发。
网硕互联帮助中心




评论前必须登录!
注册