当前位置:网站首页>STM32入门开发 采用IIC硬件时序读写AT24C08(EEPROM)
STM32入门开发 采用IIC硬件时序读写AT24C08(EEPROM)
2022-07-07 09:21:00 【华为云】
一、环境介绍
编程软件: keil5
操作系统: win10
MCU型号: STM32F103ZET6
STM32编程方式: 寄存器开发 (方便程序移植到其他单片机)
IIC总线: STM32本身支持IIC硬件时序的,上篇文章已经介绍了采用IIC模拟时序读写AT24C02,这篇文章介绍STM32的硬件IIC配置方法,并读写AT24C08。
模拟时序更加方便移植到其他单片机,通用性更高,不分MCU;硬件时序效率更高,每个MCU配置方法不同,依赖硬件本身支持。
器件型号: 采用AT24C08 EEPROM存储芯片
二、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硬件时序完整代码
/*函数功能: 初始化IIC总线硬件连接: SCL---PB6 SDA---PB7*/void IIC_Init(void){ /*1. 时钟配置*/ RCC->APB2ENR|=1<<3; //PB /*2. GPIO口模式配置*/ GPIOB->CRL&=0x00FFFFFF; GPIOB->CRL|=0xFF000000; //复用开漏输出 GPIOB->ODR|=0x3<<6; /*3. GPIO口时钟配置(顺序不能错)*/ RCC->APB1ENR|=1<<21; //I2C1时钟 RCC->APB1RSTR|=1<<21; //开启复位时钟 RCC->APB1RSTR&=~(1<<21);//关闭复位时钟 /*4. 配置IIC的核心寄存器*/ I2C1->CR2=0x24<<0; //配置主机频率为36MHZ I2C1->CCR|=0x2D<<0; //配置主机频率是400KHZ I2C1->CR1|=1<<0; //开启IIC模块 /* CCR=主机时钟频率/2/IIC总线的频率 45=36MHZ/2/400KHZ ---0x2D */}/*函数功能: 发送起始信号当时钟线为高电平的时候,数据线由高电平变为低电平的过程*/void IIC_SendStart(void){ I2C1->CR1|=1<<8; //产生起始信号 while(!(I2C1->SR1&1<<0)){} //等待起始信号完成 I2C1->SR1=0; //清除状态位}/*函数功能: 停止信号当时钟线为高电平的时候,数据线由低电平变为高电平的过程*/void IIC_SendStop(void){ I2C1->CR1|=1<<9;}/*函数功能: 发送地址数据*/void IIC_SendAddr(u8 addr){ u32 s1,s2; I2C1->DR=addr; //发送数据 while(1) { s1=I2C1->SR1; s2=I2C1->SR2; if(s1&1<<1) //判断地址有没有发送成功 { break; } }}/*函数功能: 发送数据*/void IIC_SendOneByte(u8 addr){ u32 s1,s2; I2C1->DR=addr; //发送数据 while(1) { s1=I2C1->SR1; s2=I2C1->SR2; if(s1&1<<2) //判断数据有没有发送成功 { break; } }}/*函数功能: 接收一个字节数据*/u8 IIC_RecvOneByte(void){ u8 data=0; I2C1->CR1|=1<<10; //使能应答 while(!(I2C1->SR1&1<<6)){} //等待数据 data=I2C1->DR; I2C1->CR1&=~(1<<10); //关闭应答使能 return data;}
4.2 AT24C08.c 这是AT24C08完整的读写代码
*函数功能: 写一个字节函数参数: u8 addr 数据的位置(0~1023) u8 data 数据范围(0~255)*/void AT24C08_WriteOneByte(u16 addr,u8 data){ u8 read_device_addr=AT24C08_READ_ADDR; u8 write_device_addr=AT24C08_WRITE_ADDR; if(addr<256*1) //第一个块 { write_device_addr|=0x0<<1; read_device_addr|=0x0<<1; } else if(addr<256*2) //第二个块 { write_device_addr|=0x1<<1; read_device_addr|=0x1<<1; } else if(addr<256*3) //第三个块 { write_device_addr|=0x2<<1; read_device_addr|=0x2<<1; } else if(addr<256*4) //第四个块 { write_device_addr|=0x3<<1; read_device_addr|=0x3<<1; } addr=addr%256; //得到地址范围 IIC_SendStart();//起始信号 IIC_SendAddr(write_device_addr);//发送设备地址 IIC_SendOneByte(addr); //数据存放的地址 IIC_SendOneByte(data); //发送将要存放的数据 IIC_SendStop(); //停止信号 DelayMs(10); //等待写}/*函数功能: 读一个字节函数参数: u8 addr 数据的位置(0~1023)返回值: 读到的数据*/u8 AT24C08_ReadOneByte(u16 addr){ u8 data=0; u8 read_device_addr=AT24C08_READ_ADDR; u8 write_device_addr=AT24C08_WRITE_ADDR; if(addr<256*1) //第一个块 { write_device_addr|=0x0<<1; read_device_addr|=0x0<<1; } else if(addr<256*2) //第二个块 { write_device_addr|=0x1<<1; read_device_addr|=0x1<<1; } else if(addr<256*3) //第三个块 { write_device_addr|=0x2<<1; read_device_addr|=0x2<<1; } else if(addr<256*4) //第四个块 { write_device_addr|=0x3<<1; read_device_addr|=0x3<<1; } addr=addr%256; //得到地址范围 IIC_SendStart();//起始信号 IIC_SendAddr(write_device_addr);//发送设备地址 IIC_SendOneByte(addr); //将要读取数据的地址 IIC_SendStart();//起始信号 IIC_SendAddr(read_device_addr);//发送设备地址 data=IIC_RecvOneByte();//读取数据 IIC_SendStop(); //停止信号 return data;}/*函数功能: 从指定位置读取指定长度的数据函数参数: u16 addr 数据的位置(0~1023) u16 len 读取的长度 u8 *buffer 存放读取的数据返回值: 读到的数据*/void AT24C08_ReadByte(u16 addr,u16 len,u8 *buffer){ u16 i=0; IIC_SendStart();//起始信号 IIC_SendAddr(AT24C08_WRITE_ADDR);//发送设备地址 IIC_SendOneByte(addr); //将要读取数据的地址 IIC_SendStart();//起始信号 IIC_SendAddr(AT24C08_READ_ADDR);//发送设备地址 for(i=0;i<len;i++) { buffer[i]=IIC_RecvOneByte();//读取数据 } IIC_SendStop(); //停止信号}/*函数功能: AT24C08页写函数函数参数: u16 addr 写入的位置(0~1023) u8 len 写入的长度(每页16字节) u8 *buffer 存放读取的数据*/void AT24C08_PageWrite(u16 addr,u16 len,u8 *buffer){ u16 i=0; IIC_SendStart();//起始信号 IIC_SendAddr(AT24C08_WRITE_ADDR);//发送设备地址 IIC_SendOneByte(addr); //数据存放的地址 for(i=0;i<len;i++) { IIC_SendOneByte(buffer[i]); //发送将要存放的数据 } IIC_SendStop(); //停止信号 DelayMs(10); //等待写}/*函数功能: 从指定位置写入指定长度的数据函数参数: u16 addr 数据的位置(0~1023) u16 len 写入的长度 u8 *buffer 存放即将写入的数据返回值: 读到的数据*/void AT24C08_WriteByte(u16 addr,u16 len,u8 *buffer){ u8 page_byte=16-addr%16; //得到当前页剩余的字节数量 if(page_byte>len) //判断当前页剩余的字节空间是否够写 { page_byte=len; //表示一次性可以写完 } while(1) { AT24C08_PageWrite(addr,page_byte,buffer); //写一页 if(page_byte==len)break; //写完了 buffer+=page_byte; //指针偏移 addr+=page_byte;//地址偏移 len-=page_byte;//得到剩余没有写完的长度 if(len>16)page_byte=16; else page_byte=len; //一次可以写完 }}
4.3 main.c 这是AT24C08测试代码
#include "stm32f10x.h"#include "beep.h"#include "delay.h"#include "led.h"#include "key.h"#include "sys.h"#include "usart.h"#include <string.h>#include <stdio.h>#include "exti.h"#include "timer.h"#include "rtc.h"#include "adc.h"#include "ds18b20.h"#include "ble.h"#include "esp8266.h"#include "wdg.h"#include "oled.h"#include "rfid_rc522.h"#include "infrared.h"#include "iic.h"#include "at24c08.h"u8 buff_tx[50]="1234567890";u8 buff_rx[50];u8 data=88;u8 data2;int main(){ u8 key; LED_Init(); KEY_Init(); BEEP_Init(); TIM1_Init(72,20000); //辅助串口1接收,超时时间为20ms USART_X_Init(USART1,72,115200); IIC_Init(); //IIC总线初始化 printf("usart1 ok\n"); while(1) { key=KEY_Scanf(); if(key) { //AT24C08_WriteByte(100,50,buff_tx); //AT24C08_ReadByte(100,50,buff_rx); //printf("buff_rx=%s\n",buff_rx); //测试第0块// data=AT24C08_ReadOneByte(0);// AT24C08_WriteOneByte(0,data+1);// printf("data=%d\n",data); //测试第1块// data=AT24C08_ReadOneByte(300);// AT24C08_WriteOneByte(300,data+1);// printf("data=%d\n",data); //测试第2块// data=AT24C08_ReadOneByte(600);// AT24C08_WriteOneByte(600,data+1);// printf("data=%d\n",data); //测试第3块 data=AT24C08_ReadOneByte(900); AT24C08_WriteOneByte(900,data+1); printf("data=%d\n",data); } }}
边栏推荐
- seata 1.3.0 四種模式解决分布式事務(AT、TCC、SAGA、XA)
- 面试被问到了解哪些开发模型?看这一篇就够了
- Antd select selector drop-down box follows the scroll bar to scroll through the solution
- mif 文件格式记录
- The sixth training assignment
- Activity生命周期
- Which securities company is the best and safest to open an account for the subscription of new shares
- Debezium同步之Debezium架构详解
- Ffmpeg record a video command from RTSP
- Kitex 重试机制
猜你喜欢
自动化测试框架
From pornographic live broadcast to live broadcast E-commerce
关于在云服务器上(这里用腾讯云)安装mysql8.0并使本地可以远程连接的方法
The database synchronization tool dbsync adds support for mongodb and es
verilog设计抢答器【附源码】
学习笔记|数据小白使用DataEase制作数据大屏
[pyqt] the cellwidget in tablewidget uses signal and slot mechanism
测试开发基础,教你做一个完整功能的Web平台之环境准备
Still cannot find RPC dispatcher table failed to connect in virtual KD
RationalDMIS2022 高级编程宏程序
随机推荐
基于华为云IOT设计智能称重系统(STM32)
科普达人丨一文弄懂什么是云计算?
Kitex 重试机制
90后,辞职创业,说要卷死云数据库
Socket socket programming
变量的解构赋值
How to get hardware information in unity
Une fois que l'uniapp a sauté de la page dans onlaunch, cliquez sur Event Failure resolution
技术分享 | 抓包分析 TCP 协议
MIF file format record
Mysql的json格式查询
Process control (creation, termination, waiting, program replacement)
Arduino board description
测试优惠券要怎么写测试用例?
From pornographic live broadcast to live broadcast E-commerce
Introduction to shell programming
Verilog design responder [with source code]
Project ERROR: Unknown module(s) in QT: core gui
从色情直播到直播电商
Unity downloads files through the server address