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