十三、51单片机之EEPROM(I2C)

news/2024/5/7 23:26:04/文章来源:https://blog.csdn.net/weixin_42727214/article/details/130071122

1、EEPROM简介

(1)存储设备类型:ROM、RAM、PROM(可编程ROM)、EPROM(可擦除ROM)、EEPROM(电可擦除ROM)。

(2)为什么需要EEPROM?

某些数据内容我们需要掉电不丢失且在程序运行中可以修改这些数据内容,这就需要用到EEPROM。

(3)EEPROM和flash(闪存)的区别。

EEPROM是按功能分类的一种存储设备类型;flash是存储设备生产的一种工艺。EEPROM就可能采用了flash工艺,常见的U盘也是采用flash工艺。

(4)这里的EEPROM芯片具体型号是AT24C02,采用I2C时序进行读写。

2、AT24C02数据手册

(1)引脚介绍

A0、A1、A2I2C总线上器件地址
GND电源地
VCC电源正极
WP写保护引脚
SCL串行时钟输入
SDA串行地址和数据输入/输出

(2)AT24C02中02表示容量。

3、原理图

EEPROM芯片使用AT24C02。

(1)E0、E1、E2标识I2C总线的从地址。

(2)VDD表示电源正,VSS接电源地。

(3)WE(Write Enable)写保护引脚。(上划线代表低电平使能,低电平状态才能写)

(4)SDA(Serial Data)串行数据)引脚接单片机引脚P2^0。

(5)SCL(Serial Clock 串行时钟)引脚接单片机引脚P2^1。

4、I2C总结

(1)主CPU和其附属芯片(I2C设备)之间最常用的接口,尤其是各种传感器,因此在物联网时代非常重要。

(2)三根线:GND、SCL、SDA,串行通信(只有一根数据传输线),电平式通信(相对于差分信号)。

(3)总线式结构,可以一对多,总线上可以挂上百个器件,用从地址来区分。

(4)主从式,由主设备来发起通信及总线仲裁,从设备被动响应。

(5)通信速率一般(kbps级别),不适合语音、视频等信息类型。

5、EEPROM编程思路

主要包含下面两部分:

(1)I2C接口底层时序。起始信号、停止信号、发送字节、读取字节。

(2)EEPROM的寄存器读写时序。

6、I2C从设备的地址

(1)从设备的地址是由从设备自身定义的,不同的从器件的地址定义方式是不同的,要查具体的芯片数据手册来确定。

(2)同一个I2C总线上只有一个主设备,但是从设备可以有多个。这多个从设备的从地址不能相同(硬件设计工程师必须保证这一点。因为从地址是不能通过软件设定的)

(3)分析原理图和AT24C02的数据手册,得出: 从设备地址是:读地址:0xA1,写地址:0xA0.

7、示例程序

(1)app.c文件

#include <reg52.h>
#include "drv_uart.h"
#include "at24c02.h"
#include "intrins.h"/*函数声明*/
void Delay20ms();		  //@12.000MHz
void Delay500ms();		//@12.000MHzvoid main(void)
{unsigned char  ReadData[10] = "aaaaaaaaaa";  //保存从EP读取的数据unsigned char WriteData[10] = "zjd6661234";  //保存写入EP的数据unsigned char AddrBase = 0x05;               //EP内存操作基地址unsigned char i = 0;                         //用于for循环UartInit();		                        //串口初始化/*注释写操作,检测断电是否丢失*/for(i=0; i<10; i++){AT24C02_WriteByte(AddrBase+i,WriteData[i]);Delay20ms();}for(i=0; i<10; i++){ReadData[i] = AT24C02_ReadByte(AddrBase+i);Delay20ms();}for(i=0; i<10; i++){UartSendByte(ReadData[i]);}while(1){UartSendString("hello world.\r\n");Delay500ms();}
}void Delay20ms()		//@12.000MHz
{unsigned char i, j;i = 39;j = 230;do{while (--j);} while (--i);
}void Delay500ms()		//@12.000MHz
{unsigned char i, j, k;_nop_();i = 4;j = 205;k = 187;do{do{while (--k);} while (--j);} while (--i);
}

(2)drv_i2c.c

#include "drv_i2c.h"
#include <intrins.h>
#include <reg52.h>sbit SCL = P2^1;      
sbit SDA = P2^0; /*函数声明*/
void Delay10us();		//@12.000MHz/**功能:I2C总线起始信号   *参数:无*返回值:无*注:(1)起始信号:在SCL时钟信号在高电平期间SDA信号产生一个下降沿(2)起始之后SDA和SCL都为0*/
void I2C_Start()
{SCL = 1;SDA = 1;   Delay10us();SDA = 0;      /*SDA下降延*/Delay10us();SCL = 0;      /*为后续读写做好准备*/Delay10us();
}/**功能:I2C总线停止信号*参数:无*返回值:无*注:(1)终止信号:在SCL时钟信号高电平期间I2C_SDA信号产生一个上升沿(2)结束之后保持SDA和SCL都为1,表示总线空闲*/
void I2C_Stop()
{SCL = 1;SDA = 0;Delay10us();SDA = 1;Delay10us();
}/**功能:I2C发送一个字节*参数:Data: 待发送数据ack: 1表示等待应答信号,0表示不等待应答信号*返回值:发送成功返回1,发送失败返回0。*注:(1)发送完一个字节I2C_SCL=0, 需要应答则应答设置为1,否则为0。(2)SCL低电平时,MCU把数据准备好,SCL高电平,从设备把数据读走。*/
uchar I2C_SendByte(uchar Data, uchar ack)
{uchar i = 0;   				/*用于for循环*/uchar TimeCount = 0;	/*等待应答信号计数*/ 				SCL = 0;      				/*预防I2C起始信号最后没有把SCL拉低*/for(i=0; i<8; i++)    /*发送8bit,从高位到低位*/{SDA = Data >> 7;	 //起始信号之后I2C_SCL=0,所以可以直接改变I2C_SDA信号Data = Data << 1;Delay10us();SCL = 1;Delay10us();SCL = 0;Delay10us();	}SDA = 1;			/*主设备释放SDA线给从设备去操作*/Delay10us();       /*延时5uS*/SCL = 1;			/*SCL第9个周期*/while(SDA && (ack == 1)) /*等待应答,也就是等待从设备把SDA拉低*/{TimeCount++;if(TimeCount > 400)	       /*如果超过2000us没有应答发送失败,或者为非应答,表示接收结束*/{SCL = 0;Delay10us();	return 0;}}SCL = 0;      /*为发送下一字节做准备*/Delay10us();return 1;		
}/**功能:I2C读取一个字节*参数:无*返回值:读取的一字节数据*/
uchar I2C_ReadByte()
{uchar i = 0;       /*用于for循环*/uchar Data = 0;    /*保存读取的数据*/SCL = 0;			/*预防起始信号或发送字节后SCL不是0*/SDA = 1;			/*起始和发送一个字节之后I2C_SCL都是0*/Delay10us();for(i=0; i<8; i++)   /*接收8个bit*/{SCL = 1;     /*通知从设备MCU要开始读了,可以放1bit数据到SDA*/Delay10us();Data <<= 1;  /*读取高位在前*/Data |= SDA;Delay10us();SCL = 0;     /*为下bit传输做准备*/Delay10us();}return Data;		
}void Delay10us()		//@12.000MHz
{unsigned char i;_nop_();i = 2;while (--i);
}

(3)drv_i2c.h

#ifndef __DRV_I2C_H__
#define __DRV_I2C_H__#define uchar unsigned char
#define uint unsigned int/*函数声明*/
void I2C_Start();                           /*I2C总线起始信号*/
void I2C_Stop();                            /*I2C总线停止信号*/
uchar I2C_SendByte(uchar Data, uchar ack);  /*I2C发送一个字节*/
uchar I2C_ReadByte();                       /*I2C读取一个字节*/#endif

(4)drv_uart.c

#include "drv_uart.h"
#include <reg52.h>/**功能:串口初始化函数,8数据位,1停止位,无校验位,波特率4800*参数:无*返回值:无*/
void UartInit(void)
{SCON = 0x50;  //串口工作在模式1,8位数据位,允许串行接收PCON = 0x80;  //波特率加倍TMOD = 0x20;  //设置T1为模式2 TH1 = 243;    //波特率4800	,TH1 = 晶振频率/12/32/波特率TL1 = 243;    //8位自动重装,意识是TH1用完了之后下一个周期TL1会自动重装到TH1去。TR1 = 1;      //开启定时器1//ES = 1;       //打开串口中断//EA = 1;       //打开总中断
}/**功能:通过串口发送一个字节数据*参数:需要发送的内容*返回值:无*/
void UartSendByte(unsigned char Dat)
{SBUF = Dat;      //准备好需要发送的一个字节while(TI == 0);  //确认串口发送没有再忙,while循环需要加超时判断TI = 0;					 //软件复位TI标志位
}/**功能:通过串口发送字符串*参数:待发送的字符串*返回值:无*/
void UartSendString(unsigned char *str)
{while(*str != '\0')    //等待字符串发完*/{UartSendByte(*str);  //发送一个字符str++;               //指针指向下一个字符}
}/**功能:串口中断接收函数*参数:无*返回值:无*注意:中断函数通过中断编号识别,中断编号可通过查数据手册得到*/
void Uart_Isr() interrupt 4 
{unsigned char ReceiveBit;if(RI == 1){ReceiveBit = SBUF;		//读取SBUF,读取串口接收到的一个字节RI = 0;}UartSendByte(ReceiveBit);		  //接收到的内容原封不动发回去
}

(5)drv_uart.h

#ifndef __DRV_UART_H__
#define __DRV_UART_H__/*函数声明*/
void UartInit(void);                       /*串口初始化函数*/
void UartSendByte(unsigned char Dat);      /*通过串口发送一个字节数据*/
void UartSendString(unsigned char *str);   /*通过串口发送字符串*/#endif

(6)at24c02.c

#include "at24c02.h"/**功能:AT24C02写入一个字节*参数:Addr:AT24C02存储数据的地址Data:写入的数据*返回值:无*/
void AT24C02_WriteByte(uchar Addr, uchar Data)
{I2C_Start();I2C_SendByte(0xA0, 1);  // 发送AT24C02写地址I2C_SendByte(Addr, 1);  // 发送要写入数据的内存地址I2C_SendByte(Data, 1);  // 发送要写入的数据I2C_Stop();
}/**功能:AT24C02读取一个字节*参数:Addr:读取内容存放的内存地址*返回值:读取到的内容*/
uchar AT24C02_ReadByte(uchar Addr)
{uchar Data = 0;I2C_Start();I2C_SendByte(0xA0, 1);  // 发送AT24C02写地址I2C_SendByte(Addr, 1);  // 发送读取内容存放地址I2C_Start();I2C_SendByte(0xA1, 1);  // 发送AT24C02读地址Data = I2C_ReadByte();  // 读取数据I2C_Stop();return Data;
}

(7)at24c02.h

#ifndef __AT24C02_H__
#define __AT24C02_H__#include "drv_i2c.h"/*AT24C02写入一个字节*/
void AT24C02_WriteByte(uchar Addr, uchar Data);
/*AT24C02读取一个字节*/
uchar AT24C02_ReadByte(uchar Addr);#endif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_104148.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

毕业-单片机-嵌入式~三年经历回顾

入行嵌入式软件开发 20年6月疫情第一次缓和、实操51单片机&#xff1b;20年9月郑州实习、温湿度采集类低功耗产品、初次接触ARM Cortex M0/M3 单片机&#xff1b;21年5月毕业来到杭州、不懂应届生的宝贵青春&#xff01;匆匆忙忙进厂&#xff5e;人生中第一个项目&#xff1a;…

简单理解内存分页机制

文章目录 1.CPU寻址方式2.段式内存访问的缺点3.80386两级页表4.PAE三级页表5.x64四级页表6.虚拟内存 思考一个问题&#xff1a;如果没有这样的分页机制时应用程序是怎么访问物理内存地址&#xff1f; 1.CPU寻址方式 Effective Address Base (Index * Scale) Displacement …

CAD DLL 15 crack增加了对SLDASM、FSAT

CAD DLL 15 crack增加了对SLDASM、FSAT 改进的3D&#xff1a; 提高了打开三维文件的速度。 提高了SAT、STEP、SLDPRT、X_T、X_B、OBJ格式的阅读能力。 增加了对SLDASM、FSAT、SAB、SMT、IPT、IFC格式的支持。 增加了导出为SAT、SAB、STL、OBJ格式的功能。 改进了SAT、STE…

在f1tenth仿真中如何实现更快速的跑圈-曲线分析篇

本文使用蓝桥云课&#xff0c;即开即用&#xff0c;如果配置第三方课程资源&#xff0c;通常也在10分钟内完成。 效果如下&#xff1a; 全部参考资料如下&#xff1a; 蓝桥ROS之f1tenth案例学习与调试&#xff08;失败&#xff09; 蓝桥ROS之f1tenth案例学习与调试&#xff…

Salesforce官方_中文学习、考证资源

Salesforce将Trailhead描述为学习热门技能的有趣且免费的平台。该平台有助于缩小技能差距&#xff0c;是所有Salesforce用户的宝藏资源。 Trailhead适合所有学习者。它涵盖了适用于Salesforce任何角色的主题和学习模块&#xff0c;从管理员、开发人员、销售主管到最终用户。学…

第7章 “字典”

1.字典简介 字典是什么&#xff1f; 解答&#xff1a;与集合类似&#xff0c;也是一种存储唯一值的数据结构&#xff0c;但它是以键值对的形式来存储。(键值对是最重要的特性)在Es6中新增了字典&#xff0c;名为Map字典的常用操作&#xff1a;增删改查 const map new Map()/…

免费ChatGPT接入网站-网站加入CHATGPT自动生成关键词文章排名

网站怎么接入chatGPT 要将ChatGPT集成到您的网站中&#xff0c;需要进行以下步骤&#xff1a; 注册一个OpenAI账户&#xff1a;访问OpenAI网站并创建一个账户。这将提供访问API密钥所需的身份验证凭据。 获取API密钥&#xff1a;在您的OpenAI控制台中&#xff0c;您可以找到您…

OSCP-Nickel(爆破pdf、本地http提权)

目录 扫描 HTTP 提权 扫描 FileZilla不接受匿名FTP登录。 端口21上的SSH和3389上的RDP很少是初始入口点,但是如果遇到一些凭据,可以记住这一点。 HTTP 打开Web浏览器并导航到端口8089和3333,用于的HTTP服务器。端口8089似乎是某种类型的开发环境。 单击一个按钮重定向到…

Tomcat 配置与部署

http 协议就是 http 客户端和 http 服务器之间通信的协议 , 而Tomcat 就是 java 圈子中最广泛使用的 http 服务器. 下载Tomcat Tomcat官网 Tomcat 的版本 , 和后续的 servlet 版本是强相关的 , 此处使用 tomcat 8 , 对应的 servlet 就是 3.1 下载一个 zip 压缩包解压缩即可 T…

输入 jupyter notebook 报错 ModuleNotFoundError: No module named ‘pysqlite2‘ 解决方案

今天在cmd命令行中输入jupyter notebook想要打开jupyter时&#xff0c;出现了以下问题&#xff1a;即找不到模块‘pysqlite2’。 找到出问题的文件“sessionmanager.py”&#xff0c;发现出问题的地方在于&#xff1a;尝试导入sqlite3没有导致失败 因此&#xff0c;以下是解决…

java反转字符串的方法

1、首先我们定义一个方法&#xff0c;用来反转字符串。用 public static void &#xff08;String&#xff09;方法初始化一个对象&#xff0c;然后使用 private static &#xff08;&#xff09;方法对该对象进行初始化&#xff0c;并检查是否有某个字符串和字符串本身相同&am…

复现Nginx 解析漏洞

目录 漏洞原理 漏洞复现 编译环境 制作图片马 一&#xff1a;随便弄一张图片 二&#xff1a;准备写一个.php文件&#xff0c;写上木马 三&#xff1a;合成图片马 上传图片马 修复漏洞 漏洞原理 1、 由于nginx.conf的如下配置导致nginx把以’.php’结尾的文件交给fast…

科大讯飞版ChatGPT开始内测《讯飞星火》

科大讯飞版ChatGPT产品&#xff0c;提前交卷了&#xff01; 就在昨夜&#xff0c;讯飞骤然向开发者提供了内测通道&#xff0c;取名为讯飞星火认知大模型对外开启内测。 还有个神奇的英文名字Spark Desk&#xff0c;据说有“火花桌面智能助手”的意思。 申请的过程很简单。用…

leetcode21 - - 合并两个有序链表

文章目录 1.题目描述2.解题思路方法1&#xff1a;方法2&#xff1a; 1.题目描述 题目链接&#xff1a;力扣21&#xff0c;合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 2.解题思路 方法1&#xff1a;…

Docker安装mysql8.0文档

第一步需要安装Docker基础环境&#xff0c;具体可以看看这篇 docker基础篇 第二步&#xff0c;拉取mysql8.0的镜像 docker pull mysql:8.0 第三步&#xff0c;镜像启动和文件挂载 复制下面命令执行&#xff0c;33006是对外访问暴露的端口&#xff0c;当然你也可以设置为3306…

这个假期有这些游戏就不怕无聊了

1、塞尔达传说旷野之息 Switch端的优秀游戏体验不容错过&#xff01; 人气王《塞尔达传说》&#xff01; 被玩家誉为“唯一让人长大后有种回到童年的感觉的作品”。 豆瓣网友写道&#xff1a;“在雨夜&#xff0c;我在寺庙里看到了一条白龙划过天空&#xff0c;在岩壁上看到了…

SpringBoot拦截器的使用

Hi I’m Shendi SpringBoot拦截器的使用 简介 最近要实现一个全局对象的传递&#xff0c;在接口中直接通过增加函数参数来直接使用的这种方式 之前一直使用的是过滤器&#xff0c;但这种需求过滤器是没有办法实现的&#xff0c;过滤器可以给请求注入字符串&#xff0c;但不能…

JavaEE初阶学习:初识网络

1.网络发展史 1.独立模式 独立模式:计算机之间相互独立&#xff1b; 2.网络互连 随着时代的发展&#xff0c;越来越需要计算机之间互相通信&#xff0c;共享软件和数据&#xff0c;即以多个计算机协同工作来完成业务&#xff0c;就有了网络互连。 网络互连&#xff1a;将多…

yolov5半自动打标签(opencv版本),识别目标画框并将坐标信息保存在xml中

文章目录 1.yolov5预训练模型推理2. opencv边缘检测结果展示 yolov5训练数据集时&#xff0c;需要对数据进行打标签&#xff0c;可以通过两种方法进行半自动化打标签。 1.yolov5预训练模型推理 yolov5预训练模型&#xff1a;将待打标签的图片输入预训练模型中进行推理&#xf…

深入分析,Redis为什么这么快?

我们都知道Redis很快&#xff0c;它QPS可达10万&#xff08;每秒请求数&#xff09; Redis为什么这么快&#xff1f; 基于内存实现高效的数据结构合理的数据编码合理的线程模型虚拟内存机制 基于内存实现 我们都知道内存读写是比磁盘读写快很多的。Redis是基于内存存储实现的…