当前位置: 首页 > news >正文

江科大51单片机笔记【11】AT24C02数据存储秒表

一、数据存储

先把需要的模块导入做个测试

//main.c

#include <REGX52.H>
#include " LCD1602.h"
#include " Key.h"

void main()
{
	LCD_Init();
	LCD_ShowString(1,1,"Hello");
	while(1)
	{
 		
	
	}

}

代码思路

分成两块写,一块写I2C.c,一块写AT24C02.c。I2C写起始,终止,发送,接收,发送应答,接收应答;AT写两个数据帧,一个是写入数据帧,一个是读取数据帧。

1.I2C模块

记得在.h文件里声明好

第一步先位定义

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

下面的函数写作均可以看上一节推文的图来照着写

第二步写

起始函数

假如我们先发送再接收数据帧,就无法确定上一节结束后的SCL和SDA的状态,所以一开始要置1初始化,然后先拉下SDA(置0),再拉下SCL

void I2C_Start(void)
{
	I2C_SDA=1;
	I2C_SCL=1;
	I2C_SDA=0;
	I2C_SCL=0;
}

结束函数

跟起始函数一样的我们无法确定初始状态,所以要初始化,但因为SCL无论发送接收还是应答最后都是0,所以在这里不初始化也没问题

void I2C_Stop(void)
{
	I2C_SDA=0;
	I2C_SCL=1;	
	I2C_SDA=1;

}

发送数据函数

在开始时SCL是0,SDA=传入数据的最高位,我们定义参数unsigned char Byte为传入数据。然后把SCL置1再置0(这里扩展一下,除了写周期是5ms,其他操作基本在1us以下,不用加延时函数)接下来SCL一直置1置0,而SDA要根据Byte的数据来决定,所以我们用for循环来读取Byte的位数据传给SDA

void I2C_SendByte(unsigned char Byte)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		I2C_SDA=Byte&(0x80>>i);
		I2C_SCL=1;
		I2C_SDA=0;
	}
}

接收数据函数

既然是接收,就要写有返回值的函数

在这里SDA初始化需要为1,然后我们对SDA的值做判断,如果SDA=1,我们就对该值置1,如果SDA=0,我们就不进行操作。我们只需要定义Byte=0x00,然后对0x80进行或等于,最后再套上for循环

unsigned char I2C_ReceiveByte(void)
{
	unsigned char i,Byte=0x00;
	I2C_SDA=1;
	for(i=0;i<8;i++)
	{
		I2C_SCL=1;
		if(I2C_SDA){Byte|=(0x80>>i);}	
		I2C_SCL=0;
	}
	return Byte;
}

发送应答函数

下面介绍一种C51特有的数据类型,bit,只有一位且只能存0和1

我们就定义一个参数bit作为应答位

先让SDA=应答位,再把SCL拉高再拉低

void I2C_SendAck(bit AckBit)
{
	I2C_SDA=AckBit;
	I2C_SCL=1;
	I2C_SCL=0;
}

接收应答函数 

这里跟前面一样,但可能有一点难理解,这样解释一下

我们要明确,我们操作的始终是主机,主机接收应答时,从机时自动发送应答的,并不是我们操作从机做出什么应答,我们只需接收从机发送的应答即可

我们将SDA置1把SDA控制权给从机后,从机就会把SDA置1或0(从机发送应答,主机接收应答)所以我们只需接收SDA即可

unsigned char I2C_ReceiveAck(void)
{
	bit AckBit;
	I2C_SDA=1;
	I2C_SCL=1;
	Ackbit=I2C_SDA;
	I2C_SCL=0;
	return Ackbit;
}

最后加上注释我们的I2C模块就完成啦

#include <REGX52.H>

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

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

/**
  * @brief  I2C停止
  * @param  无
  * @retval 无
   */
void I2C_Stop(void)
{
	I2C_SDA=0;
	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;
	}
}


/**
  * @brief  I2C接收一个字节
  * @param  无
  * @retval 接收到的一个字节数据
   */
unsigned char I2C_ReceiveByte(void)
{
	unsigned char i,Byte=0x00;
	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;
}
//I2C.h

#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);
void I2C_SendAck(bit Ackbit);
unsigned char I2C_ReceiveAck(void);

#endif

2.AT24C02模块

这里我们要写两个函数,一个是字节写,一个是数据读

先来写字节写

首先是调用I2C起始,然后是I2C发送字节,在这里SLAVE ADDRESS+W是固定值0xA0,我们直接定义它,等下的SLAVE ADDRESS+R是0xA1,但是我们只需要定义一个,然后改他的最后一位就可以。

这里可以简单测试一下我们之前的代码有没有错,没错的情况是LED灯全亮,把地址故意写错就只会亮268

//AT24C02.c
#define AT24C02_ADDRESS 0xA0

void AT24C02_WriteByte(unsigned char WordAddress,Data)
{
	unsigned char Ack;
	I2C_Start();
	I2C_SendByte(AT24C02_ADDRESS);
	Ack=I2C_ReceiveAck();
	if(Ack==0)P2=0x00;

}

这是完整代码,不难,只需要照着图写,每一个步骤对应一个函数调用,跟拼图一样

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();
}

下面写数据读

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;
}

 在这里我们验证一下

正常情况下是在LCD第二行显示066,这里很容易出问题,因为前面写了很多函数,一错误千奇百怪,解决方法也各不相同,我第一次显示255,是因为AT函数里忘加终止函数了,第二次显示002是因为在I2C函数里SDA和SCL的顺序乱了,调整过来后也显示成功066了。

还有前面提到的,写入操作是5ms,在写入后我们要跟上一个延时函数,不然会出错

#include <REGX52.H>
#include " LCD1602.h"
#include " Key.h"
#include " AT24C02.h"
#include " Delay.h"

unsigned char Data;

void main()
{
	LCD_Init();
	LCD_ShowString(1,1,"Hello");
	AT24C02_WriteByte(1,66);
	Delay(5);
	Data=AT24C02_ReadByte(1);
	LCD_ShowNum(2,1,Data,3)
	while(1)
	{	
	
	}

}

最后加上注释我们的AT24C02模块也完成啦

//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,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;
}
//AT24C02.h

#ifndef __AT24C02_H__
#define __AT24C02_H__

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

#endif

3.数据存储主函数

如果按下按键1,数据++,LCD显示;如果按下按键2,数据--,LCD显示;如果按下按键3,把数据写入AT24C02,即存储,因为数据是16位,就要拆开,把高八位存到位置1,把高八位存到位置0,在LCD上显示写入成功,延时一秒再清零;如果按下按键4,把数据读出来,先读出位置0,再读出位置1,存到数据,然后LCD显示读取成功,延时一秒再清零。

//main


#include <REGX52.H>
#include " LCD1602.h"
#include " Key.h"
#include " AT24C02.h"
#include " Delay.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)
		{
			AT24C02_WriteByte(0,Num%256);
			Delay(5);
			AT24C02_WriteByte(1,Num/256);
			Delay(5);
			LCD_ShowString(2,1,"Write OK");
			Delay(1000);
			LCD_ShowString(2,1,"         ");
		}
		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(1000);
			LCD_ShowString(2,1,"       ");
		}
	}

}

到这里我们就完成啦。烧录后按按键1会加,按按键2会减,然后按按键3存储,断电后重启再按按键4会读取。

二、秒表(定时器扫描按键数码管)

总体思路:

Key和Nixie都需要中断,但是Timer0里只能有一个中断函数,不能定义多个

我们就需要一种新的写法,在main里写一个中断函数,然后在Key和Nixie里调用这个中断函数,假如Key需要每隔多少s一次中断,我们就令main里的定时器计数多少,这样每隔多久就会自动调用多少 

这样就避免模块里多个函数融合在一起,或者交叉使用,这样干净简洁

1.定时器扫描按键

下面解释一下定时器扫描按键是怎么工作

我们知道按键按下松开会有一下下的抖动,按键默认是高电平,原来我们是检测是否产生低电平,如果有就Delay把抖动滤掉,然后while死循环等检测到高电平,然后Delay把抖动滤掉,这样的while死循环死等是对CPU的资源浪费

现在我们每个20ms读取当前的按键值,而不是死等,这个20ms就可以直接把抖动滤掉,即使在抖动中也无所谓,因为不管读到高/低电平,最终出来的数都会是没有抖动的,高电平就和上一个20ms的接轨,低电平就和下一个20ms的接轨。

我们还需要用一个变量记住当前的状态和上一个状态,如果现在是0,上一个状态是1,现在就是按键按下的瞬间;如果现在是1,上一个状态是0,现在就是按键松开的瞬间。

我们在检测到按键松手的瞬间就定义一个变量标识符,只有当标识符为1才会一直读取按键值,这样就可以避免死等

//key.c


#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 int Key_GetState()
{
	unsigned char KeyNumber=0;
	
	if(P3_1==0){KeyNumber=1;}
	if(P3_0==0){KeyNumber=2;}
	if(P3_2==0){KeyNumber=3;}
	if(P3_3==0){KeyNumber=4;}
		
	return KeyNumber;	
}

void Key_Loop(void)
{
	static unsigned char 	NowState,LastState;
	LastState=NowState;
	NowState=Key_GetState();
	if(LastState==1&&NowState==0)
	{
		Key_KeyNumber=1;
	}
	if(LastState==2&&NowState==0)
	{
		Key_KeyNumber=2;
	}
	if(LastState==3&&NowState==0)
	{
		Key_KeyNumber=3;
	}
	if(LastState==4&&NowState==0)
	{
		Key_KeyNumber=4;
	}

}
//Key.h

#ifndef __KEY_H_
#define __KEY_H_

unsigned int Key();
void Key_Loop(void);

#endif
//main.c

#include <REGX52.H>
#include " Timer0.h"
#include " Key.h"
#include " Delay.h"
#include " Nixie.h"

unsigned char KeyNum;
unsigned char Temp;

void main()
{
	Timer0_Init();
	while(1)
	{
 		KeyNum=Key();
		if(KeyNum)Temp=KeyNum;
		Nixie(1,Temp);
	}

}

void Timer0_Routine() interrupt 1
{
	static unsigned int	T0Count;
	TL0 = 0x18;  //设置定时初值
	TH0 = 0xFC;  //设置定时初值
	T0Count++;
	if(T0Count>=20)
	{
		T0Count=0;
		Key_Loop();
	}
	
}

2.定时器扫描数码管

数码管也有Delay,如果Delay太久会影响显示效果,即主函数不能有过多的操作,而定时器又可以替代Delay的作用

定时器扫描数码管可以确保数码有很高的优先级,一直在扫描,不会被Delay

定义一个显示缓存区,存9个数据

unsigned char Nixie_Buf[9];

unsigned char NixieTable[]={0x3F,0x06,0x5b,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};

先清零位选,再段选,再位选,这样我们只需隔一段时间调用这个函数,也能起到延时的效果 

void Nixie(unsigned char Location,Number)
{
	P0=0x00;
	switch(Location)
	{
		
		case 1:P2_4=0;P2_3=0;P2_2=0;break;
		case 2:P2_4=0;P2_3=0;P2_2=1;break;
		case 3:P2_4=0;P2_3=1;P2_2=0;break;
		case 4:P2_4=0;P2_3=1;P2_2=1;break;
		case 5:P2_4=1;P2_3=0;P2_2=0;break;
		case 6:P2_4=1;P2_3=0;P2_2=1;break;
		case 7:P2_4=1;P2_3=1;P2_2=0;break;
		case 8:P2_4=1;P2_3=1;P2_2=1;break;
	}
	P0=NixieTable[Number];
}

写一个循环函数,每调用一次显示一位,无条件每隔2ms显示一次

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

这样我们只需要在主函数里调用Nixie_Loop这个函数,就可以做到同时扫描8位

下面再优化一下,把函数也改的规范一点

Nixie_Buf是数据缓存区,一共有9位,但是我们的数码管只有八位,所以Loop函数是从数组的第二位,也就是[1]开始的,所以作为第一位的[0]被跳过,也正是因为跳过了第一位,所以数组的长度是8+1=9

NixieTable是数码管显示数字对应的段码表,新增第十位对应是0,原因后面解释

而Nixie_Buf[]和NixieTable[]数组,是通过函数Nixie_Loop()函数中调用函数Nixie()来实现的

我们可以观察到Nixie_Loop()函数里,调用了Nixie_Scan(i,Nixie_Buf[i]);我们知道Nixie_Scan函数里前面的参数i是决定在哪个LED上显示,而后面的参数Nixie_Buf[i],被嵌套在P0=NixieTable[Number]里,他会通过i的选择,在Nixie_Buf里找到对应的数,再通过这个数,在NixieTable里找到与这个数相对应的数。

而我们前面把Nixie_Buf[]全给10(跳过第一位),也就是说无论

i=任何数

Nixie_Buf[i]=10

NixieTable[10]=0

即什么都不显示

此时我们再写一个新的函数Nixie_SetBuf,就可以通过传入参数,而改变Nixie_Buf里的值,这样就能选取到NixieTable[]其他的数,也就是数码管能显示其他的数字了

//Nixie.c

include <REGX52.H>
#include "Delay.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};

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=0;P2_3=0;P2_2=0;break;
		case 2:P2_4=0;P2_3=0;P2_2=1;break;
		case 3:P2_4=0;P2_3=1;P2_2=0;break;
		case 4:P2_4=0;P2_3=1;P2_2=1;break;
		case 5:P2_4=1;P2_3=0;P2_2=0;break;
		case 6:P2_4=1;P2_3=0;P2_2=1;break;
		case 7:P2_4=1;P2_3=1;P2_2=0;break;
		case 8:P2_4=1;P2_3=1;P2_2=1;break;
	}
	P0=NixieTable[Number];
}

void Nixie_Loop(void)
{
	static unsigned char i;
	Nixie_Scan(i,Nixie_Buf[i]);
	i++;
	if(i>=9){i=1;}
}
//Nixie.h

#ifndef __NIXIE_H
#define __NIXIE_H

void Nixie_Scan(unsigned char Location,Number);
void Nixie_Loop(void);
void Nixie_SetBuf(unsigned char Location,Number);

#endif

 这样就能实现同时在数码管123上都显示我们按下的按键数值

注意这里我们还修改了定时器的计时值,分成两个,T0Count1是按键的,T0Count2是数码管的

//main

#include <REGX52.H>
#include " Timer0.h"
#include " Key.h"
#include " Delay.h"
#include " Nixie.h"

unsigned char KeyNum;
unsigned char Temp;

void main()
{
	Timer0_Init();
	while(1)
	{
 		KeyNum=Key();
		if(KeyNum)
		{
			Nixie_SetBuf(1,KeyNum);
			Nixie_SetBuf(2,KeyNum);
			Nixie_SetBuf(3,KeyNum);
		}
	}
}

void Timer0_Routine() interrupt 1
{
	static unsigned int	T0Count1,T0Count2;
	TL0 = 0x18;  //设置定时初值
	TH0 = 0xFC;  //设置定时初值
	T0Count1++;
	if(T0Count1>=20)
	{
		T0Count1=0;
		Key_Loop();
	}
	
	T0Count2++;
	if(T0Count2>=2)
	{
		T0Count2=0;
		Nixie_Loop();
	}
	
}

下面我们实现在数码管上显示秒表

我们先让数码管显示00-00-00,显示的函数前面我们写了,0也有,那这个“-”呢,自然就是要在NixieTable里加上“0x40”了(这里一个小妙招,我们对3和8在数码管上是不是正好缺了一个“-”,那么“-”就等于0x70对应“8”减去0x30对应“3”刚好就等于0x40)

然后我们安在主函数里调用显示

我们定义了分,秒,和百分秒

void main()
{
	Timer0_Init();
	while(1)
	{
 		KeyNum=Key();
		if(KeyNum)
		{
		}
			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,MinSec/10);
			Nixie_SetBuf(8,MinSec%10);
	}
}

接着我们怎么让他跑起来呢(计时)

我们就要用到计时器,我们先像刚才一样定义一个循环函数,然后在定时器里定义一个新的计时,来循环调用这个循环函数

	T0Count3++;
	if(T0Count3>=10)
	{
		T0Count3=0;
		Sec_Loop();
	}

然后我们再去写循环函数,Sec_Loop

void Sec_Loop(void)
{
	MinSec++;
	if(MinSe>=100)
	{
		MinSec=0;
		Sec++;
		if(Sec>60)
		{
			Sec=0;
			Min++;
			if(Min>=60)
			{
				Min=0;
			}	
	}

	}
}

这样我们的函数就能跑起来了

那我们应该怎么控制启动和暂停呢

定义一个变量,如果这个变量非1就不执行刚才的循环函数,然后如果keynum=1,这个变量取反一下,如果keynum=2,让分秒百分秒都清零

这样我们就实现了默认显示“00-00-00”,按下按键1开始计时,再次按下暂停;按下按键2清零

#include <REGX52.H>
#include " Timer0.h"
#include " Key.h"
#include " Delay.h"
#include " Nixie.h"

unsigned char KeyNum;
unsigned char Min,Sec,MinSec;
unsigned char RunFlag;

void main()
{
	Timer0_Init();
	while(1)
	{
 		KeyNum=Key();
		if(KeyNum==1)
		{
			RunFlag=!RunFlag;
		}
		if(KeyNum==2)
		{
			Min=0;
			Sec=0;
			MinSec=0;
		}
			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,MinSec/10);
			Nixie_SetBuf(8,MinSec%10);
	}
}

void Sec_Loop(void)
{
	if(RunFlag)
	{
		MinSec++;
		if(MinSec>=100)
		{
			MinSec=0;
			Sec++;
			if(Sec>60)
			{
				Sec=0;
				Min++;
				if(Min>=60)
				{
					Min=0;
				}	
		  }
	  }
	}
}

void Timer0_Routine() interrupt 1
{
	static unsigned int	T0Count1,T0Count2,T0Count3;
	TL0 = 0x18;  //设置定时初值
	TH0 = 0xFC;  //设置定时初值
	T0Count1++;
	if(T0Count1>=20)
	{
		T0Count1=0;
		Key_Loop();
	}
	
	T0Count2++;
	if(T0Count2>=2)
	{
		T0Count2=0;
		Nixie_Loop();
	}
	
	T0Count3++;
	if(T0Count3>=10)
	{
		T0Count3=0;
		Sec_Loop();
	}
	
}

接下来我们再加入存储功能,即AT24C02

如果按键3按下,写入,调用,然后延时,分别存入分秒百分秒

如果按键4按下,读出,调用

		if(KeyNum==3)
		{
			AT24C02_WriteByte(0,Min);
			Delay(5);
			AT24C02_WriteByte(1,Sec);
			Delay(5);
			AT24C02_WriteByte(2,MinSec);
			Delay(5);
		}
		if(KeyNum==4)
		{
			Min=AT24C02_ReadByte(0);
			Sec=AT24C02_ReadByte(1);
			MinSec=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,MinSec/10);
			Nixie_SetBuf(8,MinSec%10);
	}

这样我们就实现全部功能啦

下面是完整代码

#include <REGX52.H>
#include " Timer0.h"
#include " Key.h"
#include " Delay.h"
#include " Nixie.h"
#include " AT24C02.h"
#include " I2C.h"

unsigned char KeyNum;
unsigned char Min,Sec,MinSec;
unsigned char RunFlag;

void main()
{
	Timer0_Init();
	while(1)
	{
 		KeyNum=Key();
		if(KeyNum==1)
		{
			RunFlag=!RunFlag;
		}
		if(KeyNum==2)
		{
			Min=0;
			Sec=0;
			MinSec=0;
		}
		if(KeyNum==3)
		{
			AT24C02_WriteByte(0,Min);
			Delay(5);
			AT24C02_WriteByte(1,Sec);
			Delay(5);
			AT24C02_WriteByte(2,MinSec);
			Delay(5);
		}
		if(KeyNum==4)
		{
			Min=AT24C02_ReadByte(0);
			Sec=AT24C02_ReadByte(1);
			MinSec=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,MinSec/10);
			Nixie_SetBuf(8,MinSec%10);
	}
}

void Sec_Loop(void)
{
	if(RunFlag)
	{
		MinSec++;
		if(MinSec>=100)
		{
			MinSec=0;
			Sec++;
			if(Sec>60)
			{
				Sec=0;
				Min++;
				if(Min>=60)
				{
					Min=0;
				}	
		  }
	  }
	}
}

void Timer0_Routine() interrupt 1
{
	static unsigned int	T0Count1,T0Count2,T0Count3;
	TL0 = 0x18;  //设置定时初值
	TH0 = 0xFC;  //设置定时初值
	T0Count1++;
	if(T0Count1>=20)
	{
		T0Count1=0;
		Key_Loop();
	}
	
	T0Count2++;
	if(T0Count2>=2)
	{
		T0Count2=0;
		Nixie_Loop();
	}
	
	T0Count3++;
	if(T0Count3>=10)
	{
		T0Count3=0;
		Sec_Loop();
	}
	
}

相关文章:

  • 文心一言:中国大模型时代的破局者与探路者
  • pytorch 50 大模型导出的onnx模型优化尝试
  • linux环保监测4G边缘网关:环境数据的可靠传输者
  • 在基于Arm架构的华为鲲鹏服务器上,针对openEuler 20.03 LTS操作系统, 安装Ansible 和MySQL
  • 洛谷每日1题-------Day15__P1307 [NOIP 2011 普及组] 数字反转
  • EasyCVR平台赋能农业产业园:AIoT驱动的视频监控与大数据分析解决方案
  • 订单支付系统如何做到一致性
  • 自动控制原理【知识点总结、复习笔记】-2
  • OEM SQL Details and Session Details 5s 或者parallel 才会在sql monitor显示
  • 【Recon】CTF Web类题目主要类型
  • 【GPT入门】第1课准备环境
  • c语言笔记 数组指针
  • 英语开口说(先开口,再完善)——主动牺牲学习成本高昂的语法规则收益,来最大程度满足即刻对话交流需求
  • 高效数据分析实战指南:Python零基础入门
  • Unity开发时,visual studio编辑器中文注释的解决办法
  • 13.C语言指针的易错点
  • 蓝桥杯刷题周计划(第一周)
  • 【 <一> 炼丹初探:JavaWeb 的起源与基础】之 JSP 标签库:自定义标签的开发与应用
  • 4个 Vue 路由实现的过程
  • Unity大型游戏开发全流程指南
  • 武汉警方通报一起故意伤害案件:1人死亡,嫌疑人已被抓获
  • 梅花奖在上海|秦海璐:演了15年《四世同堂》,想演一辈子
  • “复旦源”一源六馆焕新启幕,设立文化发展基金首期1亿元
  • 多图|多款先进预警机亮相雷达展,专家:中国预警机已达世界先进水平
  • 墨西哥海军一载两百余人帆船撞上纽约布鲁克林大桥,多人落水
  • 浙江演艺集团7部作品组团来沪,今夏开启首届上海演出季