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

【经验分享】STM32F103通过IIC总线读取EEPROM

[复制链接]
STMCU小助手 发布时间:2022-3-19 21:00
  IIC总线是常用的串行总线,它只需要简单的两根线就可以实现数据的高速传输,同时还可以实现多机通信功能。

  在单片机中用的比较多的就是通过IIC总线操作EEPROM芯片。比较常用的EEPROM芯片就是24Cxx系列的芯片,主要用来存储系统运行过程中的关键数据。

XBW3T6STD2Y@UTLKX]RQF_A.png

要操作这个芯片的话,必须按照一定的时序去读写。这个时序通常被称为通信协议。24Cxx系列芯片通信协议如下。

I2 C 总线协议

I 2 C 总线协议定义如下:
只有在总线空闲时才允许启动数据传送
在数据传送过程中 当时钟线为高电平时 数据线必须保持稳定状态 不允许有跳变 时钟线为高电平时 数据线的任何电平变化将被看作总线的起始或停止信号。

3_LI5QTH7A70SBF[AT10$I4.png

起始信号
时钟线保持高电平期间 数据线电平从高到低的跳变作为 I 2 C 总线的起始信号

停止信号
时钟线保持高电平期间 数据线电平从低到高的跳变作为 I 2 C 总线的停止信号

U8)559_9$QUKW}VY7RZKY.png

ZD]31Z49H{PT~8NF6L1M@SL.png

6N)S]IYAAE%}}O[Q0M{62JR.png

FO1{%3OR]EU$QMB6{H~48YE.png

这里就使用最简单的几个通信时序起始、停止、读、写和应答时序。

为了IIC协议的通用性,将这几个协议封装为一个c文件,这样以后每个使用IIC协议的器件都能调用这个文件。

  1. //IIC产生起始信号
  2. void IIC_Start(void)
  3. {
  4.     //START:when CLK is high,DATA change form high to low
  5.     SDA_OUT();
  6.     IIC_SDA = 1;
  7.     IIC_SCL = 1;
  8.     delay_us(4);
  9.     IIC_SDA = 0;
  10.     delay_us(4);
  11.     IIC_SCL = 0;
  12. }
  13. //产生停止信号
  14. void IIC_Stop(void)
  15. {
  16.     //STOP:when CLK is high DATA change form low to high
  17.     SDA_OUT();
  18.     IIC_SCL = 0;
  19.     IIC_SDA = 0;
  20.     delay_us(4);
  21.     IIC_SCL = 1;
  22.     IIC_SDA = 1;
  23.     delay_us(4);
  24. }
  25. //等待应答信号
  26. //返回值: 1 应答失败
  27. //        0 应答成功
  28. u8 IIC_Wait_Ack(void)
  29. {
  30.     u8 errTime = 0;
  31.     SDA_IN();
  32.     IIC_SDA = 1;
  33.     delay_us(1);
  34.     IIC_SCL = 1;
  35.     delay_us(1);
  36.     while(READ_SDA)
  37.     {
  38.         errTime++;
  39.         if(errTime > 250)
  40.         {
  41.             IIC_Stop();
  42.             return 1;
  43.         }
  44.     }
  45.     IIC_SCL = 0;
  46.     return 0;
  47. }
  48. //产生ACK应答
  49. void IIC_Ack(void)
  50. {
  51.     IIC_SCL = 0;
  52.     SDA_OUT();
  53.     IIC_SDA = 0;
  54.     delay_us(2);
  55.     IIC_SCL = 1;
  56.     delay_us(2);
  57.     IIC_SCL = 0;
  58. }
  59. //不产生应答
  60. void IIC_NAck(void)
  61. {
  62.     IIC_SCL = 0;
  63.     SDA_OUT();
  64.     IIC_SDA = 1;
  65.     delay_us(2);
  66.     IIC_SCL = 1;
  67.     delay_us(2);
  68.     IIC_SCL = 0;
  69. }
  70. //IIC发送一个字节
  71. void IIC_Send_Byte(u8 txd)
  72. {
  73.     u8 t;
  74.     SDA_OUT();
  75.     IIC_SCL = 0;
  76.     for(t = 0; t < 8; t++)
  77.     {
  78.         if((txd & 0x80) >> 7)
  79.             IIC_SDA = 1;
  80.         else
  81.             IIC_SDA = 0;
  82.         txd <<= 1;
  83.         delay_us(2);
  84.         IIC_SCL = 1;
  85.         delay_us(2);
  86.         IIC_SCL = 0;
  87.         delay_us(2);
  88.     }
  89. }
  90. //读1个字节,ack=1时,发送ACK,ack=0,发送NACK
  91. u8 IIC_Read_Byte(unsigned char ack)
  92. {
  93.     unsigned char i, receive = 0;
  94.     SDA_IN();
  95.     for(i = 0; i < 8; i++)
  96.     {
  97.         IIC_SCL = 0;
  98.         delay_us(2);
  99.         IIC_SCL = 1;
  100.         receive <<= 1;
  101.         if(READ_SDA)
  102.             receive++;
  103.         delay_us(1);
  104.     }
  105.     if(!ack)
  106.         IIC_NAck();
  107.     else
  108.         IIC_Ack();
  109.     return receive;
  110. }
复制代码

这里将最常用的几个信号封装为函数,当操作24Cxx芯片的时候,直接调用这几个函数。

  1. #include "24Cxx.h"
  2. #include "delay.h"

  3. void AT24Cxx_Init(void)
  4. {
  5.     IIC_Init();
  6. }
  7. //在AT24CXX指定地址读出一个数据
  8. //ReadAddr:开始读数的地址
  9. //返回值  :读到的数据
  10. u8 AT24Cxx_ReadOneByte(u16 ReadAddr)
  11. {
  12.     u8 temp = 0;
  13.     IIC_Start();
  14.     if(EE_TYPE > AT24C16)
  15.     {
  16.         IIC_Send_Byte(0xA0);                        //发送写命令
  17.         IIC_Wait_Ack();
  18.         IIC_Send_Byte(ReadAddr >> 8);        //发送高地址
  19.         IIC_Wait_Ack();
  20.     }
  21.     else
  22.         IIC_Send_Byte(0xA0 + ((ReadAddr / 256) << 1));        //发送器件地址0XA0,写数据

  23.     IIC_Wait_Ack();
  24.     IIC_Send_Byte(ReadAddr % 256);                //发送低地址
  25.     IIC_Wait_Ack();
  26.     IIC_Start();
  27.     IIC_Send_Byte(0xA1);                                //进入接收模式
  28.     IIC_Wait_Ack();
  29.     temp = IIC_Read_Byte(0);
  30.     IIC_Stop();                                                        //产生一个停止条件
  31.     return temp;
  32. }
  33. //在AT24CXX指定地址写入一个数据
  34. //WriteAddr  :写入数据的目的地址
  35. //DataToWrite:要写入的数据
  36. void AT24Cxx_WriteOneByte(u16 WriteAddr, u8 DataToWrite)
  37. {
  38.     IIC_Start();
  39.     if(EE_TYPE > AT24C16)
  40.     {
  41.         IIC_Send_Byte(0xA0);                        //发送写命令
  42.         IIC_Wait_Ack();
  43.         IIC_Send_Byte(WriteAddr >> 8);        //发送高地址
  44.     }
  45.     else
  46.         IIC_Send_Byte(0xA0 + ((WriteAddr / 256) << 1));        //发送器件地址0XA0,写数据
  47.     IIC_Wait_Ack();
  48.     IIC_Send_Byte(WriteAddr % 256);
  49.     IIC_Wait_Ack();
  50.     IIC_Send_Byte(DataToWrite);
  51.     IIC_Wait_Ack();
  52.     IIC_Stop();
  53.     delay_ms(10);
  54. }
  55. //在AT24CXX里面的指定地址开始写入长度为Len的数据
  56. //该函数用于写入16bit或者32bit的数据.
  57. //WriteAddr  :开始写入的地址
  58. //DataToWrite:数据数组首地址
  59. //Len        :要写入数据的长度2,4
  60. void AT24Cxx_WriteLenByte(u16 WriteAddr, u32 DataToWrite, u8 Len)
  61. {
  62.     u8 t;
  63.     for(t = 0; t < Len; t++)
  64.     {
  65.         AT24Cxx_WriteOneByte(WriteAddr + t, (DataToWrite >> (8 * t)) & 0xff);
  66.     }
  67. }
  68. //在AT24CXX里面的指定地址开始读出长度为Len的数据
  69. //该函数用于读出16bit或者32bit的数据.
  70. //ReadAddr   :开始读出的地址
  71. //返回值     :数据
  72. //Len        :要读出数据的长度2,4
  73. u32 AT24Cxx_ReadLenByte(u16 ReadAddr, u8 Len)
  74. {
  75.     u8 t;
  76.     u32 temp = 0;
  77.     for(t = 0; t < Len; t++)
  78.     {
  79.         temp <<= 8;
  80.         temp += AT24Cxx_ReadOneByte(ReadAddr + Len - t - 1);
  81.     }
  82.     return temp;
  83. }
  84. //检查AT24CXX是否正常
  85. //这里用了24XX的最后一个地址(255)来存储标志字.
  86. //如果用其他24C系列,这个地址要修改
  87. //返回1:检测失败
  88. //返回0:检测成功
  89. u8 AT24Cxx_Check(void)
  90. {
  91.     u8 temp;
  92.     temp = AT24Cxx_ReadOneByte(255);
  93.     if(temp == 0x55)
  94.         return 0;
  95.     else
  96.     {
  97.         AT24Cxx_WriteOneByte(1023, 0x55);
  98.         temp = AT24Cxx_ReadOneByte(255);
  99.         if(temp == 0x55)
  100.             return 0;
  101.     }
  102.     return 1;
  103. }

  104. //在AT24CXX里面的指定地址开始读出指定个数的数据
  105. //ReadAddr :开始读出的地址 对24c02为0~255
  106. //pBuffer  :数据数组首地址
  107. //NumToRead:要读出数据的个数
  108. void AT24Cxx_Read(u16 ReadAddr, u8 *pBuffer, u16 NumToRead)
  109. {
  110.     while(NumToRead)
  111.     {
  112.         *pBuffer++ = AT24Cxx_ReadOneByte(ReadAddr++);
  113.         NumToRead--;
  114.     }
  115. }
  116. //在AT24CXX里面的指定地址开始写入指定个数的数据
  117. //WriteAddr :开始写入的地址 对24c02为0~255
  118. //pBuffer   :数据数组首地址
  119. //NumToWrite:要写入数据的个数
  120. void AT24Cxx_Write(u16 WriteAddr, u8 *pBuffer, u16 NumToWrite)
  121. {
  122.     while(NumToWrite--)
  123.     {
  124.         AT24Cxx_WriteOneByte(WriteAddr, *pBuffer);
  125.         WriteAddr++;
  126.         pBuffer++;
  127.     }
  128. }
复制代码

这里就是对具体的芯片操作函数,在主函数中通过这几个函数就可读写EEPROM存储芯片的内容了。

  1. int main(void)
  2. {

  3.     u8 key;
  4.     u16 i = 0, j = 0;
  5.     u8 datatemp[SIZE];

  6.     delay_init();
  7.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  8.     uart_init(115200);
  9.     LED_Init();
  10.     KEY_Init();

  11.     AT24Cxx_Init();

  12.     printf("IIC test!!!\r\n");
  13.     while(AT24Cxx_Check())
  14.     {
  15.         printf("The chip is not detected, please check whether the hardware connection is normal!!!\r\n");
  16.         delay_ms(300);
  17.         LED1 = !LED1;
  18.     }

  19.     while(1)
  20.     {

  21.         key = KEY_Sacn(0);
  22.         if(key == WKUP_PRES)
  23.         {
  24.             printf("\r\nStart Write 24C02....\r\n");
  25.             AT24Cxx_Write(0, (u8 *)TEXT_Buffer, SIZE);
  26.             printf("24C02 Write Finished!\r\n");
  27.         }
  28.         else if(key == KEY1_PRES)
  29.         {
  30.             printf("\r\nStart Read 24C02....\r\n");
  31.             AT24Cxx_Read(0, datatemp, SIZE);
  32.             printf("The Data Readed Is:  ");
  33.             for(j = 0; j < SIZE; j++)
  34.             {
  35.                 printf("%c", datatemp[j]);
  36.             }
  37.         }

  38.         i++;
  39.         if(i == 20)
  40.         {
  41.             i = 0;
  42.             LED0 = !LED0;
  43.         }
  44.         delay_ms(5);
  45.     }
  46. }
复制代码

这里通过按键来测试存储芯片,一个按键按下后向芯片内写入数据,另一个按键按下后从芯片中读取刚才写入的内容。



收藏 评论0 发布时间:2022-3-19 21:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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