pid控制的verilog程序问题
本帖最后由 fpgaw 于 2010-7-3 06:11 编辑pid控制的verilog程序
小弟刚接触eda,现在在进行毕业设计,方向是用FPGA控制直流电机,进行点位控制。资料太少。
刚编的pid控制的verilog程序,采用积分分离方式,m是视场切换指令,现在问题多多,请各位高手帮忙修改。
万望各位大侠拉小弟一把 ,切切! 问题1:kp=1.1, ki=0.0286, kd=10.2是小数应该如何处理? 问题2:为何即使将kp、 ki 、kd都改成整数,综合还是不能通过?
module pid(
rst,clk,clk2,m,p_cur, //input
uout,dataout
); //output
input rst,clk,clk2;
input m; //m is option
input p_cur; //pcur is position
output uout;
output dataout;
reg q0,q1,qa,qb;
reg u_0,u_1,u3_1,u;
reg e_0,e_1,e_2;
reg ke;
reg u1,u2,u3;
reg b1,b2,b3,b4,b5;
reg e_01,e_k;
reg uout;
regdataout;
parameter r=400, p1_0=12'b100100000000,p2_0=12'b010000000000, //initial
kp=1.1, ki=0.0286, kd=10.2;
assign p1_1 = ~p1_0+1;
assign p2_1 = ~p2_0+1;
assign pcur_1 = ~p_cur+1;
always@( posedge rst )
if(rst==1'b1)
begin
u_0 <= 0;
u_1 <= 0;
e_0 <= 0;
e_1 <= 0;
e_2 <= 0;
dataout <= 0;
uout <= 0;
end
always@( posedge clk2)
begin
case(m)
1'b0 :begin
e_0 <=p1_1-p_cur;//e_0<=p1-p_cur
if (e_0==1)
e_01 = ~e_0+1;
else e_01 = e_0;
end
1'b1 :begin
e_0 <= p2_1-p_cur; //e_0<=p2-p_cur
if (e_0==1)
e_01 = ~e_0+1;
else e_01 = e_0;
end
endcase
end
always@( posedge clk )
begin
if (e_01>r) //pd
begin
e_k = e_01-e_1;
u1 = kp*e_01;
u2 = kd*e_k;
u3 = u1+u2;
u3_1 = u3;
u = u_1+u3_1;
end
else //pid
begin
ke = ki+kd;
q0 = kp+ke;
qa = (-1)*kp;
qb = (-2)*kd;
q1 = qa+qb;
b1 = kd*e_2;
b2 = q1*e_1;
b3 = b1+b2;
b4 = q0*e_0;
b5 = b3+b4;
u = u_1+b5;
end
end
always@(posedge clk2)
begin
uout=~u+1; //link to DAC
end
always@(negedge clk2)
begin
u_1 = u;
e_2 = e_1;
e_1 = e_0;
end
endmodule 对小数我一般是进行放大,有时也可以截位。 还真是问题多多啊。<br>
首先你的always块里是时序逻辑的话最好不用阻塞赋值,<br>
更别说阻塞赋值和非阻塞赋值共用了。<br>
其次你的状态机形式不太好,不过这个倒不是一定要改,<br>
但你最好去看看置顶的关于状态机的帖子。<br>
最后你一定要用两个时钟么?一定要用时钟的两个沿分别采样么?<br>
一般不推荐这么做。 你还是先看看verilog的教程再来写代码吧。。。<br>
开始打算替你改改,后来发现要改的地方太多了 。^_^ 我同意楼上的说法,你的编码不是太正规,建议你还是先看一下网站以前介绍的有关编码风格的帖子吧,这都是牛人们实际工作中总结出来的,磨刀不误砍柴工啊 比如说你的复位信号,应该按照异步复位或者同步复位的模式来写, 你的乘法能那么直接乘吗?还是先多看看书吧! 谢谢各位大侠的热情支持,我的FPGA时钟位48MHZ,系统采样周期为50KHZ,所以要使用两个时钟。乘法好像可以直接调用乘法核,这样写应该可以,另外我还想问一下,如果我想多次调用ALU来进行计算,我该如何做? 说实话我是不知道你是什么算法,<br>
所以根本没办法确定改以后和你想的一样。<br>
只能说提点意见。<br>
你里面好多地方都是对2个常数进行运算,<br>
这样不好,你不如把能确定的数值都写成参数,<br>
用的时候直接调用,面积会小很多,速度也会有所提高。<br>
另外我看到你很多地方都用取反加1,<br>
看来是要把负数变正,<br>
其实只要用signed 定义变量就能<br>
自动综合出可以对负数进行运算的加法器,乘法器。<br>
上面是细节的问题。<br>
2个时钟的话那你就必须考虑不同时钟域的数据传输问题了,<br>
最好中间加个fifo。<br>
同时采用时钟的上升下降沿一般是不好的,<br>
我看到有人说过是因为fpga产生的时钟两个沿只有一个比较好,<br>
而且还有其他原因,我忘了,总之是能不用就不用。<br>
<br>
你的fpga上集成了alu?没用过那么高级的板子。。。。。自己试吧。 积分分离pid控制的verilog程序,请帮助修改。<br>
<br>
小弟刚接触eda,现在在进行毕业设计,方向是用FPGA控制直流电机,进行点位控制。我的FPGA时钟为48MHZ(CLK1),系统采样周期为50KHZ(CLK2),所以要使用两个时钟。刚编的pid控制的verilog程序,采用积分分离方式,m是视场切换指令,现在问题多多,请各位高手帮忙修改。<br>
万望各位大侠拉小弟一把 ,切切! <br>
积分分离的方法:在开始时不进行积分,直至偏差达到一定之后才进行积分。即人为设定一阀值 >0。当∣e(k)∣> 时,也即偏差值比较大时,采用PD控制,封闭积分作用。当∣e(k)∣≤ 时,也即偏差值∣e(k)∣比较小时采用PID控制,启用积分作用。<br>
<br>
问题1:kp=1.1, ki=0.0286, kd=10.2是小数应该如何处理? 问题2:为何即使将kp、 ki 、kd都改成整数,综合还是不能通过?问题2:为何综合时RTL原理图看不到CLK1端口和pcur端口,两个时钟应该如何处理?<br>
<br>
<br>
module pid(<br>
rst,clk1,clk2,m,pcur, //input<br>
uout,dataout<br>
); //output<br>
input rst,clk1,clk2;<br>
input m; //m 是目标选择指令<br>
input pcur; //pcur 是当前位置<br>
output uout;<br>
output dataout;<br>
reg u,u1;<br>
reg e0,e1,e2;<br>
reg q0,q1,qa,qb; //q2=kd<br>
reg e0_b,ek;<br>
reg ke;<br>
reg w1,w2,w3;<br>
reg b1,b2,b3,b4,b5;<br>
reg w3_a,b5_a;<br>
reg uout;<br>
reg dataout;<br>
wire p1_b, p2_b,pcur_b;<br>
parameter r=400, p1_a=12'b100100000000, <br>
p2_a=12'b000100000000, //p1_a,p2_a是两个目标位置的原码 <br>
kp=141,ki=4,kd=1292; // kp=1.1, ki=0.0286, kd=10.1先乘128取整<br>
<br>
assign p1_b = ~p1_a+1;<br>
assign p2_b = ~p2_a+1;<br>
assign pcur_b = ~pcur+1;<br>
<br>
always@( posedge rst ) //initial<br>
if(rst==1'b1) <br>
begin<br>
u <= 0;<br>
u1 <= 0;<br>
e0 <= 0;<br>
e1 <= 0;<br>
e2 <= 0;<br>
dataout <= 0;<br>
end<br>
<br>
always@( posedge clk2)<br>
begin<br>
case(m)<br>
1'b0 : begin<br>
e0 =p1_b - pcur_b; //计算目标1与当前位置的差值<br>
if (e0==1)<br>
e0_b = ~e0+1;<br>
else e0_b = e0;<br>
end<br>
1'b1 : begin<br>
e0 = p2_b - pcur_b; //计算目标2与当前位置的差值<br>
if (e0==1)<br>
e0_b = ~e0+1;<br>
else e0_b = e0;<br>
end<br>
endcase<br>
end<br>
<br>
always@( posedge clk1 ) <br>
begin <br>
if (e0_b>r) //pd<br>
begin<br>
ek = e0-e1; <br>
w1 = kp*e0; <br>
w2 = kd*ek; <br>
w3 = w1+w2; <br>
w3_a=w3/128; // 除128还原其应有的值<br>
u = u1+w3_a; <br>
end<br>
else //pid<br>
begin<br>
ke = ki+kd; <br>
q0 = kp+ke; <br>
qa = (-1)*kp; <br>
qb = (-2)*kd; <br>
q1 = qa+qb; <br>
b1 = kd*e2; <br>
b2 = q1*e1; <br>
b3 = b1+b2; <br>
b4 = q0*e0; <br>
b5 = b3+b4; <br>
b5_a = b5/128; // 除128还原其应有的值<br>
u = u1+b5_a; <br>
end<br>
end<br>
<br>
always@(negedge clk2) <br>
begin<br>
uout<=~u+1; //link to DAC<br>
u1 <= u; //将u、e1、e0 的值赋给u1、e2、e1以进行下一次计算<br>
e2 <= e1;<br>
e1 <= e0;<br>
end<br>
<br>
endmodule
页:
[1]
2