51单片机系列——I2C通信方式——24C02(E2PROM)的应用(仿真+源码)

仿真电路如下:
图1
代码如下:
i2c.h

#ifndef _I2C_H    //写头文件的固定格式
#define _I2C_H   //写头文件的固定格式

#include <reg52.h>

sbit SCL=P1^2;   //E2PROM24C02的引脚定义
sbit SDA=P1^3;   //E2PROM24C02的引脚定义
sbit WP=P1^4; //读写保护

unsigned char I2CSendByte(unsigned char dat);
unsigned char I2CReadByte();
void I2CStop();
void I2CStart();
void At24c02Write(unsigned char addr,unsigned char dat);  //在头文件中申明,在主函数中便可调用
unsigned char At24c02Read(unsigned char addr);

#endif      //写头文件的固定格式

i2c.c

#include "i2c.h"

void delay5us(void)   //误差 0us
{
    unsigned char a;
    for(a=1;a>0;a--);
} 

void I2CStart(){ //I2C开始信号,严格按照时序图
 SDA=1;
 SCL=1;
 delay5us(); 
 SDA=0;
 delay5us();
 SCL=0;
 delay5us(); 
}

void I2CStop(){		//停止信号
 SDA=0;
 SCL=1;
 delay5us();
 SDA=1;
 delay5us();   //后面的可以不管了,因为已经能起到stop的作用 
}

unsigned char I2CSendByte(unsigned char dat){	//写一个字节函数
  unsigned char a=8,b;
  for(a=8;a>0;a--){
   SDA=dat>>7;
  dat=dat<<1;
  delay5us();
  SCL=1;     //时钟线为低时数据才能传送,时钟线为高电平的时候数据是要求保持不变的
  delay5us();  //检测SDA是否为0,及是否应答
  SCL=0;
  delay5us();
  }
   SDA=1;   //释放时钟线和数据线,等待应答
  delay5us();
   SCL=1;
   while(SDA){   //检测是否应答,若应答SDA=0跳出循环,若SDA=1则非应答
     b++;   //SDA=1,设定一个时间跳出循环,返回0
   if(b>20){
    SCL=0;
    delay5us(); 
    return 0;
   }
   }
   SCL=0;
   delay5us(); 
   return 1;   //发送成功返回1   
}

unsigned char I2CReadByte(){	//读一个字节数据
 unsigned char a=8,dat=0;
 SDA=1; //拉高准备读
 delay5us();
 for(a=8;a>0;a--){
  SCL=1;   //SCL时数据稳定,读取时要求数据稳定
  delay5us();  
  dat<<=1;  //在这里读和写的顺序不一样,读:移位———>读数;写:写数-->移位
  dat|=SDA;  //为什么这里读数据可以直接移位然后相与呢?移位时钟线与数据线不是同一根线
  delay5us();  
  SCL=0;   //SCL为零时,SDA上的数据可以改变
  delay5us(); 
 }
 return dat;     
}

/* I2CSendByte、I2CReadByte这两个函数是用来确定8位字节数据是在什么条件下能读写出来的*/
/*可以吧I2CSendByte、I2CReadByte这两个函数理解为小环境*/
/*有了满足了小环境的读写(一个字节8位数的连续传输),就要创造大环境(即这个整体器件是在什么条件下怎么传送数据的*/
/*或者可以理解为先根据时序图写出满足I2C的读写数据方式,在写出满足器件读写的数据方式*/

void At24c02Write(unsigned char addr,unsigned char dat){ //先写地址,确定数据存放的位置;再写数据  (根据器件的写数据的流程来编写这个函数)
 I2CStart();
 I2CSendByte(0xa0);  //确定器件地址,及哪个器件
 I2CSendByte(addr);  //写器件内首地址,确定存放数据的首地址位置
 I2CSendByte(dat);   //确定玩地址后,就发送数据
 //关于应答部分已经写在发送函数中,相当于一个整体
 I2CStop();   //发送玩了,一个停止信号
}

unsigned char At24c02Read(unsigned char addr){  //读数据需要设一个返回值,靠这个返回值得到数据更容易编写
 unsigned char num;
 I2CStart();
 I2CSendByte(0xa0);  //读的时候先伪写入
 I2CSendByte(addr);  //伪写入包括两部分:确定器件地址和确定器件内首地址 
 I2CStart();     //在传送过程中,需要改变传送数据的方向时,起始信号和从机地址都会被重复产生一个,但两次读写方向正好相反
 I2CSendByte(0xa1);   //这两部很重要
 num=I2CReadByte();  //获取读的数据
 I2CStop();
 return num;
}

main.c

#include <reg52.h>
#include "i2c.h"

typedef unsigned int u16;
 typedef unsigned char u8;
/*控制读写过程*/
 sbit K1=P3^1; //保存显示数据,即写入数据至E2Prom保存
 sbit K2=P3^0; //读取保存的数据
 sbit K3=P3^2; //累加
 sbit K4=P3^3; //清零

sbit LSA=P2^2;   //三八译码器的引脚设置
sbit LSB=P2^3;
sbit LSC=P2^4;

u8 code segment[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
u8 num=0,disp[4];  //disp也可以写成一个缓冲池 :disp[4]={1,1,2,3};

void delay(u16 i){
 while(i--);
}

void Keypros(){  //按键处理函数
 if(K1==0){
  delay(100);  //消抖
  if(K1==0){
   WP=0;  /*关闭保护*/
   At24c02Write(2,num);  //写入存放的首地址和要存放的数据(AT24C02有256个地址,2是其中的一个,num可以是自己设的一个数值)
    delay(100)  ;/*等待读完成*/
    WP=1;   /*打开保护*/
  }
  while(!K1);   //这个很重要,判断开关是否断开
 }
  if(K2==0){
  delay(100);  //消抖
  if(K2==0){
   num=At24c02Read(2);  //之前写入的地址为2,因此读2 ,num最后等于读取的返回值
  }
  while(!K2);
 }
 if(K3==0){
  delay(100);  //消抖
  if(K3==0){
   num++;  // num累计
   if(num>255){
    num=0;
   }
  }
  while(!K3);
 }
 if(K4==0){
  delay(100);  //消抖
  if(K4==0){
   num=0;  
  }
  while(!K4);
 }
}

void datapros(){ //数据处理函数,将存储器里的数转换为数码管理的数
   disp[0]=segment[num/1000];  //最高位
   disp[1]=segment[num%1000/100];
   disp[2]=segment[num%1000%100/10];
   disp[3]=segment[num%1000%100%10];
}

void Dispiay(){  //显示函数
 u8 i;
 for(i=0;i<4;i++){
  switch(i){
   case 0:LSA=0;LSB=0;LSC=0;break; //显示第0位
   case 1:LSA=1;LSB=0;LSC=0;break; //显示第1位
   case 2:LSA=0;LSB=1;LSC=0;break; //显示第2位
   case 3:LSA=1;LSB=1;LSC=0;break; //显示第3位
  }
  //上面选通了是哪一位,下面就传断选数据
  P0=disp[3-i];  //根据数据处理函数得到要显示的数,在循环的条件下,将一个数利用4个数码管显示了出来
  delay(100);
  P0=0x00;  //消隐
 }
}

void main(){ //  进入主函数调用
  while(1){
  Keypros(); //先判断按键是否有操作
  datapros();  //判断玩后对获取的数据要进行处理
   Dispiay();    //数据处理完了就可以显示  
 }
 }

仿真结果图:
1、按下K3,数码管显示数字自动加1,按3下显示:
图2
2、按下K1,数码管上的显示数自动保存至24C02中(本程序中地址是2),接着按几次K3改变数码管的显示数值,然后按下K1,数码管】显示保存的数“3”.按下K4,数码管清零。

**注意:**这个项目中将I2C的主从应答部分直接编程在unsigned char I2CSendByte(unsigned char dat)函数中,没有单独的验证主从应答的子函数。

小编水平有限,但希望对大家有帮助!!!

  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值