$$ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Self-defined math definitions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Math symbol commands \newcommand{\intd}{\,{\rm d}} % Symbol 'd' used in integration, such as 'dx' \newcommand{\diff}{{\rm d}} % Symbol 'd' used in differentiation ... $$

【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开发教程

 
posted @ 2023-03-16 11:32  素衣叹风尘  阅读(254)  评论(0编辑  收藏  举报