1设计题目要求
1.1 单片机部分(必做部分)
1.1.1 基于51单片机的多路数据采集系统
设置3个功能键:当某一功能键被按下后,进入相应的功能模式。3个功能有:
1) 8路数据采集功能
通过调节可变电阻实现0-5V的电压输出作为8路输入信号使用,每路信号用2位LED显示采集的结果。报警:任意一路超过某一门限(可自己设定)时,发出报警(声音+灯闪烁,并通过灯指示是哪一路报警),同时停止采集。
2) 计数功能
利用计数功能键,实现每按一次按键,LED显示加1,从0-99计数。
3) 秒表功能
只用一个键控制。按下一个按键后时钟启动,从零开始计时,计时间隔0.01秒,再按一次后停止。再按一次后清零。如此循环。
1.1.2 电子琴
1) 8个键盘(实现中音1~7和高音1共8个音),8位LED数码管可以记忆并显示最近8个按下的音,用喇叭送出按下的音。要求必须使用8255/8155扩展实现键盘和显示部分。
2) 长时间(如2s)按下某一键(可自己定义),可播放某一歌曲。
1.2 FPGA部分(选做部分)
1.2.1 基于VHDL的多人抢答器
抢答器不少于4路,要求该系统应具备以下功能:
(1) 抢答器线路测试功能
为了保证比赛的正常进行,比赛前需要调试线路能否正常工作。有指示灯显示该系统现在是否工作正常。
(2) 第一抢答信号的鉴别和锁存功能
可以判断哪一路最先抢到回答的资格,在数码管上显示选手的号码或者让其相应的指示灯显示为绿灯表示抢答成功,并具有锁存功能,此后其他人不能抢答,一直到下一题开始。
(3) 犯规警示功能
可以判断出参赛者有没有在主持人读题的期间按下抢答器,有则声音报警并显示该选手号码或相应的红灯闪烁,同时取消其本轮抢答资格。
(4) 计时功能
可以预置时间,可以进行倒计时并且将时间显示出来。离计时结束还有10秒时有声音提示。
(5) 计分功能
可以实现加分和减分,并且显示出来。
(6) 声音提示及报警功能
在犯规和计时将结束时,要求有此功能。
(7) 有清零键和启动开关的功能
清零键:主持人可以按此键清除所有分数。
启动开关:主持人可以按此键启动抢答开始。
流程参考:
① 接通电源后,先按清零键,清除所有分数;启动开关处于关闭状态,禁止所有抢答器;
② 主持人开始读题,读题完毕后按“启动键”开始抢答,并计时开始,有数码管显示;若未在按“启动键”之前抢答视为犯规,有声音报警,并显示选手号码或者令其相应指示灯显示为红灯闪烁,取消此次答题机会,再按抢答键无效;
③ 开始抢答后,若有人抢答时,有声音提示,并显示选手号码或者令其相应的指示灯显示为绿灯闪烁,表明哪一路为第一抢答者,同时禁止其他路的抢答信号,直至下一题开始,主持人再次按“启动键”。
④ 抢答者在规定时间内回答正确,用“加分键”相应加一分,回答错误则用“减分键”扣一分;在即将到达规定时间时的前10秒,有声音提示。
2基于51单片机的多路数据采集系统
2.1 芯片介绍
2.1.1 ADC0808
1.综述:ADC0808是采样分辨率为8位的、以逐次逼近原理进行模/数转换的器件。其内部有一个8通道多路开关,它可以根据地址码锁存译码后的信号,只选通8路模拟输入信号中的一个进行A/D转换。ADC0808是ADC0809的简化版本,功能基本相同。一般在硬件仿真时采用ADC0808进行A/D转换,实际使用时采用ADC0809进行A/D转换。
2.引脚功能:ADC0808芯片有28条引脚,采用双列直插式封装,如右图所示。各引脚功能如下:
1~5和26~28(IN0~IN7):8路模拟量输入端。
8、14、15和17~21:8位数字量输出端。
22(ALE):地址锁存允许信号,输入,高电平有效。
6(START): A/D转换启动脉冲输入端,输入一个正脉冲(至少100ns宽)使其启动(脉冲上升沿使0809复位,下降沿启动A/D转换)。
7(EOC): A/D转换结束信号,输出,当A/D转换结束时,此端输出一个高电平(转换期间一直为低电平)。
9(OE):数据输出允许信号,输入,高电平有效。当A/D转换结束时,此端输入一个高电平,才能打开输出三态门,输出数字量。
10(CLK):时钟脉冲输入端。要求时钟频率不高于640KHZ。
12(VREF(+))和16(VREF(-)):参考电压输入端
11(Vcc):主电源输入端。
13(GND):地。
23~25(ADDA、ADDB、ADDC):3位地址输入线,用于选通8路模拟输入中的一路
本设计中ADC0808的接法如下图所示:
2.2 C语言程序设计
#include<reg52.h>
#define uc unsigned char
#define ui unsigned int
//共阴极数码管编码(无小数点)
uc code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
//共阴极数码管编码(有小数点)
uc code table_point[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};
uc number=255,shi,ge,second,second_behind,function,flag_watch,time[4],Voltage_Integer,Voltage_Decimal;
ui AD_Digital,AD_Analog;
sbit DULA=P2^0; //申明段选锁存器的锁存端
sbit WELA1=P2^1; //申明位选锁存器1的锁存端
sbit WELA2=P2^2; //申明位选锁存器2的锁存端
sbit LEDLA=P2^3; //申明LED锁存器的锁存端
sbit K1=P2^4; //功能键1
sbit K2=P2^5; //功能键2
sbit K3=P2^6; //功能键3
sbit LED=P2^7; //报警灯
sbit A1=P1^0; //地址线A
sbit A2=P1^1; //地址线B
sbit A3=P1^2; //地址线C
sbit ST=P1^3; //A/D转换启动信号
sbit OE=P1^4; //数据输出允许信号
sbit EOC=P1^5; //A/D转换结束信号
sbit CLK=P1^6; //时钟脉冲输入信号
sbit BEEP=P1^7; //蜂鸣器
//延时(单位ms)
void delayms(ui xms)
{
ui i,j;
for(i=xms;i>0;i--)
for(j=31;j>0;j--);
}
//报警
void alarm(uc n)
{
switch(n)
{
case 1:
LEDLA=1;P0=0xfe;LEDLA=0;break;
case 2:
LEDLA=1;P0=0xfd;LEDLA=0;break;
case 3:
LEDLA=1;P0=0xfb;LEDLA=0;break;
case 4:
LEDLA=1;P0=0xf7;LEDLA=0;break;
case 5:
LEDLA=1;P0=0xef;LEDLA=0;break;
case 6:
LEDLA=1;P0=0xdf;LEDLA=0;break;
case 7:
LEDLA=1;P0=0xbf;LEDLA=0;break;
case 8:
LEDLA=1;P0=0x7f;LEDLA=0;break;
}
while(K2==1&&K3==1)
{
BEEP=~BEEP;
LED=~LED;
switch(n)
{
case 1:
WELA1=1;P0=0xfe;WELA1=0;
DULA=1;P0=table_point[Voltage_Integer];DULA=0;
delayms(4);
WELA1=1;P0=0xfd;WELA1=0;
DULA=1;P0=table[Voltage_Decimal];DULA=0;
delayms(4);break;
case 2:
WELA1=1;P0=0xfb;WELA1=0;
DULA=1;P0=table_point[Voltage_Integer];DULA=0;
delayms(4);
WELA1=1;P0=0xf7;WELA1=0;
DULA=1;P0=table[Voltage_Decimal];DULA=0;
delayms(4);break;
case 3:
WELA1=1;P0=0xef;WELA1=0;
DULA=1;P0=table_point[Voltage_Integer];DULA=0;
delayms(4);
WELA1=1;P0=0xdf;WELA1=0;
DULA=1;P0=table[Voltage_Decimal];DULA=0;
delayms(4);break;
case 4:
WELA1=1;P0=0xbf;WELA1=0;
DULA=1;P0=table_point[Voltage_Integer];DULA=0;
delayms(4);
WELA1=1;P0=0x7f;WELA1=0;
DULA=1;P0=table[Voltage_Decimal];DULA=0;
delayms(4);break;
case 5:
WELA2=1;P0=0xfe;WELA2=0;
DULA=1;P0=table_point[Voltage_Integer];DULA=0;
delayms(4);
WELA2=1;P0=0xfd;WELA2=0;
DULA=1;P0=table[Voltage_Decimal];DULA=0;
delayms(4);break;
case 6:
WELA2=1;P0=0xfb;WELA2=0;
DULA=1;P0=table_point[Voltage_Integer];DULA=0;
delayms(4);
WELA2=1;P0=0xf7;WELA2=0;
DULA=1;P0=table[Voltage_Decimal];DULA=0;
delayms(4);break;
case 7:
WELA2=1;P0=0xef;WELA2=0;
DULA=1;P0=table_point[Voltage_Integer];DULA=0;
delayms(4);
WELA2=1;P0=0xdf;WELA2=0;
DULA=1;P0=table[Voltage_Decimal];DULA=0;
delayms(4);break;
case 8:
WELA2=1;P0=0xbf;WELA2=0;
DULA=1;P0=table_point[Voltage_Integer];DULA=0;
delayms(4);
WELA2=1;P0=0x7f;WELA2=0;
DULA=1;P0=table[Voltage_Decimal];DULA=0;
delayms(4);break;
}
}
LEDLA=1;P0=0xff;LEDLA=0;
BEEP=1;
LED=1;
}
//按键检测
void keyscan()
{
if(K1==0)
{
delayms(10);
if(K1==0)
{
function=1;
while(!K1);
}
}
if (K2==0)
{
delayms(10);
if (K2==0)
{
function=2;
if(number==100)
number=0;
while(!K2);
number++;
}
}
if (K3==0)
{
delayms(10);
if (K3==0)
{
while(!K3);
function=3;
flag_watch=flag_watch+1;
if(flag_watch==4)
{
flag_watch=1;
}
switch(flag_watch)
{
case 1:
TR0=1; //启动定时器0
break;
case 2:
TR0=0; //关闭定时器0
break;
case 3:
second=0; //清零
second_behind=0;
break;
}
}
}
}
//功能1
void function1()
{
TR1=1;
number=255;
second=0;
second_behind=0;
flag_watch=0;
OE=0;
LEDLA=0;
WELA2=0;
//IN0
A3=0;A2=0;A1=0;
ST=0;ST=1;ST=0; //启动A/D转换
while(EOC==0); //等待转换完成
OE=1;
AD_Digital=P3;
AD_Analog=50*AD_Digital/255;
Voltage_Integer=AD_Analog/10;
Voltage_Decimal=AD_Analog%10;
WELA1=1;P0=0xfe;WELA1=0;P0=0x00;
DULA=1;P0=table_point[Voltage_Integer];DULA=0;
delayms(1);
WELA1=1;P0=0xfd;WELA1=0;P0=0x00;
DULA=1;P0=table[Voltage_Decimal];DULA=0;
delayms(1);
OE=0;
if(AD_Digital>204)
{
alarm(1);
}
//IN1
A3=0;A2=0;A1=1;
ST=0;ST=1;ST=0; //启动A/D转换
while(EOC==0); //等待转换完成
OE=1; //允许输出信号
AD_Digital=P3;
AD_Analog=50*AD_Digital/255;
Voltage_Integer=AD_Analog/10;
Voltage_Decimal=AD_Analog%10;
WELA1=1;P0=0xfb;WELA1=0; P0=0x00;
DULA=1;P0=table_point[Voltage_Integer];DULA=0;
delayms(1);
WELA1=1;P0=0xf7;WELA1=0;P0=0x00;
DULA=1;P0=table[Voltage_Decimal];DULA=0;
delayms(1);
OE=0;
if(AD_Digital>204)
{
alarm(2);
}
//IN2
A3=0;A2=1;A1=0;
ST=0;ST=1;ST=0; //启动A/D转换
while(EOC==0); //等待转换完成
OE=1; //允许输出信号
AD_Digital=P3;
AD_Analog=50*AD_Digital/255;
Voltage_Integer=AD_Analog/10;
Voltage_Decimal=AD_Analog%10;
WELA1=1;P0=0xef;WELA1=0;P0=0x00;
DULA=1;P0=table_point[Voltage_Integer];DULA=0;
delayms(1);
WELA1=1;P0=0xdf;WELA1=0;P0=0x00;
DULA=1;P0=table[Voltage_Decimal];DULA=0;
delayms(1);
OE=0;
if(AD_Digital>204)
{
alarm(3);
}
//IN3
A3=0;A2=1;A1=1;
ST=0;ST=1;ST=0; //启动A/D转换
while(EOC==0); //等待转换完成
OE=1; //允许输出信号
AD_Digital=P3;
AD_Analog=50*AD_Digital/255;
Voltage_Integer=AD_Analog/10;
Voltage_Decimal=AD_Analog%10;
WELA1=1;P0=0xbf;WELA1=0;P0=0x00;
DULA=1;P0=table_point[Voltage_Integer];DULA=0;
delayms(1);
WELA1=1;P0=0x7f;WELA1=0;P0=0x00;
DULA=1;P0=table[Voltage_Decimal];DULA=0;
delayms(1);
OE=0;
if(AD_Digital>204)
{
alarm(4);
}
WELA1=1;P0=0xFF;WELA1=0;
WELA1=0;
//IN4
A3=1;A2=0;A1=0;
ST=0;ST=1;ST=0; //启动A/D转换
while(EOC==0); //等待转换完成
OE=1; //允许输出信号
AD_Digital=P3;
AD_Analog=50*AD_Digital/255;
Voltage_Integer=AD_Analog/10;
Voltage_Decimal=AD_Analog%10;
WELA2=1;P0=0xfe;WELA2=0;P0=0x00;
DULA=1;P0=table_point[Voltage_Integer];DULA=0;
delayms(1);
WELA2=1;P0=0xfd;WELA2=0;P0=0x00;
DULA=1;P0=table[Voltage_Decimal];DULA=0;
delayms(1);
OE=0;
if(AD_Digital>204)
{
alarm(5);
}
//IN5
A3=1;A2=0;A1=1;
ST=0;ST=1;ST=0; //启动A/D转换
while(EOC==0); //等待转换完成
OE=1; //允许输出信号
AD_Digital=P3;
AD_Analog=50*AD_Digital/255;
Voltage_Integer=AD_Analog/10;
Voltage_Decimal=AD_Analog%10;
WELA2=1;P0=0xfb;WELA2=0;P0=0x00;
DULA=1;P0=table_point[Voltage_Integer];DULA=0;
delayms(1);
WELA2=1;P0=0xf7;WELA2=0;P0=0x00;
DULA=1;P0=table[Voltage_Decimal];DULA=0;
delayms(1);
OE=0;
if(AD_Digital>204)
{
alarm(6);
}
//IN6
A3=1;A2=1;A1=0;
ST=0;ST=1;ST=0; //启动A/D转换
while(EOC==0); //等待转换完成
OE=1; //允许输出信号
AD_Digital=P3;
AD_Analog=50*AD_Digital/255;
Voltage_Integer=AD_Analog/10;
Voltage_Decimal=AD_Analog%10;
WELA2=1;P0=0xef;WELA2=0;P0=0x00;
DULA=1;P0=table_point[Voltage_Integer];DULA=0;
delayms(1);
WELA2=1;P0=0xdf;WELA2=0;P0=0x00;
DULA=1;P0=table[Voltage_Decimal];DULA=0;
delayms(1);
OE=0;
if(AD_Digital>204)
{
alarm(7);
}
//IN7
A3=1;A2=1;A1=1;
ST=0;ST=1;ST=0; //启动A/D转换
while(EOC==0); //等待转换完成
OE=1; //允许输出信号
AD_Digital=P3;
AD_Analog=50*AD_Digital/255;
Voltage_Integer=AD_Analog/10;
Voltage_Decimal=AD_Analog%10;
WELA2=1;P0=0xbf;WELA2=0;P0=0x00;
DULA=1;P0=table_point[Voltage_Integer];DULA=0;
delayms(1);
WELA2=1;P0=0x7f;WELA2=0;P0=0x00;
DULA=1;P0=table[Voltage_Decimal];DULA=0;
delayms(1);
OE=0;
if(AD_Digital>204)
{
alarm(8);
}
WELA2=1;P0=0xFF;WELA2=0;
}
//功能2
void function2()
{
TR1=0;
WELA2=0; //锁住数码管后四位
second=0;
second_behind=0;
flag_watch=0;
shi=number/10;
ge=number%10;
LEDLA=0;
WELA1=1;
P0=0xfe; //点亮左边第一个数码管
WELA1=0;
DULA=1;
P0=table[shi];
DULA=0;
delayms(5);
WELA1=1;
P0=0xfd; //点亮左边第二个数码管
WELA1=0;
DULA=1;
P0=table[ge];
DULA=0;
delayms(5);
}
//功能3
void function3()
{
TR1=0;
WELA2=0; //锁住数码管后四位
number=255;
time[3]=second/10;
time[2]=second%10;
time[1]=second_behind/10;
time[0]=second_behind%10;
LEDLA=0;
WELA1=1;
P0=0xfe; //左边第一个数码管
WELA1=0;
DULA=1;
P0=table[time[3]];
DULA=0;
delayms(5);
WELA1=1;
P0=0xfd; //左边第二个数码管
WELA1=0;
DULA=1;
P0=table_point[time[2]];
DULA=0;
delayms(5);
WELA1=1;
P0=0xfb; //左边第三个数码管
WELA1=0;
DULA=1;
P0=table[time[1]];
DULA=0;
delayms(5);
WELA1=1;
P0=0xf7; //左边第四个数码管
WELA1=0;
DULA=1;
P0=table[time[0]];
DULA=0;
delayms(5);
}
//初始化
void init()
{
TMOD=0x21; //设置定时器0为工作方式1,定时器1为工作方式2
TH0=(65536-10000)/256; //装初值,10ms一次中断
TL0=(65536-10000)%256;
TH1=0xfe;
TL1=0xfe;
EA=1; //开总中断
ET0=1; //开定时器0中断
ET1=1; //开定时器1中断
}
//主程序
void main()
{
init();
while(1)
{
keyscan();
if(function==1)
{
function1();
}
if(function==2)
{
function2();
}
if(function==3)
{
function3();
}
}
}
//T0定时器中断给ADC0808提供时钟信号
void T0_time() interrupt 1
{
TH0=(65536-10000)/256;
TL0=(65536-10000)%256;
second_behind=second_behind+1;
if(second_behind==100)
{
second_behind=0;
second++;
}
}
//T1定时器中断给ADC0808提供时钟信号
void T1_time() interrupt 3
{
CLK=~CLK;
}
2.3 Proteus仿真结果
2.3.1 功能1
1.正常状态:
2.报警状态:
2.3.2 功能2
2.3.3 功能3
3电子琴
3.1 芯片介绍
3.1.1 8255A
Intel 8086/8088 系列的可编程外设接口电路(Programmable Peripheral Interface)简称 PPI,型号为8255(改进型为8255A及8255A-5),具有24条输入/输出引脚、可编程的通用并行输入/输出接口电路。它是一片使用单一+5V电源的40脚双列直插式大规模集成电路。8255A的通用性强,使用灵活,通过它CPU可直接与外设相连接。
8255A在使用前要写入一个方式控制字,选择A、B、C三个端口各自的工作方式,共有三种;
方式0 :基本的输入输出方式,即无须联络就可以直接进行的 I/O方式。其中A、B、C口的高四位或低四位可分别设置成输入或输出。
方式1 :选通I/O,此时接口和外围设备需联络信号进行协调,只有A口和B口可以工作在方式1,此时C口的某些线被规定为A口或B口与外围设备的联络信号,余下的线只有基本的I/O功能,即只工作在方式0.
方式2: 双向I/O方式,只有A口可以工作在这种方式,该I/O线即可输入又可输出,此时C口有5条线被规定为A口和外围设备的双向联络线,C口剩下的三条线可作为B口方式1的联络线,也可以和B口一起方式0的I/O线。
8255A是一个并行输入、输出器件,具有24个可编程设置的I/O口,包括3组8位的I/O为PA口、PB口、PC口,又可分为2组12位的I/O口:A组包括A口及C口高4位,B组包括B口及C组的低4位。
A口可以设置为方式0、方式1、方式2,B口与C口只能设置为方式0或方式1。
本设计中8255A的接法如下图所示:
3.2 C语言程序设计
#include<reg52.h>
#include<absacc.h>
#define uc unsigned char
#define ui unsigned int
//PA、PB、PC端口及命令端口地址定义
#define PA XBYTE[0xfff8]
#define PB XBYTE[0xfff9]
#define PC XBYTE[0xfffA]
#define COM XBYTE[0xfffB]
sbit sounder=P3^0;
sbit play=P1^7;
uc note;
ui key,H0,L0;
ui code yin[]={0,64580,64684,64777,64820,64898,64968,65030,65058};//{空 DO RE MI FA SO LA XI DO}
ui code wei[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
ui duan[]={0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40};
//《两只老虎》数据表
uc code MUSIC[]={5,2,2, 6,2,2, 7,2,2, 5,2,2, //两只老虎
5,2,2, 6,2,2, 7,2,2, 5,2,2, //两只老虎
7,2,2, 1,3,2, 2,3,4, //跑得快
7,2,2, 1,3,2, 2,3,4, //跑得快
2,3,1, 3,3,1, 2,3,1, 1,3,1, 7,2,2, 5,2,2, //一只没有眼睛
2,3,1, 3,3,1, 2,3,1, 1,3,1, 7,2,2, 5,2,2, //一只没有耳朵
6,2,2, 2,2,2, 5,2,4, //真奇怪
6,2,2, 2,2,2, 5,2,4, //真奇怪
0,0,0};
//音阶频率表 高八位
uc code FREQH[]={0xF2,0xF3,0xF5,0xF5,0xF6,0xF7,0xF8,
0xF9,0xF9,0xFA,0xFA,0xFB,0xFB,0xFC,0xFC,
0xFC,0xFD,0xFD,0xFD,0xFD,0xFE,
0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFF} ;
//音阶频率表 低八位
uc code FREQL[]={0x42,0xC1,0x17,0xB6,0xD0,0xD1,0xB6,
0x21,0xE1,0x8C,0xD8,0x68,0xE9,0x5B,0x8F,
0xEE,0x44, 0x6B,0xB4,0xF4,0x2D,
0x47,0x77,0xA2,0xB6,0xDA,0xFA,0x16};
//延时(单位ms)
void delayms(ui xms)
{
ui i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}
//单音
void tone(uc note)
{
H0=yin[note]/256;
L0=yin[note]%256;
TR0=1;
}
//数组左移
void left()
{
uc i;
for(i=0;i<7;i++)
{
duan[i]=duan[i+1];
}
}
//显示
void show()
{
uc i;
for(i=0;i<8;i++)
{
PB=wei[i];
PA=duan[i];
delayms(1);
}
}
//T1延时1ms
void delay_T1(ui t)
{
while(t--!=0)
{
TH1=0xFC;
TL1=0x66; //定时1mS
TR1=1; //开启T1
while(!TF1); //等待T1溢出
TR1=0; //关闭T1
TF1=0; //清除溢出标志位
}
}
//节拍延时
void delay_beat(uc t)
{
uc i;
for(i=0;i<t;i++)
delay_T1(250);
TR0=0;
}
//播放音乐
void song()
{
uc i,k,t;
i=0;
PB=0x00; //数码管全亮
PA=0xff; //数码管全亮
while(i<96)
{
k=MUSIC[i]+7*MUSIC[i+1]-1; //去音符振荡频率所需数据
H0=FREQH[k];
L0=FREQL[k];
t=MUSIC[i+2]; //节拍时长
i=i+3;
TH0=H0; //赋值定时器时间,决定频率
TL0=L0;
TR0=1; //打开定时器
delay_beat(t); //延时所需要的节拍
}
}
//主函数
void main()
{
COM=0x89; //8255工作方式0,PA、PB输出,PC输入
TMOD=0x11;
EA=1;
ET0=1;
TR0=0;
sounder=0;
while(1)
{
show();
switch(PC)
{
case 0xfe: tone(1);left();duan[7]=0x06;break;
case 0xfd: tone(2);left();duan[7]=0x5b;break;
case 0xfb: tone(3);left();duan[7]=0x4f;break;
case 0xf7: tone(4);left();duan[7]=0x66;break;
case 0xef: tone(5);left();duan[7]=0x6d;break;
case 0xdf: tone(6);left();duan[7]=0x7d;break;
case 0xbf: tone(7);left();duan[7]=0x07;break;
case 0x7f: tone(8);left();duan[7]=0x7f;break;
}
while(PC!=0xff)
show(); //key=0xff无键按下while(0)跳出循环,有键按下则执行循环
TR0=0;
sounder=0;
if(play==0)
{
PB=0x00;
PA=0x3f;
delayms(1000); //等1秒
if(play==0)
{
PB=0x00;
PA=0x3f;
delayms(1000); //再等1秒
if(play==0)
song();
}
}
}
}
void T0_time() interrupt 1
{
TH0=H0;
TL0=L0;
sounder=~sounder;
}
3.3 Proteus仿真结果
以12345678分别表示单音DO、RE、MI、FA、SO、LA、XI、高音DO,依次按下若干按键后,结果如下图所示:
当长按自定义键2s时,数码管LED全部点亮,并播放音乐《两只老虎》
3基于VHDL的多人抢答器
3.1 芯片介绍
3.1.1 EPM7128
1.简介:EPM7128是可编程的大规模逻辑器件,为ALTERA公司的MAX7000系列产品,具有高阻抗、电可擦等特点,可用门单元为2500个,管脚间最大延迟为5ns,工作电压为+5V。
2.基本参数:
可编程逻辑类型:PLD
逻辑芯片功能:Programmable ISP
逻辑芯片基本号:7128
输入/输出线数:84
宏单元数:128
频率:95.2MHz
输入/输出接口标准:TTL, CMOS
电源电压 最小:3V
电源电压 最大:3.6V
封装类型:TQFP
工作温度范围:0°C to +85°C
SVHC(高度关注物质):Cobalt dichloride
IC标号:7128
传播延迟时间:10ns
器件标号:7128
封装类型:剥式
电源电压:3.3V
表面安装器件:表面安装
输入数:84
逻辑功能号:7128
针脚数:100
门电路数:2500
3.2 QuartusII顶层原理图
3.3 各部分Verilog程序
3.3.1 抢答部分
module qiangda(clk,
key_in,
rst_n,reset1,
k1,k2,k3,k4,
led
);
input clk;
input [4:1]key_in;//四个抢答输入
input rst_n,reset1;
output reg k1,k2,k3,k4;
output [4:1]led;
//reg [4:1]key_val;
reg lock;
always@(posedge clk or posedge rst_n or posedge reset1)
begin
if(rst_n|reset1)
begin
lock <= 0;
k1 <= 0;
k2 <= 0;
k3 <= 0;
k4<= 0;
end
else
begin
if((key_in[1] == 1)&&(lock == 0))
begin
k1 <= 1;
lock <= 1;
end
else if((key_in[2] == 1)&&(lock == 0))
begin
k2 <= 1;
lock <= 1;
end
else if((key_in[3] == 1)&&(lock == 0))
begin
k3 <= 1;
lock <= 1;
end
else if((key_in[4] == 1)&&(lock == 0))
begin
k4 <= 1;
lock <= 1;
end
else lock<=1;
end
end
assign led={~k4,~k3,~k2,~k1};
endmodule
/*
//-----------按键消抖----------------------
reg [4:1] key_samp1, key_samp1_locked;
always @ (posedge clk , posedge rst_n)
if(rst_n)
key_samp1 <= 4'h0;
else
key_samp1 <= key_in;
// 将key_samp1锁存至key_samp1_locked
always @ (posedge clk, posedge rst_n)
if(rst_n)
key_samp1_locked <= 4'h0;
else
key_samp1_locked <= key_samp1;
//--------------------------------------
//++++++++++++++++++++++++++++++++++++++
wire [4:1] key_changed1;
// 当key_samp1由1变为0时
// key_changed1由0变为1,只维持一个时钟周期
assign key_changed1 = key_samp1_locked & (~key_samp1);
//--------------------------------------
//++++++++++++++++++++++++++++++++++++++
reg [15:0] cnt1;
// 一旦有按键按下,cnt立即被清零
always @ (posedge clk, posedge rst_n)
if(rst_n)
cnt1 <= 16'h0;
else if(key_changed1)
cnt1 <= 16'h0;
else
cnt1<= cnt1 + 1'b1;
//--------------------------------------
//++++++++++++++++++++++++++++++++++++++
reg [4:1] key_samp2, key_samp2_locked;
// 只有当按键不变化(不抖动),且维持20ms
always @ (posedge clk, posedge rst_n)
if(rst_n)
key_samp2 <= 4'h0;
else if(cnt1 == 16'hFFFF)
key_samp2 <= key_in;
always @ (posedge clk, posedge rst_n)
if(rst_n)
key_samp2_locked <= 4'h0;
else
key_samp2_locked <= key_samp2;
//--------------------------------------
//++++++++++++++++++++++++++++++++++++++
wire [4:1] key_changed2;
// 当key_samp2由1变为0时
// key_changed2由0变为1,只维持一个时钟周期
assign key_changed2 = key_samp2_locked & (~key_samp2);
//--------------------------------------
//++++++++++++++++++++++++++++++++++++++
always @ (posedge clk, posedge rst_n)
if(rst_n)
key_val<= 4'h0;
else
key_val <=key_changed2;
//--------------------------------------
*/
//assign key_val=key_in;
3.3.2 计时部分
module t(
clk,rst_n,
k1,k2,k3,k4,reset1,
t1,beep
);
input clk;
input rst_n,reset1;
input k1,k2,k3,k4;
output reg [7:0]t1;
output reg beep;
reg [3:0]t;
reg flag;
parameter T1S=22'd3_999_999;
reg [21:0]Count_t;
always@(posedge clk or posedge rst_n)
begin
if(rst_n)
Count_t<=22'd0;
else if(Count_t==T1S)
Count_t<=22'd0;
else
Count_t<=Count_t+1'b1;
end
wire timer1 = Count_t[18]; // 2^19/4M
wire timer2 = Count_t[20]; // 2^21/4M
wire beep_en = timer1&timer2;
always@(posedge clk or posedge rst_n or posedge reset1) //dao ji shi
if(rst_n|reset1)
begin
t<=4'd9;
beep<=0;
end
else if(k1|k2|k3|k4)
begin
if(Count_t==T1S)
begin
if(t[3:0] != 4'b0000)
begin
t[3:0] <= t[3:0] - 4'b1;
if(t==4'd4|t==4'd3)
begin
if(beep_en)
beep<=1;
else
beep<=0;
end
end
else
begin
beep<=1;
end
end
end
always@(t) //xian shi
case(t)
4'd0: t1=8'b11000000;
4'd1: t1=8'b11111001;
4'd2: t1=8'b10100100;
4'd3: t1=8'b10110000;
4'd4: t1=8'b10011001;
4'd5: t1=8'b10010010;
4'd6: t1=8'b10000011;
4'd7: t1=8'b11111000;
4'd8: t1=8'b10000000;
4'd9: t1=8'b10011000;
default t1=8'b11111111;
endcase
endmodule
3.3.3 计分部分
module score(up,down,
clk,rst_n,reset1,
k1, k2, k3, k4,
scorea, scoreb, scorec, scored
);
input up ; //加分信号
input down; //减分信号
input clk; //时钟信号
input rst_n; //复位信号
input reset1; //chong xin kai shi
input k1,k2,k3,k4;
output reg [7:0]scorea, scoreb, scorec, scored ;
reg [3:0]score1, score2, score3, score4 ;
reg flag;
always@(posedge clk or posedge reset1 or posedge rst_n)
if(reset1)
begin
score1 <= 4'd3;
score2 <= 4'd3;
score3 <= 4'd3;
score4 <= 4'd3;
flag<=1;
end
else if(rst_n)
flag<=1;
else if(up==1&&flag==1)
begin
flag<=0;
if(k1 == 1)
begin
if(score1== 4'b1001)
score1<= 4'b0;
else
score1<= score1+1'b1;
end
if(k2 == 1)
begin
if(score2== 4'b1001)
score2<= 4'b0;
else
score2 <= score2 + 1'b1;
end
if(k3 == 1)
begin
if(score3 == 4'b1001)
score3 <= 4'b0;
else
score3<= score3 + 1'b1;
end
if(k4 == 1)
begin
if(score4 == 4'b1001)
score4 <= 4'b0;
else
score4<= score4 + 1'b1;
end
end
else if(down==1&&flag==1)
begin
flag<=0;
if(k1 == 1&&score1!= 4'b0000)
score1 <= score1 - 1'b1;
if(k2 == 1&&score2!=4'b0000)
score2 <= score2 - 1'b1;
if(k3 == 1&&score3!=4'b0000)
score3 <= score3 - 1'b1;
if(k4 == 1&&score4!=4'b0000)
score4 <= score4 - 1'b1;
end
always@(score1)
case(score1)
4'd0: scorea=8'b11000000;
4'd1: scorea=8'b11111001;
4'd2: scorea=8'b10100100;
4'd3: scorea=8'b10110000;
4'd4: scorea=8'b10011001;
4'd5: scorea=8'b10010010;
4'd6: scorea=8'b10000011;
4'd7: scorea=8'b11111000;
4'd8: scorea=8'b10000000;
4'd9: scorea=8'b10011000;
default scorea=8'b11111111;
endcase
always@(score2)
case(score2)
4'd0: scoreb=8'b11000000;
4'd1: scoreb=8'b11111001;
4'd2: scoreb=8'b10100100;
4'd3: scoreb=8'b10110000;
4'd4: scoreb=8'b10011001;
4'd5: scoreb=8'b10010010;
4'd6: scoreb=8'b10000011;
4'd7: scoreb=8'b11111000;
4'd8: scoreb=8'b10000000;
4'd9: scoreb=8'b10011000;
default scoreb=8'b11111111;
endcase
always@(score3)
case(score3)
4'd0: scorec=8'b11000000;
4'd1: scorec=8'b11111001;
4'd2: scorec=8'b10100100;
4'd3: scorec=8'b10110000;
4'd4: scorec=8'b10011001;
4'd5: scorec=8'b10010010;
4'd6: scorec=8'b10000011;
4'd7: scorec=8'b11111000;
4'd8: scorec=8'b10000000;
4'd9: scorec=8'b10011000;
default scorec=8'b11111111;
endcase
always@(score4)
case(score4)
4'd0: scored=8'b11000000;
4'd1: scored=8'b11111001;
4'd2: scored=8'b10100100;
4'd3: scored=8'b10110000;
4'd4: scored=8'b10011001;
4'd5: scored=8'b10010010;
4'd6: scored=8'b10000011;
4'd7: scored=8'b11111000;
4'd8: scored=8'b10000000;
4'd9: scored=8'b10011000;
default scored=8'b11111111;
endcase
endmodule