使用单片机非常常用。但是在FPGA中,由于浮点运算问题。使用PID就优点麻烦。
下面是我设计的一个PID的Verilog程序。模拟16bitDAC和ADC的PID闭环。在运算时通过将数据左右。提高了FPGA PID控制精度。
以下时代码和仿真结果。
//PID.V
module demo_top(
//system signals
input clk , // 时钟信号
input rst_n , // 复位信号,低电平有效
input signed [15:0] target , // 目标值
input signed [15:0] y , // 实际输出值
output signed [15:0] PID_OUT // pid输出值
);
parameter signed Kp = 32'd8;
parameter signed Ki = 32'd16;
parameter signed Kd = 32'd4;
wire signed[31:0] ek0; // e(k)
reg signed[31:0] ek1; // e(k-1)
reg signed[31:0] ek2; // e(k-2)
wire signed[31:0] d_uk;// pid增量
reg signed[31:0] uk1 ; // 上一时刻u(k-1)的值
reg signed[31:0] PID_OUT_REG = 15'd0;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin // 初始化误差值
ek1 <= 31'd0;
ek2 <= 31'd0;
uk1 <= 31'd0;
PID_OUT_REG <= 15'd0;
end
else begin
PID_OUT_REG <= PID_OUT_REG + d_uk;
ek2 <= ek1; // 再延时一个时钟周期,得到e(k-2)
ek1 <= ek0; // 延时一个时钟周期,得到e(k-1)
end
end
assign ek0 = {(target - y),16'd0}; // 计算e(k)
assign d_uk = (ek0 -ek1)/Kp + ek0/Ki + ((ek0 - ek1)-(ek1 - ek2))/Kd; // 计算pid增量
assign PID_OUT = PID_OUT_REG[31:16];
endmodule
//pid_tb.v
`timescale 1ns/1ps
module pid_tb ();
reg clk;
reg rst_n;
reg signed [15:0] target ; // 目标值
reg signed [15:0] y ; // 实际输出值
wire signed [15:0] uk0; // pid输出值
reg[15:0] i;
reg signed[15:0] mytxt[0:1997];
reg signed[15:0] adc = 16'd0;
initial
begin
clk = 1'b0;
rst_n = 1'b1;
#5 rst_n = 1'b0;
#5 rst_n = 1'b1;
target = 16'd800;
adc = 16'd0;
end
always #5 clk = ~clk;
always @ (negedge clk or negedge rst_n) begin
if(!rst_n) begin // 初始化误差值
i <= 15'd0;
y <= 15'd0;
mytxt[0] <= 15'd0;
end
else begin
if(i>8)y <= mytxt[i-7];//模拟控制输出。
else y <= i;
mytxt[i] <= uk0;
i <= i + 1;
end
end
demo_top demo_top_tb(.clk(clk),
.rst_n(rst_n),
.target(target),
.y(y),
.PID_OUT(uk0)
);
endmodule
引用: freebsder 发表于 2020-12-7 18:56 看不懂,纯支持了。
懒,没写原理。错别字也多。当自己备用吧
引用: 郝旭帅 发表于 2020-12-8 19:35 支持一波
支持,能问一下pid控制输出uk和pwm脉冲之间的关系吗