赞
踩
枯藤老树昏鸦,小桥流水人家。 — — — —《天净沙.秋思》马致远
大四毕业后白嫖了电子创新实验室的一块FPGA,这块板子适合做数电实验,为了物尽其用,趁这个暑假搭配特权同学做的《深入浅出玩转FPGA》视频学习入门一下,用它整点活。
让板载的8颗LED灯每隔1秒依次点亮,全亮后又每隔1秒依次熄灭,循环。
RTL代码编写平台:Vivado 2019.1
FPGA开发板:Xilinx BASYS3
芯片型号:Artix家族 xc7a35tcpg236-1
如下图所示,我的构想由计时模块和类38线译码器模块组合而成,时钟和复位信号输入,8颗LED灯作为输出,计时模块处理LED灯亮灭的时序逻辑,产生1Hz方波进行计数,由于计数值为0~7共8位,所以联想到38译码器,这样可以通过3位二进制数作为地址输出给后级模块去控制8颗LED灯(模拟成8位二进制数),LED状态的刷新受到计时模块时钟的控制。
查看BASYS3开发板的原理图可知,系统时钟由DSC1033CC1-100.0000T晶振产生,为100Mhz,如下图所示。
为了达到“每隔1秒亮一次”,需要利用系统时钟产生1Hz 的方波(周期为1s),通过对系统时钟上升沿的计数可以实现,由于(100MHz/100 000 000=1Hz),所以当从0计到99 999 999时就得到1Hz的频率,并且在一周期内对半周期前后的电平翻转就可以得到1Hz、占空比50%的延时时钟。
在产生了延时时钟后,需要再对这个时钟的下降沿进行计数,如下图所示,从0计数到7,每一个计数值对应了一个3位的地址,依次对应着8颗LED灯的一种亮灭状态;计数过了7后清零,并且翻转片选信号,片选信号为0时运行的是“8颗LED灯每隔1秒依次点亮”的过程,为1时运行的是“每隔1秒依次熄灭”的过程。
模块的真值表如下图所示,地址和片选共同作用使8位LED值输出对应的电平信号,高电平点亮LED,低电平熄灭LED。
- module leds(
- input clk, //100Mhz系统时钟
- input rst, //复位信号,高电平有效
- output reg [2:0] ctrl_data, //控制数据,输出到后级译码器中
- output led_clk, //1Hz时钟
- output led_cs //片选信号,输出到后级译码器用于选择状态
- );
- reg[27:0] cnt; //计数值,用于产生1Hz信号
- parameter cnt_max = 28'd99_999_999; //计数最大值
- parameter cnt_half = 28'd49_999_999; //计数中值
- reg led_clk_r; //1Hz方波时钟
- reg led_cs_r; //片选信号
-
- /****产生1Hz的方波****/
- always @ (posedge clk or posedge rst)
- begin
- if(rst) cnt <= 28'd0;
- else if(cnt < cnt_max) cnt <= cnt+1'b1;
- else cnt <= 28'd0;
- end
-
- always @ (posedge clk or posedge rst)
- begin
- if(rst) led_clk_r <= 1'b0;
- else if(cnt < cnt_half) led_clk_r <= 1'b0;
- else led_clk_r <= 1'b1;
- end
-
- assign led_clk = led_clk_r;
-
- /****在1Hz方波时钟信号下计数****/
- always @ (negedge led_clk or posedge rst)
- begin
- if(rst)
- begin
- ctrl_data <= 3'd0;
- led_cs_r <= 1'b0;
- end
- else if(ctrl_data < 3'd7) ctrl_data <= ctrl_data+1'b1;
- else
- begin
- ctrl_data <= 3'd0;
- led_cs_r <= ~led_cs_r;
- end
- end
-
- assign led_cs = led_cs_r;
-
- endmodule
- module code(
- input clk, //100Mhz系统时钟
- input rst, //复位信号,高电平有效
- output reg [7:0] leds_out //输出8个LED信号
- );
-
- wire [2:0] ctrl; //前级输入的控制地址
- wire cs; //前级输入的片选信号
-
- /****leds模块例化语句****/
- leds myleds(
- .clk(clk),
- .rst(rst),
- .ctrl_data(ctrl),
- .led_cs(cs)
- );
-
- /****类3-8线译码器的设计****/
- always @ (ctrl or cs or rst)
- begin
- if(rst) leds_out = 8'b0000_0000;
- else
- begin
- if(!cs)
- begin
- case(ctrl)
- 3'b000: leds_out = 8'b0000_0001;
- 3'b001: leds_out = 8'b0000_0011;
- 3'b010: leds_out = 8'b0000_0111;
- 3'b011: leds_out = 8'b0000_1111;
- 3'b100: leds_out = 8'b0001_1111;
- 3'b101: leds_out = 8'b0011_1111;
- 3'b110: leds_out = 8'b0111_1111;
- 3'b111: leds_out = 8'b1111_1111;
- endcase
- end
- else
- begin
- case(ctrl)
- 3'b000: leds_out = 8'b1111_1110;
- 3'b001: leds_out = 8'b1111_1100;
- 3'b010: leds_out = 8'b1111_1000;
- 3'b011: leds_out = 8'b1111_0000;
- 3'b100: leds_out = 8'b1110_0000;
- 3'b101: leds_out = 8'b1100_0000;
- 3'b110: leds_out = 8'b1000_0000;
- 3'b111: leds_out = 8'b0000_0000;
- endcase
- end
- end
-
- end
-
- endmodule
注意,板子上的按钮为摁下接到高电平,松开接到低电平,原理图如下所示,这里我们用到的是5个中的1个,在后续小节会讲到如何分配管脚,所以在设计仿真测试程序时要让rst信号由低电平跳上高电平然后过一点点时间再拉低。
- module leds_sim;
- reg clk;
- reg rst;
- wire [2:0] ctrl_data;
- wire led_clk;
- wire led_cs;
-
- leds text_leds(
- .clk(clk),
- .rst(rst),
- .ctrl_data(ctrl_data),
- .led_clk(led_clk),
- .led_cs(led_cs)
- );
-
- initial begin
- clk = 0;
- rst = 0;
- #1000 rst = 1;
- #1000 rst = 0;
- end
-
- always #5 clk = ~clk; //输出100Mhz方波信号,每隔5ns翻转一次电平
-
- endmodule
- module cod_sim;
- reg clk;
- reg rst;
- wire [7:0] leds_out;
-
- code text_code(
- .clk(clk),
- .rst(rst),
- .leds_out(leds_out)
- );
-
- initial begin
- clk = 0;
- rst = 0;
- #1000 rst = 1;
- #1000 rst = 0;
- end
-
- always #5 clk = ~clk;
- endmodule
下图为leds计时模块的行为仿真结果。
下图为leds&code总模块的仿真结果。
下图为综合后总模块的RTL原理图。
由下图所示,100Mhz系统时钟输入到了W5引脚中,所以我们给clk引脚配置为W5。
rst复位引脚我们配置为T17,原理图如下图所示。
8颗LED灯管脚号如下图所示,从右往左位数依次升高。
总的管脚配置文件如下所示。
- set_property PACKAGE_PIN T17 [get_ports rst]
- set_property PACKAGE_PIN U16 [get_ports {leds_out[0]}]
- set_property PACKAGE_PIN E19 [get_ports {leds_out[1]}]
- set_property PACKAGE_PIN U19 [get_ports {leds_out[2]}]
- set_property PACKAGE_PIN V19 [get_ports {leds_out[3]}]
- set_property PACKAGE_PIN W18 [get_ports {leds_out[4]}]
- set_property PACKAGE_PIN U15 [get_ports {leds_out[5]}]
- set_property PACKAGE_PIN U14 [get_ports {leds_out[6]}]
- set_property PACKAGE_PIN V14 [get_ports {leds_out[7]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {leds_out[7]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {leds_out[6]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {leds_out[5]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {leds_out[4]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {leds_out[3]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {leds_out[2]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {leds_out[1]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {leds_out[0]}]
- set_property IOSTANDARD LVCMOS33 [get_ports rst]
-
- set_property PACKAGE_PIN W5 [get_ports clk]
- set_property IOSTANDARD LVCMOS33 [get_ports clk]
仿真、综合、布局布线、分配管脚后,可以生成bit文件,通过USB线给开发板上电,vivado连上器件xc7a35tcpg236-1后将bit文件烧录至开发板。
实验运行视频如下所示。
QQ视频20230709170724
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。