wixzjsh头像
关注

I2C 裸机驱动学习笔记:从物理层到寄存器到抓包

一、开篇

今天系统学习了 i.MX6ULL 的 I2C 裸机驱动,从最底层的物理层一路走到寄存器操作,最后到用逻辑分析仪抓包验证。期间踩了不少坑,也解决了不少问题。这篇博客把整个学习路径整理出来,方便回顾。

学习内容:

  • I2C 物理层:开漏、上拉、START/STOP、ACK/NACK
  • i.MX6ULL I2C 寄存器:I2CR、I2SR、I2DR、IFDR
  • 代码实现:i2c_init、wait_i2c_iif、i2c_write、i2c_read
  • 工程配置:Makefile、工具链、libgcc、stdio 库
  • 调试:串口、逻辑分析仪、抓包
  • 踩坑:超时、页写、地址、时钟

二、I2C 物理层

2.1 两根线

I2C 只有两根线:

  • SDA:数据线,双向
  • SCL:时钟线,主机产生

所有设备并联在这两根线上。

2.2 开漏 + 上拉

I2C 引脚必须是开漏输出,不能是推挽。

输出结构能主动拉低能主动拉高能否并联
推挽✅✅❌ 会短路
开漏✅❌(靠上拉)✅ 线与

开漏只能主动拉低,高电平靠外部上拉电阻。这样多个设备接在一起不会短路,谁拉低谁赢。

线与:多个开漏输出接一起,只要有一个拉低,线就是低;全部释放,线才被上拉为高。

2.3 START / STOP

信号SCLSDA
START11 → 0
STOP10 → 1

START 和 STOP 都发生在 SCL 高电平时。

START:SCL 高时,SDA 从 1 变 0。
STOP:SCL 高时,SDA 从 0 变 1。

2.4 数据位传输

SCL 低时,SDA 允许改变(准备)。
SCL 高时,SDA 必须保持(采样)。

SDA 的所有变化都发生在 SCL 低电平期间。如果 SCL 高时 SDA 变化,会被误判为 START/STOP。

2.5 ACK / NACK

每发完 8 位,第 9 个时钟是应答位:

名称SDA含义
ACK拉低收到了,继续
NACK不动(高)没收到 / 结束

发送方在第 9 位释放 SDA,由接收方控制。

2.6 MSB First

I2C 规定高位先发,先发 D7,最后发 D0。

2.7 完整波形示例

发 10101100:

        D7=1    D6=0    D5=1    D4=0    D3=1    D2=1    D1=0    D0=0    ACK
         ┌─┐     ┌─┐     ┌─┐     ┌─┐     ┌─┐     ┌─┐     ┌─┐     ┌─┐     ┌─┐
SCL ─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └──
SDA ────────────┐     ┌───────┐           ┌───────────────┐
                 └─────┘       └───────────┘               └───────────────
         1   1   0   0   1   1   0   0

三、i.MX6ULL I2C 寄存器

3.1 寄存器总览

寄存器偏移作用
IADR0x00从机地址(主机不用)
IFDR0x04分频,决定 SCL 频率
I2CR0x08控制(命令)
I2SR0x0C状态(结果)
I2DR0x10数据(写=发,读=收)

寄存器结构体必须用 volatile 修饰,防止编译器优化掉循环里的读操作。

3.2 I2CR 关键位

位名称作用
7IEN使能
6IIEN中断使能
5MSTA主从 + START/STOP(边沿触发)
4MTX发送/接收方向
3TXAK接收时回 ACK/NACK
2RSTARepeat START

MSTA 边沿触发:

  • 0→1:发 START
  • 1→0:发 STOP
  • 已经是 1 再写 1 不会重复发

MTX:

  • 1 = 发送(控制器驱动 SDA)
  • 0 = 接收(控制器释放 SDA)

TXAK:

  • 0 = 收到数据回 ACK
  • 1 = 收到数据回 NACK

RSTA:读操作中间发 Repeat START。

3.3 I2SR 关键位

位名称作用
7ICF传输完成
6IAAS被寻址为从机
5IBB总线忙
4IAL仲裁丢失
1IIF中断标志(核心)
0RXAK收到的应答

IIF 是核心:每发/收完一个字节,硬件自动置 1,软件写 0 清。

RXAK:0=ACK,1=NACK,必须在清 IIF 之后读。

IBB:1=总线忙,0=空闲,STOP 后等它清 0。

IAL:仲裁丢失,单主模式用不到,但要清残留。

3.4 I2DR

写 I2DR 就是发送,不需要额外命令。硬件自动把 8 位推到 SDA,SCL 自动翻转,第 9 位收 ACK,置 IIF。

3.5 IFDR

决定 SCL 频率。i.MX6ULL 的 I2C 时钟源一般是 66MHz 或 80MHz。

改 IFDR 前必须先关 IEN:

IEN = 0 → 写 IFDR → IEN = 1

你代码里 IFDR = 0x15,对应约 100kHz。

四、代码逐段解析

4.1 i2c_init

void i2c_init(I2C_Type *base)
{
    IOMUXC_SetPinMux(IOMUXC_UART4_RX_DATA_I2C1_SDA, 1);
    IOMUXC_SetPinMux(IOMUXC_UART4_TX_DATA_I2C1_SCL, 1);
    IOMUXC_SetPinConfig(IOMUXC_UART4_RX_DATA_I2C1_SDA, 0x10B0);
    IOMUXC_SetPinConfig(IOMUXC_UART4_TX_DATA_I2C1_SCL, 0x10B0);

    base->I2CR &= ~I2CR_IEN;
    base->IFDR = 0x15;
    base->I2CR |= I2CR_IEN;
}

做两件事:

  1. 配引脚(复用为 I2C1,开漏 + 上拉)
  2. 配控制器(关 IEN → 设 IFDR → 开 IEN)

顺序不能乱:IFDR 只在 IEN=0 时可写。

4.2 wait_i2c_iif

int wait_i2c_iif(I2C_Type *base)
{
    int timeout = 100000;
    while((base->I2SR & I2SR_IIF) == 0){
        if (--timeout == 0) return -2;
    }
    base->I2SR &= ~I2SR_IIF;
    if ((base->I2SR & I2SR_RXAK) != 0)
        return -1;
    return 0;
}

三步:等 IIF → 清 IIF → 查 RXAK。

超时:防止从机不响应时死循环。

4.3 i2c_write

int i2c_write(I2C_Type *base, unsigned char dev_addr, unsigned short reg_addr,
              int reg_len, unsigned char *data, int data_len)
{
    int stat = 0;
    int i;

    base->I2SR &= ~(I2SR_IAL | I2SR_IIF);
    base->I2CR |= I2CR_MTX;
    base->I2CR |= I2CR_MSTA;

    base->I2DR = (dev_addr << 1) | 0;
    stat = wait_i2c_iif(base);
    if (stat != 0) goto stop;

    for (i = reg_len - 1; i >= 0; i--) {
        base->I2DR = reg_addr >> (i * 8);
        stat = wait_i2c_iif(base);
        if (stat != 0) goto stop;
    }

    for (i = 0; i < data_len; i++) {
        base->I2DR = data[i];
        stat = wait_i2c_iif(base);
        if (stat != 0) goto stop;
    }

stop:
    base->I2CR &= ~I2CR_MSTA;
    {
        int timeout = 100000;
        while((base->I2SR & I2SR_IBB) != 0){
            if (--timeout == 0) break;
        }
    }
    return stat;
}

核心规律:写 I2DR → 等 IIF → 查 RXAK → 写下一个字节。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/2302_81263197/article/details/166598308

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--