一.AT24C02的使用
1.AT24C02基本介绍
- AT24C02是一种可以实现掉电不丢失的存储器,可用于保存单片机运行时想要永久保存的数据信息
- 存储介质:E2PROM
- 通讯接口:I2C总线
2.CT107D中AT24C02引脚 介绍![](https://i-blog.csdnimg.cn/blog_migrate/53ce6db51e38373e1afd72cd9492553e.png)
图中A0 A1 A2决定着设备在iic中的地址
设备地址
由原理图并结合数据手册可是可知在CT107D中 读的地址为0xa1,写的地址为0xa0 scl为P2^0,sda为P2^1(在iic.c中补全)
3.AT24C02数据帧
①字节写![](https://i-blog.csdnimg.cn/blog_migrate/7e6248fef93007ec73c5c5ea5c002e28.png)
由手册可知,字节写操作如下:
- 主机(MCU)发送start信号
- 主机(MCU)发送设备地址并接收应答(write为低电平)
- 主机(MCU)发送数据要送往的地址并接收应答
- 主机(MCU)发送数据并接收应答
- 主机发送(MCU)发送stop信号
void Write_24C02(unsigned char addr,unsigned char dat)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
注意: 数据写入指令发送后需要等待一段时间(不少于50ms)才能进行下一次字节写,否则会造成写入失败(若不是连续写可不注意)
② 页写
一般来说,CT107D上的AT24C02一个地址位只能写一个8位数据,如果想存储16位(或以上)的数据,除了多次写入外,也可进行也写的操作
对照手册我们可以得知大致步骤:
- 主机(MCU)发送start信号
- 主机(MCU)发送设备地址并接收应答(write为低电平)
- 主机(MCU)发送数据要送往的地址并接收应答
- 主机(MCU)发送每一位数据并接收应答,直到最后一个数据
- 在接受最后一个应答信号后,发送stop信号
unsigned int temp //以发送无符号整型为例
void AT24C02_PageWrite()
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(0x01);
I2CWaitAck();
I2CSendByte((temp)>>8);//发送高8位
I2CWaitAck();
I2CSendByte((temp));//发送低8位
I2CWaitAck();
I2CStop();
}
③随机读
随机读并不是指在所有的地址中随机挑一个地址进行读取,而是按照给定的地址读取其中的数据。
由数据手册可知,在进行读操作之前先要进行一次“伪写”的操作:
- 主机(MCU)发送start信号
- 主机(MCU)发送设备地址并接收应答(write置低电平`)
- 主机(MCU)发送要读取的地址并接收应答
之后便开始正式读取数据
- 主机(MCU)发送start信号
- 主机(MCU)发送设备地址并接收应答(read置高电平)
- MCU接收数据并向AT24C02发送应答信号"1"(此时AT24C02作为主机接收应答信号)
- 主机(MCU)发送STOP信号
unsigned char Read_24C02(unsigned char addr)
{
unsigned char temp=0;
I2CStart(); //伪写操作
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
temp=I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return temp;
}
④顺序读
对照手册可知,顺序读可发送在随机读 和当前地址读之后,与上述两个不同的时,在接受第一个数据后发送应答信号(0),继续接受下一个数据,直到接受到最后一个数据,发送非应答信号(1),并发送stop信号
unsigned int temp
void AT24C02_SequentialRead()
{
unsigned char LSB,MSB;
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(0x01);
I2CWaitAck();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
MSB=I2CReceiveByte();//接受高8位
I2CSendAck(0);
LSB=I2CReceiveByte();//接受低8位
I2CSendAck(1);
I2CStop();
temp=(MSB<<8)|LSB;复原16位数据
}
注:代码为新驱动代码,详情请参考这篇文章:
蓝桥杯学习笔记 单片机CT107D 新驱动代码的使用.-CSDN博客
二.练习
这里借用一些小蜜蜂老师的题目
代码参考:
main.c
#include <REGX52.H>
#include "iic.h"
code unsigned char SMG_NoDot[] =
{
0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83, 0xc6,0xa1,0x86,0x8e};
unsigned char data1=0,data2=0,data3=0;
void Delay_SMG(unsigned int t)
{
while(t--);
}
void Select_HC573(unsigned char channel,unsigned char dat)
{
switch(channel)
{
case 4:
P2=(P2&0x1f)|0x80;
break;
case 5:
P2=(P2&0x1f)|0xa0;
break;
case 6:
P2=(P2&0x1f)|0xc0;
break;
case 7:
P2=(P2&0x1f)|0xe0;
break;
case 0:
P2=(P2&0x1f)|0x00;
break;
}
P0=dat;
P2=(P2&0x1f)|0x00;
}
void SMG_Display(unsigned char pos,unsigned char dat)
{
Select_HC573(6,0x01<<(pos-1));
Select_HC573(7,dat);
Delay_SMG(500);
Select_HC573(6,0x01<<(pos-1));
Select_HC573(7,0xff);
}
void SMG_Dynamic()
{
SMG_Display(1,SMG_NoDot[data1/10]);
SMG_Display(2,SMG_NoDot[data1%10]);
SMG_Display(3,0xbf);
SMG_Display(4,SMG_NoDot[data2/10]);
SMG_Display(5,SMG_NoDot[data2%10]);
SMG_Display(6,0xbf);
SMG_Display(7,SMG_NoDot[data3/10]);
SMG_Display(8,SMG_NoDot[data3%10]);
}
unsigned char Read_24C02(unsigned char addr)
{
unsigned char temp=0;
I2CStart(); //伪写操作
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
temp=I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return temp;
}
void Write_24C02(unsigned char addr,unsigned char dat)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
void Scan_Key()
{
if(P3_3==0)
{
Delay_SMG(100);
if(P3_3==0)
{
data1++;
if(data1>13)
{
data1=0;
}
Write_24C02(0x00,data1);//因为不是连续写入可以不延时
}
while(P3_3==0)
{
SMG_Dynamic();
}
}
if(P3_2==0)
{
Delay_SMG(100);
if(P3_2==0)
{
data2++;
if(data2>13)
{
data2=0;
}
Write_24C02(0x01,data2);
}
while(P3_2==0)
{
SMG_Dynamic();
}
}
if(P3_1==0)
{
Delay_SMG(100);
if(P3_1==0)
{
data3++;
if(data3>13)
{
data3=0;
}
Write_24C02(0x02,data3);
}
while(P3_1==0)
{
SMG_Dynamic();
}
}
}
void Sys_Init()
{
Select_HC573(4,0xff);
Select_HC573(5,0x00);
//数据清零
// Write_24C02(0x00,0);
// Delay_SMG(1500);//延时,防止数据写入失败
// Write_24C02(0x01,0);
// Delay_SMG(1500);
// Write_24C02(0x02,0);
// Delay_SMG(1500);
data1= Read_24C02(0x00);
data2= Read_24C02(0x01);
data3= Read_24C02(0x02);
}
void main()
{
Sys_Init();
while(1)
{
SMG_Dynamic();
Scan_Key();
}
}
iic.h
#ifndef __IIC_H
#define __IIC_H
static void I2C_Delay(unsigned char n);
void I2CStart(void);
void I2CStop(void);
void I2CSendByte(unsigned char byt);
unsigned char I2CReceiveByte(void);
unsigned char I2CWaitAck(void);
void I2CSendAck(unsigned char ackbit);
#endif
iic.c
/* # I2C代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include <REGX52.H>
#include "intrins.h"//可使用nop代码
#define DELAY_TIME 5
sbit sda=P2^1;//对照原理图进行补全
sbit scl=P2^0;
//
static void I2C_Delay(unsigned char n)
{
do
{
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
}
while(n--);
}
//
void I2CStart(void)
{
sda = 1;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 0;
I2C_Delay(DELAY_TIME);
scl = 0;
}
//
void I2CStop(void)
{
sda = 0;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 1;
I2C_Delay(DELAY_TIME);
}
//
void I2CSendByte(unsigned char byt)
{
unsigned char i;
for(i=0; i<8; i++){
scl = 0;
I2C_Delay(DELAY_TIME);
if(byt & 0x80){
sda = 1;
}
else{
sda = 0;
}
I2C_Delay(DELAY_TIME);
scl = 1;
byt <<= 1;
I2C_Delay(DELAY_TIME);
}
scl = 0;
}
//
unsigned char I2CReceiveByte(void)
{
unsigned char da;
unsigned char i;
for(i=0;i<8;i++){
scl = 1;
I2C_Delay(DELAY_TIME);
da <<= 1;
if(sda)
da |= 0x01;
scl = 0;
I2C_Delay(DELAY_TIME);
}
return da;
}
//
unsigned char I2CWaitAck(void)
{
unsigned char ackbit;
scl = 1;
I2C_Delay(DELAY_TIME);
ackbit = sda;
scl = 0;
I2C_Delay(DELAY_TIME);
return ackbit;
}
//
void I2CSendAck(unsigned char ackbit)
{
scl = 0;
sda = ackbit;
I2C_Delay(DELAY_TIME);
scl = 1;
I2C_Delay(DELAY_TIME);
scl = 0;
sda = 1;
I2C_Delay(DELAY_TIME);
}