一、数据存储
先把需要的模块导入做个测试
//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 0xA0void 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.cinclude <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_Hvoid 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();}}