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

news2024/11/18 3:45:36

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.000MHz


void 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.coloradmin.cn/o/451751.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

家庭智能开关通断—Homekit智能

智能通断器&#xff0c;也叫开关模块&#xff0c;可以非常方便地接入家中原有开关、插座、灯具、电器的线路中&#xff0c;通过手机App或者语音即可控制电路通断&#xff0c;轻松实现原有家居设备的智能化改造。 随着智能家居概念的普及&#xff0c;越来越多的人想将自己的家改…

Rust之泛型、特性和生命期(一):基本概念

开发环境 Windows 10Rust 1.69.0 VS Code 1.77.3 项目工程 这里继续沿用上次工程rust-demo 泛型、特性和生命期 每种编程语言都有有效处理概念重复的工具。在Rust中&#xff0c;一个这样的工具就是泛型&#xff1a;具体类型或其他属性的抽象替身。我们可以表达泛型的行为或…

IMX6ULL_Pro网卡配置

(15条消息) 【IMX6ULL_Pro板卡学习】配置 VMware 使用双网卡_vmware配置双网卡_爱学习的小莲的博客-CSDN博客https://blog.csdn.net/qq_52201194/article/details/127474352?ops_request_misc%257B%2522request%255Fid%2522%253A%2522168223524716800182730924%2522%252C%2522…

Windows逆向安全(一)之基础知识(十一)

二维数组 二维数组初始化 int arr[3][4]{{1,2,3,4},{5,6,7,8},{9,10,11,12} };查看反汇编 7: int arr[3][4]{ 8: {1,2,3,4}, 0040D498 mov dword ptr [ebp-30h],1 0040D49F mov dword ptr [ebp-2Ch],2 0040D4A6 mov dword …

CentOS(linux)使用Docker安装nacos

1. 拉取nacos镜像 docker pull nacos/nacos-server:2.0.3 2. 创建所需文件夹(以安装在home目录下为例) 1) 创建conf文件夹 mkdir -p /home/nacos/conf a. 新增文件application.properties(或者不增加该文件,会使用默认的) 文件内容如下: # spring server.servlet.contextP…

不知道玩什么游戏的你看过来

推荐一&#xff1a;原神 《原神》游戏设定在一个名为“蒂瓦特”的奇幻世界。 被神选中的人&#xff0c;将被赐予“神之眼”&#xff0c;引导“元素之力”&#xff0c;成为“旅者”。 在旅途中&#xff0c;结识性格各异、才华横溢、能力各异的小伙伴。 我们将一起战胜强大的敌…

PyTorch实战2:彩色图片识别(CIFAR10)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f366; 参考文章&#xff1a;365天深度学习训练营-第P2周&#xff1a;彩色图片识别&#x1f356; 原作者&#xff1a;K同学啊|接辅导、项目定制 目录 一、数据准备二、构建简单CNN网络⭐1. torch…

图扑软件 | 数字孪生智慧水泥工厂

前言 近年来&#xff0c;随着我国经济的发展和人民生活水平的提高&#xff0c;我国对于水泥行业的关注程度也越来越高&#xff0c;为了保证水泥行业的健康稳定发展&#xff0c;许多地方都在大力推动水泥生产技术创新工作。当前水泥行业的发展正处于新旧动能更迭的关键阶段&…

JavaWeb开发 —— SpringBootWeb综合案例

通过综合案例&#xff0c;我们来了解和学习在开发Web程序时&#xff0c;前端程序和后端程序以及数据库三者之间是如何交互、如何协作的&#xff0c;而通过综合案例也来掌握通过接口文档开发服务端接口的能力。 目录 一、准备工作 1. 需求说明 2. 环境搭建 3. 开发规范 二…

Postgis导出shp和gdb数据库(Postgre入门九)

背景 有时候我们需要将postgis数据库中的空间数据表导出shp格式,而PG自带的PostGIS Shapefile Import/Export Manager 导出shp大部分时候是可以用的,但是它有个缺点是,当shp字段名称超过10个字节时,字段会被切掉,如字段“afdskskkfkfjdj”被切掉后是“afdskskkfk”,所以…

文案自动修改软件-文案自动改写的免费软件下载

文章生成器ai写作机器人 随着人工智能技术的飞速发展&#xff0c;越来越多的新型产品被推向市场。其中&#xff0c;文章生成器AI写作机器人是一个备受关注的新兴行业。它使用机器学习和自然语言处理等技术&#xff0c;为用户自动生成高质量的文章和内容&#xff0c;帮助用户在…

基于OpenCV与深度神经网络——实现证件识别扫描并1比1还原证件到A4纸上

前言 1.用拍照的证件照片正反面&#xff0c;实现用证件去复印到A4纸上的效果&#xff0c;还有证件的格式化识别。 图1&#xff1a;把拍照的证件1比1还原证件到A4纸上 图2&#xff1a;证件OCR格式化识别 2.使用Yolo做目标识别,Enet做边缘检测&#xff0c;Paddle OCR做文字识别&…

【数据结构与算法】常用数据结构(一)

&#x1f600;大家好&#xff0c;我是白晨&#xff0c;一个不是很能熬夜&#xff0c;但是也想日更的人✈。如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff0c;关注一下&#x1f440;白晨吧&#xff01;你的支持就是我最大的动力&#xff01;&#x1f4aa;&#x1f4…

燃气管道定位83KHZ地下电子标识器探测仪ED-8000操作说明1

1、功能简要说明 ED-8000地下电子标识器探测仪是华翔天诚推出的一款可支持模拟电子标识器&#xff08;无 ID&#xff09;探测和数字 ID 电子标识器 探测两种工作模式&#xff0c;在模拟电子标识器&#xff08;无 ID&#xff09;探测模式下&#xff0c;可探测 所有按标准频率生…

Unity-ML-Agents安装

目录 1.下载ML-Agents 1.1 前往官网 1.2 选择版本 1.3 下载文件 2.下载Anaconda 3.虚拟环境 3.1 构建虚拟环境 3.2 创建项目&#xff0c;导入package.json 3.2.1 创建项目&#xff0c;导入package.json 3.2.2 导入成功 3.2.3 将模板项目拖入unity项目中 3.3 开始训练 …

低代码感觉很能打——可视化搭建系统,把格局做大

有人说「可视化搭建系统」说到底只是重复造轮子产生的玩具&#xff1b; 有人说「可视化搭建系统」本质是组件枚举&#xff0c;毫无意义。 片面的认知必有其产生的道理&#xff0c;但我们不妨从更高的角度出发&#xff0c;并真切落地实践&#xff0c;也许你会发现&#xff1a;我…

Java面试题总结 | Java面试题总结5- 数据结构模块(持续更新)

数据结构 文章目录 数据结构顺序表和链表的区别HashMap 和 Hashtable 的区别Java中用过哪些集合&#xff0c;说说底层实现&#xff0c;使用过哪些安全的集合类Java中线程安全的基本数据结构有哪些ArrayList、Vector和LinkedList有什么共同点与区别&#xff1f;ArrayList和Linke…

怎样正确做web应用的压力测试?

web应用&#xff0c;通俗来讲就是一个网站&#xff0c;主要依托于浏览器实现其功能。 提到压力测试&#xff0c;我们想到的是服务端压力测试&#xff0c;其实这是片面的&#xff0c;完整的压力测试包含服务端压力测试和前端压力测试。 下文将从以下几部分内容展开&#xff1a…

源码简读 - AlphaFold2的2.3.2版本源码解析 (1)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://blog.csdn.net/caroline_wendy/article/details/130323566 时间&#xff1a;2023.4.22 官网&#xff1a;https://github.com/deepmind/alphafold AlphaFold2是一种基于深度学习的方法…

torch中fft和ifft新旧版本使用

pytorch旧版本&#xff08;1.7之前&#xff09;中有一个函数torch.rfft()&#xff0c;但是新版本&#xff08;1.8、1.9&#xff09;中被移除了&#xff0c;添加了torch.fft.rfft()&#xff0c;但它并不是旧版的替代品。 torch.fft label_fft1 torch.rfft(label_img4, signal…