ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

ESP32-Bit-Pirate 的 SPI EEPROM 实战:EEPROM_SPI_WE 库读写、分页、擦除与写保护全解析

ESP32-Bit-Pirate 的 SPI EEPROM 实战:EEPROM_SPI_WE 库读写、分页、擦除与写保护全解析 嵌入式硬件开发【免费下载链接】ESP32-Bit-PirateA Hardware Hacking Tool with Web-Based CLI That Speaks Every Protocol项目地址https://gitcode.com/GitHub_Trending/es/ESP32-Bit-Pirate点击查看免费下载本篇技术指南围绕 ESP32-Bit-Pirate 仓库内置的第三方 Arduino 库 EEPROM_SPI_WE 展开系统讲解如何通过 SPI 总线读写 25 系列外部 EEPROM如 25LC512、25A512、25LC640、25LC040A 以及 25CSM04 等 24 位寻址型号。读完本文你将掌握库的构造与初始化方式、直接写入 byte/int/float/数组与字符串的方法、底层分页写入机制、连续写入加速技巧、页/扇区/全片擦除以及写保护、状态寄存器保护与深度掉电模式等完整实战能力并能在 ESP32、ESP8266、AVR 等平台上直接复用。一、库定位为硬件黑客工具补齐 SPI EEPROM 读写能力EEPROM_SPI_WE 是一个专注于 SPI 接口外部 EEPROM 的 Arduino 库。它的核心设计目标有三点屏蔽分页细节虽然 EEPROM 按页写入但库内部自动处理页边界切分调用者只需关心地址与数据无需手动计算当前地址还剩多少字节会跨页类型无关的读写借鉴 SparkFun 外部 EEPROM 库的put()/get()设计可以像操作内存一样直接写入字节、整数、浮点数、结构体与大数组面向工程应用内置擦除、写保护、状态寄存器保护、深度掉电等数据安全与低功耗能力覆盖从普通存储到数据保护的全场景。在 ESP32-Bit-Pirate 项目中该库作为lib/目录下的第三方依赖存在服务于 SPI 总线相关的外设读写场景项目中还有 SpiEepromShell 等 Shell 层封装与之呼应。库的元信息记录在 library.properties 中版本 1.1.5分类为 Data Storage声明兼容architectures*即不限定具体 MCU 架构。二、硬件接线与对象构造六种构造方式库通过 SPI 访问 EEPROM典型接线只需 4 根线外加可选的写保护引脚 WP / 保持引脚 HOLD引脚作用说明CS片选必接构造与init()时传入SCK / MOSI / MISOSPI 数据线由 SPI 外设管理WP可选写保护不接或接 GND/VCC 时仅protectStatusRegister()不可用HOLD可选保持拉低可暂停当前 SPI 事务供复用 SPI 线对象构造支持六种重载均在 EEPROM_SPI_WE.h 中定义EEPROM_SPI_WE myEEP EEPROM_SPI_WE(csPin, wpPin, spiClockSpeed); // 全部参数 EEPROM_SPI_WE myEEP EEPROM_SPI_WE(csPin, wpPin); // 使用默认 8 MHz 时钟 EEPROM_SPI_WE myEEP EEPROM_SPI_WE(csPin); // 不启用 WP 引脚 EEPROM_SPI_WE myEEP EEPROM_SPI_WE(SPI, csPin, wpPin, spiClockSpeed); // 指定 SPI 实例 EEPROM_SPI_WE myEEP EEPROM_SPI_WE(SPI, csPin, wpPin); EEPROM_SPI_WE myEEP EEPROM_SPI_WE(SPI, csPin);参数说明csPin片选引脚号wpPin写保护引脚号默认 255 表示未使用spiClockSpeedSPI 时钟频率默认 8 MHzsc 8000000实际允许值以芯片数据手册为准且可能随供电电压变化传入SPI指针的形式允许指定 SPI 实例——例如 ESP32 的双 SPI 接口FSPI/HSPI场景即可通过此方式切换。三、初始化与连接探测init() / probe()创建对象后必须在setup()中调用init()Serial.begin(9600); if (myEEP.init()) { Serial.println(EEPROM connected); } else { Serial.println(EEPROM does not respond); while(1); }init()EEPROM_SPI_WE.cpp的内部流程以MSBFIRST、SPI_MODE0建立 SPI 事务拉起 CS若指定了wpPin将其置高解除硬件写保护复位内部状态pageSize 16、smallEEPROM false、memSize 0调用writeProtect(PROTECT_NONE)解除软件写保护写入写使能命令EEP_WREN后读状态寄存器若 WEN 位0x02被置位则确认芯片在线。如果init()返回 false通常意味着接线错误、CS 引脚冲突或芯片未供电。库还提供probe()用于在运行期随时再次探测芯片是否在线EEPROM_SPI_WE.cpp实现与init()相同的写使能探测逻辑。四、基础读写byte / int / float / 数组 / String初始化后即可直接读写。参考示例 EEPROM_SPI_WE_basic_read_write.ino五种典型用法如下myEEP.setPageSize(EEPROM_PAGE_SIZE_32); // 按数据手册设置页大小 // 1. 单字节读写 byte byteToWrite 42; myEEP.write(10, byteToWrite); byte byteToRead myEEP.read(10); // 2. 整数读写 int intToWrite -4242, intToRead 0; myEEP.put(10, intToWrite); myEEP.get(10, intToRead); // 3. 浮点数读写 float floatToWrite 42.42, floatToRead 0.0; myEEP.put(10, floatToWrite); myEEP.get(10, floatToRead); // 4. char 数组读写 char charArrayToWrite[] This is no poetry, I am just a simple char array; char charArrayToRead[60] ; myEEP.put(110, charArrayToWrite); myEEP.get(110, charArrayToRead); // 5. String 对象读写 String stringToWrite Hello, I am a test string; unsigned int nextAddr myEEP.putString(200, stringToWrite); String stringToRead ; myEEP.getString(200, stringToRead);关键 API 说明write(addr, val)/read(addr)单字节读写。write()会先比较新旧值仅在新值不同时才发起写操作EEPROM_SPI_WE.cpp可显著减少不必要的写周期延长 EEPROM 寿命put(addr, t)/get(addr, t)模板函数put()取sizeof(T)字节原样写入get()原样读回EEPROM_SPI_WE.h。因此 int、float、结构体、任意类型数组都可直接使用读取端务必预分配足够空间如charArrayToRead[60]需容纳写侧字符串putString()/getString()String 对象的专用读写。putString()会把字符串连同结尾的\0一起写入并返回下一个空闲地址起始地址 字符串长度 1方便连续存储多条字符串getString()则从地址起逐字节读取直到遇到\0EEPROM_SPI_WE.cppsetPageSize()必须按数据手册设置可选EEPROM_PAGE_SIZE_16 / 32 / 64 / 128 / 256枚举定义见 EEPROM_SPI_WE.h实际由pageSize 16 eps换算EEPROM_SPI_WE.cpp。五、底层原理分页写入是如何自动完成的EEPROM 的写入以页为单位单次写指令最多写一个页若数据跨页必须拆成多次写指令且每次写指令之间要等待芯片内部的写周期完成。这正是虽然它做页写入但你无需关心页限制这句话背后的实现。看writeEEPROM()的核心逻辑EEPROM_SPI_WE.cppuint16_t positionInPage addr % pageSize; uint16_t spaceLeftInPage pageSize - positionInPage; if (spaceLeftInPage noOfBytesStillToWrite) { chunk spaceLeftInPage; // 本页剩余空间不足截断为页内剩余字节数 } // 发送 EEP_WRITE(0x02) 地址 chunk 个数据字节 // 等待写周期完成后addr 与数组下标前移 chunk继续下一段即库按当前地址距页尾还剩多少字节动态切分每次写事务写完一段后通过waitUntilReady()轮询状态寄存器的 WIP 位0x01等待内部写周期结束再写下一段从而把跨页写入完全透明化。waitUntilReady()最多轮询 256 次、每次间隔 50µsEEPROM_SPI_WE.cpp超时返回 false。因此调用者把数据一股脑交给put()库负责分页、等待与续写。这也是连续写入优化下一节的前提。六、连续写入continuousPut 大幅减少写周期等待每次页写入完成后都必须等待约 4 ms 的写周期具体以数据手册为准。如果逐条调用put()N 条数据就要等 N 次写周期。库提供的连续写入模式只在页写满和调用continuousPutDisable()时才等待写周期从而大幅提升批量写入吞吐。参考示例 EEPROM_SPI_WE_continuous_put.inomyEEP.continuousPutEnable(address); // 打开连续写并指定起始地址 myEEP.continuousPut(9999); myEEP.continuousPut(8888); // ... 连续写入多个数据 myEEP.continuousPut(0); myEEP.continuousPutDisable(); // 必须调用结束事务并等待最终写周期示例中两种写法的耗时对比逻辑也说明了收益从地址 125 起用 10 次普通put()写 10 个 int再用continuousPutEnable(125) 10 次continuousPut()continuousPutDisable()写同样数据。当所有数据落在同一页内时连续写法全程只需一次写周期等待。其底层实现EEPROM_SPI_WE.cpp是continuousPutEnable()打开 SPI 事务并发送写指令与起始地址之后的每次continuousPut()只继续在总线上推数据字节只有检测到本页写满pageEnd才抬升 CS、结束事务、等待写周期然后自动用新地址重新打开事务继续写。因此在连续写入模式下CS 在整个页写入期间保持低电平这是与普通put()的本质区别。结合 HOLD 引脚复用 SPI 总线连续写入期间 CS 保持拉低此时若把 HOLD 引脚拉低可暂停当前 SPI 事务而不丢失状态让出 SPI 线给其他外设使用HOLD 恢复高电平后继续传输。示例代码如下myEEP.continuousPutEnable(address); myEEP.continuousPut(999); myEEP.continuousPut(888); digitalWrite(holdPin, LOW); // 暂停可临时用 SPI 线控制其他设备 // ... 与其他设备通信 ... digitalWrite(holdPin, HIGH); // 恢复继续原事务 myEEP.continuousPut(555); myEEP.continuousPutDisable();注意使用该特性时HOLD 引脚必须在setup()中配置为 OUTPUT 并初始化为 HIGH。七、擦除操作页 / 扇区 / 全片库提供三级擦除能力EEPROM_SPI_WE.cppmyEEP.erasePage(3); // 擦除地址 3 所在的一页 myEEP.eraseSector(7); // 擦除地址 7 所在的一个扇区 myEEP.eraseCompleteEEPROM(); // 全片擦除三者对应的指令分别为EEP_PE(0x42)、EEP_SE(0xD8)、EEP_CE(0xC7)均在 EEPROM_SPI_WE.h 中定义擦除后存储单元恢复为 0xFF这一点与写入 0不同示例 EEPROM_SPI_WE_erase.ino 通过先put(10, 42)再擦除、随后用read(10)/read(11)观察 MSB/LSB 来验证传入的地址可以是页/扇区范围内的任意地址库会按地址定位到所属页或扇区重要限制页/扇区/全片擦除指令仅存在于较大容量 EEPROM 中小型 EEPROM128~1024 字节级通常没有EEP_CE指令。使用前务必核对数据手册。对于小型 EEPROMeraseCompleteEEPROM()会自动退化为逐字节写 0详见第九节。八、数据保护写保护分区与状态寄存器保护1. 软件写保护writeProtectmyEEP.writeProtect(PROTECT_ALL); // 完全写保护 myEEP.writeProtect(PROTECT_UPPER_HALF); // 保护上半区 myEEP.writeProtect(PROTECT_UPPER_QUARTER); // 保护上四分之一区 myEEP.writeProtect(PROTECT_NONE); // 解除写保护可选保护范围枚举定义在 EEPROM_SPI_WE.h。其实现EEPROM_SPI_WE.cpp通过EEP_WRSR(0x01) 写状态寄存器(sectors 2) | 0x80把保护位写入状态寄存器高三位。被保护区域内的一切写/擦除操作都会被芯片拒绝——示例 EEPROM_SPI_WE_sleep_and_protect.ino 演示了PROTECT_ALL下执行put(50, 4321)后读回仍是旧值 42解除保护后写入才生效。2. 状态寄存器保护protectStatusRegistermyEEP.protectStatusRegister(true); // 保护状态寄存器非易失位 myEEP.protectStatusRegister(false); // 解除该方法EEPROM_SPI_WE.cpp把状态寄存器最高位 BP 置位此后任何修改写保护分区的操作都会被拒绝前提是必须接入并启用wpPin硬件写保护引脚因为实现依赖拉低 WP 引脚来锁定状态寄存器。若对象构造时未指定wpPin该函数将静默不生效——这也解释了 README 与示例中反复强调未定义 wpPin 时只有 protectStatusRegister() 不可用的原因。九、特殊型号支持小型 EEPROM 与 24 位寻址1. 小型 EEPROM128/256/512/1024 字节小型 EEPROM 的地址线不足 8 根需通过指令码中嵌入的 A8/A9 位来扩展地址。库的处理方式是myEEP.setSmallEEPROM(); // 必须在 init() 之后调用 myEEP.setSPIClockSpeed(2000000); // 小型芯片通常需要降速示例中降至 2 MHz myEEP.setPageSize(EEPROM_PAGE_SIZE_16);调用setSmallEEPROM()后EEPROM_SPI_WE.cppwriteEEPROM()/readEEPROM()会依据地址高位选择在指令码中附加EEP_A8(0x08) 与EEP_A9(0x10) 位如EEP_READ | EEP_A8、EEP_READ | EEP_A8 | EEP_A9从而覆盖到 1024 字节级容量。示例 EEPROM_SPI_WE_small_eeproms.ino 给出了完整流程并提醒小型 EEPROM 不一定支持所有功能务必核对数据手册。2. 24 位寻址与容量上限25CSM04 等对于超过 512 kbitEEPROM_KBITS_512的型号例如采用 24 位地址的 25CSM04访问前必须先声明容量myEEP.setMemorySize(EEPROM_KBITS_4096); // 例如 4096 kbit uint32_t size myEEP.getMemorySize(); // 查询当前设置容量枚举eeprom_size_t从 2 kbit256 字节到 4096 kbit524288 字节共 12 档EEPROM_SPI_WE.h。当memSize EEPROM_KBITS_512时读写、页擦除、扇区擦除的地址都会多发送一个高字节addr 16实现 24 位地址总线。需特别注意setMemorySize()只记录容量值本身并不会主动探测芯片必须与所用芯片匹配25CSM04 不支持擦除指令因此不要对其调用erasePage()/eraseSector()/eraseCompleteEEPROM()由于 24 位寻址型号容量大若smallEEPROM未使能且芯片不支持EEP_CE全片擦除行为取决于memSize设置。3. 小型 EEPROM 的全片擦除退化小型 EEPROM 没有EEP_CE指令。eraseCompleteEEPROM()检测到smallEEPROM true时会改为遍历memSize范围内每个地址逐字节写 0EEPROM_SPI_WE.cpp。因此对小型 EEPROM 调用全片擦除前必须先setMemorySize()指定容量否则遍历长度为零擦除不生效。这与擦除 写 0xFF的语义不同——退化路径写入的是 0x00务必区分。十、低功耗深度掉电与唤醒读 IDmyEEP.deepPowerDown(); // 进入深度掉电模式EEP_DPD, 0xB9 byte id myEEP.powerUpAndReadID(); // 唤醒并读取电子签名EEP_RDID, 0xABdeepPowerDown()发送EEP_DPD指令芯片进入最低功耗状态EEPROM_SPI_WE.cpppowerUpAndReadID()通过EEP_RDID指令先发送两个哑地址字节、再读回 1 字节设备 IDEEPROM_SPI_WE.cpp同时完成唤醒与身份识别示例 EEPROM_SPI_WE_sleep_and_protect.ino 演示了写入 → 掉电 → 尝试读取 → 唤醒读 ID → 再次读取的完整闭环用于验证掉电期间数据保持、唤醒后存储完好。十一、其他实用方法速查方法作用源码位置setSPIClockSpeed(clock)运行期调整 SPI 时钟须在init()之后调用EEPROM_SPI_WE.cppisBusy()查询写周期是否进行中返回 WIP 位EEPROM_SPI_WE.cppwaitUntilReady()阻塞等待写周期结束带超时EEPROM_SPI_WE.cppeepromReadStatusReg()读取状态寄存器原始值EEPROM_SPI_WE.cpp十二、平台兼容性与验证情况根据 README 的声明作者已在多种真实硬件上验证过库的功能EEPROM 型号25LC512、25A512、25LC640、25LC040A以及后续补充的 24 位寻址型号 25CSM04MCU 平台ESP32 开发板、WEMOS D1 miniESP8266、Arduino NanoAVR。这一点与 library.properties 中architectures*、库描述中Tested on AVR-, ESP32- and ESP8266 Boards相互印证。库基于标准SPI.h与Arduino.h编写理论上任何 Arduino 兼容平台均可移植跨平台使用时重点核对目标平台的 SPI 引脚映射与时钟上限8 MHz 为默认值非所有芯片/电压组合都支持。十三、从示例到实战快速上手指南在 ESP32-Bit-Pirate 的仓库中该库附带 5 个可直接编译的示例覆盖了全部核心功能建议按顺序研读EEPROM_SPI_WE_basic_read_write.ino基础读写含全部类型EEPROM_SPI_WE_continuous_put.ino连续写入性能对比与 HOLD 引脚复用EEPROM_SPI_WE_erase.ino页/扇区/全片擦除验证EEPROM_SPI_WE_sleep_and_protect.ino深度掉电与写保护EEPROM_SPI_WE_small_eeproms.ino小型 EEPROM 与容量声明。实战要点总结接线后先跑init()探活返回 false 时优先排查 CS 与供电务必按数据手册设置setPageSize()页大小设错会导致跨页写入被芯片拒绝或数据错位大批量连续写入优先使用continuousPutEnable()/continuousPut()/continuousPutDisable()组合写入频繁且对寿命敏感的场景可依赖write()的值未变不写优化或主动利用writeProtect()对关键区域加锁遇到 128~1024 字节的小型 EEPROM 时记得依次调用setSmallEEPROM()、降低 SPI 时钟并setMemorySize()声明容量遇到 512 kbit 以上24 位寻址型号时先setMemorySize()再访问并确认该型号是否支持擦除指令。掌握以上 API 与底层机制后无论是为硬件调试工具扩展 SPI EEPROM 读写能力还是在嵌入式项目中实现参数存储、日志落盘或固件配置持久化都可以直接复用这套库的能力。赞分享嵌入式硬件开发【免费下载链接】ESP32-Bit-PirateA Hardware Hacking Tool with Web-Based CLI That Speaks Every Protocol项目地址https://gitcode.com/GitHub_Trending/es/ESP32-Bit-Pirate点击查看免费下载相关推荐ESP32-Bit-Pirate 项目中的 RF24 库 Arduino 接入指南标准 SPI、软件 SPI 与多总线切换ESP32 Bit Pirate 项目中的 RF24 库 Arduino 接入指南标准 SPI、软件 SPI 与多总线切换 RF24 是驱动 nRF24L01嵌入式硬件开发proxmark3 T5577 实战指南从块读写、Bit 级配置到密码保护与嗅探proxmark3 T5577 实战指南从块读写、Bit 级配置到密码保护与嗅探 本文以 proxmark3Iceman Fork仓库自带的 doc/T5渗透测试嵌入式物联网网络安全ESP32-Bit-Pirate 的 LoRa 引擎SX126x-Arduino 库从 1.x 到 2.0.32 的演进全解析ESP32 Bit Pirate 的 LoRa 引擎SX126x Arduino 库从 1.x 到 2.0.32 的演进全解析 导读 SX126x Ardui嵌入式硬件开发上一篇5个高频难题讲透Dify语音交互实战30分钟让应用会听会说下一篇boardgame.io React绑定教程构建现代化游戏界面创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表