自学内容网 自学内容网

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


`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环公式模块

代码如下(示例):

`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

`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不同,注意数字量之间要基于基础电压单位进行切换,避免仿真跑飞


原文地址:https://blog.csdn.net/g_125487/article/details/144354232

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!