51单片机 AT24C02

前置知识

        存储器 RAM和ROM

RAM存储快,但掉电丢失 random access memory

       SRAM static 静态 速度最快,容量较少,且成本高

       DRAM dynamic 动态刷新 成本更低,容量更大

ROM掉电不丢失,但存储慢 read only memory

       Mask ROM  掩膜ROM 依靠电路提供数据,只能读不能写

       PROM 可编程ROM 能写入,但只有一次

       EPROM 可擦除可编程ROM 可编程可以清除编程的数据 紫外线照射三十分钟可清除数据

       EP2ROM 电可擦除可编程ROM 几毫秒就可擦除数据

       FLASH 闪存

       硬盘、软盘(A、B盘留给软盘用)、光盘等

小时候家里有好多软盘,但这玩意儿不经折腾,不小心摔地下就坏了,父母总给我一些已经坏了的软盘当飞盘玩,哈哈哈!算是时代的眼泪了。

以下为EPROM

        存储器简化模型

PROM中蓝色二极管容易击穿 击穿后变为右面那个,即进行编程 不过只能击穿一次(烧录的由来,编程时使用高电压, 平常由于两个二极管反接,电流无法通过电路)

还有另一种为熔丝型PROM 电流过大烧断为断路,电流小为短路(通电)

EPRON以及E2PROM通过修复电路起到删除数据重新编程的作用

 

        AT24C02介绍

 

        引脚及应用电路原理图以及内部框架

 

 以下为新版的普中开发板原理图,以这个为标准

(新版的原理图 VSS即GND VDD为VCC)

 VDD:D=device 表示器件的意思, 即器件内部的工作电压; 

VCC:C=circuit 表示电路的意思, 即接入电路的电压;

VSS:S=series 表示公共连接的意思,通常指电路公共接地端电压;

GND(Ground)代表地线或0线.   GND就是公共端的意思,也可以说是地,但这个地并不是真正意义上的地。是出于应用而假设的一个地,对于电源来说,它就是一个电源的负极。它与大地是不同的。有时候需要将它与大地连接,有时候也不需要,视具体情况而定。

(35条消息) VDD,VCC,VSS,GND,地之间有何区别?_不一样的崽的博客-CSDN博客

        IIC总线介绍

 

用到I2C总线的产品

显示屏

DS3231

陀螺仪传感器

                I2C电路规范

 

                开漏输出与推挽输出

又说回了 上拉模式

右面是个输出端

当开关闭合时,接口直接接地,输出0

断开 有输出, 为1 (1完全没有驱动能力)

开漏模式(无上拉电阻)

电路闭合 输出0

电路断开,电路呈现(浮空状态)不稳定状态,电压不稳定

但开漏输出模式去掉了上拉电阻,当控制通信时,将不需要的器件断开后,防止上拉电阻过多影响电路操作,上面的两个电阻承担了整个电路的上拉电阻(如上为上拉电阻与开漏输出的结合使用)

开漏输出与推挽输出 - 知乎 (zhihu.com)

(43条消息) 03-开漏输出_赤道的北极熊的博客-CSDN博客

两条线的是MOS管

三角形的是输入阻抗

                 I2C时序结构

SCL变高变低为了能够前后的连贯,且防止产生多余的时序条件

每次发送一位,发送8位

最后一位1高电平, 0 低电平 (不是数据位)

释放的意思就是松手(举的例子),即SDA置1

高位在前 就是 存储时前面是高位,后面是低位

每次发送一位,发送8位

最后一位1高电平, 0 低电平 (不是数据位)

相信看图片看有点懵,我来个时序结构小总结

(按如下顺序操作)

 

起始条件:SCL 1, SDA 0 -> 1

终止条件:SCL 1, SDA 1 -> 0

发送一位(发送8位就是一个字节):数据位放在SDA上(高位在前),SCL 0 ->1 -> 0

接收一位:数据位放在SDA上(高位在前),SCL 0 ->1 -> 0

        需要注意的是:最后一位1表示高电平, 0 表示 低电平 (不是数据位)

发送应答:接受完一个字节后,主机在下一个时钟发送数据,0为应答,1为非应答

接收应答:发送完一个字节后,主机在下一个时钟接收一位数据,判断从机是否应答,0为应答,1为非应答。(主机接受前,需要释放SDA

IIC如何释放数据总线? 为什么=1就是释放? - wdliming - 博客园 (cnblogs.com)

                数据帧

   

页写,顺序读等较难的等我再学习一下再写吧(挖个坑,之后来填)

遇到的问题

第一次写不小心把寄存器看反了,数据一直不对

自己SDA以及SCL咋工作 的其实也并不是看的很懂

代码思路及展示

数据存储

//I2C.C
#include <REGX52.H>

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



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

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

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

/**
  * @brief  I2C接收一个字节
  * @param  无
  * @retval 接收到的一个字节数据
  */
unsigned char I2C_ReceiveByte(void)
{
	unsigned char i, Byte = 0x00; //Byte即为要返回的数值
	
	I2C_SDA = 1;
	
	for (i = 0; i < 8; i ++)
	{
		I2C_SCL = 1;
		if (I2C_SDA) {Byte |= (0x80 >> i);} //构造Byte ,若I2C_SDA 非0,则说明该位置不是接收数据的位置
		I2C_SCL = 0;
	}
	return Byte;
}

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

/**
  * @brief  I2C接收应答位
  * @param  无
  * @retval 接收到的应答位,0为应答,1为非应答
  */
unsigned char 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 I2C_Stop();
void I2C_SendByte(unsigned char Date);
unsigned char I2C_ReceiveByte();
void SendAck(unsigned char AckBit); //1写,0读
unsigned char ReceiveAck(void);


#endif

           

//AT24C02.C
#include <REGX52.H>
#include "I2C.H"

#define AT24C02_ADDRESS 0xA0


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

/**
  * @brief  AT24C02读取一个字节
  * @param  WordAddress 要读出字节的地址
  * @retval 读出的数据
  */
unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{
	unsigned char Date;
	
	I2C_Start();
	I2C_SendByte(AT24C02_ADDRESS);
	ReceiveAck();
	I2C_SendByte(WordAddress);
	ReceiveAck();
	I2C_Start();
	I2C_SendByte(AT24C02_ADDRESS | 0x01); //0xA1 为SLAVE ADDRESS + R
	ReceiveAck();	//之后获取总线的控制权
	Date = I2C_ReceiveByte();
	SendAck(1);
	I2C_Stop();
	
	return Date;
}

                        


#ifndef __AT24C02_H__
#define __AT24C02_H__

void AT24C02_WriteByte(unsigned char WordAddress, Date);
unsigned char AT24C02_ReadByte(unsigned char WordAddress);

#endif

    

#include <REGX52.H>
#include "LCD1602.H"
#include "DELAY.H"
#include "KEY.H"
#include "AT24C02.H"

unsigned char KeyNum;
unsigned int Num;

void main()
{
	LCD_Init();
	LCD_ShowNum(1, 1, Num, 5);

	while (1)
	{
		KeyNum = Key();
		if (KeyNum == 1)
		{
			Num ++;
			LCD_ShowNum(1, 1, Num, 5);
		}
		
		if (KeyNum == 2)
		{
			Num --;
			LCD_ShowNum(1, 1, Num, 5);
		}
		
		if (KeyNum == 3) //写入Num,需要延迟,因为写入要改变物理特性
		{
			AT24C02_WriteByte(0, Num % 256);
			Delay(5);
			AT24C02_WriteByte(1, Num / 256);
			Delay(5);
			
			LCD_ShowString(2, 1, "write perform");

		}
		
		if (KeyNum == 4) //读出Num,不需要延迟
		{
			Num = AT24C02_ReadByte(0);
			Num |= AT24C02_ReadByte(1) << 8;
			LCD_ShowNum(1, 1, Num, 5);
			LCD_ShowString(2, 1, "read perform");
			
		}
	}
}

    

秒表(定时器扫描数码管)

本程序对key和nixie模块进行了重新编写,使其方便在定时器中调用

而且提高了按钮判断的灵敏度

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


unsigned char Key_KeyNumber;

unsigned char Key(void)
{
	unsigned char temp = 0;
	temp = Key_KeyNumber;
	Key_KeyNumber = 0;
	
	return temp;
	
}

unsigned char Key_GetState()
{
	unsigned char KeyNumer = 0;
	
	if (P3_1 == 0) KeyNumer = 1;
	if (P3_0 == 0) KeyNumer = 2;
	if (P3_2 == 0) KeyNumer = 3;
	if (P3_3 == 0) KeyNumer = 4;
	
	return KeyNumer;
}


void Key_Loop(void)
{
	static unsigned char NowState, LastState;
	
	LastState = NowState; //现在的状态 0
	NowState = Key_GetState(); //now按下的
	
	if (NowState == 0 && LastState == 1) //Now 1,Last 0后表按下,Now 0, Last 1表松手
	{
		Key_KeyNumber = 1;
	}
	if (NowState == 0 && LastState == 2)
	{
		Key_KeyNumber = 2;
	}
	if (NowState == 0 && LastState == 3)
	{
		Key_KeyNumber = 3;
	}
	if (NowState == 0 && LastState == 4)
	{
		Key_KeyNumber = 4;
	}	
}


#ifndef __KEY_H__
#define __KEY_H__

unsigned char Key();
void Key_Loop(void);

#endif

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

unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0x00,0x40};

unsigned char Nixie_Buf[9];

void Nixie_SetBuf(unsigned char Location, unsigned char Number)
{
	Nixie_Buf[Location] = Number;

}

void Nixie_scan(unsigned char location,number)
{
	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];
	//Delay(1);
	//P0=0x00;
}


void Nixie_Loop()
{
	static unsigned char i = 1;
	Nixie_scan (i, Nixie_Buf[i]);
	i ++;
	if (i >= 9) i = 1;

}

#ifndef __NIXIE_H__
#define __NIXIE_H__

void Nixie_scan(unsigned char location,number);
void Nixie_Loop(void);
void Nixie_SetBuf(unsigned char Location, unsigned char Number);


#endif

//main
#include <REGX52.H>
#include "AT24C02.H"
#include "NIXIE.H"
#include "TIMER0.H"
#include "Key.h"
#include "Delay.h"

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

void main()
{
	Timer0_Init ();
	
	while (1)
	{
		KeyNum = Key();
		if (KeyNum == 1)
		{
			RunFlag = !RunFlag;
		}
		if (KeyNum == 2)
		{
			Min = 0;
			Sec = 0;
			MiniSec = 0;
		}
		if (KeyNum == 3)
		{
			AT24C02_WriteByte(0, Min);
			Delay(5);
			AT24C02_WriteByte(1, Sec);
			Delay(5);
			AT24C02_WriteByte(2, MiniSec);
			Delay(5);
		}
		if (KeyNum == 4)
		{
			Min = AT24C02_ReadByte(0);
			Sec = AT24C02_ReadByte(1);
			MiniSec = AT24C02_ReadByte(2);
		}
		Nixie_SetBuf(1, Min / 10);
		Nixie_SetBuf(2, Min % 10);
		Nixie_SetBuf(3, 17);
		Nixie_SetBuf(4, Sec / 10);
		Nixie_SetBuf(5, Sec % 10);
		Nixie_SetBuf(6, 17);
		Nixie_SetBuf(7, MiniSec / 10);
		Nixie_SetBuf(8, MiniSec % 10);
	}
}

void Sec_Loop()
{
	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 T0Count, T0Count1, T0Count2;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count++;
	if(T0Count >= 20)
	{
		T0Count = 0;
		Key_Loop();
	}
	
	T0Count1 ++;
	if(T0Count1 >= 2)
	{
		T0Count1 = 0;
		Nixie_Loop();
		
	}
	
	T0Count2 ++;
	if(T0Count2 >= 10)
	{
		T0Count2 = 0;
		Sec_Loop();
	}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值