PWM波形的FPGA实现
PWM调制,脉宽调制,Pulse Width Modulation,根据相应负载的变化来调制晶体管或MOS管栅极的偏置,来实现晶体管或MOS管导通时间的改变,从而实现开关稳压电源稳定的输出。
简单来说,就是用数字信号对模拟电路进行控制。
PWM介绍
PWM波可以用于控制步进电机的工作。
下图为一个PWM信号的示意图,脉冲的周期为Period,脉冲宽度为有效脉冲时间。占空比定义为高电平信号占整个脉冲信号周期的百分比
PWM信号设计
要产生PWM信号,可以用计数器思想,当cnt小于脉冲宽度时,PWM信号为高;当cnt大于脉冲宽度时,PWM信号为低;当cnt=Period-1时,计数器复位,往复循环。
PWM信号发生器系统由占空比、周期调整模块,PWM信号产生模块,数码管显示模块。示意图如下(参考):
按键输入用于控制占空比和周期。Duty是用于控制PWM信号的占空比,Count_P存储了周期值,Count_D存储了脉冲宽度的数值,其计算公式为
U2模块内部有一个计数器cnt1,按照前文的思想,即比较cnt1和Count_P和Count_D的值,即可生成PWM波。
增加了两个信号,用于选择显示数码管。
程序设计
程序包含顶层模块与底层模块,顶层模块引脚定义如下:
input CLK;
input RSTn; //SW0
input [3:0] col;
output [3:0] row;//row =4'b1110
input Count_D_Display; //SW1
input Count_P_Display; //SW2
output [7:0]Digitron_Out;
output [3:0]DigitronCS_Out;
output PWM_LED_Out; //LED7
output PWM_EPI_Out; //T4
output[6:0] LED_Out;
底层模块
占空比、周期调整模块功能如下:随着key0和key1的按下,duty和Count_P可以改变
module Duty_Period_Adjust_module
(
CLK, RSTn, AddDuty_In, AddPeriod_In, Duty, Count_P
);
input CLK;
input RSTn;
input AddDuty_In; //Add Duty Ratio
input AddPeriod_In; //Add Period
output reg [7:0]Duty; //Duty Ratio of PWM
output reg [23:0]Count_P; //period of PWM = Count_P/50_000_000
wire neg_AddDuty; //synthesis keep;
wire neg_AddPeriod; //synthesis keep;
//KEY0-3按下FPGA引脚输入电平由高变低,经过消抖模块的输出由低变高
key_filter U1
(
.clk( CLK ) ,
.rstn( RSTn ) ,
.key_in( AddDuty_In ) ,
.key_deb( neg_AddDuty )
);
key_filter U2
(
.clk( CLK ) ,
.rstn( RSTn ) ,
.key_in( AddPeriod_In ) ,
.key_deb( neg_AddPeriod )
);
always @ ( posedge neg_AddDuty or negedge RSTn )
begin
if( !RSTn )
Duty <= 8'd50;
else if( Duty == 8'd100 )
Duty <= 8'd0;
else
Duty <= Duty + 8'd10;
end
/*******************
While Count_P = 500_000, Period of PWM = 10ms, Frequency of PWM = 100HZ ;
While Count_P = 250_000, Period of PWM = 5ms, Frequency of PWM = 200HZ ;
While Count_P = 50_000, Period of PWM = 1ms, Frequency of PWM = 1000HZ ;
*******************/
always @ ( posedge neg_AddPeriod or negedge RSTn )
begin
if( !RSTn )
Count_P <= 24'd250_000;
else if( Count_P == 24'd500_000 )
Count_P <= 24'd50_000;
else
Count_P <= Count_P + 24'd50_000;
end
endmodule
PWM信号产生模块如下:利用cnt1与Count_D进行比较,如上所述
module PWM_Generate_module
(
CLK, RSTn, Duty, Count_P, PWM_Out, Count_D
);
input CLK;
input RSTn;
input [7:0]Duty;
input [23:0]Count_P; //period = Count_P/50_000_000
output reg PWM_Out;
output [23:0]Count_D;
reg [23:0]Cnt1;
assign Count_D = (Duty * Count_P) / 'd100;
always @ ( posedge CLK or negedge RSTn )
begin
if( RSTn == 0 )
Cnt1 <= 0;
else if( Cnt1 == Count_P - 1'b1 )
Cnt1 <= 0;
else
Cnt1 <= Cnt1 + 1'b1;
end
always @( * )
begin
if( Cnt1 <= Count_D )
PWM_Out <= 1'b1;
else
PWM_Out <= 0;
end
endmodule
数码管显示模块如下:Count_D_Display, Count_P_Display分别控制数码管显示占空比和周期
module Digitron_NumDisplay
(
CLK, RSTn, Count_D_Display, Count_P_Display, Count_D, Count_P, Duty, Digitron_Out, DigitronCS_Out
);
input CLK;
input RSTn;
input Count_D_Display;
input Count_P_Display;
input [23:0]Count_D;
input [23:0]Count_P;
input [7:0]Duty;
output [7:0]Digitron_Out;
output [3:0]DigitronCS_Out;
parameter T1MS = 16'd50000;
reg [15:0]Cnt;
reg [3:0]SingleNum;
reg [7:0]W_Digitron_Out;
reg [3:0]W_DigitronCS_Out;
reg [1:0]Display_step ; //数码管显示步骤
parameter _0 = 8'b0011_1111, _1 = 8'b0000_0110, _2 = 8'b0101_1011,
_3 = 8'b0100_1111, _4 = 8'b0110_0110, _5 = 8'b0110_1101,
_6 = 8'b0111_1101, _7 = 8'b0000_0111, _8 = 8'b0111_1111,
_9 = 8'b0110_1111, _A = 8'b0111_0111, _B = 8'b0111_1100,
_C = 8'b0011_1001, _D = 8'b0101_1110, _E = 8'b0111_1001,
_F = 8'b0111_0001, _Wu = 8'b0100_0000;
always @ ( posedge CLK or negedge RSTn )
begin
if( !RSTn )
begin
Cnt <= 16'd0;
Display_step <= 2'b00 ;
end
else if( Cnt == T1MS )
begin
Cnt <= 16'd0;
if(Display_step == 2'b11)
Display_step <= 2'b0 ;
else
Display_step <= Display_step +1'b1 ;
end
else
Cnt <= Cnt + 1'b1;
end
always@(Display_step)
begin
if(!Count_D_Display && !Count_P_Display)//只显示占空比
W_DigitronCS_Out = 4'b1111 ;
else
begin
case (Display_step)
2'b00:
W_DigitronCS_Out = 4'b1110 ;
2'b01:
W_DigitronCS_Out = 4'b1101 ;
2'b10:
W_DigitronCS_Out = 4'b1011 ;
2'b11:
W_DigitronCS_Out = 4'b0111 ;
endcase
end
end
always@(W_DigitronCS_Out)
begin
if( Count_D_Display == 1'b1 ) //Digitron Display Count_D
begin
case(W_DigitronCS_Out)
4'b1110: SingleNum = Count_D[3:0];
4'b1101: SingleNum = Count_D[7:4];
4'b1011: SingleNum = Count_D[11:8];
4'b0111: SingleNum = Count_D[15:12];
endcase
end
else if( Count_P_Display == 1'b1 )
begin
case(W_DigitronCS_Out)
4'b1110: SingleNum = Count_P[3:0];
4'b1101: SingleNum = Count_P[7:4];
4'b1011: SingleNum = Count_P[11:8];
4'b0111: SingleNum = Count_P[15:12];
endcase
end
else
begin
SingleNum = 5'b11111;
end
case(SingleNum)
0: W_Digitron_Out = _0;
1: W_Digitron_Out = _1;
2: W_Digitron_Out = _2;
3: W_Digitron_Out = _3;
4: W_Digitron_Out = _4;
5: W_Digitron_Out = _5;
6: W_Digitron_Out = _6;
7: W_Digitron_Out = _7;
8: W_Digitron_Out = _8;
9: W_Digitron_Out = _9;
10: W_Digitron_Out = _A;
11: W_Digitron_Out = _B;
12: W_Digitron_Out = _C;
13: W_Digitron_Out = _D;
14: W_Digitron_Out = _E;
15: W_Digitron_Out = _F;
default: W_Digitron_Out = _Wu;
endcase
end
assign Digitron_Out = W_Digitron_Out;
assign DigitronCS_Out = W_DigitronCS_Out;
endmodule
FPGA实现
- 先进行管脚约束
- 观察PWM波形