verilog实现PID控制

1 原理讲解

距离上一次说PID算法的事情过去蛮久了,今天又重新看了看PID的代码,其实还是存在一些不合理的地方。

整理归纳了一下原理,位置式和增量式的变化。

2 工程实现

c 复制代码
`timescale 1ns / 1ps

module pid_controller(
    input  clk,
    input  rst_n,
    input  [15:0] setpoint,
    input  [15:0] feedback,
    input  [15:0] Kp,
    input  [15:0] Ki,
    input  [15:0] Kd,
    input  [15:0] clk_prescaler,
    output reg [15:0] control_signal
);
  
    // Internal signals
    
    reg [15:0] prev_error = 16'h0000;
    reg [15:0] integral = 32'h00000000;
    reg [15:0] derivative = 16'h0000;
    
    // Clock divider for sampling rate
    reg [15:0] clk_divider = 0;
    reg sampling_flag = 0;

    always @(posedge clk or negedge rst_n) begin
    //$display("Clock trigered");
        if (~rst_n)
            clk_divider <= 16'h0000;
        else if (clk_divider == clk_prescaler) begin // clk_prescaler determines the sampling rate, thus sampling rate would be clk freq/clk_prescaler
            clk_divider <= 16'h0000;
            
            sampling_flag <= 1;
        end else begin
            clk_divider <= clk_divider + 1;
            sampling_flag <= 0;
        end
    end

    always @(posedge clk or negedge rst_n) begin
    
        if (~rst_n) begin
            // Reset logic generally specific to application
        end 
        else if (sampling_flag) begin
                      
            // PID Calculation
            integral <= integral + (Ki * (setpoint - feedback));
            $display("Integral is %d",integral);
            derivative <= Kd * ((setpoint - feedback) - prev_error);
            // Calculate control signal
            control_signal = (Kp * (setpoint - feedback)) + integral + derivative; 
            prev_error <= (setpoint - feedback);// Update previous error term to feed it for derrivative term.
        end
    end

endmodule

testbench

c 复制代码
`timescale 1ns / 1ps

module pid_tb( );
    reg  clk = 0;
    reg  rst_n = 0;
    reg  [15:0] setpoint = 0;
    reg  [15:0] feedback = 0;
    reg  [15:0] Kp = 0;
    reg  [15:0] Ki = 0;
    reg  [15:0] Kd = 0;
    reg  [15:0] clk_prescaler = 0;
    wire [15:0] control_signal;

    pid_controller DUT(.clk(clk),.rst_n(rst_n),.setpoint(setpoint),.feedback(feedback),.Kp(Kp),.Ki(Ki),.Kd(Kd),.clk_prescaler(clk_prescaler),.control_signal(control_signal));

    initial begin
        rst_n <= 0; // Assert reset
        clk_prescaler <= 5; 
        setpoint <= 20;
        Kp <= 5;
        Ki <= 2;
        Kd <= 1;
        #20 rst_n <= 1; // Deassert reset
    end

    always #1 clk = ~clk;

    always begin
    $monitor("Control signal is %d",control_signal);
        #20 feedback <= 1;
        #15 feedback <= 5;
        #15 feedback <= 8;
        #15 feedback <= 10; 
        #15 feedback <= 13;     
        #15 feedback <= 15;     
        #15 feedback <= 16;  
        #15 feedback <=25;   
        #25 $finish;
    end

endmodule
相关推荐
传感器与混合集成电路16 小时前
基于FPGA与ADC协同架构的高密度数据采集模块设计原理与应用场景分析
fpga开发·架构
Rambo.xia17 小时前
10Gbps实时图像重采样:一个FPGA项目的完整交付复盘
fpga开发
zlinear数据采集卡17 小时前
2mV纹波的代价:硬核拆解ZLinear模拟前端放大器与正负可调电源设计
arm开发·单片机·嵌入式硬件·fpga开发·架构·开源
小麦嵌入式18 小时前
FPGA入门(八):一篇讲透跑马灯、闪烁灯、呼吸灯的原理与模拟波形分析
fpga开发
Rambo.xia20 小时前
AXI4-Full突发传输掉数据——突发长度算错、WRAP边界、窄传输字节错位,调试一周才发现是协议理解错了
fpga开发
zlinear数据采集卡20 小时前
硅片里的“自动纠错“:硬核拆解LHAMP188自动归零技术原理与三种封装的PCB布局实战
arm开发·单片机·嵌入式硬件·fpga开发·开源
Mr-pn-junction1 天前
clk_gate
单片机·嵌入式硬件·fpga开发
知行合一←_←2 天前
误码仪与时钟极性
fpga开发
upper20202 天前
vivado使用那些事之综合策略
fpga开发·vivado·vivado综合策略
2CM_Embed2 天前
Quartus II 工程编译与 FPGA 下载流程记录
嵌入式硬件·fpga开发