单相逆变电源软件设计
一、题目要求
1.题目
要求设计并制作输入为15V直流电压,输出为10V正弦交流电压的单相逆变电源。
2.系统总体框图
软件思路:
FPGA:利用Matlab生成正弦及三角波查找表,在Quartus中使用rom查表产生正弦波与三角波,比较二者的大小,正弦波大于三角波为“1”,正弦波小于三角波为“0”(双极性调制),产生两路互补的SPWM波,经由逆变电路及滤波电路最终可以得到正弦交变电压,通过改变正弦波的幅值可以改变最终输出电压的大小。
单片机:接收频率设定并传给FPGA,显示当前状态,进行AD采样数字信号到具体值的转换,PID算法稳压。
AD采样:采用ADS8688进行采样,通过积分法
U
r
m
s
2
=
1
T
∫
0
T
∣
U
∣
2
d
t
U_{rms}^2=\frac{1}{T}{\int_{0}^{T}}\left| U \right| ^ 2dt
Urms2=T1∫0T∣U∣2dt得到交流有效值。
二、控制核心及环境配置
tiva系列单片机:TM4C123GH6PM
Cyclone IVE 系列FPGA:EP4CE6E22C8
IDE:Code Composer Studio 以及 Quartus 17.0
1.CCS环境配置
Code Composer Studio配置:
2.Quartus环境配置
Quartus 17.0配置:
三、软件核心功能
1.产生SPWM波
方案一:单极性调制
方案二:双极性调制
这里我使用的是方案二,双极性调制产生正弦波。
首先利用Matlab产生正弦查找表以及三角波查找表。
产生正弦查找表的代码如下:
ADDR_WIDTH=12;
DATA_WIDTH=16;
depth=2^ADDR_WIDTH;
x=ceil(((2^DATA_WIDTH/2-1)*sin(0:pi*2/depth:2*pi)+2^DATA_WIDTH/2));
fid=fopen('sinrom1.mif','w');
fprintf(fid,'width=%d;\n',DATA_WIDTH);
fprintf(fid,'depth=%d;\n',depth);
fprintf(fid,'address_radix=uns;\n');
fprintf(fid,'data_radix=uns;\n');
fprintf(fid,'Content Begin\n');
for(k=1:depth)
fprintf(fid,'%d:%d;\n',k-1,x(k));
end
fprintf(fid,'end;');
产生三角波查找表的代码如下:
ADDR_WIDTH=12;
DATA_WIDTH=16;
depth=2^ADDR_WIDTH;
x=ceil(2*(2^DATA_WIDTH/2-1)*sawtooth(0:pi*2/depth:2*pi)+2^DATA_WIDTH - 1);
y=ceil(-2*(2^DATA_WIDTH/2-1)*sawtooth(0:pi*2/depth:2*pi)+2^DATA_WIDTH - 1);
fid=fopen('trirom1.mif','w');
fprintf(fid,'width=%d;\n',DATA_WIDTH);
fprintf(fid,'depth=%d;\n',depth);
fprintf(fid,'address_radix=uns;\n');
fprintf(fid,'data_radix=uns;\n');
fprintf(fid,'Content Begin\n');
for(k=1:depth/2)
fprintf(fid,'%d:%d;\n',k-1,x(k));
end
for(j=depth/2+1:depth)
fprintf(fid,'%d:%d;\n',j-1,y(j));
end
fprintf(fid,'end;');
然后在Quartus中利用IP核生成rom查找表实例。
一路Next直到下图所示页面,如图所示选中Matlab生成的.mif文件作为查找表。
最后勾选生成实例文件即可。
生成正弦波和三角波后,比较生成互补的SPWM波,注意要设置死区!
生成SPWM波代码如下:
always @(posedge clk, negedge rst_n)
begin
if(!rst_n)
begin
spwm1_tmp <= 0;
spwm2_tmp <= 0;
cnt1 <= 0;
cnt2 <= 0;
end
else
begin
//spwm1
if(sin_sig >= tri_sig) //延后一段时间再拉高
begin
if(cnt1 >= dead) //cnt1 == dead
begin
spwm1_tmp <= 1;
cnt1 <= 0;
end
else if(spwm1_tmp != 1) //spwm1还没被拉高,则计数
cnt1 <= cnt1 + 1;
else;
end
else
begin spwm1_tmp <= 0; end
//spwm2
if(sin_sig >= tri_sig)
begin spwm2_tmp <= 0; end
else //延后一段时间再拉高
begin
if(cnt2 >= dead) //cnt == dead
begin
spwm2_tmp <= 1;
cnt2 <= 0;
end
else if(spwm2_tmp != 1) //spwm2还没被拉高,则计数
cnt2 <= cnt2 + 1;
else;
end
end
end
2.PID算法调控输出电压
调控输出电压可以通过改变正弦波的调制度来实现。这里我使用ADS8688芯片进行交流采样,计算获取交流电压的有效值(代码放在其他代码的压缩包中)。
PID调控代码如下:
void PID(void)
{
double deltK;
deltV[2] = Vset - Vrms;
deltK = Kp * (deltV[2] - deltV[1]) + Ki * deltV[2] + Kd * (deltV[2] - 2 * deltV[1] - deltV[0]);
deltV[0] = deltV[1];
deltV[1] = deltV[2];
K = K + (int)deltK;
if(K > KMAX)
K = KMAX;
else;
if(K < KMIN)
K = KMIN;
else;
}
让输出电压稳定的关键一在PID参数选取的适当,二在AD采样准确稳定。
3.顶层设计
Verilog顶层文件:
module DC_AC
(
input wire clk, // 50MHz
input wire rst_n,
input wire [7:0]ADDR,
input wire RD,WR,
input wire SDO, // ADS8688 串行数字输出
//input wire [15:0] K,
//input wire [23:0] fs,
output wire spwm1_sig,
output wire spwm2_sig,
output wire irq,
output wire RST, // ADS8688 复位信号
output wire CS, // ADS8688 片选信号(低电平有效)
output wire SCLK, // ADS8688 串行时钟
output wire SDI, // ADS8688 串行数字输入
output wire alarm,
inout wire [15:0]DATA,
inout wire [3:0]KEY_H,KEY_V
);
wire cs0,cs1,cs2,cs3,cs4,cs5,cs6;
wire software_rst_n;
wire [7:0] rddat0; //mcu读键盘值 CS0
wire [15:0] wrdat0;
wire [15:0] wrdat1; //K CS5
wire [15:0] wrdat2;
wire [15:0] wrdat3; //fs CS7
wire datacs0,datacs1;
wire [3:0]addr_ad;
wire [23:0] fc = 24'd17_000; //载波频率17k
wire [23:0] fs; //调制频率
wire [15:0] K; //调制度
wire [15:0] sin_sig;
wire [15:0] tri_sig;
wire clk_50Hz;
wire [15:0] outRMS0; //CS1
wire [15:0] outRMS1; //CS2
assign fs = wrdat3;
assign K = wrdat1;
assign alarm = (K>500)?0:1;
Sin Sin_inst
(
.clk(clk) , // input clk_sig
.rst_n(rst_n) , // input rst_n_sig
.fre(fs) , // input [23:0] fre_sig
.sin_sig(sin_sig) // output [15:0] sin_sig_sig
);
Tripul Tripul_inst
(
.clk(clk) , // input clk_sig
.rst_n(rst_n) , // input rst_n_sig
.fre(fc) , // input [23:0] fre_sig
.tri_sig(tri_sig) // output [15:0] tri_sig_sig
);
SPWM SPWM_inst
(
.clk(clk) , // input clk_sig
.rst_n(rst_n) , // input rst_n_sig
.sin_input(sin_sig) , // input [15:0] sin_input_sig
.tri_sig(tri_sig) , // input [15:0] tri_sig_sig
.K(K) ,
.spwm1_sig(spwm1_sig) , // output spwm1_sig_sig
.spwm2_sig(spwm2_sig) // output spwm2_sig_sig
);
clkdiv clkdiv_inst
(
.clk(clk) , // input clk_sig
.rst_n(rst_n) , // input rst_n_sig
.div(32'd1_000_000) , // input [31:0] div_sig
.clkout(clk_50Hz) // output clkout_sig
);
BUS BUS_inst
(
.clk(clk) , // input clk_sig
.rst_n(rst_n) , // input rst_n_sig
.ADDR(ADDR) , // input [7:0] ADDR_sig
.RD(RD) , // input RD_sig
.WR(WR) , // input WR_sig
.DATA(DATA) , // inout [7:0] DATA_sig
.software_rst_n(software_rst_n) , // output software_rst_n_sig
.cs0(cs0) , // output cs0_sig
.cs1(cs1) , // output cs1_sig
.cs2(cs2) , // output cs2_sig
.cs3(cs3) , // output cs3_sig
.cs4(cs4) , // output cs4_sig
.cs5(cs4) , // output cs5_sig
.cs6(cs4) , // output cs6_sig
.rddat0(rddat0) , // input [7:0] rddat0_sig
.rddat1(outRMS0) , // input [7:0] rddat1_sig
.rddat2(outRMS1) , // input [7:0] rddat2_sig
// .rddat3(rddat3) , // input [7:0] rddat3_sig
.wrdat0(wrdat0) , // output [7:0] wrdat0_sig
.wrdat1(wrdat1) , // output [7:0] wrdat1_sig
.wrdat2(wrdat2) , // output [7:0] wrdat2_sig
.wrdat3(wrdat3) , // output [7:0] wrdat3_sig
.addr_ad(addr_ad) , //output reg [3:0]addr_ad
.datacs0(datacs0) , //output reg datacs0
.datacs1(datacs1) //output reg datacs1
);
KEY KEY_inst
(
.clk(clk) , // input clk_sig
.rst_n(rst_n) , // input rst_n_sig
.rddat(rddat0) , // output [7:0] rddat_sig
.irq(irq) , // output irq_sig
.cs(cs0) , // input cs_sig
.KEY_H(KEY_H) , // inout [3:0] KEY_H_sig
.KEY_V(KEY_V) // inout [3:0] KEY_V_sig
);
ads8688 ads8688_inst
(
.clk_50M(clk) , // input clk_50M_sig
.rst_n(rst_n) , // input rst_n_sig
.addr(addr_ad) , // input [3:0] addr_sig
.RD(RD) , // input RD_sig
.datacs0(datacs0) , // input datacs0_sig
.datacs1(datacs1) , // input datacs1_sig
.clk_50Hz(clk_50Hz) , // input clk_50Hz_sig
.RST(RST) , // output RST_sig
.CS(CS) , // output CS_sig
.SCLK(SCLK) , // output SCLK_sig
.SDI(SDI) , // output SDI_sig
.SDO(SDO) , // input SDO_sig
.outRMS0(outRMS0) , // output [15:0] outRMS0_sig
.outRMS1(outRMS1) // output [15:0] outRMS1_sig
// .Vc(Vc)
);
endmodule
CCS中的main
函数:
#include "common.h"
#include "bus_fpga.h"
#include "LCD12864_rom_enable.h"
#include "blue.h"
#include <math.h>
/**
* main.c
* 1.7增加了附加功能:1、按键设置 2、过流保护灯亮 3、蜂鸣器
*/
const int FMIN = 1;
const int FMAX = 999;
const double FULL = 10.24;
const double Vset = 10;
const int KMAX = 990;
const int KMIN = 900;
char keydat;
int a;
int fs = 50;
int K = 900; //K的精度为1/1000
double Vrms = 0;
double Irms = 0;
double deltV[3] = {0};
float Kp = 45; //65 30 60; 38 35
float Ki = 10; //27 16 24; 17
float Kd = -12; //-10; -25 -20 -25
double V_list[5] = {0};
double I_list[5] = {0};
double V_sum;
double I_sum;
unsigned int j;
int overFlag = 0;
int fs_tmp[3] = {-1,-1,-1};
void mcuInit(void);
void getKey(void);
void getAD(int cnt);
void overCurrent(void);
void keyResponse(void); //无用
void changeFreq(void);
void PID(void);
void showPID(void); //无用
void ctrlPID(void); //无用
double change_voltage(unsigned int AD_value,double FS);
void showStatus(void);
void WRFPGA(void);
int datalen(int data);
void __key_delay(void);
int main(void)
{
mcuInit();
initialize_uart();
while(1)
{
V_sum = 0;
I_sum = 0;
for(j=0;j<5;j++){ //采样5次取平均值
__key_delay();
getKey();
changeFreq();
// getStringFVT();
// updateKey(&a);
getAD(j);
PID();
showStatus();
WRFPGA();
V_sum = V_sum + V_list[j];
I_sum = I_sum + I_list[j];
}
Vrms = V_sum / 5 * 7.80435435; // * 7.810155
Irms = I_sum / 5 * 0.47501069;
PID();
overCurrent();
}
}
四、其他
1.FPGA生成固化文件
FPGA本身并没有存储程序的功能,需要依靠外部flash芯片来实现程序的固化。以下两种方法分别介绍了用AS口和JTAG口固化程序。
方法一:生成.pof文件
step1:从Assignments进入Device。
step2:选择Device and Options。
step3:按照下图更改选项,注意这里的Device要看你FPGA上的flash芯片型号。
step4:全编译一下即可得到.pof文件。第一次烧录需要 Add Device 和 Add File。
方法二:生成.jic文件
step1:在File里面找到Convert Programming Files。
step2:选择生成.jic文件,同时选择flash芯片型号。
step3:往下翻找到Flash Loader添加设备,添加对应FPGA型号。
step4:选择SOF Data,添加.sof文件。
step5:点击Generate再重新全编译一遍即可生成.jic文件,用JTAG口,在烧录时Add File中选中相应的.jic文件即可。
烧录后,FPGA重新上电即成功固化程序。
2.其他代码
见单相逆变电源程序压缩包。文章来源:https://www.toymoban.com/news/detail-625941.html
总结
在用Tiva和FPGA进行开发的时候,要注意二者之间的通信问题,比如有时候通信通道会传一些不定值,可能换个通道就好了。在CCS中烧录程序时如果配置出错可能会导致程序无法烧录,务必要正确配置后再进行烧录,可以先了解一些配置的基础知识。文章来源地址https://www.toymoban.com/news/detail-625941.html
到了这里,关于单相逆变电源软件设计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!