之前做電機控制都是用DSP產生不同佔空比的PWM波形,但是由於項目需要,要用FPGA產生不同佔空比的PWM波形,通過查閱資料看到不少的方式方法,綜合了一下,按着自己的思路寫了一個產生6路3對PWM 波形,並且仿真和實測都滿足設計要求。
通常情況下我們的思路是設計一個計數器,增加到一定要求然後置零,往復循環即可,但是,這次有一個問題,爲了讓達到與DSP的計數模式一致,我們需要設計這個計數器,它先遞增,遞增到一定數後開始遞減,遞減到一定數後又遞增,循環反覆,應該怎麼設計呢?其實這個思想不僅僅是在電機控制中有應用,在很多其他地方都能用到,如PWM呼吸燈、VGA方塊回彈等。
設計思路:
1.設計一個計數器,根據標誌位的不同,進行加1和減1運算。
當標誌位爲0並且計數器的值小於週期值的一半減1時,計數器+1,
當標誌位爲1並且計數器的值大於0時,計數器-1,
2.設計一個標誌位,當檢測到標誌位爲1,並且計數器也爲零時,標誌位置0.
當檢測到標誌位爲0,並且計數器爲週期值得一般減1時,標誌位置1.
3.分別設計三個比較器,其中A、B路波形互補,通過給A、B路的寄存器寫入相反的值即可,
考慮到死區問題,要使B路的值小於A路值,此處設置爲2us(即100個始終)
代碼:
module PWM(
input sys_clk , //系統時鐘
input sys_rst_n, //系統復位,低電平有效
output reg PWMA_H, //PWMA路輸出
output reg PWMA_L,
output reg PWMB_H, //PWMB路輸出
output reg PWMB_L,
output reg PWMC_H, //PWMC路輸出
output reg PWMC_L
);
reg [12:0] compar_A=13'd1250;
reg [12:0] compar_B=13'd1250;
reg [12:0] compar_C=13'd1250;
reg [12:0] counter; //計數器用於產生PWM波形所需週期
parameter N =14'd5000; //設定PWM週期
//*****************************************************
// main code
//*****************************************************
//************************動作模塊**********************
reg flag;
always @(posedge sys_clk or negedge sys_rst_n)
begin
if (!sys_rst_n)
counter <= 13'b0;
else if (flag == 0 && counter< N/2 -1 )
counter <= counter + 1'b1;
else if (flag == 1 && counter> 0 )
counter <= counter - 1'b1;
end
always @(posedge sys_clk or negedge sys_rst_n)
begin
if (!sys_rst_n)
flag <= 1'b0;
else if (flag == 0 && counter == N/2 -1)
flag <= 1;
else if (flag == 1 && counter == 0)
flag <= 0;
end
//*********************6路PWM信號**********************
//*********************A路PWM信號**********************
always @(posedge sys_clk or negedge sys_rst_n)
begin
if (!sys_rst_n)
begin
PWMA_H <=0;
end
else if (counter>compar_A)
begin
PWMA_H <= 1'b1;
end
else
begin
PWMA_H <= 1'b0;
end
end
always @(posedge sys_clk or negedge sys_rst_n)
begin
if (!sys_rst_n)
begin
PWMA_L <=0;
end
else if (counter>compar_A-9'd100) //死區2us
begin
PWMA_L <= 1'b0;
end
else
begin
PWMA_L <= 1'b1;
end
end
//*********************B路PWM信號**********************
always @(posedge sys_clk or negedge sys_rst_n)
begin
if (!sys_rst_n)
begin
PWMB_H <=0;
end
else if (counter>compar_B)
begin
PWMB_H <= 1'b1;
end
else
begin
PWMB_H <= 1'b0;
end
end
always @(posedge sys_clk or negedge sys_rst_n)
begin
if (!sys_rst_n)
begin
PWMB_L <=0;
end
else if (counter>compar_B-9'd100) //死區2us
begin
PWMB_L <= 1'b0;
end
else
begin
PWMB_L <= 1'b1;
end
end
//*********************C路PWM信號**********************
always @(posedge sys_clk or negedge sys_rst_n)
begin
if (!sys_rst_n)
begin
PWMC_H <=0;
end
else if (counter>compar_C)
begin
PWMC_H <= 1'b1;
end
else
begin
PWMC_H <= 1'b0;
end
end
always @(posedge sys_clk or negedge sys_rst_n)
begin
if (!sys_rst_n)
begin
PWMC_L <=0;
end
else if (counter>compar_C-9'd100) //死區2us
begin
PWMC_L <= 1'b0;
end
else
begin
PWMC_L <= 1'b1;
end
end
endmodule
仿真波形:
可以看出能夠產生互補對稱得6路PWM波形。
上面這個圖看出標誌位在2500的時候由低變高。
上面這個圖看出標誌位在0的時候由高變低。
上面這個圖看出3路PWM_H在比較值的一半減1處由高變低。
上面這個圖看出3路PWM_L在PWM_H值小100個數值處由低變高。
示波器測試數據:
上圖位3對PWM 波形中的一對,可以看出是互補對稱的。
上圖爲局部放大,能夠看出死區的存在。
PS:
一開始設計時沒有在counter的觸發條件那加上 counter< N/2 -1和 counter> 0 ,結果導致counter 會多計一下,最終功能上稍微有所欠缺,原因是counter和flag兩個always塊並行跑,如果不加那個限制條件,那flag剛剛拉高時,counter同步變化,實際上還是執行的上一輪限制條件,最終導致結果出錯。而加了限制條件後,這個問題就解決了。