4. I2C控制器寄存器映射:基地址、时钟分频寄存器、控制寄存器(CR)、状态寄存器(SR)

I2C控制器的寄存器映射是驱动开发的核心数据手册。ARM平台上的I2C外设通常被映射到一段连续的内存地址空间,驱动通过访问这些寄存器来完成所有配置与数据交互。本章将详细解析基地址的获取方式、时钟分频寄存器的计算原理、控制寄存器的位域定义以及状态寄存器的轮询与中断判断逻辑。

4.1 基地址与寄存器偏移量

每个I2C控制器在系统内存映射中都有一个唯一的基地址(Base Address)。该地址由芯片厂商在参考手册中定义,通常位于外设总线(APB/AHB)的地址区间内。驱动通过基地址 + 偏移量(Offset)的方式访问具体寄存器。

关键概念: 基地址由硬件设计决定,不同芯片的I2C基地址不同。例如,STM32F4系列的I2C1基地址为0x40005400,而i.MX6ULL的I2C1基地址为0x021A0000。驱动代码中通常使用宏定义或设备树来声明基地址。

下表列出了典型的I2C控制器寄存器偏移量(以某通用ARM I2C控制器为例):

偏移量 寄存器名称 缩写 复位值
0x00 控制寄存器 CR 0x0000
0x04 状态寄存器 SR 0x0000
0x08 数据寄存器 DR 0x0000
0x0C 时钟分频寄存器 CKDIV 0x0000
0x10 从机地址寄存器 SAR 0x0000
调试提示: 在排查I2C通信故障时,第一步应使用调试器(如JTAG/SWD)读取基地址处的寄存器值,确认外设时钟是否使能、寄存器是否可读。若读回全为0xFFFFFFFF或0x00000000,通常表示时钟未使能或地址映射错误。

4.2 时钟分频寄存器(CKDIV)

I2C总线的SCL时钟频率由时钟分频寄存器控制。该寄存器将输入的外设时钟(PCLK/PERCLK)分频,产生符合I2C标准(100kHz、400kHz、1MHz等)的SCL信号。

分频计算公式通常为:

SCL频率 = 外设时钟频率 / (分频系数 × (高电平周期 + 低电平周期))

其中分频系数由CKDIV寄存器中的预分频位决定。

以某典型ARM I2C控制器为例,CKDIV寄存器的位域定义如下:

位域 名称 描述
[15:8] SCL_H SCL高电平保持周期(以分频后的时钟周期为单位)
[7:0] SCL_L SCL低电平保持周期(以分频后的时钟周期为单位)
常见错误: 初学者常忽略SCL高/低电平的占空比要求。I2C标准要求SCL高电平最小宽度为0.6μs(100kHz模式)或0.26μs(400kHz模式)。若分频值设置过小,会导致SCL频率超标,从设备无法响应。

下面是一个典型的时钟分频配置示例(假设外设时钟为24MHz,目标SCL频率为100kHz):

/* 计算分频值 */
#define I2C_PCLK_FREQ  24000000  /* 24 MHz */
#define TARGET_SCL     100000    /* 100 kHz */

/* 分频系数 = PCLK / (5 * SCL) 的经验公式 */
uint16_t divider = I2C_PCLK_FREQ / (5 * TARGET_SCL);

/* 设置SCL高电平和低电平各占一半 */
uint8_t scl_h = divider / 2;
uint8_t scl_l = divider / 2;

/* 写入时钟分频寄存器 */
I2C_CKDIV_REG = (scl_h << 8) | scl_l;
调试技巧: 使用逻辑分析仪或示波器测量SCL引脚的实际频率,与计算值对比。若偏差超过10%,检查外设时钟频率是否准确,以及分频公式是否与芯片手册一致。

4.3 控制寄存器(CR)

控制寄存器(CR)是I2C控制器的“大脑”,驱动通过设置CR的各个位域来启动、停止、应答、中断使能等操作。CR通常为16位或32位寄存器,每位控制一个独立功能。

下表列出了典型I2C控制寄存器的位域定义:

位 名称 描述
0 IEN I2C控制器使能位。1:使能;0:禁用
1 START 产生起始条件。写1后硬件自动发送START
2 STOP 产生停止条件。写1后硬件自动发送STOP
3 ACK 应答使能。1:发送ACK;0:发送NACK
4 INTEN 中断使能。1:允许中断请求;0:禁止
5 MODE 主从模式选择。1:主机模式;0:从机模式

典型的I2C主机发送启动序列如下:

/* 1. 使能I2C控制器 */
I2C_CR_REG |= (1 << 0);  /* 设置IEN位 */

/* 2. 设置为主机模式 */
I2C_CR_REG |= (1 << 5);  /* 设置MODE位 */

/* 3. 产生起始条件 */
I2C_CR_REG |= (1 << 1);  /* 设置START位 */

/* 4. 等待START完成(轮询状态寄存器) */
while (!(I2C_SR_REG & (1 << 1)));  /* 等待SB位 */

/* 5. 发送从机地址 + 写位 */
I2C_DR_REG = (slave_addr << 1) | 0;

/* 6. 等待地址发送完成 */
while (!(I2C_SR_REG & (1 << 2)));  /* 等待ADDR位 */
寄存器操作规范: 对CR的START和STOP位进行操作时,建议使用“读-改-写”方式,避免意外清除其他位。在中断使能前,务必先清除状态寄存器中的挂起中断标志,防止进入错误的中断服务程序。

4.4 状态寄存器(SR)

状态寄存器(SR)反映了I2C控制器的当前工作状态,包括总线忙闲、数据发送完成、应答接收、仲裁丢失等关键信息。驱动通过轮询或中断方式读取SR,判断操作是否成功。

下表列出了典型I2C状态寄存器的位域定义:

位 名称 描述
0 BUSY 总线忙标志。1:总线正在使用;0:总线空闲
1 SB 起始位发送完成标志。1:START已发送
2 ADDR 地址发送完成标志。1:地址+读写位已发送并收到应答
3 TX 数据发送完成标志。1:数据字节已发送并收到应答
4 RX 数据接收完成标志。1:已接收到一个字节数据
5 ARB 仲裁丢失标志。1:发生仲裁丢失
6 NAK 无应答标志。1:从设备未应答
故障排查重点: 当SR的NAK位被置1时,表示从设备未应答。常见原因包括:从设备地址错误、从设备未上电、SCL/SDA线路短路或上拉电阻缺失。此时应立即停止传输并发送STOP条件,避免总线死锁。

下面是一个典型的状态寄存器轮询读取流程:

/* 等待数据发送完成 */
while (!(I2C_SR_REG & (1 << 3))) {  /* 等待TX位 */
    /* 检查是否发生错误 */
    if (I2C_SR_REG & (1 << 5)) {   /* 仲裁丢失 */
        /* 处理仲裁丢失:重新发送START */
        I2C_CR_REG |= (1 << 1);    /* 重新产生START */
        break;
    }
    if (I2C_SR_REG & (1 << 6)) {   /* 无应答 */
        /* 处理NAK:发送STOP终止传输 */
        I2C_CR_REG |= (1 << 2);    /* 产生STOP */
        return -ENACK;
    }
}

/* 读取数据寄存器获取接收到的数据 */
uint8_t data = I2C_DR_REG;

4.5 寄存器映射与通信流程SVG图

下图展示了I2C控制器寄存器映射关系以及主机发送一帧数据的完整流程:

I2C控制器寄存器映射与主机发送流程 寄存器映射 基地址 + 0x00: 控制寄存器 (CR) 基地址 + 0x04: 状态寄存器 (SR) 基地址 + 0x08: 数据寄存器 (DR) 基地址 + 0x0C: 时钟分频 (CKDIV) 主机发送一帧数据流程 步骤1: 设置CKDIV配置SCL频率 步骤2: 设置CR.IEN=1使能控制器 步骤3: 设置CR.START=1产生起始条件 步骤4: 轮询SR.SB位等待START完成 步骤5: 写DR发送从机地址+读写位 步骤6: 检查SR.NAK位判断是否应答 步骤7: 发送数据/接收数据/发送STOP 关键寄存器交互 • CR控制操作类型 • SR反馈操作状态 • DR承载传输数据 • CKDIV决定通信速率

4.6 寄存器配置故障排查清单

在实际调试中,寄存器配置错误是I2C通信失败的最常见原因。以下清单总结了各寄存器的典型故障点:

寄存器 典型故障 排查方法
基地址 地址映射错误或未使能外设时钟 读取基地址处寄存器,若全为0xFF或0x00,检查时钟使能寄存器
CKDIV 分频值计算错误导致SCL频率超标 用示波器测量SCL引脚频率,与目标值对比
CR 未使能控制器或未设置主机模式 读取CR值,确认IEN和MODE位已置1
SR 轮询超时或未清除中断标志 检查SR的BUSY位是否一直为1,确认中断服务程序清除了标志
调试工具推荐: 使用寄存器转储(Register Dump)功能,在每次I2C操作前后打印所有相关寄存器的值。对比正常与异常情况下的寄存器差异,可以快速定位问题根源。