多位数码管动态显示的实验.docx
《多位数码管动态显示的实验.docx》由会员分享,可在线阅读,更多相关《多位数码管动态显示的实验.docx(20页珍藏版)》请在冰豆网上搜索。
多位数码管动态显示的实验
【006】多位数码管动态显示 [51]
实验目的:
数码管动态显示多位数字。
实验参考:
笨笨工作室实验五、多位数码动态显示。
()
实验板:
FB51A()。
该实验用到实验板的资源电路图如下:
其中P0口是段码,低电平有效。
P2口是位码,高电平有效。
口操纵第1个数码管,一直到口操纵第8个。
该板的段码表如下:
各个数码管的段码都是p0口的输出,即各个数码管输入的段码都是一样的,为了使其别离显示不同的数字,可采纳动态显示的方式,即先只让最低位显示0(含点),通过一段延时,再只让次低位显示1,如此类推。
由视觉暂留,只要咱们的延不时刻足够短,就能够够使得数码的显示看起来超级的稳固清楚。
进程如以下图。
采纳上述方式思路编写如下:
org 0000h
start:
mov a,#08h ;0 ;段码
mov p0,a
mov p2,#01h ;位码
lcalldelay_1ms
mov a,#0abh ;1
mov p0,a
mov p2,#02h
lcalldelay_1ms
mov a,#12h ;2
mov p0,a
mov p2,#04h
lcalldelay_1ms
mov a,#22h ;3
mov p0,a
mov p2,#08h
lcalldelay_1ms
mov a,#0a1h ;4
mov p0,a
mov p2,#10h
lcalldelay_1ms
mov a,#24h ;5
mov p0,a
mov p2,#20h
lcalldelay_1ms
mov a,#04h ;6
mov p0,a
mov p2,#40h
lcalldelay_1ms
; mov a,#0aah ;7
; mov p0,a
mov p0,#0aah ;感觉用这句和上面两句实现一样,可能这种适应以后会有效吧
mov p2,#80h
lcalldelay_1ms
ljmp start
delay_1ms:
mov r6,#2
temp:
mov r5,#0ffh
djnzr5,$
djnzr6,temp
ret
end
下载到板上取得测结果为从低到高八位别离显示0到7(含点)。
★上述方式逐次给P0或P2赋值,一方面程序的复杂程度增加,另外一方面会使得程序的灵活性降低。
若是要改变显示的数字,程序改动起来很麻烦。
因此要用51单片机中经常使用的一种方式:
查表法。
例如P0口输出段码时,咱们能够把要显示的段码放在一个表格中,然后每次从那个表格里面取数,送到P0口即可。
P2口输出位码时,能够把要用的位码放在另一个表格里,每次从此表中取数,送入P2口。
如此,若是要改变显示的数字,只需要改变表格里面的数。
org 0000h
start:
mov r7,#0ffh ;r7,r6查表时送入变址寄放器a(因自加1后为0,因此预置ffh)
mov r6,#0ffh
loop:
lcallplay1 ;调用显示段码子程序
lcallplay2 ;调用显示位码子程序
lcalldelay_1ms
cjne a,#80h,loop ;判断是否到了最左边的数,即第8个位码
ajmp start
play1:
;查表求段码子程序
; mov a,r7
; inc a
; mov r7,a
inc r7 ;这2句和上面三条语句实现功能相同
mov a,r7 ;a在这里做变址寄存器
mov dptr,#table1;表首址送dptr,dptr做基址寄放器
movc a,@a+dptr ;基址寄放器加变址寄放器寻址
mov p0,a
ret
play2:
;查表求位码子程序(原理同play1)
mov a,r6
inc a
mov r6,a
mov dptr,#table2
movc a,@a+dptr
mov p2,a
ret
table1:
db08h,0abh,12h,22h,0a1h,24h,04h,0aah ;段码表
table2:
db01h,02h,04h,08h,10h,20h,40h,80h ;位码表
delay_1ms:
mov r5,#02h ;延时1ms子程序
temp:
mov r4,#0ffh
djnz r4,$
djnz r5,temp
ret
end
下载到板上验证得到预想结果。
C51实现如下(参考了的例程):
#include<>
#include<> //包含了左移函数_crol_()
voiddelayms(unsignedcharms);//延时子程序
unsignedchardatadis_digit; //位选通值,传送到P2口用于选通当前数码管的数值,
//如等于0x01时,选通口数码管
unsignedcharcodedis_code[11]={0x08,0xab,0x12,0x22,0xa1, //0,1,2,3,4
0x24,0x04,0xaa,0x00,0x20,0xff};//5,6,7,8,9,off
unsignedchardatadis_buf[8]; //dis_buf显于缓冲区基地址
unsignedchardatadis_index; //显示索引,用于标识当前显示的数码管缓和冲区的偏移量
voidmain()
{
P0=0xff; //关闭所有数码管
P2=0x00;
dis_buf[0]=dis_code[0];
dis_buf[1]=dis_code[1];
dis_buf[2]=dis_code[2];
dis_buf[3]=dis_code[3];
dis_buf[4]=dis_code[4];
dis_buf[5]=dis_code[5];
dis_buf[6]=dis_code[6];
dis_buf[7]=dis_code[7];
dis_digit=0x01; //首先选通
dis_index=0; //当前偏移量为0
while
(1)
{
P0=dis_buf[dis_index]; //段码送P0口
P2=dis_digit; //选能位(即位码)
delayms
(1); //延时
dis_digit=_crol_(dis_digit,1);//位选通左移,下次选通下一位
dis_index++; //下一个段码
dis_index&=0x07; //见注释
}
}
voiddelayms(unsignedcharms) //延时子程序(晶振12M)
{
unsignedchari;
while(ms--)
{
for(i=0;i<120;i++);
}
}
★注释:
此句作用是8个数码管全数扫描完一遍以后,再回到第一个开始下一次扫描。
写回一样形式:
dis_index=dis_index&0x07。
这种方式挺新,第一次见到,十六进制的07确实是二进制的00000111,如此通过与操作能够操纵循环了。
比如dis_index经第一次循环后值为00000001,和0x07与操作后值不变仍为0x01,第二次循环时,其值为0为0x02,与0x07后仍为0x02,一直到其值增为0x07时仍是不变的,但再次循环后其值为0x80,再与0x07后就变成0x00了,如此又从初始循环了。
此句可用if(dis_index==8)dis_index=0代替,成效一样。
★通过C51用上述方式实现时,其段码放在了数组dis_code[11]中,再通过缓冲区数组dis_buf[]将程序中要挪用的值装入,如此就能够够用下标(偏移量)访问了。
如此看上去有些繁锁,但其思路比较清楚,结构上也很明了,具有通用性,便于扩展。
★另外只要把程序中的延时加长,如delayms(250),下载到板上就能够够看到事实上数码管是由低位到高位逐位显示的。
假设单单就实现那个功能而言,能够直接调入段码数组dis_code[11]中下标从0到7的值,而没必要再设置缓冲数组dis_buf[],实现如下:
#include<>
#include<> //_crol_()用
voiddelayms(unsignedcharms);//延时子程序
unsignedchardatadis_digit; //位选通值,传送到P2口用于选通当前数码管的数值,
//如等于0x01时,选通口数码管
unsignedcharcodedis_code[11]={0x08,0xab,0x12,0x22,0xa1, //0,1,2,3,4
0x24,0x04,0xaa,0x00,0x20,0xff};//5,6,7,8,9,off
unsignedchardatadis_index; //显示索引,用于标识当前显示的数码管缓和冲区的偏移量
voidmain()
{
P0=0xff; //关闭所有数码管
P2=0x00;
dis_index=0; //当前偏移量为0
dis_digit=0x01;//选通
while
(1)
{
P0=dis_code[dis_index];//段码送P0口
P2=dis_digit; //位码送P2口
delayms
(1);
dis_digit=_crol_(dis_digit,1);//位选通左移,下次选通下一名
dis_index++;
dis_index&=0x07;
}
}
voiddelayms(unsignedcharms) //延时子程序(晶振12M)
{
unsignedchari;
while(ms--)
{
for(i=0;i<120;i++);
}
}
★本来是想通过以下方式实现一次循环的:
for(dis_index=0;dis_index<8;dis_index++)
{
P0=dis_code[dis_index];//段码送P0口
P2=dis_index+1; //位码送P2口
delayms
(1);
}
可得到的总是错误的结果:
第0位到第2位这三位显示的是三个8,第3位显示的是7,高四位没有显示。
加长延时逐位观察也没有发现错误的规律,对Keil的调试也不熟悉,先把问题留到这,待找出原因后再补上。
[找出缘故啦,补上:
今天又看了一下,找到上面的错误出在哪了。
那时是想用dis_index的值做为位码的,即第一名显示0时,段码为dis_code[0],即dis_index值为0,现在位码值为1。
第二位显示1时,段码为dis_code[1],即dis_index值为1,现在位码值为2。
因此就简单用了个加1运算,将P0口的偏移值与P2口的位码联系起来。
但认真想一下位码的原理,上述方式显然是错的,只要再验证一步就明白了,即当第3位显示2时,段码为dis_code[2],dis_index值为2,加1后为3,按上述方式时就将那个3作为了位码,而正确的位码应该是4(00000100B)。
因此犯错。
事实上那个对应关系是有的,但不是简简单单的加1,位码应该是2的dis_index次幂。
即:
0--1
1--2
2--4
3--8
4--16 ……
幂次运算函数flaotpow(floatx,floaty)包含在中,返回值为xy(float型):
for(dis_index=0;dis_index<8;dis_index++)
{
P0=dis_code[dis_index];//段码送P0口
P2=(char)pow(2,dis_index); //位码送P2口
delayms(255);
}
再次下载到板上发现仍有问题,即延时很小的时候显示混乱,但加大延时时间(如程序中的值)可以观查到数码管是按位正确显示的。
另外用这种方法产生的代码量也很大(从写入速度看,很明显)。
这里仅提出了一个思路,只在此实验中适用,意义不大,到此为止。
[补充终止]
中绐出的例程是利用按时中断做的延时,参考修改到我的板上,程序如下:
#include<>
#include<> //包含了左移函数_crol_()
unsignedchardatadis_digit; //位选通值,传送到P2口用于选通当前数码管的数值,
//如等于0x01时,选通口数码管
unsignedcharcodedis_code[11]={0x08,0xab,0x12,0x22,0xa1, //0,1,2,3,4
0x24,0x04,0xaa,0x00,0x20,0xff};//5,6,7,8,9,off
unsignedchardatadis_buf[8]; //dis_buf显于缓冲区基地址
unsignedchardatadis_index; //显示索引,用于标识当前显示的数码管缓和冲区的偏移量
voidmain()
{
P0=0xff; //关闭所有数码管
P2=0x00;
TMOD=0x01; //00000001B按时计数器0工作在方式1,16位按时器/计数器
TH0=0xFC;
TL0=0x17; //预置初值FC17H=64535D,216-64535=1001us=1ms
IE=0x82; //BT0溢出中断许诺
dis_buf[0]=dis_code[0x0];
dis_buf[1]=dis_code[0x1];
dis_buf[2]=dis_code[0x2];
dis_buf[3]=dis_code[0x3];
dis_buf[4]=dis_code[0x4];
dis_buf[5]=dis_code[0x5];
dis_buf[6]=dis_code[0x6];
dis_buf[7]=dis_code[0x7];
dis_digit=0x01; //选通第0位数码管
dis_index=0; //偏移初值为0
TR0=1; //启动T0
while
(1); //循环等待中断
}
voidtimer0()interrupt1 //按时器0中断效劳程序,用于数码管的动态扫描
{
TH0=0xFC; //发生中断定时/计数器重装初值
TL0=0x17; //感觉此处(及上)应该是0x18,而不是17,分析如下
P2=0x00; //先关闭所有数码管
P0=dis_buf[dis_index]; //段码送P0口
P2=dis_digit; //位码送P2口
dis_digit=_crol_(dis_digit,1); //位选通值左移,下次中断时选通下一名数码管
dis_index++;
dis_index&=0x07; //8个数码管全部扫描完一遍之后,再回到第一个开始下一次扫描
}
★按时器/计数器的输入脉冲周期与机械周期一样,为时钟振荡频率的1/12。
晶振用12M时,输入脉冲周期距离为1us。
机械周期为1us。
设T0的初值为X,计算初值的方式:
本例中按时器用方式1,是16位的按时器,即最大值为216=65536,超过此值将发生溢出,引发中断,进入中断处置程序。
那个地址要让其延时1ms,即1000us,那么有式216-X=1000,可得X=64536,换算为16进制为FC18,即初值TH0=0xFC,TL0=0x18。
即按时器由64536开始计数,经1000次计数后值为65536,将发生按时中断,再进入中断处置子程序后,从头装和初值,如此循环下去。
而在上例中其装入的初值并非FC18(64536),而是FC17(64535)。
我想大概认为其计数范围在0~65565的原因吧,我也想过这个问题,是用216-计数初值=中断距离 呢,仍是用(216-1)-计数初值=中断距离呢?
顺手查了几本书,说法不一,只是用前者的较多,我自己也以为前者比较合理,因为在运算机中16位的二进制不能表示65536,在列位均为1时表示的值为65535,即65535H=111B,也能够说65536是溢出取得的。
而何时响应中断就成了关键,拿上例来讲,如设初值为64535(FC17),那么计数到65535时,已经计数为1000个,即1ms,但现在并未发生溢出,因此也没有触发中断。
而是在下一个计数后才发生。
确切值应为1001us。
假设初值为64536(FC18),那么恰好为所需值,因此上例中的初值应该用FC18而不是FC17。
这仅仅是我自己的一点观点,至于是不是如此,还有待进一步考证。
最终下载到实验板上结果:
######################################补充########################################
用Proteus仿真结果如下(某一状态的截图):
★该电路段码是按与板上接法对应的,即按前面的段码表顺序连接。
另外那个八位的仿真数码管最左端是第一名,最右端是第八位,与板上的顺序相反,因此接为了统一,该图以板为准连接。
上图不加上拉电阻也可仿真出结果,只是P0口高电平显示为灰,即高阻。
引用地址:
多功能数字钟的设计方案
一、蜂鸣器的设计
二、温度传感器(DB18B20)设计
3、液晶(1620)模块
4、正弦波转换为方波电路图
五、+12V交流电压转换为+5V直流电压
六、红外发射模块
7、红外接收模块
八、综合电路图
方案报告:
校电子设计大赛设计与总结报告
(题目:
多功能数字钟)
一、任务与要求
任务:
设计制作一个24小时制多功能数字钟。
要求:
a.大体要求
一、具有时刻设置(小时和分钟)、闹钟时刻设置、闹钟开、闹钟关功能。
二、数字显示小时、分钟,有AM、PM指示器,闹钟就绪灯,蜂鸣器。
3、220V供电。
b.发挥部份
一、键盘切换现场环境温度显示。
(0~60℃1℃)
二、键盘切换电网频率、电压显示。
3、电压欠压、过压报警(~220V+/—10%)功-能。
4、非接触止闹功能。
二、方案比较及作品模块介绍
多功能数字钟,具有时钟时刻设置、闹钟时刻设置、闹钟开、闹钟关等功能,数字显示日期、小时、分钟、秒,有AM、PM指示器,闹钟就绪灯(由指示灯指示是不是设有闹钟)、蜂鸣器。
可用键盘切换现场温度、电网频率、电压等,能动态刷新显示以上各测量参数。
还能够进行电压欠压、过压报警(220V±10%)。
当闹钟启动后,能够通过遥控器遥控止闹。
电子钟的设计包