四位竞赛抢答器实验报告Word格式文档下载.docx
《四位竞赛抢答器实验报告Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《四位竞赛抢答器实验报告Word格式文档下载.docx(21页珍藏版)》请在冰豆网上搜索。

(1)在Keil中编好程序后,点击编译运行。
图1编译运行结果
(2)编译运行无错误后,还需要对项目进一步设置,以满足要求。
图2晶振频率设置
图3勾选CreateHEXfile
图4生成HEX文件
(3)生成好HEX文件后,将其导入到Proteus里的AT89C51芯片中。
图5导入HEX文件至AT89C51
(4)点击运行按钮进行软件仿真。
仿真时,显示数值与旋钮抽头电压相关,调动旋钮,按下复位键,数值改变,说明PCF8591工作正常。
按下选手抢答按键,系统正常提示2s警告;
主持人按下开始后,选手抢答正确计分与显示编号,程序正常;
主持人按下系统清除按钮,选手编号与分数消失,显示倒计时最大时间,程序正常。
(5)记录软件仿真结果,同时去机房进行硬件调试。
1.2软件调试遇到的问题及解决过程说明
(1)PCF8591芯片与单片机通信发生错误,无论程序使用应答还是非应答,在I2C调试器里,都显示进行的是应答,同时发送的数据最后一位均不正确。
图6I2C调试器发现错误
后经过仔细排查,发现代码中的一处for循环i=1,程序出错,修改后,发送数据正确,但应答位仍然不正确。
再次进行排查错误,发现是Noack程序里,sda没有设成1,修改为0后,I2C调试器显示符合预期。
(2)仿真后,发现音响完全没声音,无论给它什么频率,都不发出声音。
图7使用该器件无声音
后检查电路,发现电路没有问题,想到可能是元器件的问题,后将其改为SPEAKER,发现声音正常发出,问题解决。
(3)七段数码管显示错误,发现使用了不是实验箱上的共阴极数码管,修改位7SEG-MPX4-CC-BLUE后,问题解决。
1.3软件仿真运行和硬件实际验证的区别
Proteus仿真软件使用起来十分顺手,但是在程序比较复杂,定时器溢出频率高时,仿真容易卡、慢。
Proteus仿真软件进行仿真时,不需要画晶振电路和复位电路,是有效减少工作量,还可以使用虚拟示波器、I2C调试器等器件进行问题查找、结果确认,十分方便与快捷。
但其仅仅运行在理想的条件下,缺少了现实里的许多因素影响,容易导致我们思考问题简单化。
硬件实际验证时,发现了很多软件仿真时没发现的问题,执行程序时,速度比仿真软件快很多,反应十分迅速,无论定时器溢出频率多高,都不会卡、慢,相比之下,结果更舒适。
硬件实际验证时,需要考虑到硬件的局限性,进行程序与实际晶振频率的匹配,I/O口与相应模块的连线,总体需要考虑更多因素,不再是理想情况。
二、运行效果
1、Proteus电路图
图8Proteus接线图
2、滑动变阻器在0%时,倒计时最大时间。
Time=20+255*30/255=50
图9倒计时最大时间
3、滑动变阻器在50%时,倒计时最大时间
Time=20+128*30/255=34
4、滑动变阻器在100%时,倒计时最大时间
Time=20+0*30/255=20
5、选手进行抢答
图10一号选手抢到
图11三号选手抢到
图12三号选手第二次抢到
6、无人抢答,系统警告
图13倒计时结束无人抢答,系统警告
7、硬件仿真
图14旋钮调到最小,显示20
图15调节旋钮,时间改变
抢答、计分、计时结束
硬件仿真时,实际晶振频率与程序中所使用的不同,导致倒计时的速度很快。
后修改程序,时间正常。
三、系统优化
3.1软件设计
刚开始设计时,在主函数里发送方波以驱动扬声器发声,后发现声音频率变化,可能是每次主函数执行时间不同。
后修改为使用定时器T1产生方波信号来驱动扬声器发声,修改后,声音频率正常,仿真却很卡。
询问老师后,了解到是仿真软件发出脉冲的时间变慢了,这没法去除。
本来准备使用独立式键盘,后翻阅实验指导书了解到,实验箱上只有4个独立按键可供使用,后调整为2*3矩阵键盘,修改相应程序以便完成硬件验证。
3.2硬件设计
一开始,使用Proteus里的电路图进行硬件连接,连接好后硬件验证发现数码管的a段一直亮,仔细检查后发现是实验箱I/O口出现故障,之后连续换了三四个实验箱,终于找到了一个实验箱数码管显示正确,但却有一个数码管不亮,确认是I/O口故障后,将程序里的循环变量0x7f改为0x77,将第一位线接到另一个I/O口后,数码管显示正确。
之后程序正常运行起来,但是倒计时却不符合要求,仔细检查后,发现实验箱晶振频率为11.0592Mhz,我的程序里面用的是12Mhz,修改了各个定时器的初值,重新硬件验证,现象变为正常。
由于实际的运行有很多不确定的因素,因此软件仿真不能完全符合实际要求,具体的优化还需要进行实际电路验证来完成。
四、设计总结
这次课程设计,成功的实现了四位竞赛抢答器的功能,并对I2C通信协议有了更深刻的理解,也更清晰的了解到了Proteus软件仿真的局限性,以及硬件验证的复杂性。
本次课程设计,使用的AT89C51芯片虽然已经较为老旧,但是也能实现像这种简单而又实用的功能,也让我明白了程序的书写更为重要。
其次也了解了74LS244和74LS240芯片的作用和用法,虽然在编写程序过程中有很多不会的点,但通过查找资料和反复修改,最终成功的编写出了源程序并实现了设计所要求的功能。
对一些典型的算法及应用也有了更加深刻的理解,对之后的编程有着很大的帮助,大大促进了对单片机及C51编程的学习。
本次设计,我分了4个模块进行分析,第一部分是矩阵键盘的扫描、第二部分是倒计时功能的实现、第三部分是音响提示功能、第四部分是模数转换功能。
这四部分中,只有第四部分模数转换比较困难,花费了我大量的时间,才得以完成,其他三个部分单独实现都比较简单,但是组合起来实现时,各个程序之间的关系就变得复杂起来,这让我花费了许多时间使其成功组合在一起。
过程中虽然出现很多问题但经过不断调试分析,最终都解决了这些问题,系统的调试是一个很繁琐的过程,需要有耐心,细心的进行调试。
在代码设计阶段,我们最大的问题就是前期没有总体把握,代码冗余前后紊乱,参考了大量的代码后,我们改进了我们代码的书写方式如做成模块方便调用,添加注释,显得干净而整洁。
代码内部关于寄存器内部数据比较输出等还有些问题,尤其是对程序设计语句的理解和运用,不能够充分理解每个语句的具体含义,这里有待更深一步的学习。
经过这次课程设计后,我总结出绘制电路不仅要能实现功能,还要符合实验箱上的硬件原理图,以便于自己后面的硬件验证,事实证明,我这样做确实使我的硬件验证比其他没有按照原理图来连接的同学来说简单了许多,没有大面积修改程序。
如何根据想要实现的功能选择合适的芯片模块,实现正确的功能才是最重要的,这次的设计在设计之初也走了不少弯路,好在思路清晰:
键号采集、模数转换、I2C通信、CPU处理、控制输、音响提示,现在回想起来倒也不难。
这次课程设计是自己安排时间使用Proteus软件独立设计,老师给的参考图完全依靠自己查阅资料,所以在设计开始的时候走了不少弯路,遇到了很多困难。
开始时思路很不清晰,无从下手,经过一天的思考才具体的制定出一套理论上可行的方案,仅仅能实现矩阵键盘读取数值,不能减计数和显示等很多实际上的考虑。
设计电路中间我们也遇到很多原理性的问题,比如单片机如何写定时器工作代码,矩阵键盘如何读取键值方便调用等。
为了能解决这些问题,我重新温习了一下课堂上学过的单片机知识,参考了很多芯片文件,并且在XX上查阅了许多文档,使自己在这方面的知识逐渐的丰富了起来,水平有了明显的提高。
然而遇到问题还是很多没有解决,在理论情况下能实现的功能,在硬件验证情况下很难实现。
存在的问题很多:
源码错误不断、接线错误、不能实现功能等等情况,都导致了实验不能完全成功。
而且,经过这次实验,我明白了以下两点:
一、前期的布局尽可能合理,这个可以为接下来的调试节省很大的精力;
其次,在实验时一定要细心操作,集中注意力,避免犯小错误;
最后,并不是所有的实验都是理论上可行实际上就能运行的,学会调试找出其中的问题远远要比接线操作更重要。
经过本次课程设计,我都单片机系统的设计与调试的过程有了更深刻的认识,明白了调试的先后顺序,懂得了功能的模块化实现,明白了问题的查找与解决方法,更重要的是,我明白了前期准备的重要性,前期准备的越充分,后面完成起来就更快捷、流畅。
本次课设,我从中学到了课堂上没法传授的知识,收货颇丰。
附件:
#include<
reg51.h>
intrins.h>
#defineucharunsignedchar
#defineuintunsignedint
sbitX1=P1^0;
sbitX2=P1^1;
sbitY0=P1^2;
sbitY1=P1^3;
sbitY2=P1^4;
//2*3矩阵键盘使用的端口
sbitscl=P3^0;
//定义scl端口
sbitsda=P3^1;
//定义sda端口
sbitsound=P3^5;
//定义音频输出的端口
charcodedx516[3]_at_0x003b;
//硬件调试时必须加入的代码
uchardisplay[4]={10,10,10,10};
//四位数码管显示的数组
ucharscore[4]={0,0,0,0};
//记录四位选手的分数
ucharseg[12]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xbf};
//共阳极数码管段码
ucharkeyval=10,countdown,t=20,ct=3;
charbuzzy;
bitclrflag=0,startflag=0,keyflag=0,one=1,two=1;
//定义各种标志位
voiddelay5us();
//函数声明
voidi2c_init();
voidi2c_start();
voidi2c_stop();
voidi2c_ack();
voidi2c_noack();
voidi2c_sendByte(uchardata1);
voidsend();
uchargettime();
uchari2c_recByte();
voidkeyscan();
voiddelay(uintx)//延时函数
{
ucharz;
while(x--)for(z=0;
z<
100;
z++);
}
voiddelay5us()//延时5us函数,用于模拟I2C通信
_nop_();
_nop_();
voidmain()//主函数
uchari,j=0x77;
TMOD=0x11;
//使用T0、T1方式一
TH0=19456/256;
TL0=19456%256;
TH1=64876/256;
TL1=64876%256;
ET1=1;
ET0=1;
PT0=1;
EA=1;
//开中断
send();
do{
countdown=gettime();
//获取PCF8591的数据
}while(ct--);
while
(1)
{
keyscan();
//键盘扫描
display[2]=countdown/10;
//显示到数码管上
display[3]=countdown%10;
for(i=0;
i<
4;
i++)//动态显示程序
{
j=_crol_(j,1);
P2=0xff;
P0=seg[display[i]];
//数组的嵌套使用
P2=j;
delay(5);
}
}
voidkeyscan()//键盘扫描程序
delay(10);
X1=0;
X2=1;
if(Y0==0){keyval=1;
keyflag=1;
if(Y1==0){keyval=2;
if(Y2==0){keyval=3;
X1=1;
X2=0;
if(Y0==0){keyval=4;
if(Y1==0)startflag=1;
if(Y2==0)clrflag=1;
if(startflag)
if(!
keyflag)TR0=1;
//按下开始,定时器T0启动
if(two){buzzy=2;
TR1=1;
two=!
two;
}//2s单音提示
if(keyflag&
&
one)
{
display[0]=keyval;
//编号锁存
display[1]=11;
score[keyval-1]++;
//分数记录
countdown=score[keyval-1];
//分数显示
one=!
one;
TR1=1;
buzzy=2;
//2s提示音
}
if(clrflag)//系统清除按键
startflag=0;
keyflag=0;
TR0=0;
one=1;
two=1;
countdown=gettime();
display[0]=10;
display[1]=10;
TH0=19456/256;
TL0=19456%256;
t=20;
clrflag=0;
else
countdown=gettime();
clrflag=0;
if(keyflag)//选手违规抢答
TR0=1;
//2s警告单音提示
voidtimer0()interrupt1//定时器T0中断服务子程序
t--;
if(t==0)
t=20;
if(buzzy>
0)buzzy--;
else{TR1=0;
if(startflag==0&
TR0==1)TR0=0;
if(countdown>
0&
startflag&
!
keyflag)countdown--;
if(countdown==0&
startflag)
//计时结束,未抢答,2s警告
//2s
voidtimer1()interrupt3//定时器T1,确定声音频率
if(buzzy>
0)sound=!
sound;
elsesound=0;
voidi2c_init()//I2C初始化程序
scl=1;
sda=1;
delay5us();
voidi2c_start()//I2C开始标志(S)
sda=0;
scl=0;
voidi2c_stop()//I2C停止标志(P)
voidi2c_ack()//I2C接受应答位
if(sda)
scl=0;
i2c_stop();
voidi2c_noack()//I2C发送零应答
voidi2c_sendByte(uchardata1)//I2C发送一字节数据
uchari,temp;
temp=data1;
for(i=0;
8;
i++)
sda=temp&
0x80;
delay5us();
scl=1;
temp=temp<
<
1;
uchari2c_recByte()//I2C接收一字节数据
sda=1;
temp=(temp<
1)|sda;
returntemp;
voidsend()//PCF8591初始化
i2c_init();
i2c_start();
i2c_sendByte(0x90);
//发送地址,并确定写入
i2c_ack();
i2c_sendByte(0x00);
//发送控制码
i2c_stop();
}
uchargettime()//接收PCF8591数据
uchartemp;
i2c_sendByte(0x91);
//发送地址,并确定读
temp=i2c_recByte();
//接受PCF数据
i2c_noack();
temp=20+temp*30/255;
//倒计时时间为20-50