1. 首页
  2. 技术文章

STM32F10x学习笔记–硬件I2C通讯AT24C02第二篇之写入一个字节数据

  • 准备知识

上一章节,我们学习了对STM32的GPIO和I2C进行初始化,今天我们学习STM32通过I2C总线向AT24C02写入1个字节的数据。ARM为主设备,EEPROM为从设备。我从AT24C02数据手册中可以 找到写入1个字节的时序图,如下图1.

STM32F10x学习笔记--硬件I2C通讯AT24C02第二篇之写入一个字节数据

   图1 AT24C02写时序图

从图1中我们可以看到,主机向从机写入一个字节数据,首先,发送起始位,然后发送EEPROM的地址,这里的地址是我们上一章介绍的10100000地址,就是写地址0xa0。发送成功后,接收到应答后(ACK),发送我们需要写入数据对应的EEPROM地址。接收ACK,然后在写入数据,接收ACK,发送停止位即可。那么,对于EEPROM的地址,在手册里介绍有32页。每一页8个字节,那么32*8=256个地址,所以AT24C02的地址是从0x0~0xff 。此时,我们在查看STM32中文参考手册,在第24.3.3章I2C主模式下,我们可以看到STM32,在主发送7位地址的I2C通信的时序图,如下图2.

STM32F10x学习笔记--硬件I2C通讯AT24C02第二篇之写入一个字节数据

  图2 STM32系列I2C主设备写时序图

图2中的7位主发送时序图,STM32在每一次发送一个状态都会有个事件状态,比如产生起始条件成功后,会产生EV5事件;那么对于设计者我们在每一次发送一个状态后,只要判断该事件是否有触发就能知道,发送状态否成功。与图1相对比,图2增加了事件判断,并且支持多数据一次写入外,其他与图1是相同的。那么为了方便设计我们所需的写入任意一个字节数据,我们把图1和图2整合以下,时序如下图3:

STM32F10x学习笔记--硬件I2C通讯AT24C02第二篇之写入一个字节数据

图3  写一个字节到AT24C02的时序图

如图3,地址1是EEPROM的设备地址,此时是其的写地址0xa0,地址2是写入数据的在EEPROM内部地址0~0xff。那么我们的写入过程如下:发送起始位-检测EV5事件-发送8位从设备地址-检测事件EV6-发送写入EEPROM地址-检测EV8事件-写入数据-检测EV8-2事件-发送停止位。

了解发送时序我们开始写代码:

void eeprom_ByteWrite(unsigned char *pBuffer, unsigned char WriteAddr)

{
/**********产生起始位*****************/
I2C_GenerateSTART(I2C1,ENABLE) ;

delay_for_eeprom(0x100); //CPU工作比较快  延迟一定时间后检测,直接检测 有可能先执行了,EV5事件才会到来,导致错过事件响应。

/**********检测EV5事件 是否响应*********/

if( I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT))
{
/**********发送EEPROM写地址***********/
I2C_Send7bitAddress(I2C1,eeprom_addr_wr,I2C_Direction_Transmitter); //eprom_addr_wr 定义的宏,值0xa1

delay_for_eeprom(0x100);

/**********检测EV6事件 是否响应*********/

if(I2C_CheckEvent (I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ))     //有EV6事件代表地址发送成功和应答成功

{
I2C_SendData (I2C1,WriteAddr);         //发送需要写入的地址位

delay_for_eeprom(0x100);

/*********检测EV8事件 是否响应*********/
if(I2C_CheckEvent (I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTING ))
{
I2C_SendData (I2C1,*pBuffer);

delay_for_eeprom(0x100);

/*********检测EV8-2事件 是否响应*********/

if(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED ) )
{

I2C_GenerateSTOP(I2C1,ENABLE);

printf("写入1个字节完成")  ;   // 写入1个字节完成

delay_for_eeprom(0x100);        //这个延迟为了 保证数据数据写入寄存器已经写入完成,不影响下次写入或者读取。 参考AT24C02手册P8页 ACKNOWLEDGE POLLING
}

else printf("写入1个字节的EV8-2事件失败")  ; //没有检测到EV8-2事件

}

else printf("写入1个字节的EV8事件失败")  ;   //没有检测到EV8事件

}

else printf("写入1个字节的EV6事件失败")  ;  //没有检测到EV6事件

}

else  printf("写入1个字节的产生起始位失败")  ;   //没有检测到EV5事件

}

 

上面代码中,实现了写一个字节到ROM的任意地址。我们知道,写入的地址最大是0xFF,所以无符号CHAR型就足够了。 数据也是一样,每一个地址只能写一个字节8bit数据。 delay_for_eeprom(0x100)函数是延迟函数,是为了等待EV事件响应或者等待数据从寄存器写完清空,从而不影响下次写入。延迟中0X100为经验值,延时不合适,会打印哪里出错,这样调试会很方便。虽然嵌套if语句有些繁琐。 当然我们这里可以不使用延迟 改用while函数等待事件响应,但是使用while有个缺点,如果等待不上事件响应,那样就会进入死循环,造成死机。对于一个设备来说,不能因为一个功能问题影响整个系统,比如说我们是用电脑,鼠标通信不正常不能使用了,会让电脑死机蓝屏,这样设计肯定不行,所以这里采用了延迟在判断的方式。对于检测事件库函数,在库函数里面有非常详细介绍。只要翻开看了就不会看不明白。当然这个代码只是写入数据,没有读取,所以没办法把测试哦,下一章节我们会学习任意字节数量的读取最后我在把延迟函数的代码复制上来。

void delay_for_eeprom(u16 time)
{
u16 i=0;

while(time--)
{
i=10; //根据自己情况自己定义
while(i--)
{
;
}
}
}

原创文章,作者:小峰,如若转载,请注明出处:http://www.wfblog.com/archives/193.html

联系我们

400-800-8888

在线咨询:点击这里给我发消息

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息