0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

AS32X601的I2C模块操作EEPROM详解

安芯 来源:jf_29981791 作者:jf_29981791 2025-12-21 21:39 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

国科安芯推出的AS32X601系列MCU芯片内置的I2C模块提供了符合工业标准的两线串行制接口,可用于MCU和外部I2C设备的通讯。I2C总线使用两条串行线:串行数据线SDA和串行时钟线SCL。 I2C接口模块实现了I2C协议的标准模式和快速模式,支持多主机I2C总线架构。其标准模式为100K,快速模式400K。而EEPROM,作为一种支持字节级单独擦写、数据掉电不丢失的存储器,其存储容量(从几字节到数百千字节)恰好满足了大量嵌入式应用对中小规模非易失性数据存储的需求。将EEPROM与并行地址/数据总线相连的传统方式会占用大量I/O口,在引脚资源紧张的微控制器(如众多8位、32位MCU)上显得笨重且不经济,因此,AS32X601系列开发板搭载了一块24C02 eeprom。本文旨在系统阐述I2C EEPROM的工作原理与核心操作流程。内容将涵盖I2C通信的基本框架,EEPROM的器件寻址方式,以及针对字节写入等关键流程

一、硬件设计

二、I2C时序

①Start开始信号、Stop停止信号:

这两个信号由主机产生,不属于数据域交互:

在SCL的高电平时,主机将SDA的电平由 高–>低是Start信号(下降沿);

在SCL的高电平时,主机将SDA的电平由 低–>高是Stop信号(上升沿);

②7位寻址

AS32X601的I2C只支持7位寻址模式,配置过程中从机地址需要左移1位才为实际地址。

③数据方向

0写/1读

④应答ACK、非应答NACK

在SCL的一个时钟周期内,从机在SCL的高电平时,将SDA的电平由高拉低(或者继续保持低电平状态) 则是ACK信号;

从机在SCL的高电平时,如果SDA的电平一直是 高电平 则是NACK信号;

三、时钟

I2C0、I2C1时钟来自APB0,I2C2、I2C3时钟来自ABP1。具体配置可见I2C_CTLR寄存器

四、I2C初始化

1.配置I2Cx需要的GPIO为复用功能。

2.通过配置I2C_INITSTRUCT初始化I2Cx,包括时钟分频,从机地址,ACK,高低电平时间等

3.按需求配置中断,并配置IRQ_HANDLER;

4.调用收发接口,并处理数据

五、如何操作EEPROM

5.1按字节写入函数

FlagStatus I2C_MEEPROMWriteByte(I2C_TypeDef* I2Cx, uint8_t addr, uint16_t reg, uint16_t data, uint32_t timeout)

{

unsigned int num;

/ 等待总线释放 /

while (!I2C_CheckStatus(I2Cx, I2C_BUS_IDLE))

{

I2C_StartClear(I2Cx);

I2C_GenerateStop(I2Cx);

I2C_ClearITPendingBit(I2Cx);

if ((timeout--) == 0)

{

return RESET;

}

delay_ms(1);

}

I2C_GenerateStart(I2Cx);

/ 等待启动信号完成 /

while (!I2C_CheckStatus(I2Cx, MASTER_START_READY))

{

if ((timeout--) == 0)

{

return RESET;

}

delay_ms(1);

}

I2C_Send7bitAddress(I2Cx, addr, I2C_WRITE);

I2C_StartClear(I2Cx);

I2C_ClearITPendingBit(I2Cx);

/ 等待从机接收完成地址并发送ack /

while (!I2C_CheckStatus(I2Cx, MSEND_WADDR_ACK))

{

if ((timeout--) == RESET)

{

I2C_GenerateStop(I2Cx);

I2C_ClearITPendingBit(I2Cx);

return RESET;

}

delay_ms(1);

}

I2C_SendData(I2Cx, (uint8_t)(reg >> 0));

I2C_ClearITPendingBit(I2Cx);

/ 等待从机接收完成数据并发送ack /

while (!I2C_CheckStatus(I2Cx, MSEND_DATA_ACK))

{

if ((timeout--) == 0)

{

I2C_GenerateStop(I2Cx);

I2C_ClearITPendingBit(I2Cx);

return RESET;

}

delay_ms(1);

}

I2C_SendData(I2Cx, data);

I2C_ClearITPendingBit(I2Cx);

/ 等待从机接收完成数据并发送ack /

while (!I2C_CheckStatus(I2Cx, MSEND_DATA_ACK))

{

if ((timeout--) == 0)

{

I2C_GenerateStop(I2Cx);

I2C_ClearITPendingBit(I2Cx);

return RESET;

}

delay_ms(1);

}

I2C_GenerateStop(I2Cx);

I2C_ClearITPendingBit(I2Cx);

return 1;

}

代码执行流程详细解释如下:

等待总线空闲:函数首先进入一个循环,反复检查I2C总线是否处于空闲(I2C_BUS_IDLE)状态。如果总线被占用(忙状态),它会尝试通过调用I2C_StartClear和I2C_GenerateStop来清除可能的异常状态并发送停止信号,试图释放总线。每次循环都会递减超时计数器timeout并延迟1毫秒。如果timeout减到0,函数会返回RESET。这个步骤确保了本次传输开始时总线是可用的。

发起起始条件:确认总线空闲后,函数调用I2C_GenerateStart在I2C总线上产生一个起始条件(Start Condition),这标志着一次传输序列的开始。

等待起始条件完成:紧接着,函数进入另一个循环,等待起始条件成功发出的状态(MASTER_START_READY)。同样,这里也有超时检查和1ms延迟,防止程序死锁。超时则返回失败。

发送从机地址(写模式):起始条件成功后,函数调用I2C_Send7bitAddress,将参数addr(EEPROM的7位设备地址)和写操作位(I2C_WRITE,通常值为0)组合成一个8位字节发送出去。随后清除相关状态和中断标志。

等待从机地址应答:函数循环等待从设备(EEPROM)对收到地址的应答信号(MSEND_WADDR_ACK)。如果EEPROM存在于总线上并识别出自己的地址,它会拉低SDA线作为应答(ACK)。函数检测到这个状态才能继续。此处有一个代码瑕疵:超时判断写成了(timeout--) == RESET,虽然RESET很可能定义为0,但不如其他地方的== 0直观统一。超时或失败会发送停止条件并返回失败。

发送EEPROM内部存储地址(存在严重错误):地址应答后,函数准备发送要写入的EEPROM内部单元地址reg。这是一个关键错误。对于16位地址的EEPROM(如reg是uint16_t),需要发送两个字节:先发送高8位,再发送低8位。但代码中I2C_SendData(I2Cx, (uint8_t)(reg >> 0))的reg >> 0等于reg本身,所以它只发送了reg的低8位,完全遗漏了高8位。这会导致写入到错误的EEPROM位置。

等待内部地址字节应答:发送(不完整的)地址字节后,循环等待EEPROM对此数据字节的应答(MSEND_DATA_ACK)。有超时处理。

发送要写入的数据:收到地址字节应答后,调用I2C_SendData(I2Cx, data)发送数据。这里有一个潜在问题:参数data是uint16_t类型,但函数被命名为WriteByte,且I2C_SendData通常发送一个字节。这里发生了隐式截断,只有data的低8位被发送出去。函数意图和参数类型不匹配。

等待数据字节应答:再次循环等待EEPROM对收到数据字节的应答。有超时处理。

结束传输:数据成功发送并得到应答后,函数调用I2C_GenerateStop产生停止条件(Stop Condition),结束本次I2C通信。然后清除中断标志。

5.2读函数

FlagStatus I2C_MEEPROMRead(I2C_TypeDef* I2Cx, uint8_t addr, uint16_t reg, uint8_t* pData, uint32_t Size, uint32_t timeout)

{

uint32_t num = 0x00;

/ 等待总线释放 /

while (!I2C_CheckStatus(I2Cx, I2C_BUS_IDLE))

{

I2C_StartClear(I2Cx);

I2C_GenerateStop(I2Cx);

I2C_ClearITPendingBit(I2Cx);

if ((timeout--) == 0)

{

return RESET;

}

delay_ms(1);

}

I2C_AcknowledgeConfig(I2Cx, I2C_IICAA_ACK);

I2C_GenerateStart(I2Cx);

/ 等待启动信号完成 /

while (!I2C_CheckStatus(I2Cx, MASTER_START_READY))

{

if ((timeout--) == 0)

{

I2C_StartClear(I2Cx);

I2C_GenerateStop(I2Cx);

I2C_ClearITPendingBit(I2Cx);

return RESET;

}

delay_ms(1);

}

I2C_Send7bitAddress(I2Cx, addr, I2C_WRITE);

I2C_StartClear(I2Cx);

I2C_ClearITPendingBit(I2Cx);

/ 等待从机接收完成地址并发送ack /

while (!I2C_CheckStatus(I2Cx, MSEND_WADDR_ACK))

{

if ((timeout--) == 0)

{

I2C_GenerateStop(I2Cx);

I2C_ClearITPendingBit(I2Cx);

return RESET;

}

delay_ms(1);

}

I2C_SendData(I2Cx, (uint8_t)(reg >> 8));

I2C_ClearITPendingBit(I2Cx);

/ 等待从机接收完成数据并发送ack /

while (!I2C_CheckStatus(I2Cx, MSEND_DATA_ACK))

{

if ((timeout--) == 0)

{

I2C_GenerateStop(I2Cx);

I2C_ClearITPendingBit(I2Cx);

return 0;

}

delay_ms(1);

}

I2C_SendData(I2Cx, (uint8_t)(reg >> 0));

I2C_ClearITPendingBit(I2Cx);

/ 等待从机接收完成数据并发送ack /

while (!I2C_CheckStatus(I2Cx, MSEND_DATA_ACK))

{

if ((timeout--) == 0)

{

I2C_GenerateStop(I2Cx);

I2C_ClearITPendingBit(I2Cx);

return RESET;

}

delay_ms(1);

}

I2C_GenerateStart(I2Cx);

I2C_ClearITPendingBit(I2Cx);

/ 等待从机接收完成数据并发送ack /

while (!I2C_CheckStatus(I2Cx, MASTER_START_REPEAT))

{

if ((timeout--) == 0)

{

I2C_GenerateStop(I2Cx);

I2C_ClearITPendingBit(I2Cx);

return RESET;

}

delay_ms(1);

}

I2C_Send7bitAddress(I2Cx, addr, I2C_READ);

I2C_ClearITPendingBit(I2Cx);

/ 等待从机接收完成地址并发送ack /

while (!I2C_CheckStatus(I2Cx, MSEND_RADDR_ACK))

{

if ((timeout--) == 0)

{

I2C_StartClear(I2Cx);

I2C_GenerateStop(I2Cx);

I2C_ClearITPendingBit(I2Cx);

return RESET;

}

delay_ms(1);

}

for (num = 0; num < Size; num++)

{

if (num == (Size - 1))

{

/* IIC sends NACK */

I2C_AcknowledgeConfig(I2Cx, I2C_IICAA_NACK);

}

else

{

I2C_AcknowledgeConfig(I2Cx, I2C_IICAA_ACK);

}

I2C_StartClear(I2Cx);

I2C_ClearITPendingBit(I2Cx);

/* Wait for the slave to send the completed data, and the host will send an ack */

while (!(I2C_CheckStatus(I2Cx, MREAD_DATA_ACK) || I2C_CheckStatus(I2Cx, MREAD_DATA_NACK)))

{

if ((Timeout--) == 0)

{

I2C_StartClear(I2Cx);

I2C_GenerateStop(I2Cx);

I2C_ClearITPendingBit(I2Cx);

return RESET;

}

delay_ms(1);

}

*pData++ = I2C_ReceiveData(I2Cx);

}

I2C_StartClear(I2Cx);

I2C_GenerateStop(I2Cx);

I2C_ClearITPendingBit(I2Cx);

return SET;

}

代码执行流程详细解释如下:

函数参数说明:

I2Cx: I2C外设指针

addr: EEPROM设备地址(7位)

reg: EEPROM内部起始地址(16位)

pData: 指向接收数据缓冲区的指针

Size: 要读取的字节数

timeout: 超时计数值(注意:函数内部有一处拼写错误写成了Timeout)

代码执行流程详细解释:

等待总线空闲 :函数首先检查I2C总线是否空闲(I2C_BUS_IDLE)。如果总线忙,执行清理操作(I2C_StartClear)并发送停止信号(I2C_GenerateStop),尝试释放总线。每次循环都递减超时计数器并延迟1ms,超时则返回RESET。

配置应答 :调用I2C_AcknowledgeConfig(I2Cx, I2C_IICAA_ACK)使能主设备的数据应答功能,这是为后续接收数据做准备。

发起起始条件 :生成起始条件(I2C_GenerateStart)开始传输,并等待起始条件成功(MASTER_START_READY)。超时则清理总线并返回失败。

发送设备地址(写模式) :发送EEPROM的7位地址和写方向位(I2C_WRITE),因为EEPROM读取操作需要先发送要读取的内部地址,这相当于一个"伪写"操作。清除相关状态后,等待EEPROM应答地址(MSEND_WADDR_ACK)。

发送重复起始条件 :为了从写操作切换到读操作,需要发送一个重复起始条件(Repeated Start)。调用I2C_GenerateStart,然后等待重复起始条件完成(MASTER_START_REPEAT)。这是I2C协议中在不释放总线的情况下改变数据传输方向的标准做法。

发送设备地址(读模式) :再次发送EEPROM的7位地址,但这次带读方向位(I2C_READ)。等待EEPROM对此读地址的应答(MSEND_RADDR_ACK)。

循环接收数据 :这是函数的核心部分,循环接收Size个字节的数据:

在接收倒数第二个字节时(num == (Size - 1)),将主设备的应答配置为不应答(I2C_IICAA_NACK),这是I2C协议规定的:主设备在接收最后一个字节前发送不应答信号,通知从设备停止发送。

对于其他字节,使能应答(I2C_IICAA_ACK)。

等待从设备发送数据完成的状态(MREAD_DATA_ACK或MREAD_DATA_NACK)。这里使用了逻辑或||,表示等待任意一种接收完成状态。

从I2C数据寄存器读取数据(I2C_ReceiveData(I2Cx))并存储到pData指向的缓冲区,然后指针递增。

结束传输 :所有数据接收完成后,生成停止条件(I2C_GenerateStop)结束本次I2C通信,清除相关状态。

六、下板验证

我们操作I2C写入0~0x3f数据,结果如下:

操作波形如图:

读取完最后一个数据后发送NACK:

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • mcu
    mcu
    +关注

    关注

    147

    文章

    19262

    浏览量

    405285
  • EEPROM
    +关注

    关注

    9

    文章

    1148

    浏览量

    86499
  • I2C
    I2C
    +关注

    关注

    28

    文章

    1568

    浏览量

    132021
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    探索UMFT201XA USB to I2C开发模块:特性、配置与应用

    的FT201XQ开发的模块,它是USB到I2C接口设备,具备电池充电器检测功能。即使在FT201X未被枚举的情况下,也能让电池从专用充电器端口以更高电
    的头像 发表于 05-15 14:30 135次阅读

    FTDI UMFT200XD USB 转 I2C 模块:设计与应用详解

    FTDI UMFT200XD USB 转 I2C 模块:设计与应用详解 在电子设计领域,USB 转 I2C 模块是实现设备通信的重要组件。F
    的头像 发表于 05-15 14:30 158次阅读

    利用I2C接口采用轮询方式读写EEPROM芯片

    利用I2C接口,采用轮询方式读写EEPROM芯片 #define TESTI2C2 //I2C1 = 1I2C2 =
    发表于 01-23 06:00

    RA MCU众测宝典 | I²C读取EEPROM

    I2C协议读取EEPROM数据。瑞萨嵌入式小百科将带着大家从I2C总线原理、EEPROM(AT24C02)特性,到FSP中
    的头像 发表于 01-13 18:05 8883次阅读
    RA MCU众测宝典 | <b class='flag-5'>I</b>²<b class='flag-5'>C</b>读取<b class='flag-5'>EEPROM</b>

    基于FPGA的I2C控制模块设计

    I2C_WRITE_WDATA.v模块实现I2C写时序,I2C_Controller (I2C控制器)例化了
    的头像 发表于 12-26 09:48 6457次阅读
    基于FPGA的<b class='flag-5'>I2C</b>控制<b class='flag-5'>模块</b>设计

    ISO154x-Q1低功耗双向I2C隔离器:设计与应用详解

    ISO154x-Q1低功耗双向I2C隔离器:设计与应用详解 在电子设计领域,隔离器对于保障电路安全、稳定运行起着至关重要的作用。今天,我们就来深入探讨TI推出的ISO1540-Q1
    的头像 发表于 12-19 09:25 574次阅读

    ISO164x热插拔双向I2C隔离器:特性、应用与设计要点

    ISO164x热插拔双向I2C隔离器:特性、应用与设计要点 在电子工程师的日常设计中,可靠的通信隔离器件至关重要。ISO164x热插拔双向I2C隔离器凭借其卓越的性能和丰富的特性,成为
    的头像 发表于 12-17 14:15 1168次阅读

    CW32单片机I2C接口来读写EEPROM芯片

    所有所需读取的数据全部读取,之后再发送停止信号。 四、硬件连接 如下图所示,MCU和EEPROM通过I2C总线互连。 五、实例演示:MCU采用页写和顺序读操作时序完成EERPOM的访问
    发表于 12-09 07:43

    基于CW32 MCU的I2C接口优化稳定读写EEPROM关键技术

    问题,实现多个EEPROM设备的稳定读写操作。 通过这些优化技术,能够有效提升CW32 MCU在I2C通信中的稳定性和可靠性,确保在复杂环境下的数据传输安全和稳定。
    发表于 12-03 07:29

    深入剖析I2C协议

    I2C也是一种可以多主设备,多从设备的总线协议,通过地址索引,I2C可以使能所需从设备,I2C的出现主要是用来实现不同集成电路组件之间的控制功能,比如通过I2C协议,连接MCU与LC
    的头像 发表于 08-21 15:10 4069次阅读
    深入剖析<b class='flag-5'>I2C</b>协议

    基于 AS32X601 微控制器的定时器模块(TIM)技术研究与应用实践

    摘要: 本文全面介绍了国科安芯推出的AS32X601系列微控制器的定时器模块(TIM),包括其系统架构、功能特性、应用场景以及工程实践要点。通过对芯片的详细分析,揭示了其高性能运行的基础。本文详细
    的头像 发表于 08-19 16:44 1110次阅读

    【沁恒CH585开发板免费试用体验】I2C 读写EEPROM (三)

    */ I2C_Stop();/* 发送停止信号 */ return ucAck; } 注释很清楚,对照I2C的协议看就行。 接着就是实现AT2C02的读写操作
    发表于 08-05 22:25

    【沁恒CH585开发板免费试用体验】I2C 读写EEPROM (二)

    ); } 任意写 在实际过程中,我们经常需要任意写数据,这里就调用页写的操作,来实现任意字节的写操作。 /** * @brief将缓冲区中的数据写到I2C EEPROM中 * @pa
    发表于 08-05 22:13

    AS32X601芯片Flash擦写调试技术解析

    Flash 擦写操作流程,探讨擦写过程中可能遭遇的挑战及应对策略,旨在为芯片应用开发者、硬件工程师等专业人士提供系统且详实的参考资料,助力其精准操控 AS32X601 芯片 Flash,保障嵌入式系统稳定可靠运行。
    的头像 发表于 07-22 13:47 1024次阅读
    <b class='flag-5'>AS32X601</b>芯片Flash擦写调试技术解析

    AS32系列MCU芯片I2C模块性能解析与调试

    国科安芯推出的AS32X601内置的I2C模块提供了符合工业标准的两线串行制接口,可用于MCU和外部IIC设备的通讯。IIC总线使用两条串行线:串行数据线SDA和串行时钟线SCL。 IIC接口
    的头像 发表于 06-20 16:31 1156次阅读
    AS32系列MCU芯片<b class='flag-5'>I2C</b><b class='flag-5'>模块</b>性能解析与调试