当前位置: 首页 > article >正文

基于FPGA的PI环调节电压

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


http://www.kler.cn/a/429638.html

相关文章:

  • Wireshark抓包教程(2024最新版个人笔记)
  • C# 25Dpoint
  • C++中的STL
  • TiDB常见操作指南:从入门到进阶
  • 【Linux 之一 】Linux常用命令汇总
  • ssh2详细使用步骤,以及常用方法介绍
  • Xcode编译的时候运行python脚本
  • 计算机网络——三大交换技术
  • 准确率99.9%的离线IP地址定位库 ip2region - python 示例
  • GauHuman阅读笔记【3D Human Modelling】
  • 知从科技闪耀汽车智能底盘大会:共探软件安全新篇章
  • ElasticSearch常见面试题汇总
  • 《maven 常用知识详解 3:聚合与继承》
  • Blender均匀放缩模型
  • 第8章:CSS预处理器 --[CSS零基础入门]
  • vue3 实现音频转文字组件
  • 深入理解 React 状态管理:useState 和 useReducer
  • java工具包介绍
  • FCOS: Fully Convolutional One-Stage Object Detection——全卷积一阶段目标检测
  • 【CMD、PowerShell和Bash设置代理】
  • 项目文章 | RNA-seq+WES-seq+机器学习,揭示DNAH5是结直肠癌的预后标志物
  • C++小小复习一下
  • js:事件监听
  • STM32进阶 ADC模式转换
  • 1-6 ESP32控制LED灯
  • 11.关于vim编辑器的简单配置