基于EP4CE6F17C8的FPGA单数码管秒计数实例
一、电路模块
本例的电路模块与“基于EP4CE6F17C8的FPGA数码管动态显示实例”中的完全一样,此处就不再给出了。
二、实验代码
本例实现1个数码管循环显示字符1~F,显示间隔为1秒,代码使用Verilog编写,采用例化的形式,共有三个文件。
先编写数码管实现显示字形解码的程序,模块名称为seg_decode,文件名称为seg_decode.v,代码如下。
module seg_decode( input[3:0] char_val, //显示的字形,可显示0~F十六个字形,所以需要4位 output reg[7:0] seg7 //字形编码,包含小数点,共8位 ); always@(*) //敏感信号为所有输入量 begin case(char_val) 4'd0:seg7 <= 8'b1100_0000; //字形0的编码 4'd1:seg7 <= 8'b1111_1001; //字形1的编码 4'd2:seg7 <= 8'b1010_0100; //字形2的编码 4'd3:seg7 <= 8'b1011_0000; //字形3的编码 4'd4:seg7 <= 8'b1001_1001; //字形4的编码 4'd5:seg7 <= 8'b1001_0010; //字形5的编码 4'd6:seg7 <= 8'b1000_0010; //字形6的编码 4'd7:seg7 <= 8'b1111_1000; //字形7的编码 4'd8:seg7 <= 8'b1000_0000; //字形8的编码 4'd9:seg7 <= 8'b1001_0000; //字形9的编码 4'ha:seg7 <= 8'b1000_1000; //字形A的编码 4'hb:seg7 <= 8'b1000_0011; //字形B的编码 4'hc:seg7 <= 8'b1100_0110; //字形C的编码 4'hd:seg7 <= 8'b1010_0001; //字形D的编码 4'he:seg7 <= 8'b1000_0110; //字形E的编码 4'hf:seg7 <= 8'b1000_1110; //字形F的编码 default:seg7 <= 7'b111_1111; //默认不显示 endcase end endmodule
接下来编写秒计数程序,模块名称为count_m16,文件名称为count_m16.v,代码如下。
module count_m16( input clk, //板载50HMz系统时钟 input rst, //复位按键 input en, //计数使能位 output reg[3:0]data //计数值,从0~F共16位,所以用4位 ); always@(posedge clk or negedge rst) //敏感信号为时钟上沿或复位下沿 begin if(rst == 0) //低电平复位 data <= 4'd0; //复位时计数值清零 else if(en) //如果计数使能,则执行计数,否则保持上一次的值不变 begin if(data == 4'd15) //如果计数到达F时, data <= 4'd0; //计数值清零 else data <= data + 4'd1; //否则计数值加1 end end endmodule
最后编写数码管显示程序,并设置为顶层模块,模块名称为seg_count,文件名称为seg_count.v,代码如下。
module seg_count( input clk, //板载50HMz系统时钟 input rst, //复位按键 output [7:0] seg, //段码端口 output bit //位选端口 ); assign bit = 1'b0; //点亮数码管 reg [25:0] cnt; //定义26位时钟计数器 reg sec; //定义秒信号 always@(posedge clk or negedge rst) //敏感信号为时钟上沿或复位下沿 begin if(rst == 0) //低电平复位时秒计数清零 begin sec <= 1'b0; cnt <= 26'd0; end else if(cnt == 26'd49_999_999) //时钟计数器到达1秒时 begin cnt <= 26'd0; //时钟计数器清零 sec <= 1'b1; //产生秒信号 end else begin sec <= 1'b0; //否则秒信号清零 cnt <= cnt + 26'd1; //时钟计数器加1,即来一次时钟脉冲加一次 end end wire [3:0] count_data; //定义显示的值 //下面例化1个秒计数单元 count_m16 U0(.clk(clk), .rst(rst), .en(sec), .data(count_data)); //下面例化1个字形解码单元 seg_decode S0(.char_val(count_data), .seg7(seg)); endmodule
三、代码说明
1、count_m16模块主要负责十六进制计数,在系统时钟的同步下,来一个en脉冲计一次数,计满后回零。计数的快慢取决于en端口输入的脉冲周期,产生的计数值则通过data端口向外输出。
2、seg_decode模块主要负责计数值到字形码的解码(转换),通过char_val端口向内输入计数值,转换成字形编码后通过seg7端口向外输出。
3、seg_count模块为顶层模块,负责产生秒时钟信号(即en脉冲),通过例化十六进制计数单元,把秒信号en传入,之后获得按秒变化的计数值count_data。同时通过例化数码管字形解码单元,把计数值count_data传入,之后获得字形编码seg7。最终获取的字形编码通过顶层的seg端口连接到数码管段码引脚,最低位数码管的位选引脚直接连接到低电平点亮。
4、元件例化是EDA设计中一种常用的手段,有点类似C语言中的函数调用,但它例化出来的是逻辑元器件。通过例化不仅能产生出实体元件,还能通过端口名关联的方式与外部进行连接,从而实现信号的输入和输出。
四、实验步骤
FPGA开发的详细步骤请参见“基于EP4CE6F17C8的FPGA开发流程(以半加器为例)”一文,本例只对不同之处进行说明。
本例工程放在D:\EDA_FPGA\Exam_4文件夹下,工程名称为Exam_4。有三个模块文件,一个名称为seg_count.v,设置为顶层实体,另外两个名称分别为seg_decode.v和count_m16.v,用于提供例化。其余步骤与“基于EP4CE6F17C8的FPGA开发流程”中的一样。
接下来看管脚约束,本例中只用到了最低位的1个数码管,一共有9个引脚,再加上时钟晶振和复位按钮,一共11个。具体的端口分配如下图所示。
对于未用到的引脚设置为三态输入方式,多用用途引脚全部做为普通I/O端口,电压设置为3.3-V LVTTL(与”基于EP4CE6F17C8的FPGA开发流程“中的一样)。需要注意,程序中的每个端口都必须为其分配管脚,如果系统中存在未分配的I/O,软件可能会进行随机分配,这将造成不可预料的后果,存在烧坏FPGA芯片的风险。
接下来对工程进行编译,编译完成后,可查看一下逻辑器件的消耗情况,如下图所示。
另外,还可以点击菜单Tools->Netlist Viewers->RTL Viewer,查看一下生成的RTL电路图,如下图所示。
最后进行程序下载,并查看结果。下面是数码管动态显示十六进制秒计数图片的其中几张。
…………
当按下复位键后,数码管熄灭,如下图所示。