您现在的位置是:首页 >技术教程 >51单片机(十二)AT24C02(I2C)网站首页技术教程

51单片机(十二)AT24C02(I2C)

乘凉~ 2024-06-17 10:14:38
简介51单片机(十二)AT24C02(I2C)

❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分;建议先学习51单片机,其是STM32等高级单片机的基础;这样再学习STM32时才能融会贯通。
☀️ 专栏适用人群 :适用于想要从零基础开始学习入门单片机,且有一定C语言基础的的童鞋。
?专栏目标:实现从零基础入门51单片机和STM32单片机,力求在玩好单片机的同时,能够了解一些计算机的基本概念,了解电路及其元器件的基本理论等。

⭐️ 专栏主要内容: 主要学习51单片机的功能、各个模块、单片机的外设、驱动等,最终玩好单片机和单片机的外设,全程手敲代码,实现我们所要实现的功能。
? 专栏说明 :如果文章知识点有错误的地方,欢迎大家随时在文章下面评论,我会第一时间改正。让我们一起学习,一起进步。
?专栏主页:http://t.csdn.cn/HCD8v

本学习过程参考:https://space.bilibili.com/383400717

单片机安装软件、各种资料以及源码的路径:
https://pan.baidu.com/s/1vDTN2o8ffvczzNQGfyjHng
提取码:gdzf

本节主要介绍学习AT24C02(I2C)的相关知识,包括AT24C02(I2C)基础知识介绍、本节目标等;基础知识包括存储器介绍、AT24C02介绍、I2C总线介绍、AT24C02数据帧;并利用两个小实验来写程序进行练习,分别是AT24C02数据存储以及秒表(定时器扫描按键数码管),最后附上相关代码。

一、AT24C02(I2C)和本节目标

1.1 AT24C02(I2C)基础知识

1.1.1 存储器介绍

在这里插入图片描述
存储器分为RAM和ROM两种;RAM表示随机存储器;ROM表示只读存储器;

  • RAM的特点是存储速度特别快,但是掉电会丢失;
  • ROM的存储速度比较慢,但是特点是掉电后不会丢失。

所以一般都是两者结合使用,他们各自的种类如上图所示。

存储器的简化模型
在这里插入图片描述
存储器内部分为地址总线(横向)和数据总线(纵向);

当第一根地址总线被给到高电平1时,表示被选中,此时如果将纵向的第1,2,3根线连上,其他线不连,则表示第一根地址总线存储的数据为1110 0000 ,如下图所示:

在这里插入图片描述

当然纵向的数据总线并不是直接连在地址总线上的,为了防止各个行之间的互相干扰,而是通过二极管的方式,如图中右侧的Mask ROM和PROM,二极管可以保证电流只是一个方向;

在这里插入图片描述
图中的蓝色二极管比较特殊,其很容易被击穿;当断开时,两个二极管都是断开;但是当有高电压过来时,蓝色二极管被击穿,也就成了右边的短路接通状态;因此PROM只能写入数据一次,就是因为蓝色二极管被击穿后无法恢复,只能被击穿一次;(所以单片机下载程序的过程也被成为烧录,就是烧毁的意思);

1.1.2 AT24C02介绍

在这里插入图片描述

AT24C02是一种掉电不丢失的存储器;其存储介质是E2PROM(电可擦除可编程ROM);使用的通讯接口是I2C总线;

引脚和应用电路

在这里插入图片描述
WP是写保护接口。

内部结构框图

在这里插入图片描述

1.1.3 I2C总线介绍

详细的介绍参考视频:https://www.bilibili.com/video/BV1Mb411e7re?p=27&vd_source=e2638d12685eef84cda913d9d67be0a9第45分钟开始,建议详细看一下这一部分。

在这里插入图片描述

I2C电路规范

在这里插入图片描述
I2C总线的时序图:

在这里插入图片描述

在这里插入图片描述
绿色代表发送一个字节;

在这里插入图片描述

紫色代表接收一个字节;

在这里插入图片描述
黄色表示发送应答;黑色表示接收应答;0表示收到;

发送一帧数据
在这里插入图片描述

接收一帧数据
在这里插入图片描述

复合格式
在这里插入图片描述

在这里插入图片描述

手册上的时序图:

在这里插入图片描述

1.1.4 AT24C02数据帧

1.2 本节目标

目标1:AT24C02数据存储
刚开始hi初始化后,LCD1602上显示0000,每按下K1按键,数字加一,即变成0001,再按下变成0002;按下K2时数字减1;0000减1是65535;按下K3按键是将当前数字保存下来;即使断电再上电后,按下K4按键也能恢复上次保存的数字。

在这里插入图片描述

目标2:秒表(定时器扫描按键数码管)

目标2实现一个秒表,按下K1按键,秒表开始工作:
在这里插入图片描述
再按下K1,秒表暂停;按下K2,秒表清零

在这里插入图片描述
按下K3,保存当前数字,
在这里插入图片描述

然后断电,再重新上电后,按下K4,恢复原来保存的数字

在这里插入图片描述

二、AT24C02数据存储

代码路径:51单片机入门教程资料课件及程序源码程序源码KeilProject12-1 AT24C02数据存储
具体代码:

#include <REGX52.H>
#include "LCD1602.h"
#include "Key.h"
#include "AT24C02.h"
#include "Delay.h"

unsigned char KeyNum;
unsigned int Num;

void main()
{
	LCD_Init();
	LCD_ShowNum(1,1,Num,5);
	while(1)
	{
		KeyNum=Key();
		if(KeyNum==1)	//K1按键,Num自增
		{
			Num++;
			LCD_ShowNum(1,1,Num,5);
		}
		if(KeyNum==2)	//K2按键,Num自减
		{
			Num--;
			LCD_ShowNum(1,1,Num,5);
		}
		if(KeyNum==3)	//K3按键,向AT24C02写入数据
		{
			AT24C02_WriteByte(0,Num%256);
			Delay(5);
			AT24C02_WriteByte(1,Num/256);
			Delay(5);
			LCD_ShowString(2,1,"Write OK");
			Delay(1000);
			LCD_ShowString(2,1,"        ");
		}
		if(KeyNum==4)	//K4按键,从AT24C02读取数据
		{
			Num=AT24C02_ReadByte(0);
			Num|=AT24C02_ReadByte(1)<<8;
			LCD_ShowNum(1,1,Num,5);
			LCD_ShowString(2,1,"Read OK ");
			Delay(1000);
			LCD_ShowString(2,1,"        ");
		}
	}
}

AT24C02.c:AT24C02存储的相关操作

#include <REGX52.H>
#include "I2C.h"

#define AT24C02_ADDRESS		0xA0

/**
  * @brief  AT24C02写入一个字节
  * @param  WordAddress 要写入字节的地址
  * @param  Data 要写入的数据
  * @retval 无
  */
void AT24C02_WriteByte(unsigned char WordAddress,Data)
{
	I2C_Start();
	I2C_SendByte(AT24C02_ADDRESS);
	I2C_ReceiveAck();
	I2C_SendByte(WordAddress);
	I2C_ReceiveAck();
	I2C_SendByte(Data);
	I2C_ReceiveAck();
	I2C_Stop();
}

/**
  * @brief  AT24C02读取一个字节
  * @param  WordAddress 要读出字节的地址
  * @retval 读出的数据
  */
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);
	I2C_ReceiveAck();
	Data=I2C_ReceiveByte();
	I2C_SendAck(1);
	I2C_Stop();
	return Data;
}

I2C.c:I2C总线的相关操作

#include <REGX52.H>

sbit I2C_SCL=P2^1;
sbit I2C_SDA=P2^0;

/**
  * @brief  I2C开始
  * @param  无
  * @retval 无
  */
void I2C_Start(void)
{
	I2C_SDA=1;
	I2C_SCL=1;
	I2C_SDA=0;
	I2C_SCL=0;
}

/**
  * @brief  I2C停止
  * @param  无
  * @retval 无
  */
void I2C_Stop(void)
{
	I2C_SDA=0;
	I2C_SCL=1;
	I2C_SDA=1;
}

/**
  * @brief  I2C发送一个字节
  * @param  Byte 要发送的字节
  * @retval 无
  */
void I2C_SendByte(unsigned char Byte)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		I2C_SDA=Byte&(0x80>>i);
		I2C_SCL=1;
		I2C_SCL=0;
	}
}

/**
  * @brief  I2C接收一个字节
  * @param  无
  * @retval 接收到的一个字节数据
  */
unsigned char I2C_ReceiveByte(void)
{
	unsigned char i,Byte=0x00;
	I2C_SDA=1;
	for(i=0;i<8;i++)
	{
		I2C_SCL=1;
		if(I2C_SDA){Byte|=(0x80>>i);}
		I2C_SCL=0;
	}
	return Byte;
}

/**
  * @brief  I2C发送应答
  * @param  AckBit 应答位,0为应答,1为非应答
  * @retval 无
  */
void I2C_SendAck(unsigned char AckBit)
{
	I2C_SDA=AckBit;
	I2C_SCL=1;
	I2C_SCL=0;
}

/**
  * @brief  I2C接收应答位
  * @param  无
  * @retval 接收到的应答位,0为应答,1为非应答
  */
unsigned char I2C_ReceiveAck(void)
{
	unsigned char AckBit;
	I2C_SDA=1;
	I2C_SCL=1;
	AckBit=I2C_SDA;
	I2C_SCL=0;
	return AckBit;
}

最终的效果就是目标1.

三、秒表(定时器扫描按键数码管)

代码路径:51单片机入门教程资料课件及程序源码程序源码KeilProject12-2 秒表(定时器扫描按键数码管)
具体代码:

#include <REGX52.H>
#include "Timer0.h"
#include "Key.h"
#include "Nixie.h"
#include "Delay.h"
#include "AT24C02.h"

unsigned char KeyNum;
unsigned char Min,Sec,MiniSec;
unsigned char RunFlag;

void main()
{
	Timer0_Init();
	while(1)
	{
		KeyNum=Key();
		if(KeyNum==1)			//K1按键按下
		{
			RunFlag=!RunFlag;	//启动标志位翻转
		}
		if(KeyNum==2)			//K2按键按下
		{
			Min=0;				//分秒清0
			Sec=0;
			MiniSec=0;
		}
		if(KeyNum==3)			//K3按键按下
		{
			AT24C02_WriteByte(0,Min);	//将分秒写入AT24C02
			Delay(5);
			AT24C02_WriteByte(1,Sec);
			Delay(5);
			AT24C02_WriteByte(2,MiniSec);
			Delay(5);
		}
		if(KeyNum==4)			//K4按键按下
		{
			Min=AT24C02_ReadByte(0);	//读出AT24C02数据
			Sec=AT24C02_ReadByte(1);
			MiniSec=AT24C02_ReadByte(2);
		}
		Nixie_SetBuf(1,Min/10);	//设置显示缓存,显示数据
		Nixie_SetBuf(2,Min%10);
		Nixie_SetBuf(3,11);
		Nixie_SetBuf(4,Sec/10);
		Nixie_SetBuf(5,Sec%10);
		Nixie_SetBuf(6,11);
		Nixie_SetBuf(7,MiniSec/10);
		Nixie_SetBuf(8,MiniSec%10);
	}
}

/**
  * @brief  秒表驱动函数,在中断中调用
  * @param  无
  * @retval 无
  */
void Sec_Loop(void)
{
	if(RunFlag)
	{
		MiniSec++;
		if(MiniSec>=100)
		{
			MiniSec=0;
			Sec++;
			if(Sec>=60)
			{
				Sec=0;
				Min++;
				if(Min>=60)
				{
					Min=0;
				}
			}
		}
	}
}

void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count1,T0Count2,T0Count3;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count1++;
	if(T0Count1>=20)
	{
		T0Count1=0;
		Key_Loop();	//20ms调用一次按键驱动函数
	}
	T0Count2++;
	if(T0Count2>=2)
	{
		T0Count2=0;
		Nixie_Loop();//2ms调用一次数码管驱动函数
	}
	T0Count3++;
	if(T0Count3>=10)
	{
		T0Count3=0;
		Sec_Loop();	//10ms调用一次数秒表驱动函数
	}
}

最终的效果是目标2。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。