用VerilogHDL编写的可调占空比的PWM波形设计

既然是PWM,当然需要占空比可调,我选用的是CycloneII系列的FPGA,使用50MHz的时钟源。

开发板如下图:

通过开发板上的K2,K1键控制PWM的大小,具体是如何实现的呢?

系统采用50MHz的晶振作为时钟源,设定PWM的周期为1ms,也就是说计数器需要计数50000次,计数器一旦大于50000,自动清零,并重新进行下一轮的计数。

在这50000次计数中,可以设定n(0<=n<=50000),只要计数器值小于n(n=duty_cycle*sd),则令PWM输出为1,否则输出为0,这就是PWM占空比调节原理。

理想情况下,我们可以设定50000个级数用于调节PWM的占空比,但现实中没必要也不可能设置这么多的级数,所以根据需要,代码设置了五级,这样从0开始,每调一级(duty_cycle的值加1或减1),占空比增加20%,每一级的计数间隔为sd=10000。

代码实现:

 1 always@ (posedge clk or posedge key_ctrl[3]) begin
 2     if(key_ctrl[3]) counter <= 16'd0;          //计数器复位
 3     else 
 4     begin
 5     counter <= counter+1;
 6         if ( counter <= duty_cycle*sd )    //设置PWM为1的时间间隔
 7             PWM_out <= 1;
 8         else
 9             PWM_out <= 0;
10         if (counter >=50000)
11             counter <= 16'd0;
12     end
13 end

下面的问题就是怎么通过K2,K1控制duty_cycle大小的问题了。

在设计中曾试着通过两个always语句对duty_cycle赋值,其中一个是加,一个是减。结果是编译出错,原因是不能在并行块中对同一个变量进行赋值。

错误代码1:

 1 always @(posedge key_ctrl[1])begin 
 2     duty_cycle <= duty_cycle + 1'b1;
 3     if(duty_cycle >=5)
 4         duty_cycle <=5;  //最大值不超过5
 5     else
 6         duty_cycle <= duty_cycle;
 7 end
 8 
 9 always @(posedge key_ctrl[0])begin    
10     duty_cycle1 <= duty_cycle1 - 1'b1;
11         if(duty_cycle1 <= 0)
12         duty_cycle1 <=0; //最小值不小于0
13             else
14          duty_cycle1 <= duty_cycle1;
15 end
16 
17 assign duty_cycle = duty_cycle1;

然后试着写在一个always语句中,这次虽然编译通过,可还是无法实现。如果有网友知道还望不吝赐教。

错误代码2:

 1 always@(posedge key_ctrl[1] or posedge key_ctrl[0])begin                   
 2      if(key_ctrl[1])                             //如果key_ctrl[1]按下
 3         begin
 4           if(duty_cycle < 3'b110)                //且duty_cycle值小于6
 5             duty_cycle <= duty_cycle + 3'b001;   //则duty_cycle值加1
 6           else
 7             duty_cycle <= duty_cycle;            //否则duty_cycle值不变  
 8         end
 9      else
10      if(key_ctrl[0])                             //如果key_ctrl[0]按下
11         begin
12           if(duty_cycle > 3'b000)                //且duty_cycle值大于0
13             duty_cycle <= duty_cycle - 3'b001;   //则duty_cycle值减1
14           else
15             duty_cycle <= duty_cycle;            //否则duty_cycle值不变
16         end
17          
18       else
19             duty_cycle <= duty_cycle;
20 end

最后又在网上查阅了好多资料,对于如何通过不同的按键对一个变量赋值找到了一种完美的解决办法,就是利于case语句。

正确代码:

 1 always@( posedge clk or negedge rst_n )  begin   
 2   if( !rst_n )   
 3    begin
 4     duty_cycle <= 0; 
 5    end
 6   else
 7    begin
 8     case ( {key_ctrl[1],key_ctrl[0]} )
 9      2'b00:begin
10               duty_cycle <= duty_cycle;
11            end
12      2'b10:begin
13            if ( duty_cycle >= 5 )
14                begin
15                duty_cycle <= duty_cycle;
16                end
17            else
18                begin
19                duty_cycle <= duty_cycle + 1'b1;
20                end
21            end
22      2'b01:begin
23            if ( duty_cycle == 'h00 )
24               begin
25                 duty_cycle <= duty_cycle;
26              
27               end
28            else
29               begin
30                duty_cycle <= duty_cycle - 1'b1;
31               end
32            end
33      2'b11:begin
34            duty_cycle <= duty_cycle;
35            end
36     endcase
37    end  
38  end

你们可能发现了,代码中控制duty_cycle的是key_ctrl[1],key_ctrl[0],而不是K2,K1,这是因为采用独立按键会产生抖动,key_ctrl[1],key_ctrl[0]是K2,K1经按键消抖后的值。

下面附上采用吴厚航(网名特权同学)的按键消抖代码:

 1 reg[3:0] key_rst; 
 2 
 3 always @(posedge clk )
 4 //    if (!rst_n) key_rst <= 4'b1111;
 5 //    else 
 6      key_rst <= {rst_n,key3,key2,key1};
 7 
 8 reg[3:0] key_rst_r; 
 9 
10 always @ ( posedge clk)
11 //    if (!rst_n) key_rst_r <= 4'b1111;
12 //    else 
13      key_rst_r <= key_rst;
14    
15 
16 wire[3:0] key_an = key_rst_r & (~key_rst);
17 reg[19:0]  cnt;    
18 
19 always @ (posedge clk)
20 //    if (!rst_n) cnt <= 20'd0;    
21 //    else 
22     if(key_an) cnt <=20'd0;
23     else cnt <= cnt + 1'b1;
24   
25 reg[3:0] low_sw;
26 
27 always @(posedge clk)
28 //    if (!rst_n) low_sw <= 4'b1111;
29 //    else 
30      if (cnt == 20'hfffff)     
31       low_sw <= {rst_n,key3,key2,key1};
32       
33 
34 reg  [3:0] low_sw_r;      
35 
36 always @ ( posedge clk )
37 //    if (!rst_n) low_sw_r <= 4'b1111;
38 //    else 
39      low_sw_r <= low_sw;
40      
41      wire[3:0] key_ctrl = low_sw_r[3:0] & ( ~low_sw[3:0]);
原文地址:https://www.cnblogs.com/bigpo/p/3722979.html