之前做电机控制都是用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同步变化,实际上还是执行的上一轮限制条件,最终导致结果出错。而加了限制条件后,这个问题就解决了。
上述源代码下载点击此处下载(附仿真文件)