当前位置:   article > 正文

STM32入门开发: 采用IIC硬件时序读写AT24C08(EEPROM)_基于stm32f1的at24c08数据读写

基于stm32f1的at24c08数据读写

一、环境介绍

编程软件: keil5

操作系统: win10

MCU型号: STM32F103ZET6

STM32编程方式: 寄存器开发 (方便程序移植到其他单片机)

IIC总线:  STM32本身支持IIC硬件时序的,上篇文章已经介绍了采用IIC模拟时序读写AT24C02,这篇文章介绍STM32的硬件IIC配置方法,并读写AT24C08。

文章地址: https://xiaolong.blog.csdn.net/article/details/117586108

模拟时序更加方便移植到其他单片机,通用性更高,不分MCU;硬件时序效率更高,每个MCU配置方法不同,依赖硬件本身支持。

器件型号: 采用AT24C08  EEPROM存储芯片

完整的工程源码下载地址,下载即可编译运行测试(包含了模拟IIC时序、STM32硬件IIC时序分别驱动AT24C02和AT24C08):  https://download.csdn.net/download/xiaolong1126626497/19399945

二、AT24C08存储芯片介绍

2.1 芯片功能特性介绍

AT24C08 是串行CMOS类型的EEPROM存储芯片,AT24C0x这个系列包含了AT24C01、AT24C02、AT24C04、AT24C08、AT24C16这些具体的芯片型号。

他们容量分别是:1K (128 x 8)、2K (256 x 8)、8K (1024 x 8)、16K (2048 x 8)  ,其中的8表示8位(bit)

它们的管脚功能、封装特点如下:

芯片功能描述:

AT24C08系列支持I2C,总线数据传送协议I2C,总线协议规定任何将数据传送到总线的器件作为发送器。任何从总线接收数据的器件为接收器;数据传送是由产生串行时钟和所有起始停止信号的主器件控制的。主器件和从器件都可以作为发送器或接收器,但由主器件控制传送数据(发送或接收)的模式。

芯片特性介绍:

1. 低压和标准电压运行
          –2.7(VCC=2.7伏至5.5伏)
          –1.8(VCC=1.8伏至5.5伏)

2. 两线串行接口(SDA、SCL)

3. 有用于硬件数据保护的写保护引脚

4. 自定时写入周期(5毫秒~10毫秒),因为内部有页缓冲区,向AT24C0x写入数据之后,还需要等待AT24C0x将缓冲区数据写入到内部EEPROM区域.

5. 数据保存可达100年

6. 100万次擦写周期

7. 高数据传送速率为400KHz、低速100KHZ和IIC总线兼容。 100 kHz(1.8V)和400 kHz(2.7V、5V)

8. 8字节页写缓冲区
       这个缓冲区大小与芯片具体型号有关: 8字节页(1K、2K)、16字节页(4K、8K、16K)
      

2.2 芯片设备地址介绍

因为IIC协议规定,每次传递数据都是按8个字节传输的,AT24C08是1024字节,地址的选择上与AT24C02有所区别;

IIC设备的标准地址位是7位。上面这个图里AT24C08的1010是芯片内部固定值,A2 是硬件引脚、由硬件决定电平;P1、P0是空间存储块选择,每个存储块大小是256字节,寻址范围是0~255,AT24C08相当于是4块AT24C02的构造;最后一位是读/写位(1是读,0是写),读写位不算在地址位里,但是根据IIC的时序顺序,在操作设备前,都需要先发送7位地址,再发送1位读写位,才能启动对芯片的操作,我们在写模拟时序为了方便统一写for循环,按字节发送,所以一般都是将7地址位与1位读写位拼在一起,组合成1个字节,方便按字节传输数据。

我现在使用的开发板上AT24C08的原理图是这样的:

那么这个AT24C08的标准设备地址分别是:

第一块区域:  0x50(十六进制),对应的二进制就是: 1010000

第二块区域:  0x51(十六进制),对应的二进制就是: 1010001

第三块区域:  0x52(十六进制),对应的二进制就是: 1010010

第四块区域:  0x53(十六进制),对应的二进制就是: 1010011

如果将读写位组合在一起,读权限的设备地址: 

第一块区域:  0xA1(十六进制),对应的二进制就是: 10100001

第二块区域:  0xA3(十六进制),对应的二进制就是: 10100011

第三块区域:  0xA5(十六进制),对应的二进制就是: 10100101

第四块区域:  0xA7(十六进制),对应的二进制就是: 10100111

如果将读写位组合在一起,写权限的设备地址: 

第一块区域:  0xA0(十六进制),对应的二进制就是: 10100000

第二块区域:  0xA2(十六进制),对应的二进制就是: 10100010

第三块区域:  0xA4(十六进制),对应的二进制就是: 10100100

第四块区域:  0xA6(十六进制),对应的二进制就是: 10100110

2.3  对AT24C08 按字节写数据的指令流程(时序)

     详细解释:

1.  先发送起始信号

2.  发送设备地址(写权限)

3. 等待AT24C08应答、低电平有效

4. 发送存储地址、AT24C08内部一共有256个字节空间,寻址是从0开始的,范围是(0~255);发送这个存储器地址就是告诉AT24C08接下来的数据改存储到哪个地方。

5. 等待AT24C08应答、低电平有效

6. 发送一个字节的数据,这个数据就是想存储到AT24C08里保存的数据。

7. 等待AT24C08应答、低电平有效

8. 发送停止信号

 

2.3  对AT24C08 按页写数据的指令流程(时序)

 详细解释:

1.  先发送起始信号

2.  发送设备地址(写权限)

3. 等待AT24C08应答、低电平有效

4. 发送存储地址、AT24C08内部一共有256个字节空间,寻址是从0开始的,范围是(0~255);发送这个存储器地址就是告诉AT24C08接下来的数据改存储到哪个地方。

5. 等待AT24C08应答、低电平有效

6. 可以循环发送8个字节的数据,这些数据就是想存储到AT24C08里保存的数据。

    AT24C08的页缓冲区是16个字节,所有这里的循环最多也只能发送16个字节,多发送的字节会将前面的覆盖掉。

   需要注意的地方:  这个页缓冲区的寻址也是从0开始,比如:  0~15算第1页,16~32算第2页......依次类推。 如果现在写数据的起始地址是3,那么这一页只剩下13个字节可以写;并不是说从哪里都可以循环写16个字节。

      详细流程: 这里程序里一般使用for循环实现 

     (1).  发送字节1

     (2). 等待AT24C08应答,低电平有效

     (3). 发送字节2

     (4). 等待AT24C08应答,低电平有效

     .........

     最多8次.   

7. 等待AT24C08应答、低电平有效

8. 发送停止信号

2.4  从AT24C08任意地址读任意字节数据(时序)

AT24C08支持当前地址读、任意地址读,最常用的还是任意地址读,因为可以指定读取数据的地址,比较灵活,上面这个指定时序图就是任意地址读。

 详细解释:

1.  先发送起始信号

2.  发送设备地址(写权限)

3. 等待AT24C08应答、低电平有效

4. 发送存储地址、AT24C08内部一共有2048个字节空间,寻址是从0开始的,范围是(0~1024);发送这个存储器地址就是告诉AT24C08接下来应该返回那个地址的数据给单片机。

5. 等待AT24C08应答、低电平有效

6.  重新发送起始信号(切换读写模式)

7. 发送设备地址(读权限)

8.  等待AT24C08应答、低电平有效

9. 循环读取数据:  接收AT24C08返回的数据.

   读数据没有字节限制,可以第1个字节、也可以连续将整个芯片读完。

10. 发送非应答(高电平有效)

11. 发送停止信号

三、IIC总线介绍

       2.1 IIC总线简介

I2C(Inter-Integrated Circuit)总线是由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备,是微电子通信控制领域广泛采用的一种总线标准。具有接口线少,控制方式简单,器件封装形式小,通信速率较高等优点。

I2C规程运用主/从双向通讯。器件发送数据到总线上,则定义为发送器,器件接收数据则定义为接收器。主器件和从器件都可以工作于接收和发送状态。

I2C 总线通过串行数据(SDA)线和串行时钟(SCL)线在连接到总线的器件间传递信息。每个器件都有一个唯一的地址识别,而且都可以作为一个发送器或接收器(由器件的功能决定)。

I2C有四种工作模式:
       1.主机发送
       2.主机接收
       3.从机发送
       4.从机接收

I2C总线只用两根线:串行数据SDA(Serial Data)、串行时钟SCL(Serial Clock)。

总线必须由主机(通常为微控制器)控制,主机产生串行时钟(SCL)控制总线的传输方向,并产生起始和停止条件。

SDA线上的数据状态仅在SCL为低电平的期间才能改变。

2.2 IIC总线上的设备连接图

I2C 总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成。通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递。在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。

其中上拉电阻范围是4.7K~100K。

2.3 I2C总线特征

I2C总线上的每一个设备都可以作为主设备或者从设备,而且每一个从设备都会对应一个唯一的地址(可以从I2C器件的数据手册得知)。主从设备之间就通过这个地址来确定与哪个器件进行通信,在通常的应用中,我们把CPU带I2C总线接口的模块作为主设备,把挂接在总线上的其他设备都作为从设备。

1.    总线上能挂接的器件数量
     I2C总线上可挂接的设备数量受总线的最大电容400pF 限制,如果所挂接的是相同型号的器件,则还受器件地址的限制。
    一般I2C设备地址是7位地址(也有10位),地址分成两部分:芯片固化地址(生产芯片时候哪些接地,哪些接电源,已经固定),可编程地址(引出IO口,由硬件设备决定)。
     例如: 某一个器件是7 位地址,其中10101 xxx  高4位出厂时候固定了,低3位可以由设计者决定。
则一条I2C总线上只能挂该种器件最少8个。
如果7位地址都可以编程,那理论上就可以达到128个器件,但实际中不会挂载这么多。

2.    总线速度传输速度:
I2C总线数据传输速率在标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s。一般通过I2C总线接口可编程时钟来实现传输速率的调整。

3.    总线数据长度
I2C总线上的主设备与从设备之间以字节(8位)为单位进行双向的数据传输。

 2.4 I2C总线协议基本时序信号

空闲状态:SCL和SDA都保持着高电平。

起始条件:总线在空闲状态时,SCL和SDA都保持着高电平,当SCL为高电平期间而SDA由高到低的跳变,表示产生一个起始条件。在起始条件产生后,总线处于忙状态,由本次数据传输的主从设备独占,其他I2C器件无法访问总线。

停止条件:当SCL为高而SDA由低到高的跳变,表示产生一个停止条件。

答应信号:每个字节传输完成后的下一个时钟信号,在SCL高电平期间,SDA为低,则表示一个应答信号。

非答应信号:每个字节传输完成后的下一个时钟信号,在SCL高电平期间,SDA为高,则表示一个应答信号。应答信号或非应答信号是由接收器发出的,发送器则是检测这个信号(发送器,接收器可以从设备也可以主设备)。

注意:起始和结束信号总是由主设备产生。

 

2.5  起始信号与停止信号

起始信号就是:  时钟线SCL处于高电平的时候,数据线SDA由高电平变为低电平的过程。SCL=1;SDA=1;SDA=0;

停止信号就是: 时钟线SCL处于低电平的时候,  数据线SDA由低电平变为高电平的过程。SCL=1;SDA=0;SDA=1;

2.6  应答信号

数据位的第9位就时应答位。 读取应答位的流程和读取数据位是一样的。示例:   SCL=0;SCL=1;ACK=SDA;       这个ACK就是读取的应答状态。

2.7 数据位传输时序

通过时序图了解到,SCL处于高电平的时候数据稳定,SCL处于低电平的时候数据不稳定。

那么对于写一位数据(STM32--->AT24C08): SCL=0;SDA=data; SCL=1; 

那么对于读一位数据(STM32<-----AT24C08): SCL=0;SCL=1;data=SDA;  

  2.8 总线时序

四、IIC总线时序代码、AT24C08读写代码

  在调试IIC模拟时序的时候,可以在淘宝上买一个24M的USB逻辑分析仪,时序出现问题,使用逻辑分析仪一分析就可以快速找到问题。

 

4.1 iic.c  这是STM32的IIC硬件时序完整代码

  1. /*
  2. 函数功能: 初始化IIC总线
  3. 硬件连接:
  4. SCL---PB6
  5. SDA---PB7
  6. */
  7. void IIC_Init(void)
  8. {
  9. /*1. 时钟配置*/
  10. RCC->APB2ENR|=1<<3; //PB
  11. /*2. GPIO口模式配置*/
  12. GPIOB->CRL&=0x00FFFFFF;
  13. GPIOB->CRL|=0xFF000000; //复用开漏输出
  14. GPIOB->ODR|=0x3<<6;
  15. /*3. GPIO口时钟配置(顺序不能错)*/
  16. RCC->APB1ENR|=1<<21; //I2C1时钟
  17. RCC->APB1RSTR|=1<<21; //开启复位时钟
  18. RCC->APB1RSTR&=~(1<<21);//关闭复位时钟
  19. /*4. 配置IIC的核心寄存器*/
  20. I2C1->CR2=0x24<<0; //配置主机频率为36MHZ
  21. I2C1->CCR|=0x2D<<0; //配置主机频率是400KHZ
  22. I2C1->CR1|=1<<0; //开启IIC模块
  23. /*
  24. CCR=主机时钟频率/2/IIC总线的频率
  25. 45=36MHZ/2/400KHZ ---0x2D
  26. */
  27. }
  28. /*
  29. 函数功能: 发送起始信号
  30. 当时钟线为高电平的时候,数据线由高电平变为低电平的过程
  31. */
  32. void IIC_SendStart(void)
  33. {
  34. I2C1->CR1|=1<<8; //产生起始信号
  35. while(!(I2C1->SR1&1<<0)){} //等待起始信号完成
  36. I2C1->SR1=0; //清除状态位
  37. }
  38. /*
  39. 函数功能: 停止信号
  40. 当时钟线为高电平的时候,数据线由低电平变为高电平的过程
  41. */
  42. void IIC_SendStop(void)
  43. {
  44. I2C1->CR1|=1<<9;
  45. }
  46. /*
  47. 函数功能: 发送地址数据
  48. */
  49. void IIC_SendAddr(u8 addr)
  50. {
  51. u32 s1,s2;
  52. I2C1->DR=addr; //发送数据
  53. while(1)
  54. {
  55. s1=I2C1->SR1;
  56. s2=I2C1->SR2;
  57. if(s1&1<<1) //判断地址有没有发送成功
  58. {
  59. break;
  60. }
  61. }
  62. }
  63. /*
  64. 函数功能: 发送数据
  65. */
  66. void IIC_SendOneByte(u8 addr)
  67. {
  68. u32 s1,s2;
  69. I2C1->DR=addr; //发送数据
  70. while(1)
  71. {
  72. s1=I2C1->SR1;
  73. s2=I2C1->SR2;
  74. if(s1&1<<2) //判断数据有没有发送成功
  75. {
  76. break;
  77. }
  78. }
  79. }
  80. /*
  81. 函数功能: 接收一个字节数据
  82. */
  83. u8 IIC_RecvOneByte(void)
  84. {
  85. u8 data=0;
  86. I2C1->CR1|=1<<10; //使能应答
  87. while(!(I2C1->SR1&1<<6)){} //等待数据
  88. data=I2C1->DR;
  89. I2C1->CR1&=~(1<<10); //关闭应答使能
  90. return data;
  91. }

4.2 AT24C08.c 这是AT24C08完整的读写代码

  1. *
  2. 函数功能: 写一个字节
  3. 函数参数:
  4. u8 addr 数据的位置(0~1023)
  5. u8 data 数据范围(0~255)
  6. */
  7. void AT24C08_WriteOneByte(u16 addr,u8 data)
  8. {
  9. u8 read_device_addr=AT24C08_READ_ADDR;
  10. u8 write_device_addr=AT24C08_WRITE_ADDR;
  11. if(addr<256*1) //第一个块
  12. {
  13. write_device_addr|=0x0<<1;
  14. read_device_addr|=0x0<<1;
  15. }
  16. else if(addr<256*2) //第二个块
  17. {
  18. write_device_addr|=0x1<<1;
  19. read_device_addr|=0x1<<1;
  20. }
  21. else if(addr<256*3) //第三个块
  22. {
  23. write_device_addr|=0x2<<1;
  24. read_device_addr|=0x2<<1;
  25. }
  26. else if(addr<256*4) //第四个块
  27. {
  28. write_device_addr|=0x3<<1;
  29. read_device_addr|=0x3<<1;
  30. }
  31. addr=addr%256; //得到地址范围
  32. IIC_SendStart();//起始信号
  33. IIC_SendAddr(write_device_addr);//发送设备地址
  34. IIC_SendOneByte(addr); //数据存放的地址
  35. IIC_SendOneByte(data); //发送将要存放的数据
  36. IIC_SendStop(); //停止信号
  37. DelayMs(10); //等待写
  38. }
  39. /*
  40. 函数功能: 读一个字节
  41. 函数参数:
  42. u8 addr 数据的位置(0~1023)
  43. 返回值: 读到的数据
  44. */
  45. u8 AT24C08_ReadOneByte(u16 addr)
  46. {
  47. u8 data=0;
  48. u8 read_device_addr=AT24C08_READ_ADDR;
  49. u8 write_device_addr=AT24C08_WRITE_ADDR;
  50. if(addr<256*1) //第一个块
  51. {
  52. write_device_addr|=0x0<<1;
  53. read_device_addr|=0x0<<1;
  54. }
  55. else if(addr<256*2) //第二个块
  56. {
  57. write_device_addr|=0x1<<1;
  58. read_device_addr|=0x1<<1;
  59. }
  60. else if(addr<256*3) //第三个块
  61. {
  62. write_device_addr|=0x2<<1;
  63. read_device_addr|=0x2<<1;
  64. }
  65. else if(addr<256*4) //第四个块
  66. {
  67. write_device_addr|=0x3<<1;
  68. read_device_addr|=0x3<<1;
  69. }
  70. addr=addr%256; //得到地址范围
  71. IIC_SendStart();//起始信号
  72. IIC_SendAddr(write_device_addr);//发送设备地址
  73. IIC_SendOneByte(addr); //将要读取数据的地址
  74. IIC_SendStart();//起始信号
  75. IIC_SendAddr(read_device_addr);//发送设备地址
  76. data=IIC_RecvOneByte();//读取数据
  77. IIC_SendStop(); //停止信号
  78. return data;
  79. }
  80. /*
  81. 函数功能: 从指定位置读取指定长度的数据
  82. 函数参数:
  83. u16 addr 数据的位置(0~1023)
  84. u16 len 读取的长度
  85. u8 *buffer 存放读取的数据
  86. 返回值: 读到的数据
  87. */
  88. void AT24C08_ReadByte(u16 addr,u16 len,u8 *buffer)
  89. {
  90. u16 i=0;
  91. IIC_SendStart();//起始信号
  92. IIC_SendAddr(AT24C08_WRITE_ADDR);//发送设备地址
  93. IIC_SendOneByte(addr); //将要读取数据的地址
  94. IIC_SendStart();//起始信号
  95. IIC_SendAddr(AT24C08_READ_ADDR);//发送设备地址
  96. for(i=0;i<len;i++)
  97. {
  98. buffer[i]=IIC_RecvOneByte();//读取数据
  99. }
  100. IIC_SendStop(); //停止信号
  101. }
  102. /*
  103. 函数功能: AT24C08页写函数
  104. 函数参数:
  105. u16 addr 写入的位置(0~1023)
  106. u8 len 写入的长度(每页16字节)
  107. u8 *buffer 存放读取的数据
  108. */
  109. void AT24C08_PageWrite(u16 addr,u16 len,u8 *buffer)
  110. {
  111. u16 i=0;
  112. IIC_SendStart();//起始信号
  113. IIC_SendAddr(AT24C08_WRITE_ADDR);//发送设备地址
  114. IIC_SendOneByte(addr); //数据存放的地址
  115. for(i=0;i<len;i++)
  116. {
  117. IIC_SendOneByte(buffer[i]); //发送将要存放的数据
  118. }
  119. IIC_SendStop(); //停止信号
  120. DelayMs(10); //等待写
  121. }
  122. /*
  123. 函数功能: 从指定位置写入指定长度的数据
  124. 函数参数:
  125. u16 addr 数据的位置(0~1023)
  126. u16 len 写入的长度
  127. u8 *buffer 存放即将写入的数据
  128. 返回值: 读到的数据
  129. */
  130. void AT24C08_WriteByte(u16 addr,u16 len,u8 *buffer)
  131. {
  132. u8 page_byte=16-addr%16; //得到当前页剩余的字节数量
  133. if(page_byte>len) //判断当前页剩余的字节空间是否够写
  134. {
  135. page_byte=len; //表示一次性可以写完
  136. }
  137. while(1)
  138. {
  139. AT24C08_PageWrite(addr,page_byte,buffer); //写一页
  140. if(page_byte==len)break; //写完了
  141. buffer+=page_byte; //指针偏移
  142. addr+=page_byte;//地址偏移
  143. len-=page_byte;//得到剩余没有写完的长度
  144. if(len>16)page_byte=16;
  145. else page_byte=len; //一次可以写完
  146. }
  147. }

 4.3 main.c 这是AT24C08测试代码

  1. #include "stm32f10x.h"
  2. #include "beep.h"
  3. #include "delay.h"
  4. #include "led.h"
  5. #include "key.h"
  6. #include "sys.h"
  7. #include "usart.h"
  8. #include <string.h>
  9. #include <stdio.h>
  10. #include "exti.h"
  11. #include "timer.h"
  12. #include "rtc.h"
  13. #include "adc.h"
  14. #include "ds18b20.h"
  15. #include "ble.h"
  16. #include "esp8266.h"
  17. #include "wdg.h"
  18. #include "oled.h"
  19. #include "rfid_rc522.h"
  20. #include "infrared.h"
  21. #include "iic.h"
  22. #include "at24c08.h"
  23. u8 buff_tx[50]="1234567890";
  24. u8 buff_rx[50];
  25. u8 data=88;
  26. u8 data2;
  27. int main()
  28. {
  29. u8 key;
  30. LED_Init();
  31. KEY_Init();
  32. BEEP_Init();
  33. TIM1_Init(72,20000); //辅助串口1接收,超时时间为20ms
  34. USART_X_Init(USART1,72,115200);
  35. IIC_Init(); //IIC总线初始化
  36. printf("usart1 ok\n");
  37. while(1)
  38. {
  39. key=KEY_Scanf();
  40. if(key)
  41. {
  42. //AT24C08_WriteByte(100,50,buff_tx);
  43. //AT24C08_ReadByte(100,50,buff_rx);
  44. //printf("buff_rx=%s\n",buff_rx);
  45. //测试第0块
  46. // data=AT24C08_ReadOneByte(0);
  47. // AT24C08_WriteOneByte(0,data+1);
  48. // printf("data=%d\n",data);
  49. //测试第1块
  50. // data=AT24C08_ReadOneByte(300);
  51. // AT24C08_WriteOneByte(300,data+1);
  52. // printf("data=%d\n",data);
  53. //测试第2块
  54. // data=AT24C08_ReadOneByte(600);
  55. // AT24C08_WriteOneByte(600,data+1);
  56. // printf("data=%d\n",data);
  57. //测试第3块
  58. data=AT24C08_ReadOneByte(900);
  59. AT24C08_WriteOneByte(900,data+1);
  60. printf("data=%d\n",data);
  61. }
  62. }
  63. }

 串口调试助手源码下载地址: https://blog.csdn.net/xiaolong1126626497/article/details/116040983 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/438189
推荐阅读
相关标签
  

闽ICP备14008679号