北邮数电实验报告双色点阵.docx
《北邮数电实验报告双色点阵.docx》由会员分享,可在线阅读,更多相关《北邮数电实验报告双色点阵.docx(27页珍藏版)》请在冰豆网上搜索。
北邮数电实验报告双色点阵
北京邮电大学
数电综合实验报告
实验名称:
双色点阵显示控制器
学院:
姓名:
班级:
学号:
班内序号:
一.设计课题的任务要求
用8×8点阵设计双色点阵显示控制器
基本要求:
1、固定红色显示一个汉字或图形,显示亮度4级可调,用一个btn按钮实现亮度调节,亮度变化视觉效果要尽量明显。
2、用从红到绿8级渐变色显示一个固定汉字或图形。
3、分别用单字循环显示、左右滚动显示、上下滚动显示三种显示方式单色显示四个汉字或图形,显示过程中,显示方式用一个btn按键进行切换。
4、显示的图形或汉字要尽量饱满美观。
提高要求:
滚动显示过程中实现四种显示颜色的自动变换,颜色变化视觉效果要尽量明显。
自拟其它功能。
二.系统设计
1.设计思路
考虑本程序设计要求模块明确单一但需要变化多种状态,因此采用单模块多状态多进程方式实现。
其中防抖进程用于检测输入,本程序使用BTN按键进行输入控制,而按键的键入可能由于抖动产生多个上升沿,对程序的控制产生影响。
防抖动采用了延时原理来对冒险或长时间按键电平进行处理。
分频使用了多个进程产生程序所需要的多个时钟频率。
包括:
clk1(扫描时钟大于480Hz但不过高)
clk2(占空比调节1clk1周期的整数倍,但不过高)
clk3(占空比调节2同clk2,但占空比不同)
clk4(占空比调节3同clk2,但占空比不同)
clk5(按键时钟约100Hz)
clk6(滚动变化计时约1Hz)
状态机变化进程用于管理状态变化,当遇到按键电平信号时根据规则改变对应状态。
显示进程包括主要显示进程和一些辅助进程,辅助进程完成了时钟对应计数器计数,主进程则根据状态变化显示不同显示模式下不同的图案。
2.总体框图
(1)
总体结构框图:
(2)逻辑划分框图
Mode
P
pPMode
PMode
Mode
P
P
P
(3)MDS图
P=1
Mode=1
Mode=1
Mode=1reset
Mode=1
P=1
3.各进程介绍
1分频进程们
分频进程含一个从50M(也可能是25M)分出的扫描频率clk1,这个频率要大于60*8=480Hz(因为人眼分辨频率为60Hz左右,要保证每排扫描时达到60Hz则需480Hz)。
然后以clk1为基准频率,将clk2、clk3、clk4以clk1的8倍数作占空比调整。
具体是,clk2、clk3、clk4均记为clk1的48分频,然后占空比调为32:
16,24:
24,16:
32
做8倍数的原因是为了保证至少每扫完一屏才有一次占空比调整,否则在主频率不足情况下可能有些排暗有些排亮(详见问题分析)
最后再将周期约为100ms的按键时钟clk5和周期为1s的滚动时钟clk6分出就可以了。
2状态机进程
没什么可说的,我写的是单进程状态机,防止了多进程状态机可能带来的冒险。
敏感量直接写的是防抖动输出的信号,本以为没必要加同步时序,但事实证明这是有问题的。
(详见代码分析)
3防抖动进程
防抖动我用的是老师给的代码。
先阐述一下原理:
按键输入电平做敏感量,将输入赋值给resetmp1。
resetmp1被赋值后,将这段值有时延的赋值给resetmp2,并利用这段时间去判断是否按键仍为1。
则面对下面两种情况下防抖程序可以做出调整
A.突然翘起的冒险:
此种情况下若r1得到赋值,则r2在下一个时钟沿得到赋值,但由于此时r1电平为低,不能判断成功
B.长时间按下:
此种情况下r1得到赋值,在赋值给r2的间歇判断正确,而后r2得到赋值后不再有效,因此输出仅有一个按键时钟高电平
Clk
In_signal
Resetmp1
Resetmp2
Out_signal
但在使用该进程的过程中遇到了问题,具体见问题分析。
(4)显示进程
首先说一下点阵显示的原理:
如下图所示,列高电平有效,行低电平有效,当对应高电平和低电平时中间的灯就亮了。
而不同颜色则是由不同亮度的红色灯和绿色灯光混合而成。
由于都是整排显示,为了达到整屏显示效果,需要每排进行扫描,当扫描频率较高时人眼无法分辨则可达到整屏显示。
数码管原理类似,这里不再赘述。
接着说一下亮度调节:
前面已经说过时钟分频的问题,在显示每排前对占空比时钟进行判断,则有些情况会亮,有些情况会灭,这样就能很好的形成明暗效果。
最后说一下滚动:
滚动我是用最笨的方法实现的,即一屏一屏的画出每屏图案。
当时我是觉得可能循环之类的不太好实现,不过有同学使用了循环减少了代码量,我觉得以后很值得借鉴。
三.仿真波形及分析
1、防抖动波形的测试
这两张张图中,mode为输入,并设置了很多毛刺作为干扰电平,可以从图中看出多个干扰电平出现时仅输出一个周期的高电平。
那为什么仅有毛刺时也会有输出呢?
因为毛刺刚好位于clk5的下降沿,这样的话也会造成程序误判一次输出(这也是这个防抖动程序的不足之处)。
而连续多次下降沿毛刺造成的结果却是r1连续两个周期(其实是一个半)的高电平,同步电路就是这样一个特点。
当仅有冒险且冒险未在clk5边沿时,则不会触发电平。
如下图所示
关于同步电路问题的下一步讨论(包含同步时钟选用的问题)放在问题分析部分。
2、显示波形测试
从图中可以看出当Mode改变后red_out开始有波形出现,这说明模式已经从m0转变为m1_p1
改变p后,则有:
Clk2为0时则red输出为0,这时就有了亮度的变化。
其它亮度变化以此类推,这里不再赘述。
以上波形显示了最重要的状态变化过程,状态变化后按该状态显示内容显示,只要状态机能正常工作,仿真的任务就基本完成。
四、源代码解析
(由于源程序后半部分属于机械重复,因此省略)
(这里按50M时钟输入写)
LibraryIEEE;
USEIEEE.STD_LOGIC_1164.All;
ENTITYscIS
PORT(
clk,mode,p,reset:
INSTD_LOGIC;--reset用于复位到m0
red_out,green_out:
OUTstd_logic_vector(7downto0):
="00000000";
row_out:
OUTstd_logic_vector(7downto0):
="11111111";
num_out:
OUTstd_logic_vector(7downto0):
="00000000";--用于数码管输出
num_choose:
Outstd_logic_vector(5downto0):
="111110");--用于选择数码管
ENDsc;
architectureaofscis
TYPEstate_typeIS(m0,m1_p1,m1_p2,m1_p3,m1_p4,m2,m3_p1,m3_p2,m3_p3);
signalmode_mp1,mode_mp2,mode_out:
STD_logic;--防抖动临时信号(下同)
signalp_mp1,p_mp2,p_out:
STD_logic;
signalclk1,clk2,clk3,clk4,clk5,clk6:
STD_logic;
signaltmp1:
IntegerRange0To1000000:
=0;
signaltmp2:
IntegerRange0To200000:
=0;
signaltmp3:
IntegerRange0To50000000:
=0;
signaltmp4:
IntegerRange0To30:
=0;
signaltmp5:
IntegerRange0To30:
=0;
signalcount:
IntegerRange0To8:
=0;
signalcount_2:
IntegerRange0To4:
=0;
signalcount_3:
IntegerRange0To32:
=0;
signalclk_tmp:
STD_logic;
signalstate:
state_type;
signalred,green:
std_logic_vector(7downto0):
="00000000";
signalrow:
std_logic_vector(7downto0):
="11111110";
Begin
p1_1:
Process(clk)
Begin
if(clk'eventANDclk='1')then
iftmp1=50000then
tmp1<=0;
else
tmp1<=tmp1+1;
endif;
iftmp1<25000then
clk1<='1';
else
clk1<='0';
endif;
endif;
endprocessp1_1;
p1_2:
Process(clk)
Begin
if(clk'eventANDclk='1')then
iftmp2=500000then
tmp2<=0;
else
tmp2<=tmp2+1;
endif;
iftmp2<250000then
clk5<='1';
else
clk5<='0';
endif;
endif;
endprocessp1_2;
p1_3:
Process(clk)
Begin
if(clk'eventANDclk='1')then
iftmp3=50000000then
tmp3<=0;
else
tmp3<=tmp3+1;
endif;
iftmp3<25000000then
clk6<='1';
else
clk6<='0';
endif;
endif;
endprocessp1_3;
p1_4:
Process(clk1)
Begin
if(clk1'eventANDclk1='1')then
iftmp4=31then
tmp4<=0;
else
tmp4<=tmp4+1;
endif;
iftmp4<24then
clk2<='1';
else
clk2<='0';
endif;
iftmp4<8then
clk4<='1';
else
clk4<='0';
endif;
endif;
endprocessp1_4;
p1_5:
Process(clk1)
Begin
if(clk1'eventANDclk1='1')then
iftmp5=15then
tmp5<=0;
else
tmp5<=tmp5+1;
endif;
iftmp5<8then
clk3<='1';
else
clk3<='0';
endif;
endif;
endprocessp1_5;
小结:
上面几个分频进程分出了程序所需的6个频率
p2:
Process(clk5)
Begin
if(clk5'eventANDclk5='0')then
mode_mp2<=mode_mp1;--延迟一个周期赋值
mode_mp1<=mode;
p_mp2<=p_mp1;
p_mp1<=p;
endif;
endProcessp2;
p2_out:
Process(clk1)--这里单独拿出一个进程用于赋值
Begin
if(clk1'eventandclk1='1')then
mode_out<=clk5andmode_mp1and(notmode_mp2);
p_out<=clk5andp_mp1and(notp_mp2);
endif;
endProcessp2_out;
小结:
上述两个进程完成了防抖动功能,和老师给的代码不同的地方在于单独拿出了一个进程用于与操作,原因在于这里的输出值依旧是signal变量,必须在进程内赋值才能在仿真时显示出来。
(否则会被优化掉!
)
第二个问题就是为什么赋值进程敏感量使用了clk1?
其实这里使用什么时钟作为同步时钟并不重要,重要的在于这个时钟周期必须小于clk5周期的一半。
为什么呢?
因为防抖动的输出仅为clk5的高电平,必须要在这一时刻得到同步时钟沿。
那为什么不就用clk5呢?
因为使用clk5则得到的有效电平长度为clk5整个周期,相当于不具备了防止冒险的作用,影响防抖动进程。
p3:
Process(p_out,mode_out,reset)
Begin
if(clk5'eventandclk5='0')then
CasestateIs
whenm0=>if(mode_out='1')thenstate<=m1_p1;
endif;
whenm1_p1=>if(p_out='1')thenstate<=m1_p2;
elsif(mode_out='1')thenstate<=m2;
elsifreset='1'thenstate<=m0;
elsestate<=m1_p1;
endif;
whenm1_p2=>if(p_out='1')thenstate<=m1_p3;
elsif(mode_out='1')thenstate<=m2;
elsifreset='1'thenstate<=m0;
elsestate<=m1_p2;
endif;
whenm1_p3=>if(p_out='1')thenstate<=m1_p4;
elsif(mode_out='1')thenstate<=m2;
elsifreset='1'thenstate<=m0;
elsestate<=m1_p3;
endif;
whenm1_p4=>if(p_out='1')thenstate<=m1_p1;
elsif(mode_out='1')thenstate<=m2;
elsifreset='1'thenstate<=m0;
elsestate<=m1_p4;
endif;
whenm2=>if(mode_out='1')thenstate<=m3_p1;
elsifreset='1'thenstate<=m0;
elsestate<=m2;
endif;
whenm3_p1=>if(p_out='1')thenstate<=m3_p2;
elsif(mode_out='1')thenstate<=m1_p1;
elsifreset='1'thenstate<=m0;
elsestate<=m3_p1;
endif;
whenm3_p2=>if(p_out='1')thenstate<=m3_p3;
elsif(mode_out='1')thenstate<=m1_p1;
elsifreset='1'thenstate<=m0;
elsestate<=m3_p2;
endif;
whenm3_p3=>if(p_out='1')thenstate<=m3_p1;
elsif(mode_out='1')thenstate<=m1_p1;
elsifreset='1'thenstate<=m0;
elsestate<=m3_p3;
endif;
EndCase;
endif;
EndProcessp3;
小结:
这一部分是状态机,值得一提的是状态机的同步时钟。
开始本以为没必要加的,结果不行,原因在于这是一个需要反复判断的进程,没有同步时钟系统是无法执行这些的。
时钟的选择上依旧采用了clk5。
值得一提的是选择时钟沿时一定选择下降沿。
因为在防抖动过程中当clk5高电平结束时则完成了防抖动信号的赋值,然而完成赋值是有一定延时的,这一延时过程中触发状态机,然后开始判断
p4_counter1:
Process(clk1)
Begin
if(clk1'eventandclk1='1')then
if(count=7)thencount<=0;
elsecount<=count+1;
endif;
endif;
endProcessp4_counter1;
p4_counter2:
Process(clk6)
Begin
if(clk6'eventandclk6='1')then
if(count_2=3)thencount_2<=0;
elsecount_2<=count_2+1;
endif;
if(count_3=31)thencount_3<=0;
elsecount_3<=count_3+1;
endif;
endif;
endProcessp4_counter2;
小结:
这里利用时钟计数来完成扫描,滚动等变化
p4:
Process(count,count_2,count_3)
Begin
CasestateIs
whenm0=>row<="11111111";red<="00000000";green<="00000000";num_out<="00000000";
whenm1_p1=>
--if(clk1='1')then
CasecountIs
when0=>row<="11111101";red<="01111110";green<="00000000";
when1=>row<="11111011";red<="00001000";green<="00000000";
when2=>row<="11110111";red<="00001000";green<="00000000";
when3=>row<="11101111";red<="00101110";green<="00000000";
when4=>row<="11011111";red<="00101000";green<="00000000";
when5=>row<="10111111";red<="11111111";green<="00000000";
when6=>row<="01111111";red<="00000000";green<="00000000";
when7=>row<="11111110";red<="00000000";green<="00000000";
whenothers=>row<="11111110";
EndCase;
num_out<="00000110";
--Endif;
whenm1_p2=>
if(clk2='1')then
CasecountIs
when0=>row<="11111101";red<="01111110";green<="00000000";
when1=>row<="11111011";red<="00001000";green<="00000000";
when2=>row<="11110111";red<="00001000";green<="00000000";
when3=>row<="11101111";red<="00101110";green<="00000000";
when4=>row<="11011111";red<="00101000";green<="00000000";
when5=>row<="10111111";red<="11111111";green<="00000000";
when6=>row<="01111111";red<="00000000";green<="00000000";
when7=>row<="11111110";red<="00000000";green<="00000000";
whenothers=>row<="11111110";
EndCase;
elsered<="00000000";
Endif;
num_out<="01011011";
whenm1_p3=>
if(clk3='1')then
CasecountIs
when0=>row<="11111101";red<="01111110";green<="00000000";
when1=>row<="11111011";red<="00001000";green<="00000000";
when2=>row<="11110111";red<="00001000";green<="00000000";
when3=>row<="11101111";red<="00101110";green<="00000000";
when4=>row<="11011111";red<="00101000";green<="00000000";
when5=>row<="10111111";red<="11111111";green<="00000000";
when6=>row<="