一句话: I2C 只能访问 256 个地址但很多芯片的寄存器远不止 256 个。Page Select 用下半区固定、上半区可切换的窗口映射机制解决这个问题。这篇用文件柜做比喻5 分钟看懂。系列文章《嵌入式外设实战》 — NTC测温、SPI通信、I2C从机、定点数运算——外设驱动从入门到精通 附完整代码 ✅ 用文件柜抽屉讲透 用分和元讲透附代码 省引脚绝招 一张表讲清四种模式 多一根线差多远 电压不同的区别 用VTEC波动代替硬件信号 连续N次满足才算数 硬件脚悬空了也别慌 pid-feedforward-lookup when-to-drop-pid-d-term pid-deadzone-silence[TOC]适合谁读适合嵌入式开发者、单片机初学者及遇到类似问题的工程师问题256 个地址不够用I2C 通信协议里一个设备地址对应 256 个寄存器位置0x00 到 0xFF。但实际芯片——比如光模块的 CMIS 协议——动不动就好几百个寄存器。光 Page 00h 就有 256 字节加上 Page 01h、Page 02h、Page 1Ah、Page 1Bh……总共有上千字节要管理。256 个门牌号怎么塞下上千个房间解法把地址空间劈成两半把 256 个地址切成两个区域区域地址范围特点下半区0x00 — 0x7F128 字节永远不变放最常用的东西上半区0x80 — 0xFF128 字节可变根据当前页显示不同内容比喻一个文件柜想象一个文件柜┌─────────────────────┐ │ 0x00 - 0x7F │ ← 下半区固定抽屉 │ 温度 | 电压 | 状态 │ 永远在这里直接拉开就能看 │ 告警 | 版本 | 控制 │ ├─────────────────────┤ │ 0x80 - 0xFF │ ← 上半区只有一个抽屉口 │ │ 但后面有多个页可以切换 │ [当前显示的页] │ └─────────────────────┘下半区像常用的工具挂在墙上——不用翻找伸手就拿。上半区像只有一个开口的文件柜抽屉——你只能看到当前选中的那一页。想看另一页先拨一下选择开关抽屉里的内容就换了。实际操作怎么切换页第一步写 Page Select 寄存器地址0x7F在下半区所以永远可以直接访问是 Page Select 寄存器。写入页码主机 → I2C 写 0x7F 0x00 // 选择 Page 00h // 此时 0x80-0xFF 显示 Page 00h 的上半区 主机 → I2C 写 0x7F 0x01 // 选择 Page 01h // 同一个 0x80-0xFF内容全换了 主机 → I2C 写 0x7F 0x1A // 选择 Page 1Ah // 又换了——ELSFP 通道控制信息第二步读写上半区页切换后正常读写0x80-0xFF就行——硬件自动帮你映射到正确的物理寄存器。// 读 Page 1Ah 的 0x90 寄存器 I2C_Write(0x7F, 0x1A); // 先切页 uint8_t data I2C_Read(0x90); // 再读——拿到的就是 Page 1Ah 0x90主机读温度的例子主机想知道模块温度在 Page 00h 的0x0E-0x0F1. 主机 I2C 读地址 0x0E → 直接读因为 0x0E 0x80 在下半区 → 拿到温度高字节 2. 主机 I2C 读地址 0x0F → 同样在下半区 → 拿到温度低字节 全程不需要动 Page Select。主机想读 ELSFP 通道状态在 Page 1Ah 的0xDC1. 主机 I2C 写 0x7F 0x1A ← 先选 Page 1Ah 2. 主机 I2C 读 0xDC ← 此时 0xDC 位于上半区显示 Page 1Ah 的内容 → 拿到通道状态固件侧怎么实现从设备MCU I2C 从机内部维护一个g_u8CurrentPage变量展开查看完整代码31行展开查看完整代码31行展开查看完整代码31行展开查看完整代码31行展开查看完整代码31行volatile uint8_t g_u8CurrentPage 0x00; // 默认 Page 00h // I2C 写事件处理 void I2C_WriteHandler(uint8_t u8Addr, uint8_t u8Data) { if (u8Addr 0x7F) { // 写 0x7F → 切换当前页 g_u8CurrentPage u8Data; return; } if (u8Addr 0x80) { // 上半区 → 写入当前页对应的物理位置 WritePageRegister(g_u8CurrentPage, u8Addr, u8Data); } else { // 下半区 → 直接写 WriteLowerRegister(u8Addr, u8Data); } } // I2C 读事件处理 uint8_t I2C_ReadHandler(uint8_t u8Addr) { if (u8Addr 0x80) { // 上半区 → 从当前页对应的物理位置读 return ReadPageRegister(g_u8CurrentPage, u8Addr); } else { // 下半区 → 直接读 return ReadLowerRegister(u8Addr); } }核心逻辑就一个判断地址 0x80 直接读写地址 0x80 加上当前页去查。为什么下半区放这些下半区的东西是无论你在哪一页都需要访问的地址内容为什么放下半区0x0E-0x0F模块温度主机随时要看不能换页0x03模块状态一插电就要知道模块在什么状态0x08-0x0B告警标志出故障了立刻感知不依赖页0x7FPage Select 本身自举——开关必须放在固定的地方上半区放需要看但不是每时每刻都要的——比如配置参数、广告信息、每个通道的详细数据。总结概念一句说清Page Select 是什么用0x7F寄存器选择上半区映射到哪一页下半区0x00-0x7F永远不变放紧急和常用数据上半区0x80-0xFF根据 Page Select 切换内容固件实现一个g_u8CurrentPage变量 地址判断类比文件柜的共享抽屉口——转动选择开关抽屉内容就换这个技巧不限于 CMIS 光模块任何I2C 寄存器太多塞不下的芯片都这么干。理解一次终身受用。 点个收藏下次调试直接用。有问题欢迎评论区交流看到了都会回。下一篇《附完整代码》——NTC测温、SPI通信、I2C从机、定点数运算——外设驱动从入门到精通 点个收藏下次调试直接用。有问题欢迎评论区交流看到了都会回。下一篇《附完整代码》——NTC测温、SPI通信、I2C从机、定点数运算——外设驱动从入门到精通 点个收藏下次调试直接用。有问题欢迎评论区交流看到了都会回。下一篇《附完整代码》——NTC测温、SPI通信、I2C从机、定点数运算——外设驱动从入门到精通 点个收藏下次调试直接用。有问题欢迎评论区交流看到了都会回。下一篇《附完整代码》——NTC测温、SPI通信、I2C从机、定点数运算——外设驱动从入门到精通 点个收藏下次调试直接用。有问题欢迎评论区交流看到了都会回。下一篇《附完整代码》——NTC测温、SPI通信、I2C从机、定点数运算——外设驱动从入门到精通 点个收藏下次调试直接用。有问题欢迎评论区交流看到了都会回。下一篇《附完整代码》——NTC测温、SPI通信、I2C从机、定点数运算——外设驱动从入门到精通 点个收藏下次调试直接用。有问题欢迎评论区交流看到了都会回。下一篇《附完整代码》——NTC测温、SPI通信、I2C从机、定点数运算——外设驱动从入门到精通 点个收藏下次调试直接用。有问题欢迎评论区交流看到了都会回。下一篇《附完整代码》——NTC测温、SPI通信、I2C从机、定点数运算——外设驱动从入门到精通 点个收藏下次调试直接用。有问题欢迎评论区交流看到了都会回。下一篇《附完整代码》——NTC测温、SPI通信、I2C从机、定点数运算——外设驱动从入门到精通 点个收藏下次调试直接用。有问题欢迎评论区交流看到了都会回。下一篇附完整代码——NTC测温、SPI通信、I2C从机、定点数运算——外设驱动从入门到精通 点个收藏下次调试直接用。有问题欢迎评论区交流看到了都会回。下一篇附完整代码——NTC测温、SPI通信、I2C从机、定点数运算——外设驱动从入门到精通有用的话点个收藏下次调试直接用。有问题欢迎评论区交流看到了都会回。下一篇附完整代码——NTC测温、SPI通信、I2C从机、定点数运算——外设驱动从入门到精通