单片机学习笔记---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

相关文章

SQL Server修改数据库名及物理数据文件名操作步骤

《SQLServer修改数据库名及物理数据文件名操作步骤》在SQLServer中重命名数据库是一个常见的操作,但需要确保用户具有足够的权限来执行此操作,:本文主要介绍SQLServer修改数据... 目录一、背景介绍二、操作步骤2.1 设置为单用户模式(断开连接)2.2 修改数据库名称2.3 查找逻辑文件名

canal实现mysql数据同步的详细过程

《canal实现mysql数据同步的详细过程》:本文主要介绍canal实现mysql数据同步的详细过程,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的... 目录1、canal下载2、mysql同步用户创建和授权3、canal admin安装和启动4、canal

MySQL存储过程之循环遍历查询的结果集详解

《MySQL存储过程之循环遍历查询的结果集详解》:本文主要介绍MySQL存储过程之循环遍历查询的结果集,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言1. 表结构2. 存储过程3. 关于存储过程的SQL补充总结前言近来碰到这样一个问题:在生产上导入的数据发现

使用SpringBoot整合Sharding Sphere实现数据脱敏的示例

《使用SpringBoot整合ShardingSphere实现数据脱敏的示例》ApacheShardingSphere数据脱敏模块,通过SQL拦截与改写实现敏感信息加密存储,解决手动处理繁琐及系统改... 目录痛点一:痛点二:脱敏配置Quick Start——Spring 显示配置:1.引入依赖2.创建脱敏

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

详解如何使用Python构建从数据到文档的自动化工作流

《详解如何使用Python构建从数据到文档的自动化工作流》这篇文章将通过真实工作场景拆解,为大家展示如何用Python构建自动化工作流,让工具代替人力完成这些数字苦力活,感兴趣的小伙伴可以跟随小编一起... 目录一、Excel处理:从数据搬运工到智能分析师二、PDF处理:文档工厂的智能生产线三、邮件自动化:

Python数据分析与可视化的全面指南(从数据清洗到图表呈现)

《Python数据分析与可视化的全面指南(从数据清洗到图表呈现)》Python是数据分析与可视化领域中最受欢迎的编程语言之一,凭借其丰富的库和工具,Python能够帮助我们快速处理、分析数据并生成高质... 目录一、数据采集与初步探索二、数据清洗的七种武器1. 缺失值处理策略2. 异常值检测与修正3. 数据

pandas实现数据concat拼接的示例代码

《pandas实现数据concat拼接的示例代码》pandas.concat用于合并DataFrame或Series,本文主要介绍了pandas实现数据concat拼接的示例代码,具有一定的参考价值,... 目录语法示例:使用pandas.concat合并数据默认的concat:参数axis=0,join=

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

C#代码实现解析WTGPS和BD数据

《C#代码实现解析WTGPS和BD数据》在现代的导航与定位应用中,准确解析GPS和北斗(BD)等卫星定位数据至关重要,本文将使用C#语言实现解析WTGPS和BD数据,需要的可以了解下... 目录一、代码结构概览1. 核心解析方法2. 位置信息解析3. 经纬度转换方法4. 日期和时间戳解析5. 辅助方法二、L