SPWM学习
1、基本介绍:
正弦脉宽调制法(Sine Pulse Width Modulation , SPWM)是将每一个正弦周期内的多个脉冲作自然或者规律的宽度调制,使其依次调制出相当于正弦函数值的相位角核面积等效于正弦波的脉冲序列,形成等幅不等宽的正弦化电流输出。其中每周基波(正弦调制波)与所含调制输出的脉冲总数之比即为载波比。
2、原理简介:
SPWM信号生成的原理是根据正弦波的周期性特征,提供调整脉冲宽度的方式来模拟正弦波信号。SPWM信号的频率由基准频率控制,其幅值则由宽度调制比决定。当宽度调制比等于1时,输出信号的幅值达到最大值;当宽度调制比等于0时,输出信号的幅值为0。
!!!!简单来说就是正弦波的幅值决定了脉冲宽度。这里的解释详见百度百科,放个链接正弦脉宽调制_百度百科 (baidu.com)
3、一种SPWM生成方法:
可以采用的方法分为软件与硬件两种;
先来说一说硬件上的方法:可以用模拟电路构成三角波载波和正弦调制波发生电路,用比较器来确定它们的交点,在交点时刻对功率开关器件的通断进行控制,就可以生成SPWM波形。但这种模拟电路结构复杂,难以实现精确的控制。
再来说一说软件上实现的方法:使用DDS存储三角波核正弦波的波形文件,三角载波的频率为正弦基波频率的十倍以上,将高频三角载波与低频正弦波进行调制,通过两者的幅值大小进行对比,进行SPWM信号的控制,三角波大于正弦波时,SPWM信号为0,反之,为1,产生了不规则占空比的下面的SPWM波形。
4、具体步骤
4.1、生成正弦波coe文件
使用matlab进行coe文件的生成,coe文件是xilinx的fpga一种rom波形储存文件,Altera的文件格式为mif文件,类似的,这里由于本人所使用的是xilinx系列的FPGA因此只演示coe文件的生成,具体其他不懂的,可以去百度或者看看其他帖子。
clc;
close all;
clear;
w0 = 400e3;
w1 = 25e3;
t = 0:2*pi/(2048-1):2*pi;%时间长度为2Π,取了2^11个点
y0 = round(1000*sawtooth(16*t,0.5))+1000;%%triangle wave
y1 = round(1000*sin(t))+1000;%%sine wave
l1 = length(t);
plot(t,y0);
hold on
plot(t,y1);
%%生成文件三角波coe文件
fid1=fopen('triwave.coe','w+');
fprintf(fid1,'memory_initialization_radix = 16;\n');
fprintf(fid1,'memory_initialization_vector =\n');
for i=1:l1
fprintf(fid1,'%x',y0(i));
fprintf(fid1,',\n');
end
%%生成文件正弦波coe文件
fid1=fopen('sinwave.coe','w+');
fprintf(fid1,'memory_initialization_radix = 16;\n');
fprintf(fid1,'memory_initialization_vector =\n');
for i=1:l1
fprintf(fid1,'%x',y1(i));
fprintf(fid1,',\n');
end
这里演示的三角波的频率为400khz,正弦波的频率为25khz,三角波是正弦波的十六倍频。为了读取的方便,统一取2^11个点,也就是2048,因此后面地址位宽为11bit,深度为2048。
生成的数据分别是一个周期的正弦波采样和十六个周期的三角波采样,正弦波的周期是三角波的十六倍。
生成的coe文件可以导入rom ip核,如果不确定生成的coe文件是否有问题,可以在matlab中查看。
备注一下,生成的coe文件中数据不要存在小数等,rom ip核会报错,可以尝试在三角波以及正弦波函数进行适当的放缩。
4.2、配置ROM的IP核
1、首先先建立好工程项目,vivado新建工程就不多说了。
2、点击IP Catalog
3、搜索ROM,点击Block Memory Generator
4、根据查看到的波形coe文件,且最大值为2000,二进制位数为10位,转换为16进制保存位3位,所以数据宽度width为3个16进制数,1个16进制数4位,共12位;共2050行,减去两行声明,共2048行,深度depth位2048。(但是我这里只用了11位也够了)
5、选择sin的coe文件,导入
6、点击 ok,点击 Generate 生成 ip 核
7、三角波也是亦然
4.3、编写verilog代码
SPWM.v
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/08/08 15:30:25
// Design Name:
// Module Name: SPWM
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module SPWM(
sys_clk,
sys_rst,
en,
spwm_r,
spwm_l
);
input sys_clk,sys_rst,en;//定义输入信号如时钟,复位,使能;
output spwm_l,spwm_r;//定义了SPWM输出信号
//中间信号定义
reg [10:0] addr_sin;
reg [10:0] addr_tri;
reg [10:0] data_sin_r;
reg [10:0] data_tri_r;
wire [10:0] data_sin;
wire [10:0] data_tri;
reg compare_sult;
ip_sin rom_sin_uut (
.clka(sys_clk), // input wire clka
// .wea(wea), // input wire [0 : 0] wea
.addra(addr_sin), // input wire [10 : 0] addra
// .dina(dina), // input wire [10 : 0] dina
.douta(data_sin) // output wire [10 : 0] douta
);
ip_tri rom_tri_uut (
.clka(sys_clk), // input wire clka
// .wea(wea), // input wire [0 : 0] wea
.addra(addr_tri), // input wire [10 : 0] addra
// .dina(dina), // input wire [10 : 0] dina
.douta(data_tri) // output wire [10 : 0] douta
);
//使用一个计数器进行地址轮询
always@(posedge sys_clk or negedge sys_rst)
begin
if(!sys_rst)
addr_sin <= 16'd0;
else if(addr_sin == 16'd2047)
addr_sin <= 16'd0;
else
addr_sin = addr_sin + 16'd1;
end
//使用一个计数器进行地址轮询
always@(posedge sys_clk or negedge sys_rst)
begin
if(!sys_rst)
addr_tri <= 16'd0;
else if(addr_tri == 16'd2047)
addr_tri <= 16'd0;
else
addr_tri = addr_tri + 16'd1;
end
always@(posedge sys_clk)
begin
if(en == 0)
compare_sult <= 0;
else
compare_sult <= (data_sin_r >= data_tri_r)?1:0;
end
always@(posedge sys_clk)
begin
data_sin_r <=data_sin;
data_tri_r <=data_tri;
end
assign spwm_r = compare_sult;
assign spwm_l = ~spwm_r;
endmodule
testbench_spwm.v
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/08/08 16:53:27
// Design Name:
// Module Name: testbench_spwm
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module testbench_spwm(
);
reg sys_clk=0;
reg en;
reg sys_rst;
wire spwm_r;
wire spwm_l;
initial begin
#10
en = 1;
sys_rst =1;
#100
sys_rst = 0;
#10
sys_rst = 1;
#10
en = 0;
#150
en = 1;
end
always #5 sys_clk <= ~sys_clk;
SPWM spwm_uut(
.sys_clk(sys_clk),
.sys_rst(sys_rst),
.en(en),
.spwm_r(spwm_r),
.spwm_l(spwm_l)
);
endmodule
4.4、仿真结果
现在只是做了仿真,后续应该会有上板测试。
参考资料: