单相逆变电源软件设计

单相逆变电源软件设计

一、题目要求

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=T10TU2dt得到交流有效值。

二、控制核心及环境配置

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.其他代码

单相逆变电源程序压缩包

总结

在用Tiva和FPGA进行开发的时候,要注意二者之间的通信问题,比如有时候通信通道会传一些不定值,可能换个通道就好了。在CCS中烧录程序时如果配置出错可能会导致程序无法烧录,务必要正确配置后再进行烧录,可以先了解一些配置的基础知识。

  • 4
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值