尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

天空星HC32F4A0PITB开发板AT24C02 EEPROM I2C驱动移植与数据存储实战

天空星HC32F4A0PITB开发板AT24C02 EEPROM I2C驱动移植与数据存储实战 天空星HC32F4A0PITB开发板AT24C02 EEPROM I2C驱动移植与数据存储实战最近在做一个需要保存配置参数的项目用到了AT24C02这颗EEPROM芯片。我发现很多刚开始用天空星HC32F4A0PITB开发板的朋友在驱动外部I2C设备时容易卡在时序和配置上。今天我就把自己在天空星开发板上移植AT24C02驱动、实现数据掉电保存的完整过程分享出来手把手带你走一遍。AT24C02是个很常用的EEPROM电可擦除可编程只读存储器容量是2K位也就是256字节。它通过I2C接口通信特别适合存储一些需要掉电保存的小数据比如设备校准参数、运行记录、用户设置等。咱们这篇教程就是要把这个模块在天空星开发板上用起来。1. 准备工作了解AT24C02模块在开始写代码之前咱们先看看手头的硬件。我用的这个AT24C02模块是从立创商城买的如果你手头有类似的模块引脚顺序可能不一样但核心的I2C接口都一样。模块关键参数工作电压1.8V-5.5V很宽3.3V系统直接能用通信接口I2C也叫IIC存储容量2048位也就是256字节时钟速度5V供电时最高1MHz其他电压下最高400kHz注意I2C通信需要两根线——SCL时钟线和SDA数据线这两根线都需要接上拉电阻。好在天空星开发板的GPIO内部可以配置上拉咱们直接用就行。模块的I2C设备地址是固定的写操作地址是0xA0读操作地址是0xA1。这个在后面的代码里会用到。2. 硬件连接与引脚配置首先得把模块和开发板连起来。AT24C02就4个引脚VCC、GND、SCL、SDA。接线方式VCC→ 开发板3.3VGND→ 开发板GNDSCL→ 开发板PB09我选这个引脚SDA→ 开发板PB08我选这个引脚为什么选PB08和PB09其实只要是普通的GPIO引脚都行我习惯用B口的这两个你完全可以根据自己的板子布局换其他引脚记得在代码里改一下就行。在代码里引脚定义是在头文件bsp_at24c02.h里做的//端口移植 #define PORT_AT24C02 GPIO_PORT_B #define GPIO_SDA GPIO_PIN_08 #define GPIO_SCL GPIO_PIN_09这里GPIO_PORT_B表示B端口GPIO_PIN_08和GPIO_PIN_09就是具体的引脚编号。如果你要换到PA05和PA06就改成GPIO_PORT_A和对应的引脚号。3. 驱动代码详解从GPIO初始化到读写函数移植驱动说白了就是把别人写好的代码拿过来根据咱们自己的硬件平台改一改。原始代码提供了完整的I2C模拟时序和AT24C02操作函数咱们需要理解每一部分是干什么的。3.1 GPIO初始化配置I2C通信的GPIO有点特殊需要配置为开漏输出模式。开漏模式是什么意思呢简单说就是引脚只能拉低电平输出0不能主动输出高电平输出1。高电平靠外部上拉电阻拉上去。这样做是为了避免多个设备同时驱动总线时产生冲突。void AT24C02_GPIO_Init(void) { stc_gpio_init_t stcGpioInit; // 定义GPIO结构体 // 关闭寄存器保护天空星MCU的特殊操作允许修改配置 LL_PERIPH_WE(LL_PERIPH_ALL); (void)GPIO_StructInit(stcGpioInit); stcGpioInit.u16PinState PIN_STAT_SET; // 引脚初始状态设为高 stcGpioInit.u16PinOutputType PIN_OUT_TYPE_NMOS; // 开漏输出 stcGpioInit.u16PinDir PIN_DIR_OUT; // 输出模式 stcGpioInit.u16PullUp PIN_PU_ON; // 上拉开启重要 (void)GPIO_Init(PORT_AT24C02, GPIO_SDA|GPIO_SCL, stcGpioInit); GPIO_SetPins(PORT_AT24C02,GPIO_SDA|GPIO_SCL); // 初始化为高电平 }这里有几个关键点PIN_OUT_TYPE_NMOS就是开漏输出模式PIN_PU_ON开启了内部上拉电阻这样就不需要外接上拉电阻了初始化后把引脚都设为高电平这是I2C总线的空闲状态3.2 I2C时序模拟最核心的部分AT24C02用的是标准I2C协议但天空星的硬件I2C外设配置起来比较麻烦而且这个EEPROM速度要求不高所以咱们用GPIO模拟I2C时序这样更灵活也更容易理解I2C的工作原理。I2C起始信号void IIC_Start(void) { SDA_OUT(); // 设置SDA为输出模式 SDA(1); // SDA先拉高 delay_us(5); SCL(1); // SCL再拉高 delay_us(5); SDA(0); // SDA在SCL高电平时拉低产生起始条件 delay_us(5); SCL(0); // SCL拉低准备传输数据 delay_us(5); }起始信号就是在SCL高电平期间SDA从高变低。这个时序一定要对早了晚了都不行。I2C停止信号void IIC_Stop(void) { SDA_OUT(); SCL(0); SDA(0); SCL(1); // SCL先拉高 delay_us(5); SDA(1); // SDA在SCL高电平时从低变高产生停止条件 delay_us(5); }停止信号和起始信号相反是在SCL高电平期间SDA从低变高。发送一个字节void Send_Byte(uint8_t dat) { int i 0; SDA_OUT(); SCL(0); // 拉低时钟开始数据传输 for(i 0; i 8; i) { SDA((dat 0x80) 7); // 取最高位发送 delay_us(1); SCL(1); // 时钟拉高从机读取数据位 delay_us(5); SCL(0); // 时钟拉低准备下一位 delay_us(5); dat 1; // 左移准备发送下一位 } }这里要注意I2C协议规定数据在SCL低电平期间变化在SCL高电平期间保持稳定。所以咱们在SCL拉高前设置好SDA保持整个高电平期间不变。读取一个字节unsigned char Read_Byte(void) { unsigned char i, receive 0; SDA_IN(); // SDA设置为输入模式准备读取 for(i 0; i 8; i) { SCL(0); delay_us(5); SCL(1); // 时钟拉高主机读取数据位 delay_us(5); receive 1; // 左移为下一位腾出空间 if(SDA_GET()) // 读取SDA电平 { receive | 1; // 如果为高最低位置1 } delay_us(5); } SCL(0); return receive; }读取时主机要在SCL低电平期间释放SDA不驱动然后拉高SCL这时候从机会把数据放到SDA上主机读取这个电平。3.3 AT24C02读写函数实现有了基础的I2C时序函数现在来实现AT24C02的具体读写操作。写入一个字节void AT24C02_WriteByte(unsigned char WordAddress, unsigned char Data) { IIC_Start(); Send_Byte(AT24C02_ADDRESS_READ); // 发送设备地址写标志 I2C_WaitAck(); // 等待从机应答 Send_Byte(WordAddress); // 发送要写入的存储地址 I2C_WaitAck(); Send_Byte(Data); // 发送要写入的数据 I2C_WaitAck(); IIC_Stop(); }写操作的流程是起始信号 → 发送设备地址0xA0→ 发送要写的存储地址 → 发送数据 → 停止信号。注意AT24C02写入数据后需要一点时间大概5ms来真正把数据写到存储单元里所以写操作后要加延时否则紧接着读可能会读到旧数据。读取一个字节unsigned char AT24C02_ReadByte(unsigned char WordAddress) { unsigned char Data; IIC_Start(); Send_Byte(AT24C02_ADDRESS_READ); // 发送设备地址写标志 I2C_WaitAck(); Send_Byte(WordAddress); // 发送要读取的存储地址 I2C_WaitAck(); IIC_Start(); // 再次发送起始信号 Send_Byte(AT24C02_ADDRESS_WRITE); // 发送设备地址读标志 I2C_WaitAck(); Data Read_Byte(); // 读取数据 IIC_Send_Ack(1); // 发送非应答信号停止读取 IIC_Stop(); return Data; }读操作稍微复杂点先像写操作一样发送地址然后重新发送起始信号再发送读地址最后读取数据。读取完成后要发送一个非应答信号NACK告诉从机我读完了。4. 在工程中测试验证代码写好了得验证一下到底能不能用。我在main函数里写了个简单的测试程序#include board.h #include bsp_uart.h #include stdio.h #include bsp_at24c02.h int32_t main(void) { unsigned char dat1 0; unsigned char dat2 0; board_init(); uart1_init(115200U); // 初始化串口用于打印调试信息 AT24C02_GPIO_Init(); // 初始化AT24C02的GPIO printf(start\r\n); // 向地址0写入数据48 AT24C02_WriteByte(0, 48); delay_ms(5); // 等待写入完成 // 向地址8写入数据66 AT24C02_WriteByte(8, 66); delay_ms(5); // 从地址0读取数据 dat1 AT24C02_ReadByte(0); delay_ms(5); // 从地址8读取数据 dat2 AT24C02_ReadByte(8); delay_ms(5); // 通过串口打印读取的数据 printf(dat1 %d\r\n, dat1); delay_ms(1); printf(dat2 %d\r\n, dat2); delay_ms(1); while(1) { // 主循环 } }这个测试程序做了几件事初始化系统和串口方便看结果初始化AT24C02的GPIO往地址0写48往地址8写66再把这两个地址的数据读出来通过串口打印读取的值如果一切正常串口会输出start dat1 48 dat2 66如果看到这个输出恭喜你AT24C02驱动移植成功了5. 实际使用中的注意事项和常见问题在实际项目中使用AT24C02时有几个坑我踩过这里提醒一下1. 写入延时问题AT24C02写入数据需要时间手册上叫写周期时间一般是5ms。如果你连续写入多个字节每写一个最好加个delay_ms(5)不然可能写入失败。2. 地址范围AT24C02只有256字节地址范围是0-255。如果你写的地址超过255它会回绕到开头。比如写地址256实际上会写到地址0。3. 页写入功能AT24C02支持页写入一次可以连续写一页8字节。但要注意如果跨页了地址会自动回绕到当前页开头可能会覆盖之前的数据。我建议新手还是一个字节一个字节地写虽然慢点但稳妥。4. I2C上拉电阻虽然代码里开启了内部上拉但如果通信距离比较长或者干扰比较大最好还是在SCL和SDA上加个4.7kΩ的外部上拉电阻这样通信更稳定。5. 时序延时调整代码里的delay_us(5)是经验值如果你的系统主频特别高或者特别低可能需要调整这些延时。原则是SCL高电平或低电平的时间不能太短要保证从机有足够时间响应。移植成功后你就可以用这个驱动来保存各种需要掉电保存的数据了。比如保存温湿度传感器的校准值、记录设备运行时间、存储用户设置参数等。AT24C02虽然容量小但对于很多嵌入式应用来说完全够用关键是便宜、稳定、容易买到。如果你在移植过程中遇到问题可以检查以下几点接线是否正确特别是VCC和GND不要接反引脚配置是否正确一定要配置为开漏输出上拉延时是否足够特别是写入后的延时设备地址是否正确AT24C02是0xA0/0xA1好了关于天空星HC32F4A0PITB开发板驱动AT24C02的内容就这些。实际用起来你会发现EEPROM虽然简单但在很多项目中都是必不可少的。掌握了这个你就能轻松实现数据的掉电保存功能了。
返回列表