应朋友要求帮忙修改一下某宝上的程序,作为应付期末作业
这里我们删除了时钟模块和DS12B20温度模块,在作业中没有要求到,增加一个DI发光二极管作为秒表指示 。
#include
#define uint unsigned int
#define uchar unsigned char
#define u8 unsigned char
#define yh 0x80 //LCD第一行的初始位置,因为LCD1602字符地址首位D7恒定为1(100000000=80)
#define er 0x80+0x40 //LCD第二行初始位置(因为第二行第一个字符位置地址是0x40)
//液晶屏的与C51之间的引脚连接定义(显示数据线接C51的P0口)
sbit en=P2^7;
sbit rw=P2^6; //如果硬件上rw接地,就不用写这句和后面的rw=0了
sbit rs=P2^5;
//校时按键与C51的引脚连接定义
sbit KEY1=P3^0; //KEY1
sbit KEY2=P3^1; //KEY2
sbit KEY3=P3^2; //KEY3
sbit KEY4=P3^3; //KEY4
sbit DQ=P3^7; //
sbit buzzer=P2^0; //蜂鸣器,通过三极管8550驱动,端口低电平响
//sbit LCD_Back=P2^4; //LCD背光开关
sbit LED = P2^1;
uchar a;
uint Timer=0;
uchar Timer_ss=0;
uchar Timer_mm=0;
uchar Timer_hh=0;
uchar A_Timer_mm=1;
uchar A_Timer_hh=0;
bit A_T_Flag=1; //闹钟开关标志位
bit A_S_Flag=1; //闹钟设置标志位
uchar Set_Timer=0; //设置标志位
uchar *tab2="Timer:00:00:00";
uchar *tab1="Alarm Clock!";
变量初始化以及硬件外设初始化
//定时器0初始化
void Timer0_Inir(void)
{
TMOD |= 0x01; //定时器0 模式1
TL0 = (65536-500)%256;
TH0 = (65536-500)/256; //进中断时间为500uS
TCON |= 0x10; //开启定时器0中断
ET0 = 1;//定时器0 允许中断
EA = 1; //开启总中断
}
定时器初始化函数
工作方式 | M1 | M0 | 说明 |
---|---|---|---|
方式0 | 0 | 0 | 定时器/计数器T0工作在方式0时,16位计数器只用了13位,即TH0的高8位和TL0的低5位,组成一个13位定时器/计数器(最大计数值8192)。 |
方式1 | 0 | 1 | 定时器T0工作方式1与工作方式0类同,差别在于其中的计数器的位数。工作方式0以13位计数器参与计数,工作方式1则以16位计数器参与计数(最大计数值65536)。 |
方式2 | 1 | 0 | 自动重装THx存放初值(预置数) ,TLx用于计数,计数完毕则自动将THx的值赋给TLx。(除了重装初值和最大计数值以外,其他同方式0) |
方式3 | 1 | 1 | 这种式作方式之下,定时/计数器0被拆成2个独立的定时/计数器来用。其中,TL0可以构成8位的定时器或计数器的工作方式,而TH0则只能作为定时器来用。我们知道作定时、计数器来用,需要控制,计满后溢出需要有溢出标记,T0被分成两个来用,那就要两套控制及、溢出标记了,从何而来呢?TL0还是用原来的T0的标记,而TH0则借用T1的标记。 |
//延时函数,后面经常调用
void delay(uint xms)//延时函数,有参函数
{
uint x,y;
for(x=xms;x>0;x--)
for(y=110;y>0;y--);
}
延时函数
void write_1602com(uchar com)//****液晶写入指令函数****
{
rs=0;//数据/指令选择置为指令
rw=0; //读写选择置为写
P0=com;//送入数据
delay(1);
en=1;//拉高使能端,为制造有效的下降沿做准备
delay(1);
en=0;//en由高变低,产生下降沿,液晶执行命令
}
LCD1602的指令写入函数,控制1602的工作模式,显示位置、光标等
void write_1602dat(uchar dat)//***液晶写入数据函数****
{
rs=1;//数据/指令选择置为数据
rw=0; //读写选择置为写
P0=dat;//送入数据
delay(1);
en=1; //en置高电平,为制造下降沿做准备
delay(1);
en=0; //en由高变低,产生下降沿,液晶执行命令
}
LCD1602的数据写入函数,控制1602显示的内容
void lcd_init()//***液晶初始化函数****
{
write_1602com(0x38);//设置液晶工作模式,意思:16*2行显示,5*7点阵,8位数据
write_1602com(0x0c);//开显示不显示光标
write_1602com(0x06);//整屏不移动,光标自动右移
write_1602com(0x01);//清显示
write_1602com(yh);//日历显示固定符号从第一行第1个位置之后开始显示
for(a=0;a<12;a++)
{
write_1602dat(tab1[a]);//向液晶屏写名称显示的固定符号部分
//delay(3);
}
write_1602com(er);//时间显示固定符号写入位置,从第2个位置后开始显示
for(a=0;a<14;a++)
{
write_1602dat(tab2[a]);//写显示时间固定符号,两个冒号
//delay(3);
}
}
已知1206自带ASCII编码,所以直接输入字符编码即可显示,无需取模
void Beep_d(void)
{
buzzer=0;
delay(100);
buzzer=1;
}
蜂鸣器短响函数
void LCD_TimeDispay(u8 hh,u8 mm,u8 ss)
{
write_1602com(er+6);//时间显示固定符号写入位置,从第2个位置后开始显示
write_1602dat((hh/10)+0x30);//显示时十位
write_1602dat((hh%10)+0x30);//显示时个位
write_1602com(er+9);//时间显示固定符号写入位置,从第2个位置后开始显示
write_1602dat((mm/10)+0x30);//显示时十位
write_1602dat((mm%10)+0x30);//显示时个位
write_1602com(er+12);//时间显示固定符号写入位置,从第2个位置后开始显示
write_1602dat((ss/10)+0x30);//显示时十位
write_1602dat((ss%10)+0x30);//显示时个位
}
LCD时间显示刷新函数,CPU空余时间(按键未按下、蜂鸣器未响是)都在运行,保证时间的实时显示
void Alarm_Clock(void)
{
if(A_T_Flag)
{
write_1602com(yh+13);//闹钟开关显示
write_1602dat('A');
write_1602dat('O');
write_1602dat('N');
if(Timer_mm == A_Timer_mm)
{
if(Timer_hh == A_Timer_hh)
{
Beep_d();
}
}
}
else
{
write_1602com(yh+13);//闹钟开关显示
write_1602dat('A');
write_1602dat('O');
write_1602dat('F');
}
}
闹钟功能开关判断函数
void Key_Scan(void)
{
if(!KEY1)
{
Beep_d(); //蜂鸣器短响,提示按键按下
write_1602com(yh+13);//闹钟开关显示
write_1602dat('T');
write_1602dat('S');
Set_Timer++;
if(Set_Timer==1)
{
write_1602dat('M'); //设置分
}
else if(Set_Timer==2)
{
write_1602dat('H'); //设置时
}
if(Set_Timer>2)
Set_Timer = 0;
while(!KEY1); //等待按键松开
}
else if(!KEY2)
{
Beep_d(); //蜂鸣器短响,提示按键按下
if(Set_Timer==1) //Set_Timer==1 表示设置时钟分
{
Timer_mm++;
if(Timer_mm>=60)
{
Timer_mm=0;
}
LCD_TimeDispay(Timer_hh,Timer_mm,0); //显示当前设置时间
}
else if(Set_Timer==2) //Set_Timer==2 表示设置时钟时
{
Timer_hh++;
if(Timer_hh>=24)
{
Timer_hh=0;
}
LCD_TimeDispay(Timer_hh,Timer_mm,0); //显示当前设置时间
}
else if(Set_Timer==3) //Set_Timer==3 表示设置闹钟分
{
A_Timer_mm++;
if(A_Timer_mm>=24)
{
A_Timer_mm=0;
}
LCD_TimeDispay(A_Timer_hh,A_Timer_mm,0); //显示当前设置闹钟时间
}
else if(Set_Timer==6) //Set_Timer==6 表示设置闹钟时
{
A_Timer_hh++;
if(A_Timer_hh>=24)
{
A_Timer_hh=0;
}
LCD_TimeDispay(A_Timer_hh,A_Timer_mm,0); //显示当前设置闹钟时间
}
else
{
write_1602com(yh+13);
write_1602dat('A');
write_1602dat('C');
write_1602dat('D');
LCD_TimeDispay(A_Timer_hh,A_Timer_mm,0); //显示当前设置闹钟时间
delay(2000);
}
while(!KEY2); //等待按键松开
}
else if(!KEY3)
{
Beep_d();
write_1602com(yh+13);//闹钟设置显示
write_1602dat('A');
write_1602dat('S');
Set_Timer += 3;
if(Set_Timer==3)
{
write_1602dat('M'); //闹钟设置分
}
else if(Set_Timer==6)
{
write_1602dat('H'); //闹钟设置时
}
if(Set_Timer>6)
Set_Timer = 0; //标志位清零,正常显示
LCD_TimeDispay(A_Timer_hh,A_Timer_mm,0); //显示当前设置闹钟时间
while(!KEY3);
}
else if(!KEY4)
{
A_T_Flag = !A_T_Flag; //闹钟功能标志位取反
if(A_T_Flag) //A_T_Flag == 1 闹钟功能开
{
Beep_d();
delay(500);
Beep_d();
delay(500);
Beep_d();
}
else //A_T_Flag == 0 闹钟功能关
{
Beep_d();
}
while(!KEY4); //等待按键松开
}
}
按键扫描函数
void main(void)
{
lcd_init(); //初始化LCD1206
Beep_d(); //开机提示音
Timer0_Inir(); //初始化定时器0
while(1)
{
if(Set_Timer==0) //判断标志位,为零为正常显示
{
LCD_TimeDispay(Timer_hh,Timer_mm,Timer_ss);
Alarm_Clock();
}
Key_Scan();
}
}
主函数
void Timer0_Hard(void) interrupt 1//定时器0 中断函数
{
Timer++;
if(Timer==1000) // 500us*1000=500ms,500ms进一次if
{
LED = !LED; //LED取反
write_1602com(er+11);//秒:写入位置,从第11个位置后开始显示
write_1602dat(' ');//清除秒:显示
}
else if(Timer>=2000) // 500us*2000=1s,1s进一次if
{
Timer = 0; //计数值清零
Timer_ss++; //时钟秒自增
LED = !LED; //LED取反
write_1602com(er+11);//秒:写入位置,从第11个位置后开始显示
write_1602dat(':');//显示秒:
}
if(Timer_ss>=60)
{
Timer_ss = 0; //60秒到,秒清零,分加1
Timer_mm++;
}
else if(Timer_mm>=60)
{
Timer_mm = 0; //60分到,分清零,时加1
Timer_hh++;
}
else if(Timer_hh>=24)
Timer_hh = 0; //24时到,时清零
TL0 = (65536-500)%256; //定时器0计数器值重装
TH0 = (65536-500)/256;
}