基于FPGA的PI环调节电压
- 硬件资源
- 实验过程
- 公式分析
- Verilog代码
- AD7606控制代码
- PI环公式模块
- TOP顶层模块
- 仿真分析
硬件资源
FPGA开发板
AD7606-AD采样模块
AD9767-DA输出模块
实验过程
默认DA输出初始电压-AD获取DA输入电压-FPGA负责根据目标值进行调节-最终DA输出电压稳定在目标电压附近-改变目标值DA输出随之改变
公式分析
%计算target和meas的误差
error = meas(AD)- target(我都会设置为1.0V)
%通过pi环计算ref值,系数定为:Kp=0.5 Ki=0.05
%先计算积分
integral = integral_prev + δerror
%然后计算需要送出去的值要增加减少多少
δu = Kp * error + Ki * integral
%然后计算出要送出去的值
u(DA_OUT2) = u_prev + δu
%限幅送出去的电压
u(DA_OUT2) = [0.8, 1.2]
Verilog代码
AD7606控制代码
`timescale 1ns / 1ps
module ad7606_if(input clk,input rst_n,input [15:0] ad_data, //ad7606 datainput ad_busy, //ad7606 busyinput first_data, //ad7606 first dataoutput [2:0] ad_os, //ad7606output reg ad_cs, //ad7606 AD csoutput reg ad_rd, //ad7606 AD data readoutput reg ad_reset, //ad7606 AD resetoutput reg ad_convstab, //ad7606 AD convert startoutput ad_data_valid,output reg ad_data_valid_temp,output reg [15:0] ad_ch1,output reg [15:0] ad_ch2,output reg [15:0] ad_ch3,output reg [15:0] ad_ch4,output reg [15:0] ad_ch5,output reg [15:0] ad_ch6,output reg [15:0] ad_ch7,output reg [15:0] ad_ch8,output reg [3:0] state
);reg [15:0] rst_cnt;
reg [5:0] i;
reg [15:0] start_count;
//reg [3:0] state;parameter IDLE=4'd0;
parameter AD_CONV=4'd1;
parameter Wait_1=4'd2;
parameter Wait_busy=4'd3;
parameter READ_CH1=4'd4;
parameter READ_CH2=4'd5;
parameter READ_CH3=4'd6;
parameter READ_CH4=4'd7;
parameter READ_CH5=4'd8;
parameter READ_CH6=4'd9;
parameter READ_CH7=4'd10;
parameter READ_CH8=4'd11;
parameter READ_DONE=4'd12;assign ad_os=3'b000;// oversample
assign ad_data_valid = state == READ_DONE ? 1'b1 : 1'b0;
always@(posedge clk or negedge rst_n)
beginif(rst_n == 1'b0)beginrst_cnt <= 16'd0;ad_reset <= 1'b0;endelse if(rst_cnt < 16'hffff)beginrst_cnt <= rst_cnt + 16'd1;ad_reset <= 1'b1;endelsead_reset <= 1'b0;
endalways@(posedge clk or negedge rst_n)
beginif(rst_n == 1'b0)beginad_data_valid_temp<=1'b0;endelse if(ad_data_valid ==1'b1)beginad_data_valid_temp<=1'b1;endelsead_data_valid_temp<=1'b0;
endalways@(posedge clk)
beginif(ad_reset==1'b1)beginstate <= IDLE;ad_ch1 <= 0;ad_ch2 <= 0;ad_ch3 <= 0;ad_ch4 <= 0;ad_ch5 <= 0;ad_ch6 <= 0;ad_ch7 <= 0;ad_ch8 <= 0;ad_cs <= 1'b1;ad_rd <= 1'b1;ad_convstab <= 1'b1;i <= 6'd0;start_count<=16'd0;endelsebegincase(state)IDLE:beginad_cs<=1'b1;ad_rd<=1'b1;ad_convstab<=1'b1;if(start_count==50) beginstart_count <= 16'd0;state<=AD_CONV;endelsestart_count<=start_count+1;endAD_CONV:beginif(i==2) begin //wait 2 clocki <= 6'd0;state<=Wait_1;ad_convstab<=1'b1;endelse begini <= i + 6'd1;ad_convstab<=1'b0; endendWait_1:beginif(i==5) begin //wait 5 clocki <= 6'd0;state<=Wait_busy;endelsei <= i + 6'd1;endWait_busy:beginif(ad_busy==1'b0) begin //wait busy lowi <= 6'd0;state<=READ_CH1;endendREAD_CH1:beginad_cs<=1'b0; //cs validif(i==3) beginad_rd<=1'b1;i <= 6'd0;ad_ch1<=ad_data; //read CH1state<=READ_CH2;endelse beginad_rd<=1'b0;i <= i + 6'd1;endendREAD_CH2:beginif(i==3) beginad_rd<=1'b1;i <= 6'd0;ad_ch2<=ad_data; //read CH2state<=READ_CH3;endelse beginad_rd<=1'b0;i <= i + 6'd1;endendREAD_CH3:beginif(i==3) beginad_rd<=1'b1;i <= 6'd0;ad_ch3<=ad_data; //read CH3state<=READ_CH4;endelse beginad_rd<=1'b0;i <= i + 6'd1;endendREAD_CH4: beginif(i==3) beginad_rd<=1'b1;i <= 6'd0;ad_ch4<=ad_data; //read CH4state<=READ_CH5;endelse beginad_rd<=1'b0;i <= i + 6'd1;endendREAD_CH5:beginif(i==3) beginad_rd<=1'b1;i <= 6'd0;ad_ch5<=ad_data; //read CH5state<=READ_CH6;endelse beginad_rd<=1'b0;i <= i + 6'd1;endendREAD_CH6:beginif(i==3) beginad_rd<=1'b1;i <= 6'd0;ad_ch6<=ad_data; //read CH6state<=READ_CH7;endelse beginad_rd<=1'b0;i <= i + 6'd1;endendREAD_CH7:beginif(i==3) beginad_rd<=1'b1;i <= 6'd0;ad_ch7<=ad_data; //read CH7state<=READ_CH8;endelse beginad_rd<=1'b0;i <= i + 6'd1;endendREAD_CH8:beginif(i==3) beginad_rd<=1'b1;i <= 6'd0;ad_ch8<=ad_data; //read CH8state<=READ_DONE;endelse beginad_rd<=1'b0;i <= i + 6'd1;endendREAD_DONE:beginad_rd<=1'b1;ad_cs<=1'b1;state<=IDLE;enddefault:state<=IDLE;endcaseendendendmodule
PI环公式模块
代码如下(示例):
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/11/26 12:06:29
// Design Name:
// Module Name: ad_calculate
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//module ad_calculate(input clk,input rst_n,input ad_data_valid,input ad_data_valid_temp,input signed [15:0] ad_ch1,input signed [15:0] ad_ch2,input signed [15:0] ad_ch3,output reg cal_mode,output wire flag_error,output [15:0] data_out //7863 - 5242);parameter v_1 = 16'd6578;parameter k_p = 16'd50;//*100parameter k_i = 16'd5;//*100reg ad_data_valid_temp3;reg ad_data_valid_temp4;reg ad_data_valid_temp2;reg signed [15:0] error ;reg signed [15:0] delta_error ;reg signed [15:0] error_prev ;reg signed [31:0] integral ;reg signed [31:0] integral_prev ;reg signed [31:0] delta_u_t ;reg signed [31:0] u_t ;reg signed [31:0] u_t_prev ;assign data_out=u_t[15:0];always @(posedge clk or negedge rst_n)beginif(!rst_n)begincal_mode<=1'd0;end else if(ad_data_valid==1'b1 && ad_ch1 > $signed('d16383) )begincal_mode<=1'd0;endelse if(ad_data_valid==1'b1 && ad_ch1< $signed('d16383))begincal_mode<=1'd1;endelse begincal_mode<=cal_mode;endendassign flag_error=((ad_ch2>>2)>(ad_ch3>>2))?1:0;always @(posedge clk or negedge rst_n)beginif(!rst_n)beginad_data_valid_temp2<=1'd0;ad_data_valid_temp3<=1'd0;ad_data_valid_temp4<=1'd0;end else beginad_data_valid_temp2<=ad_data_valid_temp;ad_data_valid_temp3<=ad_data_valid_temp2;ad_data_valid_temp4<=ad_data_valid_temp3;endend//计算error flag_error 1->ad_ch3>1.0v 0->ad_ch3<1.0v always @(posedge clk or negedge rst_n)beginif(!rst_n)beginerror<=16'd0;end else if(ad_data_valid==1'b1 )begin
// error<=ad_ch3-ad_ch2;error<=-(ad_ch2-ad_ch3);endelse beginerror<=error;endendalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginerror_prev<=16'd0;end else if (ad_data_valid_temp==1'b1 )beginerror_prev<=error;endendalways @(posedge clk or negedge rst_n)beginif(!rst_n)begindelta_error<=16'd0;end else if (ad_data_valid_temp==1'b1 )begindelta_error<=error-error_prev;endelse begindelta_error<=delta_error;endend// always @(posedge clk or negedge rst_n)begin
// if(!rst_n)begin
// integral_prev<=32'd0;
// end
// else begin
// integral_prev<=integral;
// end
// end// always @(posedge clk or negedge rst_n)begin
// if(!rst_n)begin
// integral<=32'd0;
// end
// else if(ad_data_valid_temp==1'b1 && integral < $signed('d150000))begin
// integral<=integral_prev+error;
// end
// else begin
// integral<=integral;
// end
// endalways @(posedge clk or negedge rst_n)beginif(!rst_n)begindelta_u_t<=32'd0;end else if(ad_data_valid_temp2==1'b1 && (error >$signed('d100) || error<$signed(-'d100)))begindelta_u_t<=(50*delta_error+5*error)/100;endelse if (ad_data_valid_temp4==1'b1 && (error >$signed('d100) || error<$signed(-'d100)))begindelta_u_t<=32'd0;endendalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginu_t<=32'd7863; //对应0.9Vend else if(ad_data_valid_temp3==1'b1 )beginu_t<=u_t_prev+delta_u_t;endelse beginu_t<=u_t;endendalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginu_t_prev<=32'd0;end else beginu_t_prev<=u_t;endendendmodule
TOP顶层模块
还例化了一个PLL IP核 -生成100m hz的ad_clk
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/11/30 11:10:40
// Design Name:
// Module Name: top
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module top(//system clocksinput sys_clk,input rst_n,input[15:0] ad7606_data, //ad7606 datainput ad7606_busy, //ad7606 busyinput ad7606_first_data, //ad7606 first dataoutput[2:0] ad7606_os, //ad7606output ad7606_cs, //ad7606 AD csoutput ad7606_rd, //ad7606 AD data readoutput ad7606_reset, //ad7606 AD resetoutput ad7606_convstab, //ad7606 AD convert startoutput da1_clk, //AD9767 CH1 clockoutput da1_wrt, //AD9767 CH1 enableoutput [13:0] da1_data, //AD9767 CH1 data outputoutput led,output adc_clk, output [3:0] state, output da2_clk, //AD9767 CH2 clockoutput da2_wrt, //AD9767 CH2 enableoutput [13:0] da2_data //AD9767 CH2 data output);
wire [15:0] data_out; //AD9767 CH2 data output
wire [13:0] da2_data_temp; //
wire ad_data_valid;
wire ad_data_valid_temp;
//wire adc_clk;
//wire [3:0] state ;
wire cal_mode; //wire adc_clk;
wire signed[15:0] ad_ch1;
wire signed[15:0] ad_ch2;
wire signed[15:0] ad_ch3;
wire signed[15:0] ad_ch4;
wire signed[15:0] ad_ch5;
wire signed[15:0] ad_ch6;
wire signed[15:0] ad_ch7;
wire signed[15:0] ad_ch8;
//wire [3:0] state;
wire [31:0] ad_v;
wire [31:0] da_add;
assign ad_v=da2_data_temp*152;
assign da_add=ad_v/610;
assign da2_data_temp=data_out[13:0];
assign da2_data=da_add+$unsigned('d8192);
assign da1_clk=adc_clk;
assign da1_wrt=adc_clk;
assign da1_data=(cal_mode==1'b0)?14'h2000:14'h2666;assign da2_clk=adc_clk;
assign da2_wrt=adc_clk;
assign led=cal_mode;
adc_pll u_adc_pll_m0(.clk_in1 (sys_clk ),.clk_out1 (adc_clk ),.reset (1'b0 ),.locked ( ));ad_calculate u_ad_calculate(.clk(adc_clk),.rst_n(rst_n),.ad_data_valid(ad_data_valid),.ad_data_valid_temp(ad_data_valid_temp),.ad_ch1(ad_ch1),.ad_ch2(ad_ch2),.ad_ch3(ad_ch3),.cal_mode(cal_mode),.flag_error(),.data_out(data_out));ad7606_if ad7606_if_m0(.clk (adc_clk ),.rst_n (rst_n ),.ad_data (ad7606_data ), //ad7606 data.ad_busy (ad7606_busy ), //ad7606 busy.first_data (ad7606_first_data ), //ad7606 first data.ad_os (ad7606_os ), //ad7606.ad_cs (ad7606_cs ), //ad7606 AD cs.ad_rd (ad7606_rd ), //ad7606 AD data read.ad_reset (ad7606_reset ), //ad7606 AD reset.ad_convstab (ad7606_convstab ), //ad7606 AD convert start.ad_data_valid (ad_data_valid ),.ad_data_valid_temp (ad_data_valid_temp ),.ad_ch1 (ad_ch1 ),.ad_ch2 (ad_ch2 ),.ad_ch3 (ad_ch3 ),.ad_ch4 (ad_ch4 ),.ad_ch5 (ad_ch5 ),.ad_ch6 (ad_ch6 ),.ad_ch7 (ad_ch7 ),.ad_ch8 (ad_ch8 ),.state (state )
);endmodule
仿真分析
仿真注意:
AD7606数字量范围与AD9767不同,注意数字量之间要基于基础电压单位进行切换,避免仿真跑飞