本实验是单片机开发的一次小作业,因为系统涉及独立按键(开关键、上下左右键,自行定义)、LED 数码管、蜂鸣器、中断、定时器等相关设施,对51单片机的使用比较全面,所以拿来举例复盘。
系统采用实时时钟,通过设定可以控制 220V 电源的接通时刻与接通的时间长短。
51单片机主要特点总结
1.单片机系统框架
2.单片机存储器配置特点
3单片机I/O口展示
4中断简介
本次小实验相关东西
1.资源定义:
数码管 1-8 功能分配:
数码管 1-2: 分钟
数码管 3-4: 秒
数码管 5: 空闲,全黑,模式显示,1:开始时间设置,2:结束时间设置,3:运行
数码管 6-8: 显示 220 或 000 按键:
KEY1: 开机,设置键,单击,开机,双击切换设置模式,长按,系统关机;
KEY2: (单击)左移键,相当于光标,移动当前编辑位,左移
KEY3: (单击)右移键,相当于光标,移动当前编辑位,右移
KEY4: (单击)上,数值增加 KEY5: (单击)下,数值减小
2.功能实现:
F1开机,单击 KEY1,带 BEEP,数码管 1-4 显示全 0,数码管 5: 黑,数码管 6-8 显示全 0.
F2关机,长按 KEY1,带 BEEP,关机,数码管显示全黑.
F3设置功能切换,开机后,双击 KEY1,第 5 位数码管点亮,循环显示 1-2-3,。。。 F4[10 分].进入设置时间状态,开机后,双击 KEY1,第 5 位数码管显示 1、2,进入开始时间编辑模式,默认 1-4 数码 管显示全 0,其中最后一位,即第四位,闪烁,0.5 秒周期;
F5闪烁循环,进入编辑状态后,显示全 0,最后一位闪烁,单击 KEY2,闪烁位左移,循环;单击 KEY3,闪烁 位右移,循环。
F6数值编辑,进入编辑状态后,显示全 0,单击 KEY2,单击 KEY3,移动闪烁,单击KEY4 或KEY5,闪烁位增1或减 1,循环。
F7设置完毕,双击 KEY1,第 5 位数码管点亮,显示 3,定时系统开始运行,启动一个定时器,时间分辨率为秒,(模拟时,用“开始时间值”更新当前时间值,此时开始时间到,开始通电),此时,6-8 位显示220,表示220电源接通,同时 beep 开始,间隔 3 秒 beep 一次,持续,表示运行状态。运行到结束时间值,时,停止beep,220显示位 000,表示断电。
注意:系统的默认实时时钟为从 0 时 0 分 0 秒开始,实时时钟开机后一直运行直到关机,为了简化处理,实时时钟不必与北京时间同步。例如:设定的开始接通时间 12:00,设定结束接通时间 12:01,则实时时钟运行到12:00时开始 Beep,6-8 位显示 220,表示 220v 电源接通,12:01 时 Beep 关闭,表示 220v 电源断开,220 显示位000
3.代码展示:
3.1 初始定义
#include "reg52.h"
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
u8 code smgduan[18]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00};
//显示0~F的值
u16 flag=0,key_time,digit=0,digit2=0,state=0,duration=0,pause=0;
u16 a=0,b=0,c=0,d=0,a1=0,b1=0,c1=0,d1=0,a2=0,b2=0,c2=0,d2=0,e=0,f=0,h=0,i=0,j=0,k=0,l=200,m=1,n=0;
u16 key1=0,key1_state=0,only_k1_down=0,key1_is_s=0,key1_is_d=0,key1_is_l=0,key2=0,key2_state=0,key3=0,key3_state=0,key4=0,key4_state=0,key5=0,key5_state=0,key2_is_s=0,key3_is_s=0,key4_is_s=0,key5_is_s=0;
sbit beep=P2^0;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
sbit k1=P1^0;
sbit k2=P1^1;
sbit k3=P1^2;
sbit k4=P1^3;
sbit k5=P1^4;
3.2数码管部分
void Display(u16 a,u16 b,u16 c,u16 d,u16 e,u16 f,u16 g,u16 h)
{ P0=0;
switch(i) //位选,选择点亮的数码管,
{
case(0):
LSA=0;LSB=0;LSC=0;P0=smgduan[a]; break;//显示第1位
case(1):
LSA=1;LSB=0;LSC=0;P0=smgduan[b]; break;//显示第2位
case(2):
LSA=0;LSB=1;LSC=0;P0=smgduan[c]; break;//显示第3位
case(3):
LSA=1;LSB=1;LSC=0;P0=smgduan[d]; break;//显示第4位
case(4):
LSA=0;LSB=0;LSC=1;P0=smgduan[e]; break;//显示第5位
case(5):
LSA=1;LSB=0;LSC=1;P0=smgduan[f]; break;//显示第6位
case(6):
LSA=0;LSB=1;LSC=1;P0=smgduan[g]; break;//显示第7位
case(7):
LSA=1;LSB=1;LSC=1;P0=smgduan[h]; break;//显示第8位
}
i++;
if(i>=8)
i=0;
}
3.3控制闪烁的函数
(17为不存在的数代表灭)
void flash1()
{
if(digit%500<=250)
Display(a,b,c,d,e,0,0,0);
else
Display(17,b,c,d,e,0,0,0);
}
void flash2()
{
if(digit%500<=250)
Display(a,b,c,d,e,0,0,0);
else
Display(a,17,c,d,e,0,0,0);
}
void flash3()
{
if(digit%500<=250)
Display(a,b,c,d,e,0,0,0);
else
Display(a,b,17,d,e,0,0,0);
}
void flash4()
{
if(digit%500<=250)
Display(a,b,c,d,e,0,0,0);
else
Display(a,b,c,17,e,0,0,0);
}
3.4按键函数,包括单击,双击,长按
void key1_down()
{
if(k1==0&&only_k1_down==0)
{only_k1_down=1;flag=0;}
if(flag>=3&&k1==0)
{only_k1_down=0;key1=0;}
}
void key1_up()
{ if(k1==1)
key1=1;
}
void key1judge()
{
key1_down();key1_up();
switch(key1_state)
{
case 0:
if(key1==1)
{k=1;}
if(key1==0&&k==1)
{key1_state=1;key_time=0;k=0;}
break;
case 1:
if(key_time<300&&key1==1)
{key1_state=2;duration=2*key_time;pause=0;}
if(key_time>=300&&key1==0) //长按k1
{key1_state=0;key1_is_l=1;}
break;
case 2:
if(pause<duration)
{if(key1==0)
{key1_state=3;key_time=0;}
} //单击k1
else
{key1_state=0;key1_is_s=1;}
break;
case 3:
if(key_time<300&&key1==1)
{key1_state=0;key1_is_d=1;}
if(key_time>300&&key1==0) //双击k1
{key1_state=0;key1_is_l=1;}
break;
}
}
void key2judge()
{
switch(key2_state)
{case 0:
if(k2==0)
{key2_state=1;key_time=0;}
break;
case 1:
if(key_time<300&&k2==1) //k2单击
{key2_is_s=1;key2_state=0;}
if(key_time>=300)
{key2_state=0;}
break;
}
}
void key3judge()
{
switch(key3_state)
{case 0:
if(k3==0)
{key3_state=1;key_time=0;}
break;
case 1:
if(key_time<300&&k3==1) //k3单击
{key3_is_s=1;key3_state=0;}
if(key_time>=300)
{ key3_state=0;}
break;
}
}
void key4judge()
{
switch(key4_state)
{ case 0:
if(k4==0)
{key4_state=1;key_time=0;}
break;
case 1: //k4单击
if(key_time<300&&k4==1)
{key4_is_s=1;key4_state=0;}
if(key_time>=300)
{key4_state=0;}
break;
}
}
void key5judge()
{ switch(key5_state)
{case 0:
if(k5==0)
{key5_state=1;key_time=0;}
break;
case 1:
if(key_time<300&&k5==1)
{key5_is_s=1;key5_state=0;}
if(key_time>=300)
{key5_state=0;}
break;
}
}
3.5定时器中断
(可以结合上图定时器中断框图看)
/*******************************************************************/
void Timer0Init()
{
TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
ET0=1;//打开定时器0中断允许
EA=1;//打开总中断
TR0=1;//打开定时器
}
void Timer1Init()
{
TMOD|=0X10;//选择为定时器1模式,工作方式1,仅用TR1打开启动。
TH1=0XFC; //给定时器赋初值,定时1ms
TL1=0X18;
ET1=1;//打开定时器1中断允许
EA=1;//打开总中断
TR1=1;//打开定时器
}
void Timer0() interrupt 1
{
TH0 = 0xfc;
TL0 = 0x18;
digit++; //1ms
if(digit%10==0)
{key_time++;pause++;flag++;l++;}
}
void Timer1() interrupt 3
{
TH1 = 0xfc;
TL1 = 0x18;
digit2++; //1ms
if(digit2%1000==0)
{d++;n++;}
}
主函数,采用状态机的方法实现各功能的结合
(状态机代码的编写能力很重要)
/*****************************************************/
void main()
{
Timer0Init();
Timer1Init();
TR1=0;
while(1)
{
if(state==1&&l<25)
{if( digit%4==0)
{ beep=~beep;}
}
if(state==0&&l<25)
{if(digit%4==0)
{beep=~beep;}
}
if(state==8)
{ if(n%3==0&&n!=0)
{l=0;}
if(digit%4==0&&l<25)
{beep=~beep;}
}
if(key_time>=4000)
{ key_time=0;}
if(pause>=4000)
{ pause=0;}
if(flag>=4000)
{flag=0;}
if(l>=400)
{l=200;}
if(digit >= 8000)
{digit = 0;}
if(d==10)
{c++;d=0;}
if(c==6)
{b++;c=0;}
if(b==10)
{a++;b=0;}
if(a==6)
{a=0;b=0;c=0;d=0;}
if(digit2>=4000)
{ digit2=0;
if(n>999)
{n=0;}
}
if(digit%40==0)
{key1judge();key2judge();key3judge();key4judge();key5judge();}
if(key1_is_l==1)
{state=0;key1_is_l=0;}
switch(state)
{
case 0:
P0=0;
TR1=0;
if(m==0)
{ m=1;l=0;}
if(key1_is_s==1)
{state=1;key1_is_s=0;}
a=0;b=0;c=0;d=0;
break;
case 1:
Display(0,0,0,0,17,0,0,0);
if(m==1)
{l=0;m=0;}
if(key1_is_d==1)
{state=2;e=1;j=0;key1_is_d=0;}
break;
case 2:
flash4();
if(key2_is_s==1)
{state=3;key2_is_s=0;}
if(key3_is_s==1)
{state=5;key3_is_s=0;}
if(key4_is_s==1)
{d++;key4_is_s=0;}
if(key5_is_s==1)
{ key5_is_s=0;
if(d>0)
{d--;}
}
if(key1_is_d==1&&j==0)
{state=6;key1_is_d=0;}
if(key1_is_d==1&&j==1)
{state=7;key1_is_d=0;}
break;
case 3:
flash3();
if(key2_is_s==1)
{state=4;key2_is_s=0;}
if(key3_is_s==1)
{state=2;key3_is_s=0;}
if(key4_is_s==1)
{c++;key4_is_s=0;}
if(key5_is_s==1)
{key5_is_s=0;
if(c>0)
{c--;}
}
if(key1_is_d==1&&j==0)
{state=6;key1_is_d=0;}
if(key1_is_d==1&&j==1)
{state=7;key1_is_d=0;}
break;
case 4:
flash2();
if(key2_is_s==1)
{state=5;key2_is_s=0;}
if(key3_is_s==1)
{state=3;key3_is_s=0;}
if(key4_is_s==1)
{b++;key4_is_s=0;}
if(key5_is_s==1)
{key5_is_s=0;
if(b>0)
{b--;}
}
if(key1_is_d==1&&j==0)
{state=6;key1_is_d=0;}
if(key1_is_d==1&&j==1)
{state=7;key1_is_d=0;}
break;
case 5:
flash1();
if(key3_is_s==1)
{state=4;key3_is_s=0;}
if(key2_is_s==1)
{state=2;key2_is_s=0;}
if(key4_is_s==1)
{a++;key4_is_s=0;}
if(key5_is_s==1)
{key5_is_s=0;
if(a>0)
{a--;}
}
if(key1_is_d==1&&j==0)
{state=6;key1_is_d=0;}
if(key1_is_d==1&&j==1)
{state=7;key1_is_d=0;}
break;
case 6:
a1=a;b1=b;c1=c;d1=d;a=0;b=0;c=0;d=0;e=2;
j=1;state=2;
break;
case 7:
a2=a;b2=b;c2=c;d2=d;
a=0;b=0;c=0;d=0;e=3;
state=8;
break;
case 8:
TR1=1;
Display(a,b,c,d,3,f,f,0);
if(a==a1&&b==b1&&c==c1&&d==d1)
{f=2;}
if(a==a2&&b==b2&&c==c2&&d==d2)
{f=0;}
break;
}
}
}
总结就是,51还是具有很大可玩性的,其次就是要锻炼大项目能力,实验过程中发现这种程度的项目debug就要花很长时间了,是因为c语言编程能力还是较弱,不过加深了对硬件的理解,从根本上知道了代码的作用。