AutoLeaders控制组——51单片机学习笔记(二)

模块化编程

传统方式编程:所有的函数均放在main.c里,若使用的模块比较多,则一个文件内会有很多的代码,不利于代码的组织和管理,而且很影响编程者的思路
模块化编程:把各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数的声明,其它.c文件想使用其中的代码时,只需要#include "XXX.h"文件即可。使用模块化编程可极大的提高代码的可阅读性、可维护性、可移植性等请添加图片描述
各个模块在使用时要include头文件,且模块中的函数需要在.h文件里面声明,需要在其他模块或者住函数里用到的变量即其他文件需要引用外部变量是,需要声明该变量并在前面加上extern,表示外部文件可引用。

LCD1602

LCD1602作为一个显示窗口只要熟练运用LCD1602显示模块中定义的各类显示函数就可以了,这里就不再赘叙,以下是LCD1602调试的一个示例代码:

#include <REGX52.H>
#include "LCD1602.h"
#include "delay.h"
unsigned char num=0;
void main()
{
	LCD_Init();
	
	//LCD_ShowString(1,1,"hello");
	//LCD_ShowChar(1,6,'A');
	//LCD_ShowNum(1,7,123,3);
	//LCD_ShowHexNum(1,10,0xA8,2);
	//LCD_ShowBinNum(2,1,0xAA,8);
	while(1){
		LCD_ShowChar(1,10,'b');
		LCD_ShowNum(1,1,num,2);
		Delay(1000);
		num++;
		if(num==3)     //跳秒三秒后弹出gameover
		{
			num=0;
			while(1){
				LCD_ShowString(1,1,"game over");
				if(P3_1==0){     //按下独立按键重新开始
					Delay(20);
					while(P3_1==0);
					Delay(20);
					LCD_Init();   //在LCD1602显示模块中没有写清除函数,这里用初始化来清除原先显示
					break;        //但是只清除这里的gameover,与开头那个b无关
				}
			}
		}
	}
}

矩阵键盘

矩阵键盘与数码管有些类似,下面从扫描上分析:
数码管扫描(输出扫描)
原理:显示第1位→显示第2位→显示第3位→……,然后快速循环这个过程,最终实现所有数码管同时显示的效果;
矩阵键盘扫描(输入扫描)
原理:读取第1行(列)→读取第2行(列) →读取第3行(列) → ……,然后快速循环这个过程,最终实现所有按键同时检测的效果。
以上两种扫描方式的共性:节省I/O口。

在这里插入图片描述矩阵键盘原理图

针对矩阵键盘按键的扫描读取写一个模块函数,大致思路即逐行逐列地扫描并输出检测到低电平(被按下)的按键并返回对应的值。如下是部分代码:

unsigned char Matrixkey()
{
	unsigned char keynum=0;
	P1=0xff;
	P1_3=0;
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);keynum=1;}
	if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);keynum=5;}
	if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);keynum=9;}
	if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);keynum=13;}
	
	P1=0xff;
	P1_2=0;
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);keynum=2;}
	if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);keynum=6;}
	if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);keynum=10;}
	if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);keynum=14;}
	
	//······
	return keynum;
	
}

然后就可以做一些应用,如写一个矩阵键盘密码锁如下:

unsigned char keynum=0,i=0,j=0,flag=0;
unsigned char password[10],pass[6]={1,2,3,4,5,6};   //设置密码
void main()
{
	LCD_Init();
	
	while(1)
	{
		keynum=Matrixkey();  //*
		if(keynum){        //如果键盘输入不为0
			if(keynum<=10) 
			{
				if(i<6)    //限制6位密码
				{
					password[i]=keynum%10;    //取余,使得S10取0
					LCD_ShowNum(1,9,keynum,1);  //显示正在输入的数字
					LCD_ShowNum(1,i+1,password[i],1);  //逐位显示输入
					i++;
				}
			}
			if(keynum==16)
			{
				for(j=0;j<6;j++)       //逐位核对密码
				{
					if(pass[j]!=password[j])  
					{
						flag=1;
						break;
					}
				}
				if(flag==0){         //密码正确
					LCD_ShowString(2,1,"OK ");
				}else LCD_ShowString(2,1,"ERR");   //密码错误
			}
			if(keynum==12){          //密码输入清零
					flag=0;
					for(i=0;i<6;i++){password[i]=0;}
					i=0;
					LCD_Init();      //清空显示
				}
			if(keynum==11){          //删除输入
				if(i>0){
					password[i-1]=0;
					LCD_ShowNum(1,i,password[i-1],1);
					i--;
				}
			}
		}
	}
}

定时器

定时器简介

1.51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。定时器用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作,替代长时间的Delay,提高CPU的运行效率和处理速度。

2.STC89C52型号单片机的定时器资源:
定时器个数:3个(T0、T1、T2),T0和T1与传统的51单片机兼容,T2是STC89C52型号单片机增加的资源。
注意:定时器的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的定时器个数和操作方式,但一般来说,T0和T1的操作方式是所有51单片机所共有的。

3.定时器工作原理简介:
定时器在单片机内部就像一个小闹钟一样,根据时钟的输出信号,每隔“一秒”,计数单元的数值就增加一,当计数单元数值增加到“设定的闹钟提醒时间”时,计数单元就会向中断系统发出中断申请,产生“响铃提醒”,使程序跳转到中断服务函数中执行。

STC89C52的T0和T1均有四种工作模式:
模式0:13位定时器/计数器
模式1:16位定时器/计数器(常用)
模式2:8位自动重装模式
模式3:两个8位计数器
工作模式1框图:在这里插入图片描述

中断系统

在这里插入图片描述
示意图:
在这里插入图片描述
STC89C52中断资源:
中断源个数:8个(外部中断0、定时器0中断、外部中断1、定时器1中断、串口中断、定时器2中断、外部中断2、外部中断3)
中断优先级个数:4个
中断号:
在这里插入图片描述
下图为传统C51的中断系统原理图,即接上面定时器工作模式1框图中最后箭头指向的interrupt部分后续:
在这里插入图片描述

定时器相关寄存器

在这里插入图片描述
在这里插入图片描述

寄存器是连接软硬件的媒介,在单片机中寄存器就是一段特殊的RAM存储器。一方面,寄存器可以存储和读取数据,另一方面,每一个寄存器背后都连接了一根导线,控制着电路的连接方式。
寄存器相当于一个复杂机器的“操作按钮”。

定时器的简单应用

首先写一个定时器的模块,代码如下:

#include <REGX52.H>
void Timer0_Init(void)		//1毫秒@11.0592MHz
{
	TMOD &= 0xF0;			//设置定时器模式
	TMOD |= 0x01;			//设置定时器模式
	TL0 = 0x66;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
	ET0 = 1;
	EA = 1;
	PT0 = 0;
}

这一部分的定时器初始化设置代码可以在stc中定时器计算器中自动生成,使用时注意调设好相关信息,如使用的板子晶振频率为11.0592MHz,选择的模式是16位自动重装,还有选择的定时器时钟为1T等等。

下面是应用定时器来写一个时钟的代码:

unsigned char sec=58,min=59,hour=23,stop=0,keynum=0;
void main()
{
	LCD_Init();
	Timer0_Init();
	LCD_ShowString(1,1,"clock:");
	LCD_ShowString(2,1,"  :  :");
	while(1)
	{
		keynum=key();
		if(keynum==1)             //暂停
		{
			stop++;
			if(stop>=2)
				stop=0;
		}
		if(keynum==2)             //清零并暂停
		{
			sec=min=hour=0;
			stop=1;
		}
		LCD_ShowNum(2,1,hour,2);      //时分秒显示
		LCD_ShowNum(2,4,min,2);
		LCD_ShowNum(2,7,sec,2);
	}
}
void Time0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0=0x18;
	TH0=0xFC;
	T0Count++;
	if(T0Count>=1000)
	{
		T0Count=0;
		if(stop==0)  	//如果计时继续
		{
			sec++;     //跳秒  
			if(sec>=60){sec=0;min++;}
			if(min>=60){min=0;hour++;}
			if(hour>=24){hour=0;min=0;sec=0;}
		}
		if(stop==1);		
	}

注意在主函数后加一个中断函数,在里面设置中断间隔的时长以及中断时进行的操作,结构大致不变,所以可以注释在定时器模块里,在使用时CV并作相应修改即可。

串口通信

简介

串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。
单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大的扩展了单片机的应用范围,增强了单片机系统的硬件实力。
51单片机内部自带UART(Universal Asynchronous Receiver Transmitter,通用异步收发器),可实现单片机的串口通信。
请添加图片描述
全双工:通信双方可以在同一时刻互相传输数据
半双工:通信双方可以互相传输数据,但必须分时复用一根数据线
单工:通信只能有一方发送到另一方,不能反向传输
异步:通信双方各自约定通信速率
同步:通信双方靠一根时钟线来约定通信速率
总线:连接各个设备的数据传输线路(类似于一条马路,把路边各住户连接起来,使住户可以相互交流)

51中的UART

STC89C52有1个UART。STC89C52的UART有四种工作模式:
模式0:同步移位寄存器
模式1:8位UART,波特率可变(常用)
模式2:9位UART,波特率固定
模式3:9位UART,波特率可变
在这里插入图片描述

串口参数

波特率:串口通信的速率(发送和接收各数据位的间隔时间)
检验位:用于数据验证
停止位:用于数据帧间隔

以下分别为8位和9位数据格式的时序图:
在这里插入图片描述
在这里插入图片描述

串口与中断系统

在这里插入图片描述
在这里插入图片描述

串口相关寄存器

在这里插入图片描述SBUF:串口数据缓存寄存器,物理上是两个独立的寄存器,但占用相同的地址。写操作时,写入的是发送寄存器,读操作时,读出的是接收寄存器。

数据显示形式

HEX模式/十六进制模式/二进制模式:以原始数据的形式显示;
文本模式/字符模式:以原始数据编码后的形式显示。

代码

首先写一个UART的模块,同样在stp中可以找到工具生成初始化函数代码;

void UART_Init(void)	//4800bps@11.0592MHz
{
	PCON |= 0x80;		//使能波特率倍速位SMOD
	SCON = 0x50;		//8位数据,可变波特率,这里0x40是ren置0,只能接收;若要能接受发送需要置1,即0x50
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x20;		//设置定时器模式
	TL1 = 0xF4;			//设置定时初始值
	TH1 = 0xF4;			//设置定时重载值
	ET1 = 0;			//禁止定时器中断
	TR1 = 1;			//定时器1开始计时
	EA=1;               //打开总中断
	ES=1;               //打开串口中断
}
void UART_SendByte(unsigned char Byte)    //向电脑发送数据
{
	SBUF=Byte;
	while(TI==0);        //发送
	TI=0;                //必须软件复位
}

以下是简单的应用代码:

#include <REGX52.H>
#include "delay.h"
#include "uart.h"
unsigned char sec;
void main()
{
	UART_Init();
	
	while(1)
	{
		
	}
}
void UART_Routine() interrupt 4
{
	if(RI==1)                //接收中断标志位  
	{
		P2=~SBUF;            //控制LED亮灭
		UART_SendByte(SBUF);//同时发送数据给带电脑,双向通信
		RI=0;                //同样必须软件复位
	}
}

LED点阵屏

显示原理

LED点阵屏的结构类似于数码管,只不过是数码管把每一列的像素以“8”字型排列而已;LED点阵屏与数码管一样,有共阴和共阳两种接法,不同的接法对应的电路结构不同;LED点阵屏需要进行逐行或逐列扫描,才能使所有LED同时显示。
请添加图片描述开发板引脚对应关系

74HC595

74HC595是串行输入并行输出的移位寄存器,可用3根线输入串行数据,8根线输出并行数据,多片级联后,可输出16位、24位、32位等,常用于IO口扩展。
请添加图片描述

C51的sfr、sbit

1.sfr(special function register):特殊功能寄存器声明;
例:sfr P0 = 0x80;
声明P0口寄存器,物理地址为0x80;
2.sbit(special bit):特殊位声明;
例:sbit P0_1 = 0x81; 或 sbit P0_1 = P0^1;
声明P0寄存器的第1位;
3.可位寻址/不可位寻址:在单片机系统中,操作任意寄存器或者某一位的数据时,必须给出其物理地址,又因为一个寄存器里有8位,所以位的数量是寄存器数量的8倍,单片机无法对所有位进行编码,故每8个寄存器中,只有一个是可以位寻址的。对不可位寻址的寄存器,若要只操作其中一位而不影响其它位时,可用“&=”、“|=”、“^=”的方法进行位操作。

LED点阵屏显示动画

(详见代码注释)
先写一个控制点阵屏的模块,如下:

#include <REGX52.H>
#include "delay.h"
#define MatriLED_PORT    P0    //记得不要加分号等号
sbit RCK=P3^5;    //RCLK
sbit SCK=P3^6;    //SERCLK
sbit SER=P3^4;    //SER
void Matrix_Init()
{
	SCK=0;
	RCK=0;
}
void _74HC595_WriteByte(unsigned char Byte)
{
	unsigned char i;       //74HC595写入一个字节Byte
	for(i=0;i<8;i++)       //装弹夹,一个一个往下压,装满八个打出
	{
		SER=Byte&(0x80>>i);
		SCK=1;            //上升沿移位写入数据时上升沿有效
		SCK=0;            //复位
	}
	RCK=1;               //打开上升沿锁存,数据移出
	RCK=0;
}
void MatrixLED_ShowColumn(unsigned char Column,Data)
{
	_74HC595_WriteByte(Data);  //高位在上低位在下,1亮0灭,控制点阵屏的每一列
	MatriLED_PORT=~(0x80>>Column);
	Delay(1);              //消影
	MatriLED_PORT=0xff;    //对于点阵屏的每一列来说1灭0亮,所以复位用0xff
}

主函数部分:

#include <REGX52.H>
#include "delay.h"
#include "MatrixLED.h"
#include "timer0.h"
unsigned char code Animation[]={       //这里加code后数组在flash存放,且不能更改,不加code的话存放在ram里
	0x70,0xF8,0xFC,0x7F,0x7F,0xFC,0xF8,0x70,  //占用ram内存,但是flash内存较大,所以选择flash
	0x30,0x78,0x7C,0x3E,0x3E,0x7C,0x78,0x30,
	0x00,0x00,0x30,0x38,0x38,0x30,0x00,0x00,
	0x30,0x78,0x7C,0x3E,0x3E,0x7C,0x78,0x30,
};
unsigned char offset=0;    //用于移位
void main()
{	
	unsigned char i;      //变量i定义要在初始化之前
	Timer0_Init();        //记得两个都要初始化
	Matrix_Init();
	while(1)
	{
		for(i=0;i<8;i++)
		{
			MatrixLED_ShowColumn(i,Animation[(i+offset)%32]);  //这里加上变量再%数组大小可以实现从结尾自动回到开头
		}
		
	}
}
void Time0_Routine() interrupt 1     //利用定时器来控制动画帧数
{
  static unsigned int T0Count;
	TL0=0x18;
	TH0=0xFC;
	T0Count++;
	if(T0Count>=200)
	{
		T0Count=0;
		offset+=8;      //滚动播放+1,直接切画面+8
	}
}

DS1302

原理介绍

请添加图片描述
寄存器定义:
在这里插入图片描述
时序定义:
在这里插入图片描述

显示时BCD码与十进制的转换

BCD码(Binary Coded Decimal‎),用4位二进制数来表示1位十进制数
例:0001 0011表示13,1000 0101表示85,0001 1010不合法
在十六进制中的体现:0x13表示13,0x85表示85,0x1A不合法
BCD码转十进制:DEC=BCD/1610+BCD%16; (2位BCD)
十进制转BCD码:BCD=DEC/10
16+DEC%10; (2位BCD)

可调时钟

(详见代码注释)
首先写一个控制DS1302的的模块如下:

#include <REGX52.H>
sbit DS1302_SCLK=P3^6;        //给三个接口换名称
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;
#define DS1302_SECOND		0x80     //给地址换名称
#define DS1302_MINUTE		0x82
#define DS1302_HOUR			0x84
#define DS1302_DATE			0x86
#define DS1302_MONTH		0x88
#define DS1302_DAY			0x8A        //星期
#define DS1302_YEAR			0x8C
#define DS1302_WP			0x8E        //写入保护
unsigned char DS1302_Time[]={23,12,9,22,25,55,6};
void DS1302_Init(void)
{
	DS1302_CE=0;           //初始化函数要把CE,SCLK置0
	DS1302_SCLK=0;
}
void DS1302_WriteByte(unsigned char Command,Data)
{
	unsigned char i=0;
	DS1302_CE=1;            //开始先把CE拉高到1,但前提是SCLK为0
	for(i=0;i<8;i++)          //先给命令语句指定要接下来存储的寄存器
	{
		DS1302_IO=Command&(0X01<<i); //利用for循环和左移     
		DS1302_SCLK=1;     //写入数据时上升沿有效,所以SCLK先置1,后置0 
		DS1302_SCLK=0;     //这里由于DS1302芯片处理的速度比单片机快,所以这里SCLK两个语句间不用加delay
	}
	for(i=0;i<8;i++)     //接下来写入内容数据
	{
		DS1302_IO=Data&(0X01<<i);//与0x01即提取出数据的第一位(&是有0则0,、|是有1则1)
		DS1302_SCLK=1;
		DS1302_SCLK=0;
	}
	DS1302_CE=0;          //结束时CE归0
	
}
unsigned char DS1302_ReadByte(unsigned char Command)   //读函数要返回值
{
	unsigned char i=0,Data=0x00;
	Command|=0x01;      //这里或0x01即把上一个写入函数中的命令的首位0换成1,这样就可以读函数的命令参数共用写函数的
	DS1302_CE=1;
	for(i=0;i<8;i++)
	{
		DS1302_IO=Command&(0X01<<i);
		DS1302_SCLK=0;    //读数据时下降沿有效,所以SCLK先置0后置1
		DS1302_SCLK=1;
	}
	for(i=0;i<8;i++)
	{
		DS1302_SCLK=1;    //由于读数据时的脉冲为15个比写时少一个周期,所以这里先给SCLK置1,卡住补回一个周期
		DS1302_SCLK=0;
		if(DS1302_IO){Data|=(0X01<<i);}  //这里是判断IO口收到的数据是否为1,为1则写入Data,为0则不变,达到接收数据效果
	}
	DS1302_CE=0;
	return Data;
}
unsigned char BCD(unsigned char DCE)  //DCE转BCD
{
	unsigned char i=0;
	i=DCE/10*16+DCE%10;
	return i;
}
unsigned char DCE(unsigned char BCD)  //BCD转DCE
{
	unsigned char i=0;
	i=BCD/16*10+BCD%16;
	return i;
}
void DS1302_SetTime(void)      //把数组中设置的时间写入交给DS1302
{
	DS1302_WriteByte(DS1302_WP,0x00);       //0x00解除写保护
	DS1302_WriteByte(DS1302_YEAR,BCD(DS1302_Time[0]));    //这里用写入函数传入参数要传16进制数
	DS1302_WriteByte(DS1302_MONTH,BCD(DS1302_Time[1]));
	DS1302_WriteByte(DS1302_DATE,BCD(DS1302_Time[2]));
	DS1302_WriteByte(DS1302_HOUR,BCD(DS1302_Time[3]));
	DS1302_WriteByte(DS1302_MINUTE,BCD(DS1302_Time[4]));
	DS1302_WriteByte(DS1302_SECOND,BCD(DS1302_Time[5]));
	DS1302_WriteByte(DS1302_DAY,BCD(DS1302_Time[6]));
	DS1302_WriteByte(DS1302_WP,0X80);       //0x80开启写保护
}
void DS1302_ReadTime(void)    //读出时间并转化成DCE存入数组中
{
	DS1302_Time[0]=DCE(DS1302_ReadByte(DS1302_YEAR));
	DS1302_Time[1]=DCE(DS1302_ReadByte(DS1302_MONTH));
	DS1302_Time[2]=DCE(DS1302_ReadByte(DS1302_DATE));
	DS1302_Time[3]=DCE(DS1302_ReadByte(DS1302_HOUR));
	DS1302_Time[4]=DCE(DS1302_ReadByte(DS1302_MINUTE));
	DS1302_Time[5]=DCE(DS1302_ReadByte(DS1302_SECOND));
	DS1302_Time[6]=DCE(DS1302_ReadByte(DS1302_DAY));
}

main部分:

unsigned char keynum,MODE,TimeSetselect,TimeSetFlashFlag;
void TimeShow(void)
{
	DS1302_ReadTime();        //读入写入数组的时间
	LCD_ShowNum(1,1,20,2);
	LCD_ShowNum(1,3,DS1302_Time[0],2);
	LCD_ShowNum(1,6,DS1302_Time[1],2);
	LCD_ShowNum(1,9,DS1302_Time[2],2);
	LCD_ShowNum(2,1,DS1302_Time[3],2);
	LCD_ShowNum(2,4,DS1302_Time[4],2);
	LCD_ShowNum(2,7,DS1302_Time[5],2);
	LCD_ShowNum(1,12,DS1302_Time[6],1);
}
void TimeSet(void)
{	
	if(keynum==2)             //切换模式
	{	
		TimeSetselect++;
		TimeSetselect%=7;     //到6之后切成0
	}
	if(keynum==3)             //增加
	{
		DS1302_Time[TimeSetselect]++;
		if(DS1302_Time[0]>99){DS1302_Time[0]=0;}     //设置时间上限
		if(DS1302_Time[1]>12){DS1302_Time[1]=1;}
		if(DS1302_Time[1]==4||DS1302_Time[1]==6||DS1302_Time[1]==9||DS1302_Time[1]==11) //判断月份天数
		{
			if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
		}else if(DS1302_Time[1]==2)
		{
			if(DS1302_Time[0]%4==0)  //判断是否闰年,由于DS1302只计100年内所以简化闰年判断
			{
				if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
			}
			else if(DS1302_Time[0]%4!=0)
			{
				if(DS1302_Time[2]>28){DS1302_Time[2]=1;}
			}
		}else{
			if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
		}
		if(DS1302_Time[3]>23){DS1302_Time[3]=0;}
		if(DS1302_Time[4]>59){DS1302_Time[4]=0;}
		if(DS1302_Time[5]>59){DS1302_Time[5]=0;}
		if(DS1302_Time[6]>7){DS1302_Time[6]=1;}
	}
	if(keynum==4)          //减少
	{
		DS1302_Time[TimeSetselect]--;              //减少的部分一样要设置上限
		if(DS1302_Time[0]>99){DS1302_Time[0]=99;}  //由于定义的数组为无符号char,范围为0~255,小于0越界之后会跳到255
		if(DS1302_Time[1]<1){DS1302_Time[1]=12;}   //所以要么把数组改成有符号要么就如这里<0时写>99
		if(DS1302_Time[1]==4||DS1302_Time[1]==6||DS1302_Time[1]==9||DS1302_Time[1]==11)
		{
			if(DS1302_Time[2]<1){DS1302_Time[2]=30;}
		}else if(DS1302_Time[1]==2)
		{
			if(DS1302_Time[0]%4==0)
			{
				if(DS1302_Time[2]<1){DS1302_Time[2]=29;}
			}
			else if(DS1302_Time[0]%4!=0)
			{
				if(DS1302_Time[2]<1){DS1302_Time[2]=28;}
			}
		}else{
			if(DS1302_Time[2]<1){DS1302_Time[2]=31;}
		}
		if(DS1302_Time[3]>99){DS1302_Time[3]=23;}
		if(DS1302_Time[4]>99){DS1302_Time[4]=59;}
		if(DS1302_Time[5]>99){DS1302_Time[5]=59;}
		if(DS1302_Time[6]<1){DS1302_Time[6]=7;}
	}
	if(TimeSetselect==0&&TimeSetFlashFlag==1){LCD_ShowString(1,1,"    ");}//这里设置闪烁功能,判断选中和闪烁标志变量来控制亮灭
	else{LCD_ShowNum(1,1,20,2);LCD_ShowNum(1,3,DS1302_Time[0],2);}
	if(TimeSetselect==1&&TimeSetFlashFlag==1){LCD_ShowString(1,6,"  ");}  //用空格覆盖实现灭,注意要用ShowString
	else{LCD_ShowNum(1,6,DS1302_Time[1],2);}
	if(TimeSetselect==2&&TimeSetFlashFlag==1){LCD_ShowString(1,9,"  ");}
	else{LCD_ShowNum(1,9,DS1302_Time[2],2);}
	if(TimeSetselect==3&&TimeSetFlashFlag==1){LCD_ShowString(2,1,"  ");}
	else{LCD_ShowNum(2,1,DS1302_Time[3],2);}
	if(TimeSetselect==4&&TimeSetFlashFlag==1){LCD_ShowString(2,4,"  ");}
	else{LCD_ShowNum(2,4,DS1302_Time[4],2);}
	if(TimeSetselect==5&&TimeSetFlashFlag==1){LCD_ShowString(2,7,"  ");}
	else{LCD_ShowNum(2,7,DS1302_Time[5],2);}
	if(TimeSetselect==6&&TimeSetFlashFlag==1){LCD_ShowString(1,12," ");}
	else{LCD_ShowNum(1,12,DS1302_Time[6],1);}
}
void main()
{
	LCD_Init();
	DS1302_Init();
	Timer0_Init();
	LCD_ShowString(1,1,"    -  -  -");
	LCD_ShowString(2,1,"  :  :");
	DS1302_SetTime();              //设置写入时间
	while(1)
	{
		keynum=key();
		if(keynum==1)
		{
			MODE=1-MODE;                  //两种方式控制MODE的01切换
			if(MODE==0) DS1302_SetTime(); //这里MODE跳回显示模式后写入设置后的时间
			if(MODE==1) TimeSetselect=0;  //跳转设置模式后选择位跳回第一个
//			if(MODE==0){MODE=1;TimeSetselect=0;}
//			else if(MODE==1){MODE=0;DS1302_SetTime();}
		}
		switch(MODE)                     //用switch实现用变量MODE控制模式
		{
			case 0:TimeShow();break;
			case 1:TimeSet();break;
		}
	}
}
void Time0_Routine() interrupt 1   //定时器实现闪烁功能
{
  static unsigned int T0Count;
	TL0=0x18;
	TH0=0xFC;
	T0Count++;
	if(T0Count>=500)
	{
		T0Count=0;
		TimeSetFlashFlag=!TimeSetFlashFlag;  //定义一个变量取反10来对应亮灭
	}
}
  • 26
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值