实验目标:实现PWM呼吸灯,1s内由灭到亮再由灭到亮
实验原理:1s内分为1000个1ms,每个1ms分为1000个1us,对于每个1ms中的亮灭占比时间进行控制,如亮的时间从0到1us一直到1000us即最亮,由灭到亮同理。
代码框架思考:
1.模块的输入输出
这个简单输入clk和rst,输出led
2.需要用几个计数器
从最小周期开始计数即一个1us的计数器,一个1ms的计数器,一个1s的计数器
parameter CNT_1US_MAX = 6'd49 ,
parameter CNT_1MS_MAX = 10'd999 ,
parameter CNT_1S_MAX = 10'd999
3.每个计数器的开始截至条件
cnt_1us对于50MHZ的fpga来说计数到50-1即可
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_1us <= 6'b0;
else if(cnt_1us == CNT_1US_MAX)
cnt_1us <= 6'b0;
else
cnt_1us <= cnt_1us + 1'b1
cnt_1ms可以由cnt_1us产生,当1us计数到最大时cnt_1ms加一,直到加了999次
//cnt_1ms:1ms计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_1ms <= 10'b0;
else if(cnt_1ms == CNT_1MS_MAX && cnt_1us == CNT_1US_MAX)
cnt_1ms <= 10'b0;
else if(cnt_1us == CNT_1US_MAX)
cnt_1ms <= cnt_1ms + 1'b1;
同理,cnt_1s也由此产生
//cnt_1s:1s计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_1s <= 10'b0;
else if(cnt_1s == CNT_1S_MAX && cnt_1ms == CNT_1MS_MAX
&& cnt_1us == CNT_1US_MAX)
cnt_1s <= 10'b0;
else if(cnt_1ms == CNT_1MS_MAX && cnt_1us == CNT_1US_MAX)
cnt_1s <= cnt_1s + 1'b1;
注意:要满足上一个小周期的截至条件,这是为了确保在最小的那个计数周期处完成状态切换。
4.如何区分亮灭状态
加上一个标志位cnt_1s_en,由灭到亮为低电平,亮到灭相反,具体实现只需要在1s结束时取反即可,所以取反的条件和1s截至的条件一样,记得上一个的注意事项
//cnt_1s_en:1s计数器使能信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_1s_en <= 1'b0;
else if(cnt_1s == CNT_1S_MAX && cnt_1ms == CNT_1MS_MAX
&& cnt_1us == CNT_1US_MAX)
cnt_1s_en <= ~cnt_1s_en
5.如何实现led的pwm效果
画出波形图观察led_out与其他信号的关系
发现:由灭到亮时 led_out为低电平时间时 cnt_1s 计数器的计数值总是小于 cnt_1ms 计数器的计数(cnt_1ms > cnt_1s),相反由亮到灭时 cnt_1s 计数器的计数值总是大于cnt_1ms 计数器(cnt_1ms < cnt_1s)),即可得到led_out的赋值条件,注意区分亮灭。
//led_out:输出信号连接到外部的led灯
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
led_out <= 1'b0;
else if((cnt_1s_en == 1'b1 && cnt_1ms < cnt_1s) ||
(cnt_1s_en == 1'b0 && cnt_1ms > cnt_1s))
led_out <= 1'b0;
else
led_out <= 1'b1
完整代码如下:
module breath_led
#(
parameter CNT_1US_MAX = 6'd49 ,
parameter CNT_1MS_MAX = 10'd999 ,
parameter CNT_1S_MAX = 10'd999
)
(
input wire sys_clk , //系统时钟50Mhz
input wire sys_rst_n , //全局复位
output reg led_out //输出信号,控制led灯
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//reg define
reg [5:0] cnt_1us ;
reg [9:0] cnt_1ms ;
reg [9:0] cnt_1s ;
reg cnt_1s_en ;
//********************************************************************/
//***************************** Main Code ****************************//
//********************************************************************//
//cnt_1us:1us计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_1us <= 6'b0;
else if(cnt_1us == CNT_1US_MAX)
cnt_1us <= 6'b0;
else
cnt_1us <= cnt_1us + 1'b1;
//cnt_1ms:1ms计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_1ms <= 10'b0;
else if(cnt_1ms == CNT_1MS_MAX && cnt_1us == CNT_1US_MAX)
cnt_1ms <= 10'b0;
else if(cnt_1us == CNT_1US_MAX)
cnt_1ms <= cnt_1ms + 1'b1;
//cnt_1s:1s计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_1s <= 10'b0;
else if(cnt_1s == CNT_1S_MAX && cnt_1ms == CNT_1MS_MAX
&& cnt_1us == CNT_1US_MAX)
cnt_1s <= 10'b0;
else if(cnt_1ms == CNT_1MS_MAX && cnt_1us == CNT_1US_MAX)
cnt_1s <= cnt_1s + 1'b1;
//cnt_1s_en:1s计数器使能信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_1s_en <= 1'b0;
else if(cnt_1s == CNT_1S_MAX && cnt_1ms == CNT_1MS_MAX
&& cnt_1us == CNT_1US_MAX)
cnt_1s_en <= ~cnt_1s_en;
//led_out:输出信号连接到外部的led灯
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
led_out <= 1'b0;
else if((cnt_1s_en == 1'b1 && cnt_1ms < cnt_1s) ||
(cnt_1s_en == 1'b0 && cnt_1ms > cnt_1s))
led_out <= 1'b0;
else
led_out <= 1'b1;
endmodule
注意数据类型:在时序逻辑电路中的输出信号都是reg值