news 2026/4/23 17:58:25

简单理解:I2C中u32 device_addr 、 u32 WriteAddr和u32 reg_addr的核心区别

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
简单理解:I2C中u32 device_addr 、 u32 WriteAddr和u32 reg_addr的核心区别

u32 device_addru32 WriteAddr的核心区别在于“定位层级” 和 “作用场景” 完全不同—— 前者是 I2C 总线上的 “设备级地址”(找外部哪个 EEPROM),后者是 EEPROM 内部的 “存储单元地址”(找设备里哪个位置),可以用 “快递地址” 的逻辑快速理解:

对比维度u32 device_addr(I2C 从机地址)u32 WriteAddr(EEPROM 内部地址)
核心作用定位I2C 总线上的某个 EEPROM 设备(区分多个外部设备)定位该 EEPROM 内部的某个存储单元(区分设备内不同位置)
定位层级外部总线级(“找哪栋楼”)设备内部级(“找楼里哪个房间”)
来源 / 依据I2C 协议标准 + EEPROM 芯片规格 + 硬件接线(A0/A1/A2 引脚)EEPROM 芯片容量(地址位数由容量决定,如 24C02 是 8 位地址)
格式规则8 位有效(7 位基础地址 + 1 位读写位,写操作时读写位 = 0)8/16/24 位有效(取决于容量,如 24C64 是 16 位地址)
取值范围(示例)24C02 默认:0xA0(0x50<<1),最多 8 个设备(0xA0~0xAE)24C02(256 字节):0x00~0xFF;24C64(8KB):0x0000~0x1FFF
通信阶段I2C 起始信号后,第一个发送的地址(用于 “呼叫” 从机)设备地址确认后,发送的地址(用于 “指定存储位置”)
错误影响错误则 I2C 总线无响应(找不到设备)错误则数据写入错误位置(如覆盖其他数据,或超出容量失败)

举个实际场景例子(帮你彻底理解)

假设你有一个 I2C 总线,上面挂了两个 EEPROM 芯片(24C02 和 24C64),现在要把0x12这个数据写入 “24C02 的第 0x10 个存储单元”:

  1. 你需要先通过device_addr告诉 I2C 总线:“我要找 24C02 这个设备”—— 所以device_addr = 0xA0(24C02 的默认写地址);
  2. 24C02 收到0xA0后,回复 ACK(表示 “我在,找我有事?”);
  3. 接着你通过WriteAddr = 0x10告诉 24C02:“我要把数据写到你内部的第 0x10 个位置”;
  4. 最后发送数据0x12,24C02 就会把数据存到自己的 0x10 地址处。

如果此时你把device_addr写成 24C64 的地址(0xA0 也是 24C64 的默认地址?不,若两个芯片 A0 引脚不同,24C64 可设为 0xA2),就会呼叫到 24C64 而不是 24C02;如果WriteAddr写成 0x20,数据就会写到 24C02 的 0x20 地址,而不是目标 0x10。

总结关键区别

  • device_addr管 “外部选设备”:解决 “和谁通信” 的问题;
  • WriteAddr管 “内部选位置”:解决 “数据存在设备的哪里” 的问题;
  • 两者是 “先选设备,再选位置” 的递进关系,缺一不可,且格式、作用、取值范围完全独立。

在 I2C EEPROM 的读写函数中,u32 WriteAddru32 device_addru32 reg_addr是三个核心地址参数,作用层级和定位目标完全不同—— 简单说:device_addr找 “外部哪个 EEPROM 设备”,WriteAddr/reg_addr找 “设备内哪个存储位置”(两者本质是同一类地址,仅命名场景不同)。以下是全维度解析、对比和实操示例:

一、三个参数的核心定义与本质

参数名核心作用本质定位通俗类比
u32 device_addr定位 I2C 总线上的目标 EEPROM 设备总线级地址(设备身份证)图书馆的 “书架编号”(找哪排)
u32 WriteAddr定位 EEPROM 内部的写入起始存储单元设备内存储地址(写场景)书架上的 “写入起始格编号”
u32 reg_addr定位 EEPROM 内部的读取起始存储单元设备内存储地址(读场景)书架上的 “读取起始格编号”

二、关键区别与关联(重点!)

1.device_addrvsWriteAddr/reg_addr:层级完全不同

  • device_addr:解决 “和谁通信”I2C 总线可挂多个设备(如 24C02 + 24C64),device_addr是 EEPROM 的唯一识别码,只有地址匹配的设备才会响应主机(MCU)。格式:7位基础地址 + 1位读写位(bit0=0 写,bit0=1 读),示例:24C02 写地址0xA0、读地址0xA1

  • WriteAddr/reg_addr:解决 “通信设备内的哪个位置”两者都是 EEPROM 内部存储单元的 “偏移地址”,仅命名对应读写场景:

    • 写函数中叫WriteAddr:指定 “从哪个单元开始写入数据”;
    • 读函数中叫reg_addr:指定 “从哪个单元开始读取数据”;本质完全一致 —— 若要 “先写后读验证”,需让WriteAddr = reg_addr(写入和读取的内部位置相同)。

2.WriteAddrreg_addr:完全等价,仅命名差异

对比维度u32 WriteAddr(写场景)u32 reg_addr(读场景)
本质设备内存储单元偏移地址设备内存储单元偏移地址
地址位数由 EEPROM 容量决定(8/16/24 位)由 EEPROM 容量决定(8/16/24 位)
取值范围0 ~(EEPROM 总容量 - 1)0 ~(EEPROM 总容量 - 1)
发送方式写模式下,跟随device_addr后发送(高位→低位)读模式下,先以写模式发送(告知读取起始位置)
关联用法写入后,需用相同值作为reg_addr读取验证读取前,需与之前的WriteAddr一致才能读到对应数据

示例:

  • 写函数:向 24C64 的WriteAddr=0x0100写入数据 → 数据存在 0x0100 单元;
  • 读函数:需用reg_addr=0x0100读取 → 才能拿到刚才写入的数据。

三、格式规范与取值示例(以 24C02/24C64 为例)

1.device_addr格式(8 位有效,u32 仅为兼容)

EEPROM 型号硬件接线(A0/A1/A2)7 位基础地址写模式device_addr(bit0=0)读模式device_addr(bit0=1)
24C02均接 GND0x500x50 << 1 = 0xA00xA00x01 = 0xA1
24C64均接 GND0x500x50 << 1 = 0xA00xA00x01 = 0xA1
24C02A0 接 VCC(A1/A2 接地)0x510x51 << 1 = 0xA20xA20x01 = 0xA3

2.WriteAddr/reg_addr格式(地址位数由容量决定)

EEPROM 型号容量地址位数取值范围发送方式(示例:0x0100)
24C02256B8 位0x00 ~ 0xFF直接发 1 字节:(u8) 0x00(仅低 8 位有效)
24C648KB16 位0x0000 ~ 0x1FFF分 2 字节发:高位 0x01 → 低位 0x00
24C1024128KB24 位0x000000 ~ 0x1FFFFF分 3 字节发:0x00 → 0x01 → 0x00

四、通信流程中的协作关系(先写后读示例)

以 “向 24C64 的 0x0100 地址写入 3 字节,再读取验证” 为例,看三个参数的协作:

1. 写入流程(用device_addr=0xA0+WriteAddr=0x0100

  1. 主机发 I2C 起始信号;
  2. 主机发device_addr=0xA0(写模式)→ 24C64 地址匹配,响应 ACK;
  3. 主机发WriteAddr=0x0100(分高位 0x01、低位 0x00 发送)→ 24C64 地址指针指向 0x0100;
  4. 主机发 3 字节数据(如 0x11、0x22、0x33)→ 24C64 存储到 0x0100~0x0102;
  5. 主机发停止信号,等待 5ms 写入周期完成。

2. 读取流程(用device_addr=0xA0+reg_addr=0x0100

  1. 主机发 I2C 起始信号;
  2. 主机发device_addr=0xA0(写模式,用于告知读取起始地址)→ 24C64 响应 ACK;
  3. 主机发reg_addr=0x0100(与 WriteAddr 相同,分高位 0x01、低位 0x00 发送)→ 地址指针指向 0x0100;
  4. 主机发重复起始信号(不停止总线);
  5. 主机发device_addr=0xA1(读模式)→ 24C64 响应 ACK;
  6. 24C64 连续发 3 字节数据(0x11、0x22、0x33)→ 主机接收存入缓冲区;
  7. 主机发停止信号,读取完成。

→ 核心协作逻辑:device_addr负责 “锁定目标 EEPROM”,WriteAddr/reg_addr负责 “锁定设备内的存储位置”,三者配合实现 “精准读写”。

五、常见误区与避坑指南

  1. 混淆device_addrWriteAddr/reg_addr

    • 错误:把WriteAddr=0x0100当作device_addr发送 → 总线无响应(地址不匹配);
    • 避坑:记住 “device_addr是 8 位(如 0xA0/A1),WriteAddr/reg_addr是 8/16/24 位(如 0x00~0x1FFF)”,作用层级完全不同。
  2. WriteAddrreg_addr不一致

    • 错误:写WriteAddr=0x0100,读reg_addr=0x0200→ 读不到写入的数据;
    • 避坑:读写验证时,必须保证WriteAddr = reg_addr(起始地址相同)。
  3. device_addr未区分读写模式

    • 错误:读取时用写模式地址0xA0→ 24C64 不响应,读取全为 0xFF;
    • 避坑:读取时device_addr需置 bit0=1(如 0xA0→0xA1),写入时 bit0=0。
  4. WriteAddr/reg_addr超出 EEPROM 容量

    • 错误:24C02(最大地址 0xFF)写WriteAddr=0x100→ 数据写入无效位置;
    • 避坑:读写前检查WriteAddr + 写写字节数 ≤ 总容量reg_addr + 读取字节数 ≤ 总容量
  5. 地址位数发送错误

    • 错误:24C64(16 位地址)仅发送WriteAddr=0x0100的低 8 位(0x00)→ 写入地址错误;
    • 避坑:根据 EEPROM 容量拆分地址(16 位分 2 字节、24 位分 3 字节),按 “高位→低位” 发送。

六、完整实操代码示例(24C64 先写后读)

c

#include "xt_i2c.h" #include "delay.h" #include <stdio.h> // 配置参数(24C64) #define EEPROM_DEV_ADDR_TX 0xA0 // device_addr(写模式) #define EEPROM_DEV_ADDR_RX 0xA1 // device_addr(读模式) #define EEPROM_WRITE_ADDR 0x0100 // WriteAddr(写入起始地址) #define EEPROM_REG_ADDR 0x0100 // reg_addr(读取起始地址,与WriteAddr一致) #define WRITE_NUM 3 // 写入字节数 #define READ_NUM 3 // 读取字节数 #define EEPROM_TOTAL_SIZE 8192 // 24C64 总容量 8KB #define EEPROM_ADDR_BITS 16 // 24C64 是 16 位地址 // 单页写入函数(基础依赖) static void IIC_E2P_PageWrite(XT_I2C_TypeDef *i2c_no, u8* pBuf, u32 len, u32 dev_addr, u32 write_addr) { if (pBuf == NULL || len == 0 || (write_addr + len) > EEPROM_TOTAL_SIZE) return; while (XT_I2C_GetFlagStatus(i2c_no, XT_I2C_FLAG_BUSY) != RESET); XT_I2C_GenerateSTART(i2c_no, ENABLE); while (!XT_I2C_CheckEvent(i2c_no, XT_I2C_EVENT_MASTER_MODE_SELECT)); XT_I2C_Send7bitAddress(i2c_no, dev_addr, XT_I2C_DIRECTION_TX); while (!XT_I2C_CheckEvent(i2c_no, XT_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); // 发送 16 位 WriteAddr(高位→低位) XT_I2C_SendData(i2c_no, (u8)(write_addr >> 8)); while (!XT_I2C_CheckEvent(i2c_no, XT_I2C_EVENT_MASTER_BYTE_TRANSMITTED)); XT_I2C_SendData(i2c_no, (u8)write_addr); while (!XT_I2C_CheckEvent(i2c_no, XT_I2C_EVENT_MASTER_BYTE_TRANSMITTED)); for (u32 i = 0; i < len; i++) { XT_I2C_SendData(i2c_no, pBuf[i]); while (!XT_I2C_CheckEvent(i2c_no, XT_I2C_EVENT_MASTER_BYTE_TRANSMITTED)); } XT_I2C_GenerateSTOP(i2c_no, ENABLE); delay_ms(5); // 等待写入周期完成 } // 多字节读取函数(基础依赖) static void IIC_E2P_ReadBytes(XT_I2C_TypeDef *i2c_no, u8* pBuf, u32 len, u32 dev_addr, u32 reg_addr) { if (pBuf == NULL || len == 0 || (reg_addr + len) > EEPROM_TOTAL_SIZE) return; while (XT_I2C_GetFlagStatus(i2c_no, XT_I2C_FLAG_BUSY) != RESET); XT_I2C_GenerateSTART(i2c_no, ENABLE); while (!XT_I2C_CheckEvent(i2c_no, XT_I2C_EVENT_MASTER_MODE_SELECT)); // 发送 device_addr(写模式)和 reg_addr XT_I2C_Send7bitAddress(i2c_no, dev_addr, XT_I2C_DIRECTION_TX); while (!XT_I2C_CheckEvent(i2c_no, XT_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); XT_I2C_SendData(i2c_no, (u8)(reg_addr >> 8)); // 16 位地址高位 while (!XT_I2C_CheckEvent(i2c_no, XT_I2C_EVENT_MASTER_BYTE_TRANSMITTED)); XT_I2C_SendData(i2c_no, (u8)reg_addr); // 16 位地址低位 while (!XT_I2C_CheckEvent(i2c_no, XT_I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // 重复起始,切换为读模式 XT_I2C_GenerateSTART(i2c_no, ENABLE); while (!XT_I2C_CheckEvent(i2c_no, XT_I2C_EVENT_MASTER_MODE_SELECT)); XT_I2C_Send7bitAddress(i2c_no, dev_addr | 0x01, XT_I2C_DIRECTION_RX); while (!XT_I2C_CheckEvent(i2c_no, XT_I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); // 接收数据(最后1字节发NACK) for (u32 i = 0; i < len; i++) { XT_I2C_AcknowledgeConfig(i2c_no, (i == len-1) ? DISABLE : ENABLE); while (!XT_I2C_CheckEvent(i2c_no, XT_I2C_EVENT_MASTER_BYTE_RECEIVED)); pBuf[i] = XT_I2C_ReceiveData(i2c_no); } XT_I2C_GenerateSTOP(i2c_no, ENABLE); XT_I2C_AcknowledgeConfig(i2c_no, ENABLE); // 恢复ACK } int main(void) { u8 write_buf[WRITE_NUM] = {0x11, 0x22, 0x33}; // 待写入数据 u8 read_buf[READ_NUM] = {0}; // 接收缓冲区 I2C1_Init(); // 初始化 I2C1(配置引脚、时钟、速率 100KHz) // 1. 写入数据:device_addr=0xA0,WriteAddr=0x0100 IIC_E2P_PageWrite(XT_I2C1, write_buf, WRITE_NUM, EEPROM_DEV_ADDR_TX, EEPROM_WRITE_ADDR); // 2. 读取数据:device_addr=0xA0(先写reg_addr),reg_addr=0x0100 IIC_E2P_ReadBytes(XT_I2C1, read_buf, READ_NUM, EEPROM_DEV_ADDR_TX, EEPROM_REG_ADDR); // 3. 打印结果(验证是否一致) printf("写入数据:0x%02X 0x%02X 0x%02X\n", write_buf[0], write_buf[1], write_buf[2]); printf("读取数据:0x%02X 0x%02X 0x%02X\n", read_buf[0], read_buf[1], read_buf[2]); while(1); }

预期输出:

plaintext

写入数据:0x11 0x22 0x33 读取数据:0x11 0x22 0x33

总结

三个参数的核心关系:

  • device_addr:管 “找哪个 EEPROM”(总线级定位);
  • WriteAddr/reg_addr:管 “找设备内哪个位置”(存储级定位),两者完全等价,仅命名对应读写场景;
  • 正确用法:先通过device_addr锁定设备,再通过WriteAddr/reg_addr锁定存储位置,三者配合实现精准读写。

记住 “先选设备,再选位置” 的逻辑,就能避免绝大多数地址配置错误。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 12:18:56

为什么推荐使用720p或1080p视频进行数字人合成?

为什么推荐使用720p或1080p视频进行数字人合成&#xff1f; 在虚拟主播、AI客服、在线教育等场景中&#xff0c;数字人正以前所未有的速度走进大众视野。你可能已经注意到&#xff0c;越来越多的企业宣传视频不再依赖真人出镜&#xff0c;而是由一个表情自然、口型精准的“虚拟…

作者头像 李华
网站建设 2026/4/23 12:11:22

Canva设计头像+HeyGem合成?跨平台协作新模式

Canva设计头像 HeyGem合成&#xff1f;跨平台协作新模式 在短视频内容爆炸式增长的今天&#xff0c;企业与创作者面临的不再是“有没有内容”&#xff0c;而是“能不能快速、安全、低成本地生产大量个性化视频”。教育机构要为多位讲师生成统一脚本的课程介绍&#xff0c;银行…

作者头像 李华
网站建设 2026/4/23 13:30:05

拖放或点击上传视频文件?HeyGem支持多格式一键导入

拖放或点击上传视频文件&#xff1f;HeyGem支持多格式一键导入 在企业数字内容生产日益高频的今天&#xff0c;如何快速、稳定地将原始素材送入AI生成流水线&#xff0c;已成为决定效率的关键瓶颈。传统工具往往要求用户反复点击、手动转码、逐个提交——这种繁琐流程不仅拖慢节…

作者头像 李华
网站建设 2026/4/23 13:30:21

ComfyUI用户注意:HeyGem可与其集成实现工作流自动化

ComfyUI 与 HeyGem 深度集成&#xff1a;构建全自动数字人视频工作流 在内容创作日益依赖自动化流程的今天&#xff0c;从一段文字生成一个会说话的数字人视频&#xff0c;已经不再是科幻场景。越来越多的企业和个人开始探索如何将 AI 文字、语音、图像和视频技术串联成一条高效…

作者头像 李华
网站建设 2026/4/23 13:29:11

大模型Function Calling的函数如何调用的?

在真实开发中&#xff0c;大模型的 Function Calling&#xff08;函数调用&#xff09;不是“模型直接执行代码”&#xff0c;而是一套“声明-生成-解析-执行-反馈”的安全闭环机制。以下是我在项目中&#xff08;如智能编程助手、自动化运维 Agent&#xff09;的实际做法&…

作者头像 李华