基于FPGA的PI环调节电压

基于FPGA的PI环调节电压


硬件资源

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控制代码

c 复制代码
`timescale 1ns / 1ps
module ad7606_if(
	input                        clk,
	input                        rst_n,
	input [15:0]                 ad_data,             //ad7606 data
	input                        ad_busy,             //ad7606 busy
	input                        first_data,          //ad7606 first data
	output [2:0]                 ad_os,               //ad7606
	output reg                   ad_cs,               //ad7606 AD cs
	output reg                   ad_rd,               //ad7606 AD data read
	output reg                   ad_reset,            //ad7606 AD reset
	output reg                   ad_convstab,         //ad7606 AD convert start
	output                       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)
begin
	if(rst_n == 1'b0)
	begin
		rst_cnt <= 16'd0;
		ad_reset <= 1'b0;
	end
	else if(rst_cnt < 16'hffff)
	begin
		rst_cnt <= rst_cnt + 16'd1;
		ad_reset <= 1'b1;
	end
	else
		ad_reset <= 1'b0;
end

always@(posedge clk or negedge rst_n)
begin
	if(rst_n == 1'b0)
	begin
		ad_data_valid_temp<=1'b0;
	end
	else if(ad_data_valid ==1'b1)
	begin
		ad_data_valid_temp<=1'b1;
	end
	else
		ad_data_valid_temp<=1'b0;
end

always@(posedge clk)
begin
	if(ad_reset==1'b1)
	begin
		state <= 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;
	end
	else
	begin
		case(state)
			IDLE:
			begin
				ad_cs<=1'b1;
				ad_rd<=1'b1;
				ad_convstab<=1'b1;
				if(start_count==50) begin
					start_count <= 16'd0;
					state<=AD_CONV;
				end
				else
					start_count<=start_count+1;
			end
			AD_CONV:
			begin
				if(i==2) 
				begin                        //wait 2 clock
					i <= 6'd0;
					state<=Wait_1;
					ad_convstab<=1'b1;
				end
				else 
				begin
					i <= i + 6'd1;
					ad_convstab<=1'b0;       
				end
			end
			Wait_1:
			begin
				if(i==5) 
				begin                           //wait 5 clock
					i <= 6'd0;
					state<=Wait_busy;
				end
				else
					i <= i + 6'd1;
			end
			Wait_busy:
			begin
				if(ad_busy==1'b0) 
				begin                    //wait busy low
					i <= 6'd0;
					state<=READ_CH1;
				end
			end
			READ_CH1:
			begin
				ad_cs<=1'b0;                              //cs valid
				if(i==3) 
				begin
					ad_rd<=1'b1;
					i <= 6'd0;
					ad_ch1<=ad_data;                        //read CH1
					state<=READ_CH2;
				end
				else 
				begin
					ad_rd<=1'b0;
					i <= i + 6'd1;
				end
			end
			READ_CH2:
			begin
				if(i==3) 
				begin
					ad_rd<=1'b1;
					i <= 6'd0;
					ad_ch2<=ad_data;                        //read CH2
					state<=READ_CH3;
				end
				else 
				begin
					ad_rd<=1'b0;
					i <= i + 6'd1;
				end
			end
			READ_CH3:
			begin
				if(i==3) 
				begin
					ad_rd<=1'b1;
					i <= 6'd0;
					ad_ch3<=ad_data;                        //read CH3
					state<=READ_CH4;
				end
				else 
				begin
					ad_rd<=1'b0;
					i <= i + 6'd1;
				end
			end
			READ_CH4: 
			begin
				if(i==3) 
				begin
					ad_rd<=1'b1;
					i <= 6'd0;
					ad_ch4<=ad_data;                        //read CH4
					state<=READ_CH5;
				end
				else 
				begin
					ad_rd<=1'b0;
					i <= i + 6'd1;
				end
			end
			READ_CH5:
			begin
				if(i==3) 
				begin
					ad_rd<=1'b1;
					i <= 6'd0;
					ad_ch5<=ad_data;                        //read CH5
					state<=READ_CH6;
				end
				else 
				begin
					ad_rd<=1'b0;
					i <= i + 6'd1;
				end
			end
			READ_CH6:
			begin
				if(i==3) 
				begin
					ad_rd<=1'b1;
					i <= 6'd0;
					ad_ch6<=ad_data;                        //read CH6
					state<=READ_CH7;
				end
				else 
				begin
					ad_rd<=1'b0;
					i <= i + 6'd1;
				end
			end
			READ_CH7:
			begin
				if(i==3) 
				begin
					ad_rd<=1'b1;
					i <= 6'd0;
					ad_ch7<=ad_data;                        //read CH7
					state<=READ_CH8;
				end
				else 
				begin
					ad_rd<=1'b0;
					i <= i + 6'd1;
				end
			end
			READ_CH8:
			begin
				if(i==3) 
				begin
					ad_rd<=1'b1;
					i <= 6'd0;
					ad_ch8<=ad_data;                        //read CH8
					state<=READ_DONE;
				end
				else 
				begin
					ad_rd<=1'b0;
					i <= i + 6'd1;
				end
			end
			READ_DONE:
			begin
				ad_rd<=1'b1;
				ad_cs<=1'b1;
				state<=IDLE;
			end
			default:
				state<=IDLE;
		 endcase
	end

 end

endmodule

PI环公式模块

代码如下(示例):

c 复制代码
`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;//*100
    parameter k_i = 16'd5;//*100
    
    reg        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)begin
            if(!rst_n)begin
                cal_mode<=1'd0;
            end 
            else if(ad_data_valid==1'b1 && ad_ch1 > $signed('d16383) )begin
                cal_mode<=1'd0;
            end
            else if(ad_data_valid==1'b1 && ad_ch1< $signed('d16383))begin
                cal_mode<=1'd1;
            end
            else begin
                cal_mode<=cal_mode;
            end
        end
    assign flag_error=((ad_ch2>>2)>(ad_ch3>>2))?1:0;
   always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            ad_data_valid_temp2<=1'd0;
            ad_data_valid_temp3<=1'd0;
            ad_data_valid_temp4<=1'd0;
        end 
        else begin
            ad_data_valid_temp2<=ad_data_valid_temp;
            ad_data_valid_temp3<=ad_data_valid_temp2;
            ad_data_valid_temp4<=ad_data_valid_temp3;
        end
    end
    
    //计算error  flag_error 1->ad_ch3>1.0v 0->ad_ch3<1.0v 
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            error<=16'd0;
        end 
        else if(ad_data_valid==1'b1 )begin
//            error<=ad_ch3-ad_ch2;
              error<=-(ad_ch2-ad_ch3);
        end
        else begin
            error<=error;
        end
    end
        always @(posedge clk or negedge rst_n)begin
            if(!rst_n)begin
                error_prev<=16'd0;
            end 
            else if (ad_data_valid_temp==1'b1 )begin
                error_prev<=error;
            end
         end
         
         always @(posedge clk or negedge rst_n)begin
                     if(!rst_n)begin
                         delta_error<=16'd0;
                     end 
                     else if (ad_data_valid_temp==1'b1 )begin
                         delta_error<=error-error_prev;
                     end
                     else begin
                         delta_error<=delta_error;
                     end
                  end
    
    
//    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
//    end
    
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            delta_u_t<=32'd0;
        end 
        else if(ad_data_valid_temp2==1'b1 && (error >$signed('d100) || error<$signed(-'d100)))begin
            delta_u_t<=(50*delta_error+5*error)/100;
        end
        else if (ad_data_valid_temp4==1'b1 && (error >$signed('d100) || error<$signed(-'d100)))begin
            delta_u_t<=32'd0;
        end
    end
    
    always @(posedge clk or negedge rst_n)begin
                if(!rst_n)begin
                    u_t<=32'd7863; //对应0.9V
                end 
                else if(ad_data_valid_temp3==1'b1 )begin
                    u_t<=u_t_prev+delta_u_t;
                end
                else begin
                    u_t<=u_t;
                end
            end
        
    
    always @(posedge clk or negedge rst_n)begin
            if(!rst_n)begin
                u_t_prev<=32'd0;
            end 
            else begin
                u_t_prev<=u_t;
            end

        end
    
    
endmodule

TOP顶层模块

还例化了一个PLL IP核 -生成100m hz的ad_clk

c 复制代码
`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 clocks
	input                       sys_clk,
	input                       rst_n,
	input[15:0]                 ad7606_data,             //ad7606 data
	input                       ad7606_busy,             //ad7606 busy
	input                       ad7606_first_data,       //ad7606 first data

	
	output[2:0]                 ad7606_os,               //ad7606
	output                      ad7606_cs,               //ad7606 AD cs
	output                      ad7606_rd,               //ad7606 AD data read
	output                      ad7606_reset,            //ad7606 AD reset
	output                      ad7606_convstab,          //ad7606 AD convert start
	
	output da1_clk,             //AD9767 CH1 clock
    output da1_wrt,             //AD9767 CH1 enable
    output [13:0] da1_data,     //AD9767 CH1 data output
    
    output led,
    output adc_clk, 
    output [3:0]  state,	
    
    output da2_clk,             //AD9767 CH2 clock
    output da2_wrt,	            //AD9767 CH2 enable
    output [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不同,注意数字量之间要基于基础电压单位进行切换,避免仿真跑飞

相关推荐
1560820721926 分钟前
在vivado中,国产CH347芯片实现USB转JTAG的操作
fpga开发
数字芯片实验室5 小时前
IP验证最终回归到时序级建模
网络·网络协议·tcp/ip·fpga开发
雨洛lhw5 小时前
三模冗余资源量对比
fpga开发·三模冗余技术
XINVRY-FPGA7 小时前
XC7VX690T-2FFG1761I Xilinx AMD FPGA Virtex-7
arm开发·嵌入式硬件·fpga开发·硬件工程·fpga
FPGA_无线通信9 小时前
FPGA 组合逻辑和时序逻辑
fpga开发
Js_cold11 小时前
Xilinx FPGA温度等级及选型建议
fpga开发·fpga·vivado·xilinx
从此不归路12 小时前
FPGA 结构与 CAD 设计(第5章)上
fpga开发
洋洋Young12 小时前
【Xilinx FPGA】7 Series Clocking 设计
fpga开发·xilinx fpga
1560820721913 小时前
FPGA下AD采集时钟相位的调整
fpga开发
从此不归路13 小时前
FPGA 结构与 CAD 设计(第5章)下
fpga开发