⑴ 七段数码管动态显示实验问题怎么办
七段数码管的动态扫描显示实验一、实验名称:七段数码管的动态扫描显示实验二、实验目的:(1)进一步熟悉QuartusII软件进行FPGA设计的流程(2)掌握利用宏功能模块进行常用的计数器,译码器的设计(3)学习和了解动态扫描数码管的工作原理的程序设计方法三、实验原理:实验板上常用4位联体的共阳极7段数码管,其接口电路是把所有数码管的8个笔划段a-h同名端连在一起,而每一个数码管由一个独立的公共极COM端控制。当向数码管发送字形码时,所有数码管都接收到相同的字形码时,但究竟是那个数码管亮,取决于COM端,这一端是由I/O控制的,所以就可以自行决定何时显示哪一位。动态扫描即采用分时方法,轮流控制各个LED轮流点亮。在轮流点亮扫描过程中,每一位显示器的点亮时间是极为短暂的,但由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上每个显示器并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感。四、实验要求:实现显示0000-9999的十进制计数器。五、实验步骤1.建立工程建立名为leddisplay的工程,并建立顶层图。2.设计技术时钟设计一分频器,对50MHz分频输出到计数器,让计数器以较慢速度递增。打开File..New,新建一个.v文件。输入以下程序:mole int_div(clk, div_out);input clk;output reg div_out;reg [31:0] clk_div;parameter CLK_FREQ = 'D50_000_000; //系统时钟50MHzparameter DCLK_FREQ = 'D10; //输出频率10/2Hzalways @(posedge clk)beginif(clk_div < (CLK_FREQ/DCLK_FREQ))clk_div <= clk_div+1;
elsebeginclk_div <= 0;div_out <= ~div_out;endendendmole输入完成后,将该文件设为顶层实体,该命令在Project->Set as top-level Entity。分析该设计文件:执行工具栏处的“Start Analysis &Synthesis命令按钮,开始分析综合,此步骤在这里用于检查设计错误。分析成功后要生成一个分频器的元件符号,执行File->Create Symbol files for current file,开始建立该文件的元件符号。3.数码管扫描显示程序设计mole segmain(clk,reset_n,datain,seg_data,seg_com);input clk;input reset_n;input[31:0] datain;output[7:0] seg_data;output[7:0] seg_com;reg[7:0] seg_com;reg[7:0] seg_data;reg[3:0] bcd_led;reg[36:0] count;integer temp;always@(posedge clk)beginif(!reset_n) count<=0;elsebegincount<=count+1;temp=datain;endendalways@(count[14:12] or datain)begincase(count[14:12])3'b000:begintemp=temp%10;bcd_led=temp[3:0];seg_com=8'b11111110;
end3'b001:begintemp=temp%100/10;bcd_led=temp[3:0];seg_com=8'b11111101;end3'b010:begintemp=temp%1000/100;bcd_led=temp[3:0];seg_com=8'b11111011;end3'b011:begintemp=temp%10000/1000;bcd_led=temp[3:0];seg_com=8'b11110111;end3'b100:begintemp=temp%100000/10000;bcd_led=temp[3:0];seg_com=8'b11101111;end3'b101:begintemp=temp%1000000/100000;bcd_led=temp[3:0];seg_com=8'b11011111;end3'b110:begintemp=temp%10000000/1000000;bcd_led=temp[3:0];seg_com=8'b10111111;end3'b111:begintemp=temp%100000000/10000000;
bcd_led=temp[3:0];seg_com=8'b01111111;endendcaseendalways@(seg_com or bcd_led)begincase(bcd_led)4'h0:seg_data=8'hc0;4'h1:seg_data=8'hf9;4'h2:seg_data=8'ha4;4'h3:seg_data=8'hb0;4'h4:seg_data=8'h99;4'h5:seg_data=8'h92;4'h6:seg_data=8'h82;4'h7:seg_data=8'hf8;4'h8:seg_data=8'h80;4'h9:seg_data=8'h90;4'ha:seg_data=8'h88;4'hb:seg_data=8'h83;4'hc:seg_data=8'hc6;4'hd:seg_data=8'ha1;4'he:seg_data=8'h86;4'hf:seg_data=8'h8e;default:seg_data=8'hc0;endcaseendendmole输入完成后,将其设为顶层实体,检验后生成元件符号。4.调用宏功能模块设计计数器双击顶层图空白处,弹出symbol对话框,展开Libraries,找到lpm_counter.按步骤一步步生成4位BCD码的计数器。5.设计完整的顶层返回顶层原理图,并注意重新将顶层原理图设为顶层实体。双击顶层图空白的地方,弹出symbol对话框,展开Libraries栏的Project库,可看到上述步骤建立的一些元件符号。
按OK,在图纸上空白地方点击即可输入相应的元件,添加其它元件,完成下图连接:6.设置芯片和管脚。参照以下tcl script 文件配置芯片管脚,并运行该tcl脚本。#Setup.tclset_global_assignment -name RESERVE_ALL_UNUSED_PINS "AS INPUT TRI-STATED"set_global_assignment -name ENABLE_INIT_DONE_OUTPUT OFFset_location_assignment PIN_149 -to clkset_location_assignment PIN_90 -to reset#ledset_location_assignment PIN_148 -to 78ledcom[0]set_location_assignment PIN_147 -to 78ledcom[1] set_location_assignment PIN_160 -to 78ledcom[2]set_location_assignment PIN_159 -to 78ledcom[3]set_location_assignment PIN_162 -to 78ledcom[4]set_location_assignment PIN_161 -to 78ledcom[5] set_location_assignment PIN_166 -to 78ledcom[6]set_location_assignment PIN_164 -to 78ledcom[7]set_location_assignment PIN_145 -to 78leddata[0]set_location_assignment PIN_143 -to 78leddata[1]set_location_assignment PIN_137 -to 78leddata[2]
set_location_assignment PIN_139 -to 78leddata[3]set_location_assignment PIN_144 -to 78leddata[4]set_location_assignment PIN_146 -to 78leddata[5]set_location_assignment PIN_135 -to 78leddata[6]set_location_assignment PIN_142 -to 78leddata[7]7.编译执行菜单命令Project->Set as top-level Entity,将顶层图设为当前顶层实体,然后编译。8.下载1)下载设置:使用下载线下载配置文件到FPGA。2)下载完毕后可看到实验现象:数码管实现显示0000-9999的十进制计数器。六、实验小结(1)这是我们这学期近代电子学实验的第一次实验报告,在前几次的实验中,已经熟悉了QuartusII设计软件的基本功能、原理图输入和HDL输入方法、功能仿真、综合、配置与编程的设计过程,尤其是针对QuartusII的使用初步花了几节课的时间,毕竟这个软件对于我们来说是很陌生的。(2)在前面的实验中,学习QuartusII的基础实验中,课本上和课件上的介绍是很详细的,每一个操作步骤都是有截图的,所以跟着步骤一步一步的,只要小心谨慎,是不会出现错误的。(3)这次的实验是对前几次的实验的整合,在LED的按键控制的基础上进行的实验,在我做实验的工程中,查找错误是最麻烦的琐碎的,往往错误源于很小的失误,比如忘记建立顶层图、调用宏功能模块设计计数器时选错选项等等。这些错误看似微不足道,但是检查起来异常麻烦,所以我们在做实验的时候一定要小心谨慎,切记三心二意。(4)通过这一次的实验练习,希望为以后打下坚实的基础。
¥
5.9
网络文库VIP限时优惠现在开通,立享6亿+VIP内容
立即获取
七段数码管的动态扫描显示实验
七段数码管的动态扫描显示实验
一、实验名称:七段数码管的动态扫描显示实验
二、实验目的:
(1)进一步熟悉QuartusII软件进行FPGA设计的流程
(2)掌握利用宏功能模块进行常用的计数器,译码器的设计
(3)学习和了解动态扫描数码管的工作原理的程序设计方法
三、实验原理:
第 1 页
实验板上常用4位联体的共阳极7段数码管,其接口电路是把所有数码管的8个笔划段a-h同名端连在一起,而每一个数码管由一个独立的公共极COM端控制。
当向数码管发送字形码时,所有数码管都接收到相同的字形码时,但究竟是那个数码管亮,取决于COM端,这一端是由I/O控制的,所以就可以自行决定何时显示哪一位。动态扫描即采用分时方法,轮流控制各个LED轮流点亮。
在轮流点亮扫描过程中,每一位显示器的点亮时间是极为短暂的,但由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上每个显示器并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感。
⑵ 数码管动态显示工作原理
1、多个数码管的段码连接在一起,位码分别控制。
2、由于段码连接在一起,如果数码管全亮,则显示的数据相同,所以为了显示不同的数字,任何时刻,只能有一个数码管显示,其余不显示。
3、用软件使这几个数码管轮流显示我们需要的数字。
4、只要更新频率足够快(>100Hz),肉眼看起来,这些数码管就同时显示我们需要的数字了。
(2)数码管动态显示装置实验扩展阅读:
数码管的最常见形式有10个阴极,形状为数字0到9,某些数码管还有一个或两个小数点。然而也有其他类型的数码管显示字母、标记和符号。如一种“数码管”,其阴极为一个模板制成的面具,上面有数字形状的孔。一些俄罗斯的数码管,如IN-14,使用倒立的数字2代表5,大概是为了节约生产成本,而没有明显的技术或美学方面的原因。俄罗斯的数码管大部分都使用了倒立的2作为5。
将170伏的直流电压加在阴极和阳极之间,每一个阴极可以发出氖的的红橙色光。由于混合气体的不同,不同类型的数码管之间的颜色有所区别。寿命较长的数码管在制造中加入了汞,减少了溅射,结果发出的光的颜色为蓝色或紫色调。在某些情况下,这些颜色被玻璃上的红色或橙色过滤涂层过滤。
⑶ 什么是数码管动态显示
该实验用到实验板的资源电路图如下:
其中P0口是段码,低电平有效。P2口是位码,高电平有效。P2.0口控制第1个数码管,一直到P2.7口控制第8个。该板的段码表如下:
各个数码管的段码都是p0口的输出,即各个数码管输入的段码都是一样的, 为了使其分别显示不同的数字, 可采用动态显示的方式,即先只让最低位显示0(含点),经过一段延时,再只让次低位显示1,如此类推。由视觉暂留,只要我们的延时时间足够短,就能够使得数码的显示看起来非常的稳定清楚。过程如下图。
采用上述方法思路编写如下:
org 0000h
start: mov a,#08h ;0 ;段码
mov p0,a
mov p2,#01h ;位码
lcall delay_1ms
mov a,#0abh ;1
mov p0,a
mov p2,#02h
lcall delay_1ms
mov a,#12h ;2
mov p0,a
mov p2,#04h
lcall delay_1ms
mov a,#22h ;3
mov p0,a
mov p2,#08h
lcall delay_1ms
mov a,#0a1h ;4
mov p0,a
mov p2,#10h
lcall delay_1ms
mov a,#24h ;5
mov p0,a
mov p2,#20h
lcall delay_1ms
mov a,#04h ;6
mov p0,a
mov p2,#40h
lcall delay_1ms
; mov a,#0aah ;7
; mov p0,a
mov p0,#0aah ;感觉用这句和上面两句实现一样,可能这种习惯以后会有用吧
mov p2,#80h
lcall delay_1ms
ljmp start
delay_1ms: mov r6,#2
temp: mov r5,#0ffh
djnz r5,$
djnz r6,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: lcall play1 ;调用显示段码子程序
lcall play2 ;调用显示位码子程序
lcall delay_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: db 08h,0abh,12h,22h,0a1h,24h,04h,0aah ;段码表
table2: db 01h,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实现如下(参考了AS的例程):
#include <reg51.h>
#include <intrins.h> // 包含了左移函数_crol_()
void delayms(unsigned char ms); // 延时子程序
unsigned char data dis_digit; // 位选通值, 传送到P2口用于选通当前数码管的数值,
// 如等于0x01时,选通P2.0口数码管
unsigned char code dis_code[11]={0x08,0xab,0x12,0x22,0xa1, // 0,1,2,3, 4
0x24,0x04,0xaa,0x00,0x20, 0xff}; // 5,6,7,8,9, off
unsigned char data dis_buf[8]; // dis_buf 显于缓冲区基地址
unsigned char data dis_index; // 显示索引, 用于标识当前显示的数码管和缓冲区的偏移量
void main()
{
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; // 首先选通P2.0
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; // 见注释
}
}
void delayms(unsigned char ms) // 延时子程序(晶振12M)
{
unsigned char i;
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(1000),下载到板上就可以看到实际上数码管是由低位到高位逐位显示的。
--------------------------------------------------------------------------------
若单单就实现这个功能而言,可以直接调入段码数组dis_code[11]中下标从0到7的值,而不必再设置缓冲数组dis_buf[],实现如下:
#include <reg51.h>
#include <intrins.h> //_crol_()用
void delayms(unsigned char ms); //延时子程序
unsigned char data dis_digit; //位选通值, 传送到P2口用于选通当前数码管的数值,
//如等于0x01时,选通P2.0口数码管
unsigned char code dis_code[11]={0x08,0xab,0x12,0x22,0xa1, // 0,1,2,3,4
0x24,0x04,0xaa,0x00,0x20, 0xff}; // 5,6,7,8,9,off
unsigned char data dis_index; //显示索引, 用于标识当前显示的数码管和缓冲区的偏移量
void main()
{
P0 = 0xff; // 关闭所有数码管
P2 = 0x00;
dis_index = 0; // 当前偏移量为0
dis_digit = 0x01; // 选通P2.0
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;
}
}
void delayms(unsigned char ms) // 延时子程序(晶振12M)
{
unsigned char i;
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的调试也不熟悉,先把问题留到这,待找出原因后再补上。
[2006.5.2] 找出原因啦,补上:
今天又看了一下,找到上面的错误出在哪了。当时是想用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 ……
幂次运算函数flaot pow(float x, float y)包含在math.h中, 返回值为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);
}
再次下载到板上发现仍有问题, 即延时很小的时候显示混乱,但加大延时时间(如程序中的值)可以观查到数码管是按位正确显示的。另外用这种方法产生的代码量也很大(从写入速度看,很明显)。这里仅提出了一个思路,只在此实验中适用,意义不大,到此为止。
[补充结束]
--------------------------------------------------------------------------------
AS中绐出的例程是利用定时中断做的延时,参考修改到我的板上,程序如下:
#include <reg51.h>
#include <intrins.h> // 包含了左移函数_crol_()
unsigned char data dis_digit; // 位选通值, 传送到P2口用于选通当前数码管的数值,
// 如等于0x01时,选通P2.0口数码管
unsigned char code dis_code[11]={0x08,0xab,0x12,0x22,0xa1, // 0,1,2,3,4
0x24,0x04,0xaa,0x00,0x20, 0xff}; // 5,6,7,8,9,off
unsigned char data dis_buf[8]; // dis_buf 显于缓冲区基地址
unsigned char data dis_index; // 显示索引, 用于标识当前显示的数码管和缓冲区的偏移量
void main()
{
P0 = 0xff; //关闭所有数码管
P2 = 0x00;
TMOD = 0x01; // 00000001B 定时计数器0工作在方式1,16位定时器/计数器
TH0 = 0xFC;
TL0 = 0x17; // 预置初值 FC17H=64535D, 216-64535=1001us=1ms
IE = 0x82; // 10000010B T0溢出中断允许
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); // 循环等待中断
}
void timer0() interrupt 1 // 定时器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=1111111111111111B, 也可以说65536是溢出得到的。而何时响应中断就成了关键,拿上例来说,如设初值为64535(FC17),则计数到65535时,已经计数为1000个,即1ms,但此时并未发生溢出,因此也没有触发中断。而是在下一个计数后才发生。确切值应为1001us。若初值为64536(FC18),则恰好为所需值,所以上例中的初值应该用FC18而不是FC17。这仅仅是我自己的一点看法,至于是不是这样,还有待进一步考证。
--------------------------------------------------------------------------------
最终下载到实验板上结果:
######################################补充########################################
用Proteus仿真结果如下(某一状态的截图):
★ 该电路段码是按与板上接法对应的,即按前面的段码表次序连接。另外这个八位的仿真数码管最左端是第一位,最右端是第八位,与板上的顺序相反,所以接为了统一,该图以板为准连接。上图不加上拉电阻也可仿真出结果,只是P0口高电平显示为灰,即高阻。
⑷ 51机数码管动态显示
只改主程序就行了。
void main()//主程序
{
bit die;//移动方向标志位,0=向左,1=向右
uchar n,x;
while(1)
{
display(x);
n++;
if(n>40)
{
n=0;
if(die==0)//向左
{
x++;
if(x>7)
die=1;//改变方向
}
else//向右
{
x--;
if(x==0)
die=0;//改变方向
}
}
}
}
请采纳。