既然是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]);