赞
踩
27.1. 存储器介绍
27.2. 存储器简化模型介绍,存储原理
27.3. AT24C02介绍
•AT24C02是一种可以实现掉电不丢失的存储器,可用于保存单片机运行时想要永久保存的数据信息
•存储介质:E2PROM
•通讯接口:I2C总线
•容量:256字节
27.4. AT24C02引脚及应用电路,VCC接电源,A0~A2和GND接地,WP写保护接地,上拉电阻在单片机接口位置已经接好,开发板原理图中就不需要接了
27.5. AT24C02内部结构框图,EEPROM为存储单元网格,DEC译码器,RECOVERY数据擦除,
27.6. I2C总线介绍
•I2C总线(Inter IC BUS)是由Philips公司开发的一种通用数据总线(通信协议,实现多设备通信,并标准规范化数据通信)
•两根通信线:SCL(Serial Clock)、SDA(Serial Data)
•同步、半双工,带数据应答
•通用的I2C总线,可以使各种设备的通信标准统一,对于厂家来说,使用成熟的方案可以缩短芯片设计周期、提高稳定性,对于应用者来说,使用通用的通信协议可以避免学习各种各样的自定义协议,降低了学习和应用的难度。(标准和专利付费),下面是I2C相关的附件设备,左边第一个12864的小屏幕,中间是DS3231的时钟芯片,精度更高,右侧是陀螺仪。
27.7. I2C电路规范
•所有I2C设备的SCL连在一起,SDA连在一起
•设备的SCL和SDA均要配置成开漏输出模式(单片机IO口为弱上拉模式,可以想象成输出口带上拉电阻,输出电流受限,开漏模式则是在上述基础上取消上拉电阻,无输出的时候IO口容易受外界干扰,电压不稳定)
•SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右(有标准规定,什么速率接什么电阻)
•开漏输出和上拉电阻的共同作用实现了“线与”的功能,此设计主要是为了解决多机通信互相干扰的问题(可以实现与一个设备通信的时候,其他设备处于“断开”的状态,防止通信干扰)
27.8. I2C时序结构(六块拼图)
•起始条件:SCL高电平期间,SDA从高电平切换到低电平(S蓝色开始)
•终止条件:SCL高电平期间,SDA从低电平切换到高电平(P红色结束)
27.9. I2C时序结构_发送一个字节_绿色S byte
•发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位在前),然后拉高SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节(下面SDA是连根线麻花的状态的意思是SDA初始情况不确定是0还是1,在SCL高电平(固定SDA的0或1状态),红色框是基本结构,SDA的最终状态看数据发送完毕后的状态,如果是0就是低电平,1就是高电平)
27.10. I2C时序结构_接收一个字节_紫色byte
•接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位在前),然后拉高SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)
27.11. I2C时序结构
•发送应答:在接收完一个字节之后,主机在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
•接收应答:在发送完一个字节之后,主机在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)
27.12. I2C数据帧
•发送一帧数据(S为开始)(S:SLAVE+ADDRESS+W写,1读0写,地址前4位固定,24C02为1010,24C02芯片引脚A2,A1,A0接地0)(RA接收应答,从机应该发0)(S byte数据)(结束)
•完成任务:(上述完成)向谁发什么
27.13. I2C数据帧
•接收一帧数据:(S开始)(发送地址+R读)(RA应答0)(数据读)(主机发送应答)。。。(最后可发应答或非应答)(结束)
•完成任务:(上述完成)向谁收什么
27.14. I2C数据帧
•先发送再接收数据帧(复合格式)上述两个拼接,取消了一个P
•完成任务:向谁收指定的什么
27.15. AT24C02数据帧
•字节写:在WORD ADDRESS处写入数据DATA(发送一帧数据的变形)
•随机读:读出在WORD ADDRESS处的数据DATA(复合格式)
•AT24C02的固定地址为1010,可配置地址本开发板上为000
所以SLAVE ADDRESS+W写为0xA0(1010 0000),SLAVE ADDRESS+R读为0xA1(1010 0001)
27.16. AT24C02数据帧
•字节写:在“字地址”处写入“数据”
•随机读:读出在“字地址”处的“数据”
27.17. 24C02手册介绍24C04地址8位不够(8位256)
28.1. 程序设计思路
28.2. Proteus测试环境搭建
在Proteus中搜索 24C02找到24C02C并插入;
28.3. 按照编程思路,先做好I2C.c模块程序如下:
#include <REGX52.H> sbit I2C_SCL=P2^1; sbit I2C_SDA=P2^0; /** * @brief I2C开始 * @param 无 * @retval 无 */ void I2C_Start(void) { I2C_SDA=1; //不清楚SDA 的初始状态,所以都置1 I2C_SCL=1; I2C_SDA=0; I2C_SCL=0; } /** * @brief I2C停止 * @param * @retval */ void I2C_Stop(void) { I2C_SDA=0; //不清楚SDA 的初始状态,所以都置1 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; //所以此处无需delay; } } /** * @brief I2C接收一个字节 * @param 无 * @retval 接收到的一个字节数据 */ unsigned char I2C_ReceiveByte() { unsigned char i,Byte; 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; }
#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 I2C_SendAck(unsigned char AckBit);
unsigned char I2C_ReceiveAck(void);
#endif
#include <REGX52.h> #include "I2C.h" #define AT24C02_ADDRESS 0xa0 /** * @brief AT24C02 写入一个字节 * @param WordAddress字节要写入的地址0~255,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; }
#ifndef _AT24C02_H_
#define _AT24C02_H_
void AT24C02_WriteByte(unsigned char WordAddress, Data);
unsigned char AT24C02_ReadByte(unsigned char WordAddress);
#endif
#include <REGX52.h> #include "Four_Key.h" #include "LCD1602.h" #include "AT24C02.h" #include "delay_xms.h" unsigned char KeyNum; unsigned int Num; void main() { LCD_Init(); //LCD1602初始化 LCD_ShowString(1,1,"Hello");//不按按键显示Hello // AT24C02_WriteByte(0,123); // delay_xms(5); // AT24C02_WriteByte(1,234); // delay_xms(5); // AT24C02_WriteByte(2,345); // delay_xms(5); // Data=AT24C02_ReadByte(1); // LCD_ShowNum(2,1,Data,3); while(1) { KeyNum=Four_Key(); if(KeyNum==1) { Num++; LCD_ShowNum(1,1,Num,5);//数字随按键1按下释放后增加 } if(KeyNum==2) { Num--; LCD_ShowNum(1,1,Num,5);//数字随按键2按下释放后减小 } if(KeyNum==3) { AT24C02_WriteByte(0,Num%256); delay_xms(5); AT24C02_WriteByte(1,Num/256); delay_xms(5); LCD_ShowString(2,1,"Write OK"); delay_xms(1000); LCD_ShowString(2,1," ");//将数字拆分高低8位写入,写入后延时5ms } if(KeyNum==4) { Num=AT24C02_ReadByte(0); Num|=AT24C02_ReadByte(1)<<8; LCD_ShowNum(1,1,Num,5); LCD_ShowString(2,1,"Read OK"); delay_xms(1000); //读出并显示之前写入的数字 } } }
28.8. Proteus仿真和开发板测试无误
28.9. 新建工程AT24C02数据存储&秒表-定时器扫描按键数码管
28.10. 新建工程,程序需要实现的功能为,按下按键1,松开后秒表开始计时,再按一下1松开,秒表计时停止,按下按键2,计数清零,按下按键3,存储数据到AT24C02,按下按键4,将之前存储的数据显示在数码管上面,新建的工程中的数码管显示的模块程序为:
nixietube.c
#include <REGX52.H> #include "delay_xms.h" unsigned char Nixie_Buf[9]={0,10,10,10,10,10,10,10,10,}; unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0x40}; void Nixie_SetBuf(unsigned char Location,Number) { Nixie_Buf[Location]=Number; } void Nixie_Scan(unsigned char Location,Number) { P0=0x00;//清零; switch(Location) { case 1:P2_4=1;P2_3=1;P2_2=1;break; case 2:P2_4=1;P2_3=1;P2_2=0;break; case 3:P2_4=1;P2_3=0;P2_2=1;break; case 4:P2_4=1;P2_3=0;P2_2=0;break; case 5:P2_4=0;P2_3=1;P2_2=1;break; case 6:P2_4=0;P2_3=1;P2_2=0;break; case 7:P2_4=0;P2_3=0;P2_2=1;break; case 8:P2_4=0;P2_3=0;P2_2=0;break; } P0=NixieTable[Number];//数码管消影,因为位选-段选-位选-段选,段选数据与位选穿位导致 } void Nixie_Loop(void) { static unsigned char i;//计次 Nixie_Scan(i,Nixie_Buf[i]); i++; if(i>=9){i=1;} }
#ifndef _NIXIETUBE_H_
#define _NIXIETUBE_H_
void Nixie_SetBuf(unsigned char Location,Number);
void Nixie_Scan(unsigned char Location,Number);
void Nixie_Loop(void);
#endif
#include <REGX52.h> #include "delay_xms.h" unsigned char Key_KeyNumber; unsigned char Key(void) { unsigned char Temp=0; //加入中间变量,对Key_KeyNumber进行清0 Temp=Key_KeyNumber; Key_KeyNumber=0; return Temp; } unsigned char Key_GetStatus() { unsigned char KeyNum=0; if(P3_1==0){KeyNum=1;} if(P3_0==0){KeyNum=2;} if(P3_2==0){KeyNum=3;} if(P3_3==0){KeyNum=4;} return KeyNum; } void Key_Loop(void) { static unsigned char Now_Status,Last_Status; Last_Status=Now_Status; Now_Status=Key_GetStatus(); if(Last_Status==1 && Now_Status==0){Key_KeyNumber=1;} if(Last_Status==2 && Now_Status==0){Key_KeyNumber=2;} if(Last_Status==3 && Now_Status==0){Key_KeyNumber=3;} if(Last_Status==4 && Now_Status==0){Key_KeyNumber=4;} }
#ifndef _KEY_H_
#define _KEY_H_
unsigned char Key();
void Key_Loop();
#endif
#include <REGX52.h> #include "TimeR0.h" #include "Key.h" #include "nixietube.h" #include "delay_xms.h" #include "AT24C02.h" unsigned char KeyNum; unsigned char Min,Sec,mSec; unsigned char RunFlag; void main() { TimeR0_Init(); while(1) { KeyNum=Key(); if(KeyNum==1) { RunFlag=!RunFlag; } if(KeyNum==2) { Min=0; Sec=0; mSec=0; } if(KeyNum==3) { AT24C02_WriteByte(0,Min); delay_xms(5); AT24C02_WriteByte(1,Sec); delay_xms(5); AT24C02_WriteByte(2,mSec); delay_xms(5); } if(KeyNum==4) { Min=AT24C02_ReadByte(0); Sec=AT24C02_ReadByte(1); mSec=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,mSec/10); Nixie_SetBuf(8,mSec%10); } } void Sec_Loop(void) { mSec++; if(mSec>=100) { mSec=0; Sec++; if(Sec>=60) { Sec=0; Min++; if(Min>=60) { Min=0; } } } } void TimeR0_Routine() interrupt 1 //中断子函数 { static unsigned int T0Count1,T0Count2,T0Count3; //设置静态子函数用T0Count,防止T0Count丢失 TL0=0x18; //设置定时初始值 TH0=0xFC; //设置定时初始值 T0Count1++; if(T0Count1>=1000) { T0Count1=0; Key_Loop(); } T0Count2++; if(T0Count2>=2) { T0Count2=0; Nixie_Loop(); } T0Count3++; if(T0Count3>=10) { T0Count3=0; Sec_Loop(); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。