哈工程FPGA实验报告1616点阵.docx
《哈工程FPGA实验报告1616点阵.docx》由会员分享,可在线阅读,更多相关《哈工程FPGA实验报告1616点阵.docx(20页珍藏版)》请在冰豆网上搜索。
哈工程FPGA实验报告1616点阵
FPGA实验报告
姓名:
学号:
指导教师:
2013.6.10
实验六点阵扫描显示实验
一、实验原理
根据硬件电路可知,点阵的控制端口由行端口和列端口组成,分别为16个,相当于256个LED灯。
要是某一个灯亮,只需使对应位置上一端为高,一端为低。
该16*16的点阵的列端口由一个74154四位译码器控制。
由其资料可知,要使74154正常工作,其12脚、18脚与19脚必须接低。
20、21、22、23脚是数据输入端。
24脚为VCC,其他为数据输出端口。
16×16扫描LED点阵的工作原理同8位扫描数码管类似。
它有16个共阴极输出端口,每个共阴极对应有16个LED显示灯,所以其扫描译码地址需4位信号线(SEL0-SEL3),其汉字扫描码由16位段地址(0-15)输入。
通过时钟的每列扫描显示完整汉字。
点阵LED一般采用扫描式显示,实际运用分为三种方式:
(1)点扫描
(2)行扫描
(3)列扫描
若使用第一种方式,其扫描频率必须大于16×64=1024Hz,周期小于1ms即可。
若使用第二和第三种方式,则频率必须大于16×8=128Hz,周期小于7.8ms即可符合视觉暂留要求。
此外一次驱动一列或一行(8颗LED)时需外加驱动电路提高电流,否则LED亮度会不足。
汉字显示使用的是16×16的点阵,FPGA实验箱上有其接口电路,列选信号为SEL0,SEL1,SEL2,SEL3,经4线16线译码器输出16列,从左起为第一列,列选信号是由一个4位向量SEL[3..0]控制;行选信号为H0~H15,是由16个行信号组成的,每一行由一个单独的位来控制,高电平有效。
例如“0000”表示第0列,“0000000000000001”表示第一行的点亮。
由于列是由一个向量决定,而每一时刻的值只能有一个固定的值,因而只能使某一列的若干个点亮,因此就决定了只能用逐列扫描的方法。
例如要使第一列的2,4,6,8,行亮,则列为“0001”、行为“0000000010101010”就可以实现了
用动态分时扫描技术使LED点阵模块显示图像,需要进行两步工作。
第一步是获得数据并保存,即在存贮器中建立汉字数据库。
第二步是在扫描模块的控制下,配合行扫描的次序正确地输出这些数据。
获得图像数据的步骤是,先将要显示的每一幅图像画在一个如图3.3所示的被分成16×16共256个小方格的矩形框中,再在有笔划下落处的小方格里填上“1”,无笔划处填上“0”,这样就形成了与这个汉字所对应的二进制数据在该矩形框上的分布,再将此分布关系以32×16的数据结构组成64个字节的数据,并保存在只读存贮器ROM中。
以这种方式将若干个汉字的数据贮存在存贮器内,就完成了图像数据库的建立工作。
二、实验框图及原理图
实验原理图
实验框图
三、实验程序详解
1)行扫描
使行扫描端口依次拉低,同时较高频率进行列扫秒
moduleD_Z(clk,lie,hang,sel0,sel1,sel2);
inputclk;//时钟输入
output[4:
0]lie;//点阵引脚
output[15:
0]hang;
outputsel0;//扩张功能选择引脚
outputsel1;
outputsel2;
regclk_s;
regclk_ms;
reg[4:
0]lie;
reg[15:
0]hang;
reg[3:
0]XS_data;//显示状态
reg[3:
0]PY_data;//平移状态
reg[31:
0]XS_DIV;//显示计数
reg[31:
0]PY_DIV;//平移计数
wiresel0;
wiresel1;
wiresel2;
parameterCLK_XS='D25_000;
parameterCLK_PY='D1_000;
assignsel0=1'b0;//扩展功能选择点阵
assignsel1=1'b1;
assignsel2=1'b0;
always@(posedgeclk)//时钟分频产生毫秒时钟,用于点阵动态显示
begin
if(XS_DIVXS_DIV<=XS_DIV+1'b1;
else
begin
XS_DIV<=0;
clk_ms<=~clk_ms;
end
end
always@(posedgeclk_ms)//毫秒时钟计数产生秒级时钟,用于平移
begin
if(PY_DIV<=CLK_PY)
PY_DIV<=PY_DIV+1;
else
begin
PY_DIV<=0;
clk_s<=~clk_s;
end
end
always@(posedgeclk_ms)//显示一行点阵
begin
case(XS_data)
0:
begin
lie=5'b00000;
XS_data=1;
end
1:
begin
lie=5'b00001;
XS_data=2;
end
2:
begin
lie=5'b00010;
XS_data=3;
end
3:
begin
lie=5'b00011;
XS_data=4;
end
4:
begin
lie=5'b00100;
XS_data=5;
end
5:
begin
lie=5'b00101;
XS_data=6;
end
6:
begin
lie=5'b00110;
XS_data=7;
end
7:
begin
lie=5'b00111;
XS_data=8;
end
8:
begin
lie=5'b01000;
XS_data=9;
end
9:
begin
lie=5'b01001;
XS_data=10;
end
10:
begin
lie=5'b01010;
XS_data=11;
end
11:
begin
lie=5'b01011;
XS_data=12;
end
12:
begin
lie=5'b01100;
XS_data=13;
end
13:
begin
lie=5'b01101;
XS_data=14;
end
14:
begin
lie=5'b01110;
XS_data=15;
end
15:
begin
lie=5'b01111;
XS_data=0;
end
default
begin
lie=5'b01111;
XS_data=0;
end
endcase
end
always@(posedgeclk_s)//行平移,间隔1秒显示每一行
begin
case(PY_data)
0:
begin
hang=16'h0001;
PY_data=1;
end
1:
begin
hang=16'h0002;
PY_data=2;
end
2:
begin
hang=16'h0004;
PY_data=3;
end
3:
begin
hang=16'h0008;
PY_data=4;
end
4:
begin
hang=16'h0010;
PY_data=5;
end
5:
begin
hang=16'h0020;
PY_data=6;
end
6:
begin
hang=16'h0040;
PY_data=7;
end
7:
begin
hang=16'h0080;
PY_data=8;
end
8:
begin
hang=16'h0100;
PY_data=9;
end
9:
begin
hang=16'h0200;
PY_data=10;
end
10:
begin
hang=16'h0400;
PY_data=11;
end
11:
begin
hang=16'h0800;
PY_data=12;
end
12:
begin
hang=16'h1000;
PY_data=13;
end
13:
begin
hang=16'h2000;
PY_data=14;
end
14:
begin
hang=16'h4000;
PY_data=15;
end
15:
begin
hang=16'h8000;
PY_data=0;
end
default
begin
hang=16'h0000;
PY_data=0;
end
endcase
end
endmodule
2)列扫描
将行端口全部拉高,同时给译码器的数据输入端口加一,每次使对应显示的行端口保持低电平,与行扫描必须同时进行扫描的字符才能显示正确。
moduleD_Z(clk,lie,hang,sel0,sel1,sel2);
inputclk;//时钟输入
output[4:
0]lie;//点阵引脚
output[15:
0]hang;
outputsel0;//扩张功能选择引脚
outputsel1;
outputsel2;
regclk_s;
reg[4:
0]lie;
reg[3:
0]XS_data;//显示状态
reg[31:
0]XS_DIV;//显示计数
wire[15:
0]hang;
wiresel0;
wiresel1;
wiresel2;
parameterCLK_XS='D25_000_000;
assignsel0=1'b0;//扩展功能选择点阵
assignsel1=1'b1;
assignsel2=1'b0;
assignhang=16'b1111_1111_1111_1111;
always@(posedgeclk)//时钟分频,产生秒级时钟,用于平移
begin
if(XS_DIVXS_DIV<=XS_DIV+1'b1;
else
begin
XS_DIV<=0;
clk_s<=~clk_s;
end
end
always@(posedgeclk_s)//间隔一秒平移显示每列
begin
case(XS_data)
0:
begin
lie=5'b00000;
XS_data=1;
end
1:
begin
lie=5'b00001;
XS_data=2;
end
2:
begin
lie=5'b00010;
XS_data=3;
end
3:
begin
lie=5'b00011;
XS_data=4;
end
4:
begin
lie=5'b00100;
XS_data=5;
end
5:
begin
lie=5'b00101;
XS_data=6;
end
6:
begin
lie=5'b00110;
XS_data=7;
end
7:
begin
lie=5'b00111;
XS_data=8;
end
8:
begin
lie=5'b01000;
XS_data=9;
end
9:
begin
lie=5'b01001;
XS_data=10;
end
10:
begin
lie=5'b01010;
XS_data=11;
end
11:
begin
lie=5'b01011;
XS_data=12;
end
12:
begin
lie=5'b01100;
XS_data=13;
end
13:
begin
lie=5'b01101;
XS_data=14;
end
14:
begin
lie=5'b01110;
XS_data=15;
end
15:
begin
lie=5'b01111;
XS_data=0;
end
default
begin
lie=5'b01111;
XS_data=0;
end
endcase
end
endmodule
3)显示姓名
moduledainzhen(clk,en,column,line,sel0,sel1,sel2,stop);
inputclk;
inputstop;
output[3:
0]column;
output[15:
0]line;
reg[3:
0]column;
reg[15:
0]line;
reg[7:
0]i,j,k;//定义计算用于显示具体一个点的参数
outputen;
outputsel0;
outputsel1;
outputsel2;
assignen=0;
assignsel0=0;
assignsel1=1;
assignsel2=0;
reg[16:
0]counter;
regclk_1;
always@(posedgeclk)
begin
if(counter[15])
begin
clk_1<=~clk_1;
counter<=0;
end
else
counter<=counter+1'b1;
end
//--------------------------
taskdisplay;//定义任务display
reg[3:
0]column_out;//行显示中间变量,可以省略
reg[15:
0]line_out;//列显示中间变量,可以省略
begin
case(i)//每一个i值进来以后,判断需要显示一个点的列坐标?
0:
column_out=4'h0;//
1:
column_out=4'h1;//
2:
column_out=4'h2;//
3:
column_out=4'h3;//
4:
column_out=4'h4;//
5:
column_out=4'h5;//
6:
column_out=4'h6;//
7:
column_out=4'h7;//
8:
column_out=4'h8;//
9:
column_out=4'h9;//
10:
column_out=4'ha;//
11:
column_out=4'hb;//
12:
column_out=4'hc;//
13:
column_out=4'hd;//
14:
column_out=4'he;//
15:
column_out=4'Hf;//
endcase
k=(i+j)%48;//k用取余计算来确定,使字符能够向左移动,每次移动一步
case(k)//每一个k值进来以后,判断需要显示一行上面的
//坐标上的数据
0:
line_out=16'h2004;//刘
1:
line_out=16'h1804;//
2:
line_out=16'h0924;//
3:
line_out=16'hFFA4;//
4:
line_out=16'h0224;//
5:
line_out=16'h4224;//
6:
line_out=16'h2224;//
7:
line_out=16'h1424;//
8:
line_out=16'h1424;//
9:
line_out=16'h0824;//
10:
line_out=16'h0824;//
11:
line_out=16'h1424;//
12:
line_out=16'h2204;//
13:
line_out=16'h4304;//
14:
line_out=16'h8114;//
15:
line_out=16'h0008;//
16:
line_out=16'h1004;//妍
17:
line_out=16'h17FE;//
18:
line_out=16'h1088;//
19:
line_out=16'h1088;//
20:
line_out=16'hFC88;//
21:
line_out=16'h2488;//
22:
line_out=16'h2488;//
23:
line_out=16'h27FE;//
24:
line_out=16'h2488;//
25:
line_out=16'h4488;//
26:
line_out=16'h2888;//
27:
line_out=16'h1088;//
28:
line_out=16'h2888;//
29:
line_out=16'h4488;//
30:
line_out=16'h8108;//
31:
line_out=16'h0288;//
32:
line_out=16'h0008;//玉
33:
line_out=16'h7FFC;//
34:
line_out=16'h0100;//
35:
line_out=16'h0100;//
36:
line_out=16'h0100;//
37:
line_out=16'h0100;//
38:
line_out=16'h0110;//
39:
line_out=16'h3FF8;//
40:
line_out=16'h0100;//
41:
line_out=16'h0100;//
42:
line_out=16'h0140;//
43:
line_out=16'h0130;//
44:
line_out=16'h0110;//
45:
line_out=16'h0104;//
46:
line_out=16'hFFFE;//
47:
line_out=16'h0000;//
endcase
column=column_out;//结束判断后给行输出赋值
line=line_out;//结束判断后给列输出赋值
end
endtask
//--------------------------
always@(posedgeclk_1)
begin
i=i+1;//每个clk信号来了以后自加1
if(i==16)
begin
i=0;//八行都显示完毕后归零
if(stop)
begin
j=j+1;
end
//同时纵向所有数据向左移动一位
end//
if(j==48)j=0;//都完成移动后计数器j归零
display;//调用显示任务,clk连续不断,保持视觉暂留,形成滚动//的S字样
end
endmodule
四、管脚分配
五、实验现象
1.行扫描
点阵上的点逐行依次点亮
2.列扫描
点阵上的点逐列依次点亮
3.姓名显示
点阵上滚动显示姓名“刘妍玉”
六、实验总结
实验结果显示字符可以正常显示并在点阵上滚动,通过将扫描的行与列的管脚分别反向分配可以实现字符的镜像显示
通过实验了解了点阵显示的基本原理,通过扫描的方式将存在存储器中的数据显示出来。
实验中发现,行扫描和列扫描需要同时分别进行,即要想使得某一位置亮起,就要保持列端口一直为高,行端口依次拉低即可,但是在实验过程中由于对语言的不熟悉,在实验开始阶段遇到了很大的困难,自己写程序很吃力,分频阶段之后行扫描与列扫描的并行处理,在老师同学帮助和查找网上资料,基本熟悉了大致的程序过程,但对于自己把握大段的程序仍然有困难,需要进一步的学习。