-
准备知识
上一章节,我们学习了对STM32的GPIO和I2C进行初始化,今天我们学习STM32通过I2C总线向AT24C02写入1个字节的数据。ARM为主设备,EEPROM为从设备。我从AT24C02数据手册中可以 找到写入1个字节的时序图,如下图1.
图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.
图2 STM32系列I2C主设备写时序图
图2中的7位主发送时序图,STM32在每一次发送一个状态都会有个事件状态,比如产生起始条件成功后,会产生EV5事件;那么对于设计者我们在每一次发送一个状态后,只要判断该事件是否有触发就能知道,发送状态否成功。与图1相对比,图2增加了事件判断,并且支持多数据一次写入外,其他与图1是相同的。那么为了方便设计我们所需的写入任意一个字节数据,我们把图1和图2整合以下,时序如下图3:
图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