你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于STM32F1的CAN通信之EEPROM(AT24C02)

[复制链接]
攻城狮Melo 发布时间:2023-10-23 20:34
一、AT24C02简介
AT24C01/02/04/08/16...是一个1K/2K/4K/8K/16K位电可擦除PROM,内部含有128/256/512/1024/2048个8位字节,AT24C01有一个8字节页写缓冲器,AT24C02/04/08/16有一个16字节页写缓冲器。电压可允许低至1.8V,待机电流和工作电流分别为1uA和1mA。该器件通过I2C总线接口进行操作,这里就不再对IIC做详细介绍了,具体可见外设系列OLED篇。


二、AT24C02引脚



微信图片_20231023203334.png

AT24C02引脚定义



三、AT24C02寻址

使能芯片读写操作后,EEPROM都要求有8位的器件地址信息。

微信图片_20231023203357.png

AT24C02地址信息



器件地址信息由"1"、"0"序列组成,前4位对于所有串行EEPROM都是一样的。对于24C02/32/64,随后3位A2、A1和A0为器件地址位,必须与硬件输入引脚保持一致。

四、AT24C02读/写操作
4.1 AT24C02写操作

写操作要求主设备发送器件地址,收到应答信号后,先接收8位的字地址。接收到这个地址后EEPROM应答"0"(ACK),然后再是一个8位数据。在接收8位数据后,EEPROM应答"0"(ACK),接着必须由主器件发送停止条件来终止写序列。时序图如下


微信图片_20231023203352.png

写操作时序图



24C02器件按8字节/页执行页写,24C04/08/16器件按16字节/页执行页写,24C32/64器件按32字节/页执行页写。页写初始化与字节写相同,只是主器件不会在第一个数据后发送停止条件,而是在EEPROMEEPROM收到每个数据后都应答“0”。最后仍需由主器件发送停止条件,终止写序列。

接收到每个数据后,字地址的低3位 (24C02) 或4位(24C04/08/16) 或5位(24C32/64)内部自动加1,高位地址位不变,维持在当前页内。当内部产生的字地址达到该页边界地址时,随后的数据将写入该页的页首。如果超过8个 (24C02) 或16个 (24C04/08/16) 或32个(24C32/64) 数据传送给了EEPROM,字地址将回转到该页的首字节,先前的字节将会被覆盖。


4.2 AT24C02读操作

AT24C02的读操作有三种,分别是当前地址读,随机读和顺序读。
• 当前地址读 内部地址计数器保存着上次访问时最后一个地址加1的值。只要芯片有电,该地址就一直保存当读到最后页的最后字节,地址会回转到0。当写到某页尾的最后一个字节,地址会回转到该页的首字节。接收器件地址(读/写选择位为"1") 且EEPROM应答ACK后,当前地址的数据就随时钟送出。主器件无需应答"0",但需发送停止条件。当前地址读操作时序图如下


微信图片_20231023203350.png

读当前地址时序图



• 随机读 随机读需先写一个目标字地址,一旦EEPROM接收器件地址和字地址并应答了ACK,主器件就产生一个重复的起始条件。然后,主器件发送器件地址(读/写选择位为"1") ,EEPROM应答ACK,并随时钟送出数据。主器件无需应答"0",但需发送停止条件。这里的随机读就是读取任意一个字地址的数据,并不是随即返回一个数据的意思。随机读时序图如下


微信图片_20231023203347.png

随机读时序图




• 顺序读 顺序读可以通过“当前地址读”或“随机读”启动。主器件接收到一个数据后,应答ACK。只要EEPROM接收到ACK,将自动增加字地址并继续随时钟发送后面的数据。若达到存储器地址末尾,地址自动回转到0,仍可继续顺序读取数据。主器件不应答"0",而发送停止条件,即可结束顺序读操作。顺序读时序图如下


微信图片_20231023203342.png

顺序读时序图



五、AT24C02程序
这里给出一个AT24C02的程序,仅供参考
  1. /*******************************************************************************
  2. * 函 数 名         : IIC_Init
  3. * 函数功能     : IIC初始化
  4. * 输    入         : 无
  5. * 输    出         : 无
  6. *******************************************************************************/
  7. void IIC_Init(void)
  8. {
  9.     GPIO_InitTypeDef  GPIO_InitStructure;
  10.     RCC_APB2PeriphClockCmd(IIC_SCL_PORT_RCC|IIC_SDA_PORT_RCC,ENABLE);
  11.    
  12.     GPIO_InitStructure.GPIO_Pin=IIC_SCL_PIN;
  13.     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
  14.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
  15.     GPIO_Init(IIC_SCL_PORT,&GPIO_InitStructure);
  16.    
  17.     GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN;
  18.     GPIO_Init(IIC_SDA_PORT,&GPIO_InitStructure);
  19.    
  20.     IIC_SCL=1;
  21.     IIC_SDA=1;
  22. }
  23. /*******************************************************************************
  24. * 函 数 名         : SDA_OUT
  25. * 函数功能     : SDA输出配置   
  26. * 输    入         : 无
  27. * 输    出         : 无
  28. *******************************************************************************/
  29. void SDA_OUT(void)
  30. {
  31.     GPIO_InitTypeDef  GPIO_InitStructure;
  32.    
  33.     GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN;
  34.     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
  35.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
  36.     GPIO_Init(IIC_SDA_PORT,&GPIO_InitStructure);
  37. }

  38. /*******************************************************************************
  39. * 函 数 名         : SDA_IN
  40. * 函数功能     : SDA输入配置   
  41. * 输    入         : 无
  42. * 输    出         : 无
  43. *******************************************************************************/
  44. void SDA_IN(void)
  45. {
  46.     GPIO_InitTypeDef  GPIO_InitStructure;
  47.    
  48.     GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN;
  49.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
  50.     GPIO_Init(IIC_SDA_PORT,&GPIO_InitStructure);
  51. }

  52. /*******************************************************************************
  53. * 函 数 名         : IIC_Start
  54. * 函数功能     : 产生IIC起始信号   
  55. * 输    入         : 无
  56. * 输    出         : 无
  57. *******************************************************************************/
  58. void IIC_Start(void)
  59. {
  60.     SDA_OUT();     //sda线输出
  61.     IIC_SDA=1;      
  62.     IIC_SCL=1;
  63.     delay_us(5);
  64.      IIC_SDA=0;//START:when CLK is high,DATA change form high to low
  65.     delay_us(6);
  66.     IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
  67. }

  68. /*******************************************************************************
  69. * 函 数 名         : IIC_Stop
  70. * 函数功能     : 产生IIC停止信号   
  71. * 输    入         : 无
  72. * 输    出         : 无
  73. *******************************************************************************/
  74. void IIC_Stop(void)
  75. {
  76.     SDA_OUT();//sda线输出
  77.     IIC_SCL=0;
  78.     IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
  79.      IIC_SCL=1;
  80.     delay_us(6);
  81.     IIC_SDA=1;//发送I2C总线结束信号
  82.     delay_us(6);           
  83. }

  84. /*******************************************************************************
  85. * 函 数 名         : IIC_Wait_Ack
  86. * 函数功能     : 等待应答信号到来   
  87. * 输    入         : 无
  88. * 输    出         : 1,接收应答失败
  89.                      0,接收应答成功
  90. *******************************************************************************/
  91. u8 IIC_Wait_Ack(void)
  92. {
  93.     u8 tempTime=0;
  94.     SDA_IN();      //SDA设置为输入  
  95.     IIC_SDA=1;
  96.     delay_us(1);   
  97.     IIC_SCL=1;
  98.     delay_us(1);  
  99.     while(READ_SDA)
  100.     {
  101.         tempTime++;
  102.         if(tempTime>250)
  103.         {
  104.             IIC_Stop();
  105.             return 1;
  106.         }
  107.     }
  108.     IIC_SCL=0;//时钟输出0     
  109.     return 0;  
  110. }

  111. /*******************************************************************************
  112. * 函 数 名         : IIC_Ack
  113. * 函数功能     : 产生ACK应答  
  114. * 输    入         : 无
  115. * 输    出         : 无
  116. *******************************************************************************/
  117. void IIC_Ack(void)
  118. {
  119.     IIC_SCL=0;
  120.     SDA_OUT();
  121.     IIC_SDA=0;
  122.     delay_us(2);
  123.     IIC_SCL=1;
  124.     delay_us(5);
  125.     IIC_SCL=0;
  126. }

  127. /*******************************************************************************
  128. * 函 数 名         : IIC_NAck
  129. * 函数功能     : 产生NACK非应答  
  130. * 输    入         : 无
  131. * 输    出         : 无
  132. *******************************************************************************/      
  133. void IIC_NAck(void)
  134. {
  135.     IIC_SCL=0;
  136.     SDA_OUT();
  137.     IIC_SDA=1;
  138.     delay_us(2);
  139.     IIC_SCL=1;
  140.     delay_us(5);
  141.     IIC_SCL=0;
  142. }

  143. /*******************************************************************************
  144. * 函 数 名         : IIC_Send_Byte
  145. * 函数功能     : IIC发送一个字节
  146. * 输    入         : txd:发送一个字节
  147. * 输    出         : 无
  148. *******************************************************************************/   
  149. void IIC_Send_Byte(u8 txd)
  150. {                        
  151.     u8 t;   
  152.     SDA_OUT();      
  153.     IIC_SCL=0;//拉低时钟开始数据传输
  154.     for(t=0;t<8;t++)
  155.     {              
  156.         if((txd&0x80)>0) //0x80  1000 0000
  157.             IIC_SDA=1;
  158.         else
  159.             IIC_SDA=0;
  160.         txd<<=1;   
  161.         delay_us(2);   //对TEA5767这三个延时都是必须的
  162.         IIC_SCL=1;
  163.         delay_us(2);
  164.         IIC_SCL=0;
  165.         delay_us(2);
  166.     }  
  167. }

  168. /*******************************************************************************
  169. * 函 数 名         : IIC_Read_Byte
  170. * 函数功能     : IIC读一个字节
  171. * 输    入         : ack=1时,发送ACK,ack=0,发送nACK
  172. * 输    出         : 应答或非应答
  173. *******************************************************************************/  
  174. u8 IIC_Read_Byte(u8 ack)
  175. {
  176.     u8 i,receive=0;
  177.     SDA_IN();//SDA设置为输入
  178.     for(i=0;i<8;i++ )
  179.     {
  180.         IIC_SCL=0;
  181.         delay_us(2);
  182.         IIC_SCL=1;
  183.         receive<<=1;
  184.         if(READ_SDA)receive++;   
  185.         delay_us(1);
  186.     }      
  187.     if (!ack)
  188.         IIC_NAck();//发送nACK
  189.     else
  190.         IIC_Ack(); //发送ACK   
  191.     return receive;
  192. }
  193. /*******************************************************************************
  194. * 函 数 名         : AT24CXX_Init
  195. * 函数功能     : AT24CXX初始化
  196. * 输    入         : 无
  197. * 输    出         : 无
  198. *******************************************************************************/
  199. void AT24CXX_Init(void)
  200. {
  201.     IIC_Init();//IIC初始化
  202. }

  203. /*******************************************************************************
  204. * 函 数 名         : AT24CXX_ReadOneByte
  205. * 函数功能     : 在AT24CXX指定地址读出一个数据
  206. * 输    入         : ReadAddr:开始读数的地址
  207. * 输    出         : 读到的数据
  208. *******************************************************************************/
  209. u8 AT24CXX_ReadOneByte(u16 ReadAddr)
  210. {      
  211.     u8 temp=0;                          
  212.     IIC_Start();  
  213.     if(EE_TYPE>AT24C16)
  214.     {
  215.         IIC_Send_Byte(0XA0);    //发送写命令
  216.         IIC_Wait_Ack();
  217.         IIC_Send_Byte(ReadAddr>>8);//发送高地址     
  218.     }
  219.     else
  220.     {
  221.         IIC_Send_Byte(0XA0+((ReadAddr/256)<<1));   //发送器件地址0XA0,写数据
  222.     }     
  223.     IIC_Wait_Ack();
  224.     IIC_Send_Byte(ReadAddr%256);   //发送低地址
  225.     IIC_Wait_Ack();     
  226.     IIC_Start();        
  227.     IIC_Send_Byte(0XA1);           //进入接收模式      
  228.     IIC_Wait_Ack();  
  229.     temp=IIC_Read_Byte(0);     
  230.     IIC_Stop();//产生一个停止条件     
  231.     return temp;
  232. }

  233. /*******************************************************************************
  234. * 函 数 名         : AT24CXX_WriteOneByte
  235. * 函数功能     : 在AT24CXX指定地址写入一个数据
  236. * 输    入         : WriteAddr  :写入数据的目的地址
  237.                      DataToWrite:要写入的数据
  238. * 输    出         : 无
  239. *******************************************************************************/
  240. void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
  241. {                                
  242.     IIC_Start();  
  243.     if(EE_TYPE>AT24C16)
  244.     {
  245.         IIC_Send_Byte(0XA0);     //发送写命令
  246.         IIC_Wait_Ack();
  247.         IIC_Send_Byte(WriteAddr>>8);//发送高地址   
  248.     }
  249.     else
  250.     {
  251.         IIC_Send_Byte(0XA0+((WriteAddr/256)<<1));   //发送器件地址0XA0,写数据
  252.     }   
  253.     IIC_Wait_Ack();   
  254.     IIC_Send_Byte(WriteAddr%256);   //发送低地址
  255.     IIC_Wait_Ack();                    
  256.     IIC_Send_Byte(DataToWrite);     //发送字节         
  257.     IIC_Wait_Ack();            
  258.     IIC_Stop();//产生一个停止条件
  259.     delay_ms(10);  
  260. }

  261. /*******************************************************************************
  262. * 函 数 名         : AT24CXX_WriteLenByte
  263. * 函数功能     : 在AT24CXX里面的指定地址开始写入长度为Len的数据
  264.                      用于写入16bit或者32bit的数据
  265. * 输    入         : WriteAddr  :写入数据的目的地址
  266.                      DataToWrite:要写入的数据
  267.                      Len        :要写入数据的长度2,4
  268. * 输    出         : 无
  269. *******************************************************************************/
  270. void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len)
  271. {   
  272.     u8 t;
  273.     for(t=0;t<Len;t++)
  274.     {
  275.         AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);
  276.     }               
  277. }

  278. /*******************************************************************************
  279. * 函 数 名         : AT24CXX_ReadLenByte
  280. * 函数功能     : 在AT24CXX里面的指定地址开始读出长度为Len的数据
  281.                      用于读出16bit或者32bit的数据
  282. * 输    入         : ReadAddr   :开始读出的地址
  283.                      Len        :要读出数据的长度2,4
  284. * 输    出         : 读取的数据
  285. *******************************************************************************/
  286. u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len)
  287. {   
  288.     u8 t;
  289.     u32 temp=0;
  290.     for(t=0;t<Len;t++)
  291.     {
  292.         temp<<=8;
  293.         temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1);         
  294.     }
  295.     return temp;               
  296. }

  297. /*******************************************************************************
  298. * 函 数 名         : AT24CXX_Check
  299. * 函数功能     : 检查AT24CXX是否正常
  300. * 输    入         : 无
  301. * 输    出         : 1:检测失败,0:检测成功
  302. *******************************************************************************/
  303. u8 AT24CXX_Check(void)
  304. {
  305.     u8 temp;
  306.     temp=AT24CXX_ReadOneByte(255);//避免每次开机都写AT24CXX      
  307.     if(temp==0x36)return 0;     
  308.     else//排除第一次初始化的情况
  309.     {
  310.         AT24CXX_WriteOneByte(255,0X36);
  311.         temp=AT24CXX_ReadOneByte(255);   
  312.         if(temp==0X36)return 0;
  313.     }
  314.     return 1;            
  315. }

  316. /*******************************************************************************
  317. * 函 数 名         : AT24CXX_Read
  318. * 函数功能     : 在AT24CXX里面的指定地址开始读出指定个数的数据
  319. * 输    入         : ReadAddr :开始读出的地址 对24c02为0~255
  320.                      pBuffer  :数据数组首地址
  321.                      NumToRead:要读出数据的个数
  322. * 输    出         : 无
  323. *******************************************************************************/
  324. void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
  325. {
  326.     while(NumToRead)
  327.     {
  328.         *pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
  329.         NumToRead--;
  330.     }
  331. }

  332. /*******************************************************************************
  333. * 函 数 名         : AT24CXX_Write
  334. * 函数功能     : 在AT24CXX里面的指定地址开始写入指定个数的数据
  335. * 输    入         : WriteAddr :开始写入的地址 对24c02为0~255
  336.                      pBuffer  :数据数组首地址
  337.                      NumToRead:要读出数据的个数
  338. * 输    出         : 无
  339. *******************************************************************************/
  340. void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
  341. {
  342.     while(NumToWrite--)
  343.     {
  344.         AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
  345.         WriteAddr++;
  346.         pBuffer++;
  347.     }
  348. }
  349. .h文件如下

  350. // 核心板使用的是24c02,所以定义EE_TYPE为AT24C02
  351. // 可修改成AT24CXX系列中的任意一个
  352. #define EE_TYPE   AT24C02

  353. // IIC函数
  354. void IIC_Init(void);   // 初始化IIC的IO口     
  355. void IIC_Start(void);   // 发送IIC开始信号
  356. void IIC_Stop(void);   // 发送IIC停止信号
  357. void IIC_Send_Byte(u8 txd);   // IIC发送一个字节
  358. u8 IIC_Read_Byte(u8 ack);   // IIC读取一个字节
  359. u8 IIC_Wait_Ack(void);   // IIC等待ACK信号
  360. void IIC_Ack(void);   // IIC发送ACK信号
  361. void IIC_NAck(void);   // IIC不发送ACK信号

  362. u8 AT24CXX_ReadOneByte(u16 ReadAddr);   //指定地址读取一个字节
  363. void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite);   // 指定地址写入一个字节
  364. void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len);   // 指定地址开始写入指定长度的数据
  365. u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len);   // 指定地址开始读取指定长度数据
  366. void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite);   // 从指定地址开始写入指定长度的数据
  367. void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead);   // 从指定地址开始读出指定长度的数据

  368. u8 AT24CXX_Check(void);   // 检查器件
  369. void AT24CXX_Init(void);   // 初始化IIC
复制代码

六、应用实例
给AT24C02写入一个数据,读取一次确认写入正常。注释掉写入程序,拔掉电源。一段时间后,插上电源再次读取之前写入时的地址的值,串口打印结果。AT24C02的初始化程序如下
  1.     AT24CXX_Init();   // AT24C02初始化
  2.     while(AT24CXX_Check())  //检测AT24C02是否正常
  3.     {
  4.         printf("AT24C02检测不正常!\r\n");
  5.         delay_ms(500);
  6.     }
  7.     printf("AT24C02检测正常!\r\n");
复制代码

main函数如下
  1. u8 gWData = 0xaa;   // 准备要写入的数据
  2. u8 gRData = 0xaa;   // 存储读出的数据

  3. int main(void)
  4. {
  5.     Med_Mcu_Iint();   // 系统初始化
  6.    
  7.     AT24CXX_WriteOneByte(0,gWData);
  8.     printf("写入的数据是:%d\r\n",gWData);
  9.    
  10.     gRData = AT24CXX_ReadOneByte(0);
  11.     printf("读取的数据是:%d\r\n",gRData);
  12.    
  13.     while(1)
  14.   {
  15.     }
  16. }
复制代码

串口打印结果如下


串口打印结果



七、拓展应用
AT24C02这种掉电数据不丢失的特性,使得它可以存储一些重要数据。比如将一些校准数据写入AT24C02中,再次上电之后就不会丢失。或者用AT24C02记录开机次数等。这些原理与应用实例中的例子原理相同,这里就不再赘述了。



转载自: 二土电子
如有侵权请联系删除

微信图片_20231023203400.png
收藏 评论0 发布时间:2023-10-23 20:34

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版