学习verilog有很长时间了,一直都没有写写什么,其实这算是我第一次以博客的形式来做笔记(其实我做笔记从来都不看http://www/uc/myshow/blog/misc/gif/E___6717EN00SIGG.gif),现在来写这个博客的主要目的在于:之前由于种种原因自己记得笔记都浪费了(主要LZ的字写的自己都不忍心看)。学习verilog很长时间,但一直做的是仿真,学的有点枯燥和效率低下。两个月前终于忍不住了花了469大洋买了altera的FPGA开发板,开始了我的verilog语言的实践之路,但前一段时间也做了些小实验之类的,但感觉做完就忘了,所以决定用博客来记录下。
前面废话有点多,下面正式开始,之前做的一些无非是可编程电路的经典实验——跑马灯。就一个循环移位器加个分频。这里也就不再单独写篇文章来记录了。再接下来两个实验是比较大一点的实验——UART和PCM30,这是实验课的内容。这里不多说,有时间在贴上来。这里主要讲这一次做的实验——数码管。
数码管一个二极管型器件,算是可编程器件的经典实验之一。这里用到的数码管共阳型是8位7段数码管(就是数字型的加个点),电路图如图所示:
http://s13/bmiddle/4d6f0979nc569d9f7d39c&690
(http://www/uc/myshow/blog/misc/gif/E___6717EN00SIGG.gif请原谅我可耻的从买的FPGA板的电路图中截图。)数码管算是一种高速器件,不太好控制,尤其是动态显示时,快一点就直接看不见,慢一点就闪的很,所以要设置好列切换时间,这里据说是1ms时看的效果最好(这个不要问我怎么来的,我只记得从某个资料上看来的),这里主要分成了四个小模块(部分):分频时钟(1ms)、列循环移位(就是8'b1111_1110无限循环左移,用拼接运算符{BIT[6:0],BIT[7]})、段数据输入、段数据编码(就是将数字转换成数码管显示的段码8位)。这里详细见代码吧。代码参考过别人的,请见谅。
这个是TOP文件,就是两个模块的连接:这个很简单
module top(clk,rst_n,col,seg);
input clk;
input rst_n;
output[7:0] col;
output[7:0] seg;
wire[39:0] dis_data;
data_dsp
dsp_1(
.clk
(clk
),
.rst_n
(rst_n ),
.dis_data(dis_data)
);
dled_seg
s1 (
.clk
(clk
),
.rst_n
(rst_n ),
.dis_data(dis_data),
.col
(col
),
.seg
(seg
)
);
endmodule
接下来是data_dsp,这个是要显示数据转换成显示模块支持的数据总线格式40bit总线,共8位数据,每位由1bit
DP(就是那个点)和4bit
数字组成(0-F),这里这个程序没有输入,自己通过分频来模拟时钟的:所以有个时钟发生器(分频计数)。代码如下:
module data_dsp(
clk,
rst_n,
dis_data
);
input clk;
input rst_n;
output [39:0] dis_data;
wire [39:0] dis_data;
reg[5:0] hour,min,sec;
reg[25:0] sec_cnt;
reg [5:0] bit_1,bit_2,bit_3,bit_4,bit_5,bit_6;
parameter one_sec=26'h2faf080; //1 second
always @ (posedge clk or negedge rst_n)
if(!rst_n)
sec_cnt
<= 26'h0;
else if(sec_cnt == one_sec)
sec_cnt
<= 26'h0;
else
sec_cnt
<= sec_cnt + 1'b1;
always @ (posedge clk or negedge rst_n)
if(!rst_n)
begin
hour <=6'd23;
min
<=6'd58;
sec
<=6'd52;
end
else
if(hour ==
6'd24)
hour <= 6'd0;
else if(min
== 6'd60)
begin
hour <= hour + 1'b1;
min <= 6'd0;
end
else if(sec
== 6'd60)
begin
min <= min+1'b1;
sec <=
6'd0;
end
else
if(sec_cnt == one_sec)
sec <= sec + 1'b1;
always @ (posedge clk or negedge rst_n)
if(!rst_n)
begin
bit_1<=4'b0;
bit_2<=4'b0;
bit_3<=4'b0;
bit_4<=4'b0;
bit_5<=4'b0;
bit_6<=4'b0;
end
else
begin
bit_1<=sec %4'd10;
bit_2<=sec /4'd10;
bit_3<=min %4'd10;
bit_4<=min /4'd10;
bit_5<=hour %4'd10;
bit_6<=hour
/4'd10;
end
assign
dis_data={1'b0,bit_6[3:0],1'b0,bit_5[3:0],1'b0,1'b1,1'b1,1'b1,1'b1,1'b0,bit_4[3:0],1'b0,bit_3[3:0],1'b0,1'b1,1'b1,1'b1,1'b1,1'b0,bit_2[3:0],1'b0,bit_1[3:0]};
endmodule
这里要注意的是这里用到的除和取余是quartus自动套用自带的编译程序调用的,可能别的芯片和编译环境要自己定义。
接下来是主角显示模块:col是数码管的列选。seg为段选,这里用的是实现电路来实现的可能仿真时会出现输出信号要比应该到的时钟要晚几个周期,但相对于1ms的计数来说可以忽略。这里要注意复位信号,之前我用的复位信号是同步复位,结果下载到电路板上,显示8个F就是默认的那个数值也就是说那个case就没有起作用,直接default了,在敏感括号中加入了rst后就解决了(就是在always@(posedge
clk)的括号中加入了negedge
rst_n),但现在这个rst_n信号还有点问题(同步和异步混用)但对这个实验来说没有影响忽略。http://www/uc/myshow/blog/misc/gif/E___7399ZH00SIGG.gif可以用nlint软件看具体警告,现在不管等遇到问题正在想办法解决。
module dled_seg(
clk
,
rst_n ,
dis_data,
col
,
seg
);
input
clk;
//50M时钟输入
input
rst_n;
//复位信号,低电平有效
input [39:0] dis_data;
//显示内容,5位一个数据,共8个数据
output [7:0]
col;
//位选
output [7:0]
seg;
//段选
reg [7:0]
col_r;
//输出寄存器
reg [7:0]
seg_r;
//段码输出寄存器
reg [3:0]
tmp_data;
//十进制转段码寄存器
reg[39:0]
dis_data_r;
//显示内容寄存器
reg[15:0]
time_cnt;
//计数器
wire [7:0] col;
assign col = col_r;
wire [7:0] seg = seg_r;
parameter scan_time = 16'hc350;//1ms
always @(posedge clk or negedge
rst_n)
//将显示内容放入显示寄存器
if(!rst_n)
dis_data_r
<=
40'b111111111111111111111111111111111111111;
else
dis_data_r
<= dis_data;
always @(posedge clk or negedge
rst_n)
//1ms计数器
if(!rst_n)
time_cnt
<=16'h0;
else if(time_cnt == scan_time)
time_cnt
<= 16'h0;
else
time_cnt
<= time_cnt + 1'b1;
always @ (posedge clk or negedge
rst_n)
//列选循环移位寄存器
if(!rst_n)
col_r
<= 8'b1111_1110;
else if(time_cnt == scan_time)
col_r
<=
{col[6:0],col[7]}; //左移
else
col_r
<=
col;
//保持不变
always @(posedge clk or negedge rst_n)
begin
case(col)
8'b1111_1110: tmp_data <= dis_data_r[3:0];
8'b1111_1101: tmp_data <= dis_data_r[8:5];
8'b1111_1011: tmp_data <= dis_data_r[13:10];
8'b1111_0111: tmp_data <= dis_data_r[18:15];
8'b1110_1111: tmp_data <= dis_data_r[23:20];
8'b1101_1111: tmp_data <= dis_data_r[28:25];
8'b1011_1111: tmp_data <= dis_data_r[33:30];
8'b0111_1111: tmp_data <= dis_data_r[38:35];
default: tmp_data <= 4'hf ;
endcase
end
always @(posedge clk or negedge rst_n)
begin
case(col)
8'b1111_1110: seg_r[7] <= ~dis_data_r[4];
8'b1111_1101: seg_r[7] <= ~dis_data_r[9];
8'b1111_1011: seg_r[7] <= ~dis_data_r[14];
8'b1111_0111: seg_r[7] <= ~dis_data_r[19];
8'b1110_1111: seg_r[7] <= ~dis_data_r[24];
8'b1101_1111: seg_r[7] <= ~dis_data_r[29];
8'b1011_1111: seg_r[7] <= ~dis_data_r[34];
8'b0111_1111: seg_r[7] <= ~dis_data_r[39];
default:
seg_r[7] <= 1'b1 ;
endcase
end
always @(posedge clk or negedge rst_n)
begin
case(tmp_data)
4'h0: seg_r[6:0] <= 7'b100_0000;
4'h1: seg_r[6:0] <= 7'b111_1001;
4'h2: seg_r[6:0] <= 7'b010_0100;
4'h3: seg_r[6:0] <= 7'b011_0000;
4'h4: seg_r[6:0] <= 7'b001_1001;
4'h5: seg_r[6:0] <= 7'b001_0010;
4'h6: seg_r[6:0] <= 7'b000_0010;
4'h7: seg_r[6:0] <= 7'b111_1000;
4'h8: seg_r[6:0] <= 7'b000_0000;
4'h9: seg_r[6:0] <= 7'b001_0000;
4'ha: seg_r[6:0] <= 7'b000_1000;
4'hb: seg_r[6:0] <= 7'b000_0011;
4'hc: seg_r[6:0] <= 7'b100_0110;
4'hd: seg_r[6:0] <= 7'b010_0001;
4'he: seg_r[6:0] <= 7'b000_0110;
4'hf: seg_r[6:0] <=
7'b011_1111;
//这里不是F的段码,被我改成"-",注意
default: seg_r[6:0] <= 7'b111_1111;
endcase
end
endmodule
到这里这个实验基本就做完了,但这个实验是个基本实验,还有后续实验,接下来我准备做的实验是PCF8653
时钟芯片,用数码管显示,这个实验的内容会用上,再写,估计还得等几天,毕竟IIC不是那么容易搞定的,敬请期待
加载中,请稍候......