微商城网站制作,企业网站教程,江苏住房与城乡建设部网站,做旅游网站平台合作入驻国科安芯推出的AS32X601系列MCU芯片内置的I2C模块提供了符合工业标准的两线串行制接口#xff0c;可用于MCU和外部I2C设备的通讯。I2C总线使用两条串行线#xff1a;串行数据线SDA和串行时钟线SCL。 I2C接口模块实现了I2C协议的标准模式和快速模式#xff0c;支持多主机I2C总…国科安芯推出的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时钟来自APB0I2C2、I2C3时钟来自ABP1。具体配置可见I2C_CTLR寄存器。四、I2C初始化1.配置I2Cx需要的GPIO为复用功能。2.通过配置I2C_INITSTRUCT初始化I2Cx包括时钟分频从机地址ACK高低电平时间等3.按需求配置中断并配置IRQ_HANDLER4.调用收发接口并处理数据五、如何操作EEPROM5.1按字节写入函数FlagStatus I2C_MEEPROMWriteByte(I2C_TypeDef* I2Cx, uint8_t addr, uint16_t reg, uint16_t data, uint32_t timeout){unsignedintnum;/*等待总线释放*/while(!I2C_CheckStatus(I2Cx, I2C_BUS_IDLE)){I2C_StartClear(I2Cx);I2C_GenerateStop(I2Cx);I2C_ClearITPendingBit(I2Cx);if((timeout--) 0){returnRESET;}delay_ms(1);}I2C_GenerateStart(I2Cx);/*等待启动信号完成*/while(!I2C_CheckStatus(I2Cx, MASTER_START_READY)){if((timeout--) 0){returnRESET;}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);returnRESET;}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);returnRESET;}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);returnRESET;}delay_ms(1);}I2C_GenerateStop(I2Cx);I2C_ClearITPendingBit(I2Cx);return1;}代码执行流程详细解释如下等待总线空闲函数首先进入一个循环反复检查I2C总线是否处于空闲I2C_BUS_IDLE状态。如果总线被占用忙状态它会尝试通过调用I2C_StartClear和I2C_GenerateStop来清除可能的异常状态并发送停止信号试图释放总线。每次循环都会递减超时计数器timeout并延迟1毫秒。如果timeout减到0函数会返回RESET。这个步骤确保了本次传输开始时总线是可用的。发起起始条件确认总线空闲后函数调用I2C_GenerateStart在I2C总线上产生一个起始条件Start Condition这标志着一次传输序列的开始。等待起始条件完成紧接着函数进入另一个循环等待起始条件成功发出的状态MASTER_START_READY。同样这里也有超时检查和1ms延迟防止程序死锁。超时则返回失败。发送从机地址写模式起始条件成功后函数调用I2C_Send7bitAddress将参数addrEEPROM的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){returnRESET;}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);returnRESET;}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);returnRESET;}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);return0;}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);returnRESET;}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);returnRESET;}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);returnRESET;}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);returnRESET;}delay_ms(1);}*pData I2C_ReceiveData(I2Cx);}I2C_StartClear(I2Cx);I2C_GenerateStop(I2Cx);I2C_ClearITPendingBit(I2Cx);returnSET;}代码执行流程详细解释如下函数参数说明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