单片机学习笔记---AT24C02数据存储

2024-02-13 09:44

本文主要是介绍单片机学习笔记---AT24C02数据存储,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

AT24C02数据存储

准备工作

代码讲解

I2C.c

模拟起始位置的时序

模拟发送一个字节的时序

模拟接收应答的时序

模拟接收一个字节的时序

模拟发送应答的时序

模拟结束位置的时序

I2C.h 

AT24C02.c

字节写:在WORD ADDRESS(字地址)处写入数据DATA

随机读:读出在WORD ADDRESS处的数据DATA

AT24C02.h

 main.c


上一节讲了AT24C02和I2C相关的工作原理,这一节开始代码演示!

准备工作

新创建一个工程:AT24C02数据存储

把要用到的程序模块添加进来,这些程序模块都是我前面的博客里演示过的了

然后新建文件main.c,  AT24C02.c,  AT24C02.h, I2C.c, I2C.h

代码讲解

接下来就开始代码讲解:

I2C.c

首先我们先写I2C.c

在这个文件里面我们是按照上一篇博客所讲的各部分时序来逐个定义函数,函数体的内容就是模拟每一部分时序写的。

首先我们得根据原理图重新定义一下引脚

#include <REGX52.H>sbit I2C_SCL=P2^1;//将P2^1重命名为I2C_SCL
sbit I2C_SDA=P2^0;//将P2^0重命名为I2C_SDA
模拟起始位置的时序

void I2C_Start(void)
{I2C_SDA=1;I2C_SCL=1;I2C_SDA=0;//SCL高电平期间,SDA从高电平切换到低电平I2C_SCL=0;
}
模拟发送一个字节的时序

void I2C_SendByte(unsigned char Byte)
{unsigned char i;for(i=0;i<8;i++)//一个字节循环8次发送8位数据{//SCL低电平期间,主机将数据位依次放到SDA线上(高位在前)I2C_SDA=Byte&(0x80>>i);//从最高位开始取出,依次右移一位,直到取到最低位I2C_SCL=1;//然后拉高SCL,从机将在SCL高电平期间读取数据位I2C_SCL=0;//发送完一个字节后拉低SCL(下降沿)}
}

 注意:SCL当VCC等于5V的情况下是1000kHz=1MHz,而我们单片机的IO口翻转一次最快也就1微秒(大于0.4微秒),就是500Hz,由此可见它的频率比IO口翻转一次的频率还要快,所以即使我们拉高SCL立马又拉低也不会影响它的最大时钟,它也能很快读取到数据。

模拟接收应答的时序

unsigned char I2C_ReceiveAck(void)
{unsigned char AckBit;I2C_SDA=1;//主机接收应答之前先把SDA拉高,I2C_SCL=1;//在SCL位高电平时,主机检测从机是否应答//接下来我们不管从机的时序是怎么变化的//所以这里我们没有在代码中体现从机是拉低了SDA还是默认SDA就是高电平//我们的单片机是主机,24C02是从机。//主机和从机的程序是不一样的,我们只写主机,从机是主动检测的,//从机读取数据的时候是程序自动完成的,//我们只需要把主机的时序模拟出来就好了。AckBit=I2C_SDA;//主机接收从机的应答//如果从机不想应答或者从机不存在就默认SDA还是高电平//从机应答的话就拉低了SDA赋值给AckBitI2C_SCL=0;return AckBit;
}
模拟接收一个字节的时序

上图SDA紫色部分就是从机控制总线的时候

unsigned char I2C_ReceiveByte(void)
{unsigned char i,Byte=0x00;I2C_SDA=1;//接收之前把SDA释放for(i=0;i<8;i++)//一个字节循环8次读取8位数据{//SCL低电平期间,从机将数据位依次放到SDA线上(高位在前)I2C_SCL=1;//然后拉高SCL,主机将在SCL高电平期间读取数据位if(I2C_SDA){Byte|=(0x80>>i);}//从最高位开始读,依次右移一位,直到读到最低位I2C_SCL=0;}return Byte;
}
模拟发送应答的时序

void I2C_SendAck(unsigned char AckBit)
{I2C_SDA=AckBit;//主机发送应答给从机I2C_SCL=1;//SCL高电平期间,从机检测主机是否应答I2C_SCL=0;
}
模拟结束位置的时序

void I2C_Stop(void)
{I2C_SDA=0;//不管主机/从机是否应答,都要拉低SDA。I2C_SCL=1;I2C_SDA=1;//SCL高电平期间,SDA从低电平切换到高电平
}

I2C.h 

最后在I2C.h文件中声明一下这六个函数: 

#ifndef __I2C_H__
#define __I2C_H__void I2C_Start(void);
void I2C_Stop(void);
void I2C_SendByte(unsigned char Byte);
unsigned char I2C_ReceiveByte(void);
void I2C_SendAck(unsigned char AckBit);
unsigned char I2C_ReceiveAck(void);#endif

AT24C02.c

接下来写AT24C02.c

AT24C02.c的内容主要是按我上一篇博客讲过的这个流程图来逐个调用I2C.c中的六个函数:

字节写:在WORD ADDRESS(字地址)处写入数据DATA

随机读:读出在WORD ADDRESS处的数据DATA(这其实是一种复合格式)

上一篇博客我写过AT24C02的固定地址为1010可配置地址本开发板上为000,所以从机的写地址SLAVE ADDRESS+W为0xA0,从机的读地址SLAVE ADDRESS+R为0xA1

所以我们可以先重定义从机的写地址,将从机的写地址重定义为AT24C02_ADDRESS,然后从机的读地址我们到时候直接给字节的最低位置1就可以了:

从机的写地址=0xA0=AT24C02_ADDRESS=1010 0000

从机的读地址=AT24C02_ADDRESS|0x01=1010 0000|0000 0001=1010 0001=0xA1

#include <REGX52.H>
#include "I2C.h"#define AT24C02_ADDRESS		0xA0 //将从机的写地址重定义为AT24C02_ADDRESS
字节写:在WORD ADDRESS(字地址)处写入数据DATA

按照字节写的流程图写函数体:

void AT24C02_WriteByte(unsigned char WordAddress,Data)//Data的类型和wordAddress一样
{I2C_Start();//起始位置I2C_SendByte(AT24C02_ADDRESS);//发送从机地址I2C_ReceiveAck();//接收应答I2C_SendByte(WordAddress);//发送字节地址I2C_ReceiveAck();//接收应答I2C_SendByte(Data);//发送数据I2C_ReceiveAck();//接收应答I2C_Stop();//结束位置
}
随机读:读出在WORD ADDRESS处的数据DATA

按照字节写的流程图写函数体:

unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{unsigned char Data;I2C_Start();//写的起始位置I2C_SendByte(AT24C02_ADDRESS);//发送从机地址I2C_ReceiveAck();//接收应答I2C_SendByte(WordAddress);//发送字节地址I2C_ReceiveAck();//接收应答I2C_Start();//读的起始位置I2C_SendByte(AT24C02_ADDRESS|0x01);//发送从机地址,将从机的写地址的最低位置1I2C_ReceiveAck();//接收应答//前面说过从机接收了什么数据怎么接收的数据我们在代码中不体现出来,//我们只写主机的程序Data=I2C_ReceiveByte();//我们只要把从机里面那个指定的字节地址处的数据读出来赋值给DataI2C_SendAck(1);//读取完一个字节可以不用再应答从机I2C_Stop();//结束位置return Data;//返回读出来的数据
}

AT24C02.h

声明一下这两个函数

#ifndef __AT24C02_H__
#define __AT24C02_H__void AT24C02_WriteByte(unsigned char WordAddress,Data);
unsigned char AT24C02_ReadByte(unsigned char WordAddress);#endif

 main.c

 接下来我们在主程序里实现在WORD ADDRESS(字地址)处写入数据DATA,然后读出在WORD ADDRESS处的数据DATA,最后在液晶屏上显示我们写入并读出来的数据,结合独立按键的功能完成这个效果

先定义两个变量:

#include <REGX52.H>
#include "LCD1602.h"
#include "Key.h"
#include "AT24C02.h"
#include "Delay.h"unsigned char KeyNum;//键码
unsigned int Num;//初值,16位数据,范围是0~65535

主程序(请认真结合注释理清每一句代码的逻辑意思)

void main()
{LCD_Init();LCD_ShowNum(1,1,Num,5);while(1){KeyNum=Key();if(KeyNum==1)	//K1按键,Num自增{Num++;//第一次就按K1的时候,由0变成1LCD_ShowNum(1,1,Num,5);}if(KeyNum==2)	//K2按键,Num自减{Num--;//第一次就按K2的时候,由0变成65535LCD_ShowNum(1,1,Num,5);}if(KeyNum==3)	//K3按键,向AT24C02写入数据{AT24C02_WriteByte(0,Num%256);//在字地址0处,写入Num的低八位//%256是16进制取低8位的方法//因为Num是unsigned int型占2个字节即16位数据//所以把Num的低8位取出来写入Delay(5);//写进去不能立马读出来,ROM一般要2~3ms才能写完//手册上的写周期是5ms,意味着我们每次写入之后需要Delay 5msAT24C02_WriteByte(1,Num/256);在字地址1处,写入Num的高八位// 或256是16进制取高8位的方法Delay(5);LCD_ShowString(2,1,"Write OK");Delay(1000);//延时1000ms=1sLCD_ShowString(2,1,"        ");//第2行第1列清屏}if(KeyNum==4)	//K4按键,从AT24C02读取数据{Num=AT24C02_ReadByte(0);//低八位的字地址是0,把低八位数据读出来赋值给NumNum|=AT24C02_ReadByte(1)<<8;//高八位的字地址1,把八位数据读出来每个左移8就是高八位LCD_ShowNum(1,1,Num,5);LCD_ShowString(2,1,"Read OK ");Delay(1000);LCD_ShowString(2,1,"        ");}}
}

注意:

写进去立马读出来能读到吗?

不能!

为什么不能?

我们看一下手册上的写周期是5ms

这个写周期意味着我们每次写入之后需要Delay 5ms,写的数据帧stop一旦结束,它内部要执行一些操作,把数据写出去。所以ROM要比RAM慢一些,因为ROM有个写入时间,它这个是最长5ms,经过实测写个数据大概两三毫秒就能写完,所以我们每次写入之后需要Delay 5ms。

效果展示

效果请看视频:

AT24C02数据存储

以上就是本篇内容!

之后有时间还会补充一个“秒表(定时器扫描按键数码管)”的示例代码,敬请关注!

源码会放在评论区,自取!如有问题可评论区留言。

这篇关于单片机学习笔记---AT24C02数据存储的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/705172

相关文章

Python获取中国节假日数据记录入JSON文件

《Python获取中国节假日数据记录入JSON文件》项目系统内置的日历应用为了提升用户体验,特别设置了在调休日期显示“休”的UI图标功能,那么问题是这些调休数据从哪里来呢?我尝试一种更为智能的方法:P... 目录节假日数据获取存入jsON文件节假日数据读取封装完整代码项目系统内置的日历应用为了提升用户体验,

C# WinForms存储过程操作数据库的实例讲解

《C#WinForms存储过程操作数据库的实例讲解》:本文主要介绍C#WinForms存储过程操作数据库的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、存储过程基础二、C# 调用流程1. 数据库连接配置2. 执行存储过程(增删改)3. 查询数据三、事务处

Java利用JSONPath操作JSON数据的技术指南

《Java利用JSONPath操作JSON数据的技术指南》JSONPath是一种强大的工具,用于查询和操作JSON数据,类似于SQL的语法,它为处理复杂的JSON数据结构提供了简单且高效... 目录1、简述2、什么是 jsONPath?3、Java 示例3.1 基本查询3.2 过滤查询3.3 递归搜索3.4

MySQL大表数据的分区与分库分表的实现

《MySQL大表数据的分区与分库分表的实现》数据库的分区和分库分表是两种常用的技术方案,本文主要介绍了MySQL大表数据的分区与分库分表的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有... 目录1. mysql大表数据的分区1.1 什么是分区?1.2 分区的类型1.3 分区的优点1.4 分

Mysql删除几亿条数据表中的部分数据的方法实现

《Mysql删除几亿条数据表中的部分数据的方法实现》在MySQL中删除一个大表中的数据时,需要特别注意操作的性能和对系统的影响,本文主要介绍了Mysql删除几亿条数据表中的部分数据的方法实现,具有一定... 目录1、需求2、方案1. 使用 DELETE 语句分批删除2. 使用 INPLACE ALTER T

Python Dash框架在数据可视化仪表板中的应用与实践记录

《PythonDash框架在数据可视化仪表板中的应用与实践记录》Python的PlotlyDash库提供了一种简便且强大的方式来构建和展示互动式数据仪表板,本篇文章将深入探讨如何使用Dash设计一... 目录python Dash框架在数据可视化仪表板中的应用与实践1. 什么是Plotly Dash?1.1

Redis 中的热点键和数据倾斜示例详解

《Redis中的热点键和数据倾斜示例详解》热点键是指在Redis中被频繁访问的特定键,这些键由于其高访问频率,可能导致Redis服务器的性能问题,尤其是在高并发场景下,本文给大家介绍Redis中的热... 目录Redis 中的热点键和数据倾斜热点键(Hot Key)定义特点应对策略示例数据倾斜(Data S

Python实现将MySQL中所有表的数据都导出为CSV文件并压缩

《Python实现将MySQL中所有表的数据都导出为CSV文件并压缩》这篇文章主要为大家详细介绍了如何使用Python将MySQL数据库中所有表的数据都导出为CSV文件到一个目录,并压缩为zip文件到... python将mysql数据库中所有表的数据都导出为CSV文件到一个目录,并压缩为zip文件到另一个

SpringBoot整合jasypt实现重要数据加密

《SpringBoot整合jasypt实现重要数据加密》Jasypt是一个专注于简化Java加密操作的开源工具,:本文主要介绍详细介绍了如何使用jasypt实现重要数据加密,感兴趣的小伙伴可... 目录jasypt简介 jasypt的优点SpringBoot使用jasypt创建mapper接口配置文件加密

使用Python高效获取网络数据的操作指南

《使用Python高效获取网络数据的操作指南》网络爬虫是一种自动化程序,用于访问和提取网站上的数据,Python是进行网络爬虫开发的理想语言,拥有丰富的库和工具,使得编写和维护爬虫变得简单高效,本文将... 目录网络爬虫的基本概念常用库介绍安装库Requests和BeautifulSoup爬虫开发发送请求解