技术标签: fpga开发 FOC算法 Verilog FOC控制算法与FPGA实现 SVPWM MATLAB
碎碎念:经过近一周的调试与查错(不好意思我实在太菜了),才终于从MATLAB代码的基础上,实现了Verilog对SVPWM算法的实现,同时给出仿真的结果。
2022年10月20日更新:实在抱歉,由于之前在算法中没有考虑到输入电压值量化以及死区时间的问题,我也是在电路测试过程中才发现这个错误,今天进行了更正与修改。电压值量化的具体原理可以参考我的上一篇文章~
目录
思路与上一篇文章基本一致,但是针对Verilog中的部分特点(包括精度以及时序),本文给出各个模块的代码,以及Vivado的仿真结果。
读者可以自行和上一篇文章进行对比:FOC:【1】浅析SVPWM算法(七段式)以及MATLAB仿真验证
用来实现SVPWM,输入是Park 逆变换后的Vα与Vβ,输出是三个桥臂的控制信号。
// SVPWM模块
// Type : synthesizable
// Standard: SystemVerilog 2005 (IEEE1800-2005)
// 功能?? 用来实现SVPWM,输入是Park 逆变换后的Vα与Vβ,输出是三个桥臂的控制信号
// 包含四个部分:
// 01 扇区判断
// 02 矢量作用时间计算
// 03 逆变器开关切换时间计算
// 04 利用三角波改变开关状态
module my_SVPWM(
input wire clk, //时钟信号
input wire rstn, //复位信号
input wire in_en, //系统的输入使能信号
input wire signed [15:0] Valpha, //Park逆变换的结果Vα
input wire signed [15:0] Vbeta, //Park逆变换的结果Vβ
output wire pwm_a, //SVPWM的输出信号 PWM_a
output wire pwm_an, //SVPWM的输出信号 PWM_an
output wire pwm_b, //SVPWM的输出信号 PWM_b
output wire pwm_bn, //SVPWM的输出信号 PWM_bn
output wire pwm_c, //SVPWM的输出信号 PWM_c
output wire pwm_cn //SVPWM的输出信号 PWM_cn
);
parameter Dead_Zone = 39; //死区的宽度
//下面的是调试版本,用来观察中间变??
// module my_SVPWM(
// input wire clk, //时钟信号
// input wire rstn, //复位信号
// input wire in_en, //系统的输入使能信号
// input wire signed [15:0] Valpha, //Park逆变换的结果Vα
// input wire signed [15:0] Vbeta, //Park逆变换的结果Vβ
// output wire pwm_a, //SVPWM的输入1 PWM_a
// output wire pwm_b, //SVPWM的输入2 PWM_b
// output wire pwm_c, //SVPWM的输入3 PWM_c
// output wire [3:0] n,
// output wire [3:0] sector,
// output wire Jug_sec_in_en,
// output wire signed [16:0] x,
// output wire signed [16:0] y,
// output wire signed [16:0] z,
// output wire Jug_sec_out_en,
// output wire signed [16:0] Tfirst,
// output wire signed [16:0] Tsecond,
// output wire signed [16:0] Tzero,
// output wire Cal_time_out_en,
// output wire signed [16:0] Tcm1,
// output wire signed [16:0] Tcm2,
// output wire signed [16:0] Tcm3,
// output wire Switch_time_out_en,
// output wire signed [11:0] Ts_cnt,
// output wire Tri_gener_out_en
// );
// SVPWM的实例化过程------------------------------------------------------------------------------------------------------------
// 00 实例化需要使用到的导线
wire [3:0] n;
wire [3:0] sector;
wire Jug_sec_in_en;
wire signed [63:0] x;
wire signed [63:0] y;
wire signed [63:0] z;
wire Jug_sec_out_en;
wire signed [63:0] Tfirst;
wire signed [63:0] Tsecond;
wire signed [63:0] Tzero;
wire Cal_time_out_en;
wire signed [63:0] Tcm1;
wire signed [63:0] Tcm2;
wire signed [63:0] Tcm3;
wire Switch_time_out_en;
wire signed [11:0] Ts_cnt;
wire Tri_gener_out_en;
reg first_start;
reg [4:0] first_start_cnt;
//00 首次启动---------------------------------------------------------------------------------------------------------------------
always_ff @(posedge clk or negedge rstn) begin
if(~rstn) begin
first_start <= 0;
first_start_cnt <= 5'd0;
end else begin
if(first_start_cnt <= 20)
first_start_cnt <= first_start_cnt + 5'd1;
if(first_start_cnt == 18)
first_start <= 1;
else
first_start <= 0;
end
end
// 01 扇区判断--------------------------------------------------------------------------------------------------------------------------------------------------
// 功能:利用当前的Valpha与Vbeta判断所在的扇区
// 输入:Valpha Vbeta
// 输出:扇区数字sector,以及相关参数N
//assign Jug_sec_in_en = Tri_gener_out_en || in_en;
assign Jug_sec_in_en = Tri_gener_out_en || first_start;
Jug_sec Jug_sec(
.clk ( clk ), //时钟信号
.rstn ( rstn ), //复位信号
.in_en ( Jug_sec_in_en ), //输入有效信号
.Valpha ( Valpha ), //Park逆变换的结果Vα (是有符号数,-32768~32767)
.Vbeta ( Vbeta ), //Park逆变换的结果Vβ (是有符号数,-32768~32767)
.n ( n ), //扇区计算中常用的N
.sector ( sector ), //扇区的结果
.x ( x ), //就是 X
.y ( y ), //就是 Y
.z ( z ), //就是 Z
.out_en ( Jug_sec_out_en ) //输出使能信号
);
// 02 矢量作用时间计算--------------------------------------------------------------------------------------------------------------------------------------------------
// 功能:矢量作用时间计算
// 输入: X,Y,Z三个变量以及N
// 输出: 根据N判断出的时间长度
Cal_time Cal_time(
.clk ( clk ), //时钟信号
.rstn ( rstn ), //复位信号
.in_en ( Jug_sec_out_en ), //输入使能信号
.x ( x ),
.y ( y ),
.z ( z ),
.n ( n ),
.Tfirst ( Tfirst ),
.Tsecond ( Tsecond ),
.Tzero ( Tzero ),
.out_en ( ), //输出使能信号
.out_en2 ( ),
.out_en3 ( Cal_time_out_en )
);
// 03 计算逆变器开关切换的时间--------------------------------------------------------------------------------------------------------------------------------------------------
// 功能:利用查表的方式,计算三个相开关切换的时间
// 输入:
// 输出:
Switch_time Switch_time(
.clk ( clk ), //时钟信号
.rstn ( rstn ), //复位信号
.in_en ( Cal_time_out_en ), //输入使能信号
.n ( n ),
.Tfirst ( Tfirst ),
.Tsecond ( Tsecond ),
.Tzero ( Tzero ),
.Tcm1 ( Tcm1 ), //三个逆变器的切换时间
.Tcm2 ( Tcm2 ),
.Tcm3 ( Tcm3 ),
.out_en ( ), //输出使能信号
.out_en2 ( Switch_time_out_en )
);
// 04 产生三角??--------------------------------------------------------------------------------------------------------------------------------------------------
// 功能:绘制三角波
// 输入:
// 输出:
Tri_gener Tri_gener(
.clk ( clk ), //输入时钟
.rst ( rstn ), //复位信号
.in_en ( Switch_time_out_en ), //输入使能信号
.Ts_cnt ( Ts_cnt ), //Ts的计数器,用来产生一个周期为Ts=2*Tp的三角波
.deta_clk ( Tri_gener_out_en ) //每次输出一个时钟,就给一个高电平
);
// 05 结合三角波,产生SVPWM结果(包含死区的计算)
// 功能:结合三角波,产生SVPWM的结果
// 输入: 计算出来的输出波形切换时间Tcm1,Tcm2,Tcm3,以及当前的
// 输出:
assign pwm_a = (Ts_cnt >= Tcm1) ? 1:0;
assign pwm_b = (Ts_cnt >= Tcm2) ? 1:0;
assign pwm_c = (Ts_cnt >= Tcm3) ? 1:0;
// 06 死区时间的考虑,这里由于我电路中需要给同相信号,所以是下面的表达式
// 要根据实际电路确定正负,但是这个延时的思路是相同的
assign pwm_an = (Ts_cnt >= Tcm1-Dead_Zone) ? 1:0;
assign pwm_bn = (Ts_cnt >= Tcm2-Dead_Zone) ? 1:0;
assign pwm_cn = (Ts_cnt >= Tcm3-Dead_Zone) ? 1:0;
endmodule
用来实现输入是Park 逆变换后的Vα与Vβ,输出扇区的值。
这里需要注意的细节是,n的计算需要参考三个表达式,但是实际上只需要利用到表达式的具体符号,而不是数值。因此可以通过成乘法,来放大其中的小数部分,从而获得更加准确的正负符号判断。
相应的,对于XYZ的计算,由于是需要具体数值的,并且对精度要求较高。怎么实现浮点数的运算呢?比较好的方法是先实现所有的乘法,在进行所有的除法,这样可以获得比较好的浮点数精度。
// 用来实现扇区的判断
// Type : synthesizable
// Standard: SystemVerilog 2005 (IEEE1800-2005)
// 功能: 用来实现输入是Park 逆变换后的Vα与Vβ,输出扇区的值
module Jug_sec(
input wire clk, //时钟信号
input wire rstn, //复位信号
input wire in_en, //输入有效信号
input wire signed [15:0] Valpha, //Park逆变换的结果Vα (是有符号数,-32768~32767)
input wire signed [15:0] Vbeta, //Park逆变换的结果Vβ (是有符号数,-32768~32767)
output reg [3:0] n, //扇区计算中常用的N
output reg [3:0] sector, //扇区的结果
output reg signed [63:0] x, //就是X
output reg signed [63:0] y, //就是Y
output reg signed [63:0] z, //就是Z
output reg out_en
);
//reg [16:0] Vref1; // 不需要定义,就是 Vbeta本身
reg signed [31:0] Vref1;
reg signed [31:0] Vref2;
reg signed [31:0] Vref3;
//reg en_flag;
reg flag2;
//reg [16:0] y;
//reg [16:0] z;
wire signed [31:0] alphasqrt3;
parameter Ts = 1666;
parameter sqrt3Ts = 2884;
parameter Vdc = 78643; //V_dc_real/V_RANGE*32768;
//parameter temp = sqrt3Ts/Vdc;
always @(*)
begin
if(~rstn)
begin
n <= 4'b0000;
end
else
begin
n[2:0]<= {~Vref3[31], ~Vref2[31], ~Vbeta[15]};
end
end
always@(posedge clk)
if(~rstn)
begin
Vref1 <= 32'd0;
Vref2 <= 32'd0;
Vref3 <= 32'd0;
x <= 64'd0;
y <= 64'd0;
z <= 64'd0;
sector <= 4'b0000;
flag2 <= 1'd0;
out_en <= 1'd0;
end
else
begin
if(flag2 == 1'd1)
begin
flag2 <= 1'd0;
case(n) //通过符号来判断
4'd3: //3
begin
sector <= 4'd1;
out_en <= 1'd1;
end
4'd1:
begin
sector <= 4'd2;
out_en <= 1'd1;
end
4'd5:
begin
sector <= 4'd3;
out_en <= 1'd1;
end
4'd4:
begin
sector <= 4'd4;
out_en <= 1'd1;
end
4'd6:
begin
sector <= 4'd5;
out_en <= 1'd1;
end
4'd2:
begin
sector <= 4'd6;
out_en <= 1'd1;
end
default:
begin
sector <= 4'd0;
out_en <= 1'd0;
end
endcase
end
if(out_en)
begin
$display("OUTPUT:");
$display(" sector : %d",sector);
$display(" n : %d",n);
$display(" x : %d",x);
$display(" y : %d",y);
$display(" z : %d",z);
out_en<= 1'd0;
end
if(in_en)
begin
//实现高精度的方法,先计算所有的乘法,最后计算除法!
//对于只需要计算符号的,不需要除以分母啦
$display("--------------------05 SVPWM--------------------");
$display(" ----------05-01 Judge Section---------- ");
$display("INPUT:");
$display(" Valpha : %d",Valpha);
$display(" Vbeta : %d",Vbeta);
Vref1 <= Vbeta;
Vref2 <= (-1*Vbeta*512 + Valpha*887);///1024; //这一步,相当于alphasqrt3*根号三去掉后面的几位就是实现了除以255(新策略,都乘以256,再除以512)
Vref3 <= (-1*Vbeta*512 - Valpha*887);///1024;
x <= sqrt3Ts*(Vbeta)/Vdc;
y <= (sqrt3Ts*Vbeta*512 + sqrt3Ts*Valpha*887)/(1024*Vdc); //这里与Vref是差倍数的
z <= (sqrt3Ts*Vbeta*512 - sqrt3Ts*Valpha*887)/(1024*Vdc);
flag2 <= 1'd1;
end
end
endmodule
利用输入的XYZ,计算时间的长度。
// 计算时间的长度
// Type : synthesizable
// Standard: SystemVerilog 2005 (IEEE1800-2005)
// 功能: 利用输入的XYZ,计算时间的长度
module Cal_time(
input wire clk, //时钟信号
input wire rstn, //复位信号
input wire in_en, //输入使能信号
input wire signed [63:0] x,
input wire signed [63:0] y,
input wire signed [63:0] z,
input wire [3:0] n,
output reg signed [63:0] Tfirst,
output reg signed [63:0] Tsecond,
output reg signed [63:0] Tzero,
output reg out_en, //输出使能信号
output reg out_en2,
output reg out_en3,
output reg signed [63:0] temp2,
output reg signed [63:0] temp3,
output reg signed [63:0] temp
//output reg flag2
);
parameter Ts = 1666;
//reg signed [30:0] temp2;
//reg signed [30:0] temp3;
reg flag2;
//wire signed [30:0] temp;
//assign =
always @(posedge clk)
begin
if(in_en)
begin
$display("--------------------05 SVPWM--------------------");
$display(" ----------05-02 Calculate Time---------- ");
$display("INPUT:");
$display(" x : %d",x);
$display(" y : %d",y);
$display(" z : %d",z);
flag2 <= 1'd1;
end
if(~rstn)
begin
Tfirst <= 64'd0;
Tsecond <= 64'd0;
temp2 <= 64'd0;
temp3 <= 64'd0;
flag2 <= 1'd0;
out_en <= 1'd0;
out_en2 <= 1'd0;
out_en3 <= 1'd0;
Tzero <= 64'd0;
temp <= 64'd0;
end
else
Tzero <= (Ts - Tfirst - Tsecond)/2;
begin
if(flag2)
begin
flag2 <= 1'd0;
case(n)
4'd1:begin
Tfirst <= z;
Tsecond <= y;
out_en <= 1'd1;
end
4'd2:begin
Tfirst <= y;
Tsecond <= -1*x;
out_en <= 1'd1;
end
4'd3:begin
Tfirst <= -1*z;
Tsecond <= x;
out_en <= 1'd1;
end
4'd4:begin
Tfirst <= -1*x;
Tsecond <= z;
out_en <= 1'd1;
end
4'd5:begin
Tfirst <= x;
Tsecond <= -1*y;
out_en <= 1'd1;
end
4'd6:begin
Tfirst <= -1*y;
Tsecond <= -1*z;
out_en <= 1'd1;
end
default:
begin
Tfirst <= 17'd0;
Tsecond <= 17'd0;
out_en <= 1'd0;
end
endcase
end
if(out_en)
begin
out_en <= 1'd0;
out_en2<= 1'd1;
end
if(out_en2)
begin
out_en2 <= 1'd0;
out_en3 <= 1'd1;
end
if(out_en3)
begin
out_en3 <= 1'd0;
$display("OUTPUT:");
$display(" Tfirst : %d",Tfirst);
$display(" Tsecond : %d",Tsecond);
$display(" Tzero : %d",Tzero);
end
end
if(Tfirst + Tsecond > Ts)
begin
//temp2 <= Ts*Tfirst;
//temp3 <= Ts*Tsecond;
Tfirst <= Ts*Tfirst/(Tfirst + Tsecond);
Tsecond <= Ts*Tsecond/(Tfirst + Tsecond);
end
end
endmodule
利用时间长度信息,计算具体的开关切换时刻。
// 计算逆变器信号改变的时间
// Type : synthesizable
// Standard: SystemVerilog 2005 (IEEE1800-2005)
// 功能: 利用时间长度信息,计算具体的开关切换时刻。
module Switch_time(
input wire clk, //时钟信号
input wire rstn, //复位信号
input wire in_en, //输入使能信号
input wire [3:0] n,
input wire signed [63:0] Tfirst,
input wire signed [63:0] Tsecond,
input wire signed [63:0] Tzero,
output reg signed [63:0] Tcm1, //三个逆变器的切换时间
output reg signed [63:0] Tcm2,
output reg signed [63:0] Tcm3,
output reg out_en, //输出使能信号
output reg out_en2 //延迟一拍
);
wire signed [63:0] Ta_wire;
wire signed [63:0] Tb_wire;
wire signed [63:0] Tc_wire;
assign Ta_wire = Tzero/2;
assign Tb_wire = Ta_wire + Tfirst/2;
assign Tc_wire = Tb_wire + Tsecond/2;
reg signed [63:0] Ta;
reg signed [63:0] Tb;
reg signed [63:0] Tc;
reg flag2;
always @(*)
begin
if(!rstn)
begin
Ta <= 64'd0;
Tb <= 64'd0;
Tc <= 64'd0;
end
else
begin
Ta <= Ta_wire;
Tb <= Tb_wire;
Tc <= Tc_wire;
end
if(out_en2)
begin
Ta <= 64'd0;
Tb <= 64'd0;
Tc <= 64'd0;
end
end
always @(posedge clk) //
begin
if(in_en)
begin
flag2 <= 1'd1;
$display("--------------------05 SVPWM--------------------");
$display(" ----------05-03 Switch Time---------- ");
$display("INPUT:");
$display(" Tfirst : %d",Tfirst);
$display(" Tsecond : %d",Tsecond);
$display(" Tzero : %d",Tzero);
end
if(!rstn)
begin
Tcm1 <= 64'd0;
Tcm2 <= 64'd0;
Tcm3 <= 64'd0;
flag2 <= 1'd0;
out_en <= 1'd0;
out_en2 <= 1'd0;
end
else
begin
if(flag2)
begin
// Ta <= Tzero/2;
// Tb <= Ta + Tfirst/2;
// Tc <= Tb + Tsecond/2;
case(n)
4'd1:begin
Tcm1 <= Tb;
Tcm2 <= Ta;
Tcm3 <= Tc;
out_en <= 1'd1;
end
4'd2:begin
Tcm1 <= Ta;
Tcm2 <= Tc;
Tcm3 <= Tb;
out_en <= 1'd1;
end
4'd3:begin
Tcm1 <= Ta;
Tcm2 <= Tb;
Tcm3 <= Tc;
out_en <= 1'd1;
end
4'd4:begin
Tcm1 <= Tc;
Tcm2 <= Tb;
Tcm3 <= Ta;
out_en <= 1'd1;
end
4'd5:begin
Tcm1 <= Tc;
Tcm2 <= Ta;
Tcm3 <= Tb;
out_en <= 1'd1;
end
4'd6:begin
Tcm1 <= Tb;
Tcm2 <= Tc;
Tcm3 <= Ta;
out_en <= 1'd1;
end
default:
begin
Tcm1 <= Tb;
Tcm2 <= Ta;
Tcm3 <= Tc;
out_en <= 1'd0;
end
endcase
end
if(out_en)
begin
out_en <= 1'd0;
flag2 <= 1'd0;
end
if(out_en)
begin
out_en2 <= 1'd1;
$display("OUTPUT:");
$display(" Tcm1 : %d",Tcm1);
$display(" Tcm2 : %d",Tcm2);
$display(" Tcm3 : %d",Tcm3);
end
if(out_en2)
begin
out_en2 <= 1'd0;
// Ta <= 17'd0;
// Tb <= 17'd0;
// Tc <= 17'd0;
flag2 <= 1'd0;
out_en <= 1'd0;
end
end
end
endmodule
产生三角波,便于确定当前所处的时刻。其中的变量CYCLE_NUM可以用来控制仿真的时候,SVPWM输出的具体循环数量。
// 三角波生成模块
// Type : synthesizable
// Standard: SystemVerilog 2005 (IEEE1800-2005)
// 功能: 产生三角波,便于确定当前所处的时刻。
module Tri_gener(
input wire clk, //输入时钟
input wire rst, //复位信号
input wire in_en, //输入使能信号
// output reg Ts_cp, //对PGFS输入时钟进行同步化后的时钟,提供给Ts累加的脉冲
output reg signed [11:0] Ts_cnt, //Ts的计数器,用来产生一个周期为Ts=2*Tp的三角波
output reg deta_clk //每次输出一个时钟,就给一个高电平
);
///三角波产生//
reg [16:0] adder; //Ts有关的相位累加器
reg Ts_dir; //Ts的计数器的计数方向
reg flag2;
reg signed [5:0] cycle_num; //计数完成了多少个周期
parameter Tp = 833; //开关周期的一半;实际是833个周期,是0-832,
// pfs= 10000; //频率控制输入,5000:7K,10000:15K,20000:30K
// 1 2 3 4 5 1 2 3 4 5
// 0 1 2 1 0 1 2 1 0
// 1 2 3 4 5 6 1 2 3 4 5
// 0 1 2 3 2 1 0 1 2 1 0
parameter CYCLE_NUM = 3;
always @(posedge clk)
begin
if(in_en)
begin
flag2 <= 1'd1;
end
if(!rst)
begin
Ts_cnt <= 12'd0;
Ts_dir <= 1'b1;
deta_clk <= 1'b0;
flag2 <= 1'd0;
cycle_num<= 1'd0;
end
else
begin
// if(cycle_num == CYCLE_NUM)
// flag2 <= 1'd0;
if(flag2)
begin
if(Ts_dir)
Ts_cnt <= Ts_cnt + 12'b1;
else
Ts_cnt <= Ts_cnt - 12'b1;
if( Ts_cnt == Tp-1 ) //注意是非阻塞赋值
begin
Ts_dir <= 1'b0;
end
if( Ts_cnt == 1 ) //注意是非阻塞赋值
begin
Ts_dir <= 1'b1;
end
if( Ts_cnt == 1 && ~Ts_dir)
begin
deta_clk <= 1'b1;
cycle_num <= cycle_num + 1'b1;
end
else
begin
deta_clk <= 1'b0;
end
end
end
end
endmodule
这个是对整体模块的测试代码,可以在里面设置Vα与Vβ的值,在Quartus中进行仿真验证。
// Verilog Test Bench template for design : my_SVPWM
`timescale 1 ps/ 1 ps
module my_SVPWM_vlg_tst();
// constants
// general purpose registers
reg clk;
reg rstn;
// reg in_en;
// test vector input registers
reg signed [15:0] Valpha;
reg signed [15:0] Vbeta;
// wires
// wire Cal_time_out_en;
// wire Jug_sec_in_en;
// wire Jug_sec_out_en;
// wire Switch_time_out_en;
// wire [16:0] Tcm1;
// wire [16:0] Tcm2;
// wire [16:0] Tcm3;
// wire [16:0] Tfirst;
// wire Tri_gener_out_en;
// wire signed [11:0] Ts_cnt;
// wire [16:0] Tsecond;
// wire [16:0] Tzero;
// wire [3:0] n;
wire Tim1_Ch1; // SVPWM处理后的结果Ch1
wire Tim1_Ch1N; // SVPWM处理后的结果Ch1N,(电路层已经和CH1连接)
wire Tim1_Ch2; // SVPWM处理后的结果Ch2
wire Tim1_Ch2N; // SVPWM处理后的结果Ch2N,(电路层已经和CH2连接)
wire Tim1_Ch3; // SVPWM处理后的结果Ch3
wire Tim1_Ch3N; // SVPWM处理后的结果Ch3N,(电路层已经和CH3连接)
// wire [3:0] sector;
// wire [16:0] x;
// wire [16:0] y;
// wire [16:0] z;
parameter half_cycle = 10;
// assign statements (if any)
// my_SVPWM i1 (
// // port map - connection between master ports and signals/registers
// .Cal_time_out_en(Cal_time_out_en),
// .Jug_sec_in_en(Jug_sec_in_en),
// .Jug_sec_out_en(Jug_sec_out_en),
// .Switch_time_out_en(Switch_time_out_en),
// .Tcm1(Tcm1),
// .Tcm2(Tcm2),
// .Tcm3(Tcm3),
// .Tfirst(Tfirst),
// .Tri_gener_out_en(Tri_gener_out_en),
// .Ts_cnt(Ts_cnt),
// .Tsecond(Tsecond),
// .Tzero(Tzero),
// .Valpha(Valpha),
// .Vbeta(Vbeta),
// .n(n),
// .pwm_a(pwm_a),
// .pwm_b(pwm_b),
// .pwm_c(pwm_c),
// .sector(sector),
// .x(x),
// .y(y),
// .z(z),
// .clk(clk),
// .rstn(rstn),
// .in_en(in_en)
// );
my_SVPWM i1 (
.clk ( clk ), //时钟信号
.rstn ( rstn ), //复位信号
.in_en ( ~rstn ), //系统的输入使能信号
.Valpha ( Valpha ), //Park逆变换的结果Vα
.Vbeta ( Vbeta ), //Park逆变换的结果Vβ
.pwm_a ( Tim1_Ch1 ), //SVPWM的输出1 PWM_a
.pwm_an ( Tim1_Ch1N ), //SVPWM的输出1 PWM_an
.pwm_b ( Tim1_Ch2 ), //SVPWM的输出2 PWM_b
.pwm_bn ( Tim1_Ch2N ), //SVPWM的输出2 PWM_bn
.pwm_c ( Tim1_Ch3 ), //SVPWM的输出3 PWM_c
.pwm_cn ( Tim1_Ch3N ) //SVPWM的输出3 PWM_cn
);
initial
begin
// code that executes only once
// insert code here --> begin
clk = 0;
forever begin
#half_cycle clk = 1;
#half_cycle clk = 0;
end
// --> end
$display("Running testbench");
end
initial
begin
rstn = 1;
#5 rstn = 0;
#10 rstn = 1;
end
initial
begin
Valpha = 16'd9830;
Vbeta = -1*16'd26214;
end
// initial
// begin
// in_en = 0;
// #90 in_en = 1;
// #20 in_en = 0;
// end
endmodule
已经通过Quartus进行了仿真验证,下面展示具体的仿真结果,并与MATLAB的结果进行对应,可以看到两者之间是完全吻合的,证明算法正确。
这里测试的例子是U_alpha_real = 3、U_beta_real = -8 时的调制结果~
具体的数据结果:
输出的波形结果:
来个整体的图:
再来个细节图:
可以看到是与MATLAB的输出结果是一致的。
值得一提的是,可能有读者会问,为什么XYZ的值,MATLAB和Verilog的计算结果刚好相差了一倍呢?这是因为问题出在了Ts上,在MATLAB中,使用的是具体的时间周期长度(计数值除以了50MHz的系统时钟),而Verilog中,由于SVPWM只是需要根据要求在固定周期内按次序输出调制波形,因此具体的周期长短不构成影响(占空比比例更为重要),因此就单纯使用Ts计数值的大小代替了具体的周期长度。
2022年10月20日更新:上面这段话是针对之前代码的Quartus仿真结果(针对的是上一篇博客中,MATLAB不考虑电压量化的情况,目前在今天更新之后由于我已经根据具体周期时常,使用了对应实际的Ts值1666,已经不存在这个问题了,大家可以忽略这段话~)
这就是本期的全部内容啦,如果你喜欢我的文章,不要忘了点赞收藏,分享给身边的朋友哇~
文章浏览阅读331次。第一部分:准备工作1 安装虚拟机2 安装centos73 安装JDK以上三步是准备工作,至此已经完成一台已安装JDK的主机第二部分:准备3台虚拟机以下所有工作最好都在root权限下操作1 克隆上面已经有一台虚拟机了,现在对master进行克隆,克隆出另外2台子机;1.1 进行克隆21.2 下一步1.3 下一步1.4 下一步1.5 根据子机需要,命名和安装路径1.6 ..._创建一个hadoop项目
文章浏览阅读1.7k次。心脏滴血漏洞HeartBleed CVE-2014-0160 是由heartbeat功能引入的,本文从深入码层面的分析该漏洞产生的原因_heartbleed代码分析
文章浏览阅读1.4k次。前言ofd是国家文档标准,其对标的文档格式是pdf。ofd文档是容器格式文件,ofd其实就是压缩包。将ofd文件后缀改为.zip,解压后可看到文件包含的内容。ofd文件分析工具下载:点我下载。ofd文件解压后,可以看到如下内容: 对于xml文件,可以用文本工具查看。但是对于印章文件(Seal.esl)、签名文件(SignedValue.dat)就无法查看其内容了。本人开发一款ofd内容查看器,..._signedvalue.dat
文章浏览阅读1.8w次,点赞29次,收藏313次。整体系统设计本设计主要是对ADC和DAC的使用,主要实现功能流程为:首先通过串口向FPGA发送控制信号,控制DAC芯片tlv5618进行DA装换,转换的数据存在ROM中,转换开始时读取ROM中数据进行读取转换。其次用按键控制adc128s052进行模数转换100次,模数转换数据存储到FIFO中,再从FIFO中读取数据通过串口输出显示在pc上。其整体系统框图如下:图1:FPGA数据采集系统框图从图中可以看出,该系统主要包括9个模块:串口接收模块、按键消抖模块、按键控制模块、ROM模块、D.._基于fpga的信息采集
文章浏览阅读2.5w次。1.背景错误信息:-- [http-nio-9904-exec-5] o.s.c.n.z.filters.post.SendErrorFilter : Error during filteringcom.netflix.zuul.exception.ZuulException: Forwarding error at org.springframework.cloud..._com.netflix.zuul.exception.zuulexception
文章浏览阅读358次。1.介绍图的相关概念 图是由顶点的有穷非空集和一个描述顶点之间关系-边(或者弧)的集合组成。通常,图中的数据元素被称为顶点,顶点间的关系用边表示,图通常用字母G表示,图的顶点通常用字母V表示,所以图可以定义为: G=(V,E)其中,V(G)是图中顶点的有穷非空集合,E(G)是V(G)中顶点的边的有穷集合1.1 无向图:图中任意两个顶点构成的边是没有方向的1.2 有向图:图中..._给定一个邻接矩阵未必能够造出一个图
文章浏览阅读321次。(十二)、WDS服务器安装通过前面的测试我们会发现,每次安装的时候需要加域光盘映像,这是一个比较麻烦的事情,试想一个上万个的公司,你天天带着一个光盘与光驱去给别人装系统,这将是一个多么痛苦的事情啊,有什么方法可以解决这个问题了?答案是肯定的,下面我们就来简单说一下。WDS服务器,它是Windows自带的一个免费的基于系统本身角色的一个功能,它主要提供一种简单、安全的通过网络快速、远程将Window..._doc server2012上通过wds+mdt无人值守部署win11系统.doc
文章浏览阅读219次。python–xlrd/xlwt/xlutilsxlrd只能读取,不能改,支持 xlsx和xls 格式xlwt只能改,不能读xlwt只能保存为.xls格式xlutils能将xlrd.Book转为xlwt.Workbook,从而得以在现有xls的基础上修改数据,并创建一个新的xls,实现修改xlrd打开文件import xlrdexcel=xlrd.open_workbook('E:/test.xlsx') 返回值为xlrd.book.Book对象,不能修改获取sheett_xlutils模块可以读xlsx吗
文章浏览阅读8.2w次,点赞267次,收藏656次。运行Selenium出现'WebDriver' object has no attribute 'find_element_by_id'或AttributeError: 'WebDriver' object has no attribute 'find_element_by_xpath'等定位元素代码错误,是因为selenium更新到了新的版本,以前的一些语法经过改动。..............._unresolved attribute reference 'find_element_by_id' for class 'webdriver
文章浏览阅读198次。一:模态窗口//父页面JSwindow.showModalDialog(ifrmehref, window, 'dialogWidth:550px;dialogHeight:150px;help:no;resizable:no;status:no');//子页面获取父页面DOM对象//window.showModalDialog的DOM对象var v=parentWin..._jquery获取父window下的dom对象
文章浏览阅读1.7w次,点赞15次,收藏129次。算法(algorithm)是解决一系列问题的清晰指令,也就是,能对一定规范的输入,在有限的时间内获得所要求的输出。 简单来说,算法就是解决一个问题的具体方法和步骤。算法是程序的灵 魂。二、算法的特征1.可行性 算法中执行的任何计算步骤都可以分解为基本可执行的操作步,即每个计算步都可以在有限时间里完成(也称之为有效性) 算法的每一步都要有确切的意义,不能有二义性。例如“增加x的值”,并没有说增加多少,计算机就无法执行明确的运算。 _算法
文章浏览阅读1.5k次,点赞18次,收藏26次。网络安全的标准和规范是网络安全领域的重要组成部分。它们为网络安全提供了技术依据,规定了网络安全的技术要求和操作方式,帮助我们构建安全的网络环境。下面,我们将详细介绍一些主要的网络安全标准和规范,以及它们在实际操作中的应用。_网络安全标准规范