【FPGA学习】- FPGA实验篇(LED灯)
LED灯闪烁实验
控制板上LED灯每秒反转一次,按照亮,灭,亮,灭的顺序不停的往复循环。
设计代码:
module LED(clk, rst_n, led); input clk, rst_n; //系统时钟50MHZ,复位信号低位有效 output reg [3:0]led; reg [31:0]time_count; //计数,用来确定什么时间到达一秒 always @(posedge clk, negedge rst_n) begin if(!rst_n) begin led <= 4'b0000; time_count <= 32'd0; end else if(time_count >= 32'd49_999_999) begin //系统时钟经过49999999-0+1个时钟周期,表明已经一秒,翻转 led <= ~led; time_count <= 32'd0; end else time_count = time_count + 1; end //ila ila_inst ( // .clk(clk), // input wire clk // .probe0(led), // input wire [3:0] probe0 // .probe1(time_count) // input wire [31:0] probe1 //); endmodule
添加约束。包括引脚约束和时钟约束。
set_property PACKAGE_PIN J16 [get_ports {led[3]}] set_property PACKAGE_PIN K16 [get_ports {led[2]}] set_property PACKAGE_PIN M15 [get_ports {led[1]}] set_property PACKAGE_PIN M14 [get_ports {led[0]}] set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}] set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}] set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}] set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}] set_property PACKAGE_PIN U18 [get_ports clk] set_property PACKAGE_PIN N15 [get_ports rst_n] set_property IOSTANDARD LVCMOS33 [get_ports rst_n] set_property IOSTANDARD LVCMOS33 [get_ports clk] create_clock -period 20.000 -name clk -waveform {0.000 10.000} [get_ports clk]
实验结果。我们可以发现板子上的LED灯按照每秒亮灭的顺序依次转换。使用在线逻辑分析仪(ila),在生成的波形图上可以发现,当timecount达到最大-02faf07f(十进制:49999999)时,led从f变为0,发生翻转,依次往复,完成LED灯闪烁的功能。
按键控制LED闪烁实验
使用板上的两个按键来控制板上的两个LED的闪烁方式。没有按键按下时,两个LED保持常亮;如果按键0按下,则两个LED交替闪烁;如果按键1按下,则两个LED同时闪烁。
设计代码:
module LED(clk, rst_n, key, led); input clk; //系统时钟50MHZ input rst_n; //复位信号,低位有效 input [1:0] key; output reg [1:0] led; reg [24:0] cnt; reg led_ctrl; always @(posedge clk, negedge rst_n) begin if(!rst_n) cnt <= 25'd0; else if (cnt <= 25'd2500_0000) //计数0.5s cnt <= cnt + 1; else cnt <= 25'd0; end always @(posedge clk, negedge rst_n) begin if(!rst_n) led_ctrl <= 1'b0; else if(cnt == 25'd2500_0000) //每隔0.5s,就改变LED灯的闪烁状态 led_ctrl <= ~led_ctrl; end always @(posedge clk, negedge rst_n) begin if(!rst_n) led <= 2'b00; else case(key) 2'b10://如果按键0按下,则交替闪烁 if(led_ctrl == 1'b0) led <= 2'b01; else led <= 2'b10; 2'b01://按键1按下,则同时闪烁 if(led_ctrl == 1'b0) led <= 2'b00; else led <= 2'b11; 2'b11://都不按下,则常亮 led <= 2'b00; default: led <= 2'b00; endcase end endmodule
添加约束。包括引脚约束和时钟约束。
set_property PACKAGE_PIN M14 [get_ports {led[1]}] set_property PACKAGE_PIN M15 [get_ports {led[0]}] set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}] set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}] set_property PACKAGE_PIN U18 [get_ports clk] set_property PACKAGE_PIN N15 [get_ports rst_n] set_property IOSTANDARD LVCMOS33 [get_ports clk] set_property IOSTANDARD LVCMOS33 [get_ports rst_n] set_property PACKAGE_PIN T17 [get_ports {key[1]}] set_property PACKAGE_PIN R17 [get_ports {key[0]}] set_property IOSTANDARD LVCMOS33 [get_ports {key[1]}] set_property IOSTANDARD LVCMOS33 [get_ports {key[0]}] create_clock -period 20.000 -name clk -waveform {0.000 10.000} [get_ports clk]
实验结果。当两个按键都不按下时,LED灯常亮;当其中一个按下时,LED灯交替闪烁;当另外一个按键按下时,两个LED灯同时闪烁。
PWM呼吸灯
PWM,Pulse Width Modulation,脉冲宽度调制,简称脉宽调制。其中脉宽(W)指的是在一个脉冲周期中,高电平所持续的时间,单位是时间单位。占空比(D)指的是脉宽占整个时钟周期的比重,即D = W/T;在工程领域,PWM常常用于转机调速,舵机控制等场景。比如在进行转机调速时,使用PWM加载在负载两边的电压(幅值电压×占空比)与相同大小的直流电压是等效的,以此达到对转机调速的目的。
实现呼吸灯的效果,即由灭渐亮, 然后再由亮渐灭。
设计代码:
module LED(clk, rst_n, led); input clk; //系统时钟50MHZ input rst_n; //复位信号,低位有效 output led; reg [15:0] period_cnt; //周期计数器频率 reg [15:0] duty_cycle; //占空比数值 reg inc_dec_flag; //0递增,1递减 assign led = (period_cnt <= duty_cycle) ? 1'b1 : 1'b0; //判断led灯是亮还是灭 always @(posedge clk, negedge rst_n) begin if(!rst_n) period_cnt <= 16'd0; else if(period_cnt >= 16'd50000) //计数到50000,即1ms刷新一次 period_cnt <= 16'd0; else period_cnt <= period_cnt + 1'b1; end always @(posedge clk, negedge rst_n) begin if(!rst_n) begin duty_cycle <= 16'd0; inc_dec_flag <= 1'b0; end else if(period_cnt == 16'd50000) begin //是否要刷新 if(inc_dec_flag == 1'b0) begin //判断变亮? if(duty_cycle == 16'd50000) //占空比最大 inc_dec_flag <= 1'b1; //开始递减 else duty_cycle = duty_cycle + 16'd25; //占空比以25为单位开始递增 end else begin if(duty_cycle == 16'd0) //占空比最小 inc_dec_flag <= 1'd0; //开始递增 else duty_cycle = duty_cycle - 16'd25; //占空比以25为单位开始递增
end
end
end
ila_0 ila_inst ( .clk(clk),// input wire clk
.probe0(led), // input wire [0:0] probe0
.probe1(inc_dec_flag), // input wire [0:0] probe1
.probe2(period_cnt), // input wire [15:0] probe2
.probe3(duty_cycle) // input wire [15:0] probe3 );
endmodule
添加约束。引脚约束和时钟约束与上边两个实验一致。
实验结果。LED灯能够按照实验结果进行转换。
参考资料
[1] 黑金FPGA开发教程
[2] 正点原子FPGA开发教程