文章目录[隐藏]
1、AT24C02简介
AT24C02芯片是一种EEPROM器件。EEPROM是电可擦除可编程只读存储器,是ROM的一种。它是只读存储器,即掉电可继续存储数据,而同时又可以在高于普通电压的作用下擦除和重写。
AT24C02:256字节(256×8位);
电路连接图
2、通信方式
和学过的PCF8591(没看过的可以点我)一样,它也是挂接在P20、P21这条IIC总线上的,也就是说,AT24C02的通信方式也是IIC通信(详情)。
2.1器件地址
1、我看器件地址有啥用??
所有采用IIC通信的设备都必须要有自己的一个地址,IIC总线才能知道这个器件在哪,它是一个8位的地址。就好比我们的身份证号码一样。
2、器件地址怎么知道??
难道这玩意是自己编吗??不完全是的,
我们先看它与单片机的连接图 CT107D单片机AT24C02电路连接图:
A0、A1、A3:组成器件地址的低3位。前四位固定为1001。最后一位是方向位W/R,
A0、A1和A3可用于硬件地址编程,也就是编写器件地址第2、3、4位。最后一位地址的最后一位为方向位R/W ,当主控器对A/D器件进行读操作时为1,进行写操作时为0。前四位固定为1001,因此允许在同个I2C总线上接入8个PCF8591器件,而无需额外的硬件。
即0XA0——写操作地址+器件地址 0XA1——读操作地址+器件地址
3、AT24C02写入与读取
3.1写入操作流程
我怎么才能写东西进去??我可以写在哪??我能写多少??
第一步启动IIC,然后发送器件地址,(我要找到这个器件)
我们已经知道,AT24C02的通信方式是IIC通信,因此就要遵循IIC的规定来操作,要先找到器件在哪,然后和它建立通信。
——0XA0、0XA1都是AT24C02的器件地址,有什么区别呢?
最后一位地址的最后一位为方向位R/W ,当主控器对A/D器件进行读操作时为1,进行写操作时为0。因此0XA0是写操作。0XA1是写操作。
因此我们这里发送0XA0;>
IIC_SendByte(0xA0);//向器件发送写命令+器件地址
IIC_WaitAck();//向单片机发送应答信号
第二步,我要从哪开始存??
首先发送存储的首地址,从0到256都可以存,每个地址能放8bit,也就是一个地址放1个字节。
比如0、1、2、3这些数字就好了,表示地址0、地址1.。。。
IIC_SendByte(add);//向器件发送存储首地址
IIC_WaitAck();//向单片机发送应答信号
第三步,发送存的数据,我放的数据不止一个字节怎么办??
不用担心。如果你发送的数据,不止一个字节,它会自动把你的数据往后面的地址进行存储。换句话说,我们只需要发送首地址就好了。
IIC_SendByte(dat1);//向器件发送存储数据,这里可以是多个字节,存储地址依次延后
IIC_WaitAck();//向单片机发送应答信号
IIC_SendByte(dat2);
IIC_WaitAck();
最后一步,那必然是发送完数据,结束IIC咯
IIC_Stop();//结束总线
Delay2ms();//短暂延时,写入数据
3.2写入操作函数
void writeRom(unsigned char address,unsigned char dat)
{
IIC_Start();//启动总线
IIC_SendByte(0xA0);//向器件发送写命令+器件地址
IIC_WaitAck();//向单片机发送应答信号
IIC_SendByte(add);//向器件发送存储首地址
IIC_WaitAck();//向单片机发送应答信号
IIC_SendByte(dat);//向器件发送存储数据,这里可以是多个字节,存储地址依次延后
IIC_WaitAck();//向单片机发送应答信号
IIC_Stop();//结束总线
Delay2ms();//短暂延时,写入数据
}
3.3读取操作流程
还是IIC通信,能存当然能读,和写入操作流程一样,我就照搬一手了。
我怎么才能读东西出来??我可以读哪写地址??我能读多少??
第一步启动IIC,然后发送器件地址,(我要找到这个器件)我们是要先发送写命令
——0XA0、0XA1都是AT24C02的器件地址,有什么区别呢?
最后一位地址的最后一位为方向位R/W ,当主控器对A/D器件进行读操作时为1,进行写操作时为0。因此0XA0是写操作。0XA1是写操作。
因此我们这里发送0XA0;
IIC_SendByte(0XA0);//向器件发送写命令+器件地址
IIC_WaitAck();//向单片机发送应答信号
第二步,我要从哪开始读??
存在哪里,就发送哪里的地址
比如我存在了地址5.我就发送5
需要注意的是,我们发送命令一定要先给一个写命令0XA0
IIC_Start();//启动总线
IIC_SendByte(SlaveAddrW);//向器件发送写命令+器件地址
IIC_WaitAck();//向单片机发送应答信号
IIC_SendByte(add);//存储地址
IIC_WaitAck();//向单片机发送应答信号
第三步、接收数据
接收数据前必须要发送读命令才行。
IIC_SendByte(0XA1);//向器件发送读命令+器件地址
IIC_WaitAck();//向单片机发送应答信号
然后开始接收
dat=IIC_RecByte();//接收数据
IIC_SendAck(1); //发送非应答信号,停止接收
最后一步,那必然是读取完数据,结束IIC咯
IIC_Stop();//停止总线
我要读的数据不止一个字节怎么办??
如果是顺序存储的,就地址一次加一往后读,
不是的话,就要发送数据在的地址了
3.4读取操作函数
unsigned char readRom(unsigned char add)
{ unsigned char dat;
IIC_Start();//启动总线
IIC_SendByte(SlaveAddrW);//向器件发送写命令+器件地址
IIC_WaitAck();//向单片机发送应答信号
IIC_SendByte(add);//存储地址
IIC_WaitAck();//向单片机发送应答信号
IIC_Start();//重启总线,输入命令
IIC_SendByte(SlaveAddrR);//向器件发送读命令+器件地址
IIC_WaitAck();//向单片机发送应答信号
dat=IIC_RecByte();//接收数据
IIC_SendAck(1); //发送非应答信号,停止接收
IIC_Stop();//停止总线
return dat;
}
3、AT24C02存取实验
直接复制就能跑!
我们向AT24C02中写入数据,然后关闭电源,看掉电是否还能再把它读取出来
writeRom(0,10);//地址0写入10
writeRom(1,20);//地址1写入20
writeRom(2,40);//地址2写入40
temp1 = readRom(0);//读取地址0的数据
temp2 = readRom(1);//读取地址1的数据
temp3 = readRom(2);//读取地址2的数据
实验1(检测写入读取是否成功):我们向前三个地址写入了10,20,40三个数据,然后读取出来,在数码管上显示,
实验二(检测是否防掉电):注释掉写入函数,重新编译烧录,看之前写的数据是否还能读取出来。
通过实验我们可以知道,实验功能正常
#include <STC15F2K60S2.H>
#include "intrins.h"
#include <stdio.H>
#define DELAY_TIME 5
#define u8 unsigned char
#define u16 unsigned int
//总线引脚定义
sbit SDA = P2^1; /* 数据线 */
sbit SCL = P2^0; /* 时钟线 */
#define SlaveAddrW 0xA0
#define SlaveAddrR 0xA1
unsigned char buf[10],duanma[10];
int temp1,temp2,temp3;
void Timer0Init(void) //1毫秒@11.0592MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x66; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
void Delay2ms() //@11.0592MHz
{
unsigned char i, j;
_nop_();
_nop_();
i = 22;
j = 128;
do
{
while (--j);
} while (--i);
}
void IIC_Delay(unsigned char i)
{
do{_nop_();}
while(i--);
}
//总线启动条件
void IIC_Start(void)
{
SDA = 1;
SCL = 1;
IIC_Delay(DELAY_TIME);
SDA = 0;
IIC_Delay(DELAY_TIME);
SCL = 0;
}
//总线停止条件
void IIC_Stop(void)
{
SDA = 0;
SCL = 1;
IIC_Delay(DELAY_TIME);
SDA = 1;
IIC_Delay(DELAY_TIME);
}
//发送应答
void IIC_SendAck(bit ackbit)
{
SCL = 0;
SDA = ackbit; // 0:应答,1:非应答
IIC_Delay(DELAY_TIME);
SCL = 1;
IIC_Delay(DELAY_TIME);
SCL = 0;
SDA = 1;
IIC_Delay(DELAY_TIME);
}
//等待应答
bit IIC_WaitAck(void)
{
bit ackbit;
SCL = 1;
IIC_Delay(DELAY_TIME);
ackbit = SDA;
SCL = 0;
IIC_Delay(DELAY_TIME);
return ackbit;
}
//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)
{
unsigned char i;
for(i=0; i<8; i++)
{
SCL = 0;
IIC_Delay(DELAY_TIME);
if(byt & 0x80) SDA = 1;
else SDA = 0;
IIC_Delay(DELAY_TIME);
SCL = 1;
byt <<= 1;
IIC_Delay(DELAY_TIME);
}
SCL = 0;
}
//从I2C总线上接收数据
unsigned char IIC_RecByte(void)
{
unsigned char i, da;
for(i=0; i<8; i++)
{
SCL = 1;
IIC_Delay(DELAY_TIME);
da <<= 1;
if(SDA) da |= 1;
SCL = 0;
IIC_Delay(DELAY_TIME);
}
return da;
}
//器件地址0xa0
void writeRom(u8 add,u8 dat)
{
IIC_Start();
IIC_SendByte(SlaveAddrW);//写命令+器件地址
IIC_WaitAck();
IIC_SendByte(add);//存储地址
IIC_WaitAck();
IIC_SendByte(dat);//存储数据
IIC_WaitAck();
IIC_Stop();
Delay2ms();
}
unsigned char readRom(u8 add)
{ u8 dat;
IIC_Start();
IIC_SendByte(SlaveAddrW);//写命令+器件地址
IIC_WaitAck();
IIC_SendByte(add);//存储地址
IIC_WaitAck();
IIC_Start();
IIC_SendByte(SlaveAddrR);
IIC_WaitAck();
dat=IIC_RecByte();
IIC_SendAck(1); //发送非应答信号
IIC_Stop();
return dat;
}
void Conversion(u8 *buf,u8 *duanma)
{
u8 i,j=0,temp;
for(i=0;i<8;i++,j++)
{
switch(buf[j])
{
//根据要显示的字符获取共阳极数码管编码
case '0': temp = 0xc0; break;
case '1': temp = 0xf9; break;
case '2': temp = 0xa4; break;
case '3': temp = 0xb0; break;
case '4': temp = 0x99; break;
case '5': temp = 0x92; break;
case '6': temp = 0x82; break;
case '7': temp = 0xf8; break;
case '8': temp = 0x80; break;
case '9': temp = 0x90; break;
case 'A': temp = 0x88; break;
case 'B': temp = 0x83; break;
case 'C': temp = 0xc6; break;
case 'D': temp = 0xA1; break;
case 'E': temp = 0x86; break;
case 'F': temp = 0x8E; break;
case 'H': temp = 0x89; break;
case 'L': temp = 0xC7; break;
case 'N': temp = 0xC8; break;
case 'P': temp = 0x8c; break;
case 'U': temp = 0xC1; break;
case '-': temp = 0xbf; break;
case ' ': temp = 0xff; break;
default: temp = 0xff;
}
if(buf[j+1]=='.')
{
temp = temp&0x7f;
j++;
}
duanma[i] = temp;
}
}
void display(u8 *duanma,u8 position)
{
P0 = 0XFF;//段码消影
P2 = (P2&0X1F)|0XE0;//允许段码更新
P2 &= 0X1F;
P0 = 1<<position;//送位码
P2 = (P2&0X1F)|0XC0;
P2 &= 0X1F;
P0 = duanma[position]; //送段码
P2 = P2 & 0x1F | 0xE0; //允许段码锁存器更新
P2 &= 0x1F; //锁存段码锁存器
}
void main()
{
Timer0Init();
EA = 1;ET0 = 1;
writeRom(0,10);//地址0写入10
writeRom(1,20);//地址1写入20
writeRom(2,40);//地址2写入40
temp1 = readRom(0);//读取地址0的数据
temp2 = readRom(1);//读取地址1的数据
temp3 = readRom(2);//读取地址2的数据
while(1)
{
sprintf(buf,"%d %d %d",temp1,temp2,temp3);
Conversion(&buf,&duanma);
}
}
void Timer0() interrupt 1
{
static unsigned char i;
if(++i==8){i=0;}display(&duanma,i);
}
实验现象:
版权声明:本文为CSDN博主「学到地中海」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/C_white_llj/article/details/122764569
暂无评论