4. I2C控制器寄存器映射:基地址、时钟分频寄存器、控制寄存器(CR)、状态寄存器(SR)
I2C控制器的寄存器映射是驱动开发的核心数据手册。ARM平台上的I2C外设通常被映射到一段连续的内存地址空间,驱动通过访问这些寄存器来完成所有配置与数据交互。本章将详细解析基地址的获取方式、时钟分频寄存器的计算原理、控制寄存器的位域定义以及状态寄存器的轮询与中断判断逻辑。
4.1 基地址与寄存器偏移量
每个I2C控制器在系统内存映射中都有一个唯一的基地址(Base Address)。该地址由芯片厂商在参考手册中定义,通常位于外设总线(APB/AHB)的地址区间内。驱动通过基地址 + 偏移量(Offset)的方式访问具体寄存器。
下表列出了典型的I2C控制器寄存器偏移量(以某通用ARM I2C控制器为例):
| 偏移量 | 寄存器名称 | 缩写 | 复位值 |
|---|---|---|---|
| 0x00 | 控制寄存器 | CR | 0x0000 |
| 0x04 | 状态寄存器 | SR | 0x0000 |
| 0x08 | 数据寄存器 | DR | 0x0000 |
| 0x0C | 时钟分频寄存器 | CKDIV | 0x0000 |
| 0x10 | 从机地址寄存器 | SAR | 0x0000 |
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低电平保持周期(以分频后的时钟周期为单位) |
下面是一个典型的时钟分频配置示例(假设外设时钟为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;
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位 */
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:从设备未应答 |
下面是一个典型的状态寄存器轮询读取流程:
/* 等待数据发送完成 */
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控制器寄存器映射关系以及主机发送一帧数据的完整流程:
4.6 寄存器配置故障排查清单
在实际调试中,寄存器配置错误是I2C通信失败的最常见原因。以下清单总结了各寄存器的典型故障点:
| 寄存器 | 典型故障 | 排查方法 |
|---|---|---|
| 基地址 | 地址映射错误或未使能外设时钟 | 读取基地址处寄存器,若全为0xFF或0x00,检查时钟使能寄存器 |
| CKDIV | 分频值计算错误导致SCL频率超标 | 用示波器测量SCL引脚频率,与目标值对比 |
| CR | 未使能控制器或未设置主机模式 | 读取CR值,确认IEN和MODE位已置1 |
| SR | 轮询超时或未清除中断标志 | 检查SR的BUSY位是否一直为1,确认中断服务程序清除了标志 |