PI闭环的FPGA实现

2023-10-30

PID闭环的FPGA实现

1 原理分析

最近小张同学在做项目的时候发现PI闭环的FPGA学习资料很少,秉持着“既然没有轮子,那么自己就造一个的原则”,于是乎自己写了个PI的Verilog程序。
FPGA中实现PI闭环与DSP、STM32、arm中都不一样,由于FPGA中没有数学库,需要从底层开始解决运算中产生的小数、除法等问题。因此FPGA中实现PI闭环相比来说有一点点难度。

​ 首先,位置式PI和增量式PI的选择问题,由于位置式PI的实现原理包含历史误差的累计,并且启动瞬间或状态突然切换瞬间会有突变现象,突变现象会影响电机的动态性能,同时启动电流会很大,而这会对电机工作状态产生影响,因此采用增量式PI控制更优。当然,我觉得用位置式pi也能适用于电机的pi闭环。因为本质上来说,这两种pi实现机理并无差异,最终的控制效果也应该差异不大。

而增量式PI控制通常包括并联式PI控制和串联式PI控制,并联式PI控制可以实现 K p K_p Kp K i K_i Ki 的解耦,方便参数调试,所以采用并联式增量PI控制器。

上图是增量并联并联式PI框图,含义为参考值与实际值比较得到的差值分为两部分,一部分通过P的直接增益,另一部分通过积分的积分增益,合并输出。

下图是传递函数,只调节Kp相,可以调节幅值,对应的Ki值需要与之对应,可以调节控制系统的零点,Kp幅值增加对应的零点频率会下降,零点频率实际上是Kp和Ki的函数

典型pid系统的输出信号表达式如(1)所示
u k = K p ( ( e t ) + 1 T i ∫ 0 t e ( t ) d ( t ) + T d d e ( k ) e ( k ) ) u_k = K_p((e_t)+\frac{1}{T_i}\int ^t_0 e(t){\rm d}(t)+T_d\frac{de(k)}{e(k)}) uk=Kp((et)+Ti10te(t)d(t)+Tde(k)de(k))

采用PI控制,即无微分项,则
u k = K p ( ( e t ) + 1 T i ∫ 0 t e ( t ) d ( t ) ) u_k = K_p((e_t)+\frac{1}{T_i}\int ^t_0 e(t){\rm d}(t)) uk=Kp((et)+Ti10te(t)d(t))

2 PI的硬件实现

在数字电路中,并无法实现连续时间的积分和微分,因此数字pid中,只能以固定采样周期来进行离散化,从而实现近似的连续时间的积分和微分,利用这个思想,将式(2)进行离散化,

  1. 用一系列采样时间kT来代替时间t
  2. 以和式来代替积分: ∫ 0 t e ( t ) d ( t ) ) = ∑ e ( j T ) T = T ∑ e ( k ) \int ^t_0 e(t){\rm d}(t)) = \sum e(jT)T = T\sum e(k) 0te(t)d(t))=e(jT)T=Te(k)
  3. 向后差分 来代替 微分 d e ( t ) d t = e ( k ) − e ( k − 1 ) Δ t = e ( k ) − e ( k − 1 ) T s \frac{de(t)}{dt}=\frac{e(k)-e(k-1)}{\Delta t}=\frac{e(k)-e(k-1)}{T_s} dtde(t)=Δte(k)e(k1)=Tse(k)e(k1)

可得位置式PID的离散化如下式
u k = K p ( e k ) + K i ∑ ( e k ) + K d ( e ( k ) − e ( k − 1 ) ) u_k = K_p(e_k)+K_i\sum(e_k)+K_d(e(k)-e(k-1)) uk=Kp(ek)+Ki(ek)+Kd(e(k)e(k1))
PI中令位置式 K d K_d Kd = 0 即可得 位置式PI表达式
u k = K p ( e k ) + K i ∑ ( e k ) u_k = K_p(e_k)+K_i\sum(e_k) uk=Kp(ek)+Ki(ek)
硬件中可以对误差的累计 S u m ( k ) = S u m ( k − 1 ) + ( k ) 。 Sum(k)=Sum(k-1)+(k)。 Sum(k)=Sum(k1)+(k)

增量式PID

增量为:
Δ u ( k ) = u ( k ) − u ( k − 1 ) = k p [ e ( k ) − e ( k − 1 ) ] + k i    e ( k ) + k d   [ e ( k ) − 2 e ( k − 1 ) + e ( k − 2 ) ] \Delta u(k) = u(k)-u(k-1) =k_p[e(k)-e(k-1)]+k_i\,\,e(k)+k_d\,[e(k)-2e(k-1)+e(k-2)] Δu(k)=u(k)u(k1)=kp[e(k)e(k1)]+kie(k)+kd[e(k)2e(k1)+e(k2)]
令位置式 K d K_d Kd = 0 即可得
Δ u ( k ) = u ( k ) − u ( k − 1 ) = k p [ e ( k ) − e ( k − 1 ) ] + k i    e ( k ) \Delta u(k) = u(k)-u(k-1) =k_p[e(k)-e(k-1)]+k_i\,\,e(k) Δu(k)=u(k)u(k1)=kp[e(k)e(k1)]+kie(k)
增量式PID的输出为
u ( k ) = u ( k − 1 ) + Δ u ( k ) u(k)=u(k-1)+\Delta u(k) u(k)=u(k1)+Δu(k)

然而很多场合下需要的往往不只增量,还有上一拍的输出值,于是可知增量式PI调节器算法为
u ( k ) = u ( k − 1 ) + Δ u ( k ) = u ( k − 2 ) + Δ u ( k − 1 ) + Δ u ( k ) = u ( 0 ) + u ( 1 ) + . . . . . + u ( k − 1 ) u(k)=u(k-1)+\Delta u(k)=u(k-2)+\Delta u(k-1)+\Delta u(k)=u(0)+u(1)+.....+u(k-1) u(k)=u(k1)+Δu(k)=u(k2)+Δu(k1)+Δu(k)=u(0)+u(1)+.....+u(k1)
由于 u ( 0 ) = 0 u(0)=0 u(0)=0在具体编程操作中,对每一拍的 Δ u ( k ) \Delta u(k) Δu(k)进行累积,即为PI调节器的输出;同样地,为了避免超过允许值,仅需对输出限幅即可

事实上,由增量式PI
u ( k ) = u ( k − 1 ) + Δ u ( k ) = u ( k − 1 ) + K P ∗ ( e ( k ) − e ( k − 1 ) ) + K i T s e ( k ) u(k)=u(k-1)+\Delta u(k)=u(k-1)+K_P*(e(k)-e(k-1))+K_iT_se(k) u(k)=u(k1)+Δu(k)=u(k1)+KP(e(k)e(k1))+KiTse(k)
可得
u ( k − 1 ) = u ( k − 2 ) + Δ u ( k − 1 ) = u ( k − 2 ) + K P ∗ ( e ( k − 1 ) − e ( k − 2 ) ) + K i T s e ( k − 1 ) u(k-1)=u(k-2)+\Delta u(k-1)=u(k-2)+K_P*(e(k-1)-e(k-2))+K_iT_se(k-1) u(k1)=u(k2)+Δu(k1)=u(k2)+KP(e(k1)e(k2))+KiTse(k1)
代入上式即可约去 e ( k − 1 ) e(k-1) e(k1)项,不断迭代,由于 e ( 0 ) = 0 e(0)=0 e(0)=0,可发现其最终结果与位置式PI的表达式一致,也即两种PI算法完全相同(未超出限幅值的前提下)
因此,可以理解为无论用增量叠加的方式来计算位置式PI,还是直接计算,结果都是相同的。两者唯一的区别就是位置式PI需要同时设置积分限幅和输出限幅,而增量式PI只需输出限幅。 增量式的好处就是启动时和状态突然发生变化时不会产生突变,其控制效果与位置式PI基本相同。

3 如何在硬件中实现积分器

如何实现一个积分器(I)?

​ 创建一个加法器是最简单的形式,PI环节如上述框图描述所示。Z-1代表延迟块,T代表采样周期。
O u t ( x ) = O u t ( x − 1 ) + I n ( x ) ∗ T Out(x) = Out(x-1) + In(x)*T Outx=Out(x1)+In(x)T
​ 新输出值 = 旧输出值 + 输入*采样周期

//C语言实现
Out += In*T;

4、FPGA的硬件实现

话不多说,直接贴代码

代码简单易懂 相信各位聪明的小伙伴一看就能看懂

`timescale 1 ns/1 ns

//增量式 PI	
//仿真验证通过版本
// 至于k_p k_i 
module pid_controller_delta #(
	parameter   logic  [23:0] k_p=24'd30,   //kP
	parameter   logic  [23:0] k_i=24'd2 		 //Ki	
)(
	input  wire signed [15:0] i_real,//实际电流值
	input  wire signed [15:0] i_aim, //输入给定电流值
	input  wire 				  clk,
	input  wire					  rstn,
	input  wire					  pi_en,  //park变换是否完成的信号,高定平即完成park变换
	output wire  signed [15:0] u_out, //输出电压值 u_alpha U_beta
	output reg	 	  			  o_en    //PI 结束信号
 );
	reg signed [31:0] error_1,error_2,delta_error;//误差值 error_1为上一时刻的误差值,error_2为当前的误差值
	reg signed [31:0] multipy_p,multipy_i,multipy_i1,multipy_i2;  		 //分别代表delta_u两部分的乘积
	reg signed [31:0] u_out_temp,delta_u; 	 		 //寄存上一时刻的输出值,delta_u代表输出的增量
	reg 					en_s1,en_s2,en_s3,en_s4,en_s5; 
	
	assign 	u_out = u_out_temp[15:0];  
	function automatic logic signed [31:0] protect_add(input logic signed [31:0] a, input logic signed [31:0] b);
		automatic logic signed [32:0] y;
		y = $signed({a[31],a})+$signed({b[31],b});  //积分限幅
		//积分限幅
		if(       y  >  $signed(33'h7fffffff)  )//
			return 		 $signed(32'h7fffffff);
		else if(  y  < -$signed(32'h7fffffff)	)
			return 		-$signed(32'h7fffffff);
		else
			return		 $signed(y[31:0]);
	endfunction 
	
	function automatic logic signed [31:0] protect_mul(input logic signed [31:0] a, input logic signed [24:0] b);
	
		automatic logic signed [57:0] y;
		y = a * b;  //积分限幅
		//积分限幅
		if(       y  >  $signed(57'h7fffffff)  )//
			return 		 $signed(32'h7fffffff);
		else if(  y  < -$signed(57'h7fffffff)	)
			return 		-$signed(32'h7fffffff);
		else
			return		 $signed(y[31:0]);
	endfunction 
	
	function automatic logic signed [31:0] protect_subtract(input logic signed [31:0] a, input logic signed [31:0] b);
		automatic logic signed [32:0] y;
		y = $signed({a[31],a}) - $signed({b[31],b});
		//
		if(  			y  >  $signed(33'h7fffffff)  )
			return 	   	$signed(32'h7fffffff);
		else if (   y  < -$signed(32'h7fffffff)	)
		return 			  -$signed(32'h7fffffff);
		else 
		return 				$signed(y[31:0]);
	endfunction
	// plpeline 1  计算e(k)
	always@(posedge clk or negedge rstn)
		if(~rstn) begin
			en_s1   <= 1'b0;
			error_1 <= 0;
		end 
		else begin
			en_s1 <= pi_en;
			if(pi_en) begin
				error_1 <=  $signed({{16{i_aim[15]}},i_aim}) - $signed({{16{i_real[15]}},i_real}) ;
			end
		end
	//pipeline 2 计算e(k-1)
	always@(posedge clk or negedge rstn)
		if(~rstn) begin
			en_s2   <= 1'b0;
			error_2 <= 0;
		end
		else begin
			en_s2 <= en_s1;
			if(en_s1) begin
				error_2 <= error_1;  //e(k-1)
				multipy_i <= protect_mul(error_1,k_i);
			end 
		end
	
	always@(posedge clk or negedge rstn)
		if(~rstn) begin
			en_s3 <= 1'b0;
			multipy_i1 <= 0;
			delta_error <= 0;
		end
		else begin
			en_s3 <= en_s2;
			if(en_s2) begin
				multipy_i1 <= multipy_i;
				delta_error <= protect_subtract(error_1,error_2);
			end			
		end 	
		
	always@(posedge clk or negedge rstn)
		if(~rstn) begin
			en_s4 <= 1'b0;
			multipy_i2 <= 0;
			multipy_p <=0;
		end
		else begin
		 en_s4 <= en_s3;
		 if(en_s3) begin
			multipy_i2 <= multipy_i1;
			multipy_p  <= protect_mul(delta_error,k_p);
		 end
		end
		
	always@(posedge clk or negedge rstn)
		if(~rstn) begin
			en_s5 <= 1'b0;
			delta_u <= 0;
		end
		else begin
			en_s5 <= en_s4;
			if(en_s4) begin
			delta_u <= protect_add(multipy_i2,multipy_p);
			end
		end
		
	always@(posedge clk or	negedge rstn)
		if(~rstn) begin
			o_en <= 1'b0;
			u_out_temp <= 0;
		end 
		else begin
			o_en <= en_s5;
			if(en_s5) begin
				u_out_temp = protect_add(u_out_temp,delta_u);
			 end
		end  
endmodule  

5、仿真波形

i_aim 给定值是350 实际值是调节后的曲线,从图中可以看出,PI调节可以快速达到闭环,并进入稳态。

如果感觉博主的博客写的还可以的话,欢迎一键三连,点个关注不迷路哦~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PI闭环的FPGA实现 的相关文章

  • 检查给定 pid 的进程是否存在

    给定 Linux 进程的 pid 我想从 C 程序检查该进程是否仍在运行 Issue a kill 2 http linux die net man 2 kill系统调用0作为信号 如果调用成功 则说明存在该pid的进程 如果呼叫失败并且e
  • verilog 中的“<<”运算符

    我有一个verilog代码 其中有一行如下 parameter ADDR WIDTH 8 parameter RAM DEPTH 1 lt lt ADDR WIDTH 这里将存储什么RAM DEPTH以及什么是 lt lt 操作员在这里做
  • 模拟器和合成器之间初始化状态机的差异

    我的问题是关于合成状态机中使用的第一个状态 我正在使用莱迪思 iCE40 FPGA 用于仿真的 EDA Playground 和用于综合的莱迪思 Diamond Programmer 在下面的示例中 我生成一系列信号 该示例仅显示引用状态机
  • 如何获取值数组作为 plusargs?

    如何获取值数组作为参数 我需要从命令行获取一组未定义大小的命令 如何将这些参数放入数组或队列中 Eg CMDS READ WRITE READ N WRITE 它应该被带到一个数组中 value plusargs不支持数组 但支持字符串 看
  • 如何将时钟门映射到技术库单元

    我的设计中有以下时钟门 module my clkgate clko clki ena Clock gating latch triggered on the rising clki edge input clki input ena ou
  • 我们可以在 C 或 SystemVerilog 中使用 ifdef MACROS 中的条件吗?

    我想要那样的东西 ifdef N O gt N I define GREATER 1 else define LESSER 1 endif 但做不到 有什么解决方案或阅读吗 我很努力地想要做到这一点 但是却做不到 Verilog 不提供这样
  • 赋值语句中的“others=>'0'”是什么意思?

    cmd register process rst n clk begin if rst n 0 then cmd r lt others gt 0 elsif clk event and clk 1 then cmd r lt end if
  • Process.start() 启动的进程返回错误的进程 ID?

    我正在使用以下代码启动可执行文件 Process proc new Process proc StartInfo FileName executablePath proc Start proc WaitForInputIdle 在这次通话之
  • 映射 MMIO 区域写回不起作用

    我希望对 PCIe 设备的所有读写请求都由 CPU 缓存进行缓存 然而 它并没有像我预期的那样工作 这些是我对回写 MMIO 区域的假设 对 PCIe 设备的写入仅在缓存回写时发生 TLP 有效负载的大小是缓存块大小 64B 然而 捕获的
  • AppleScript:从应用程序隐藏/获取进程名称

    我想隐藏最前面的应用程序 我知道您可以使用以下语法隐藏进程 tell application System Events set visible of process to false end tell 我知道如何获取最前面的应用程序 pa
  • 如何在Verilog中将二维数组中的所有位设置为0?

    我构建了一个 8 2bits 数组来表示 Verilog 中的一块内存 reg 1 0 m 0 7 该存储器有一个复位信号 如果复位为1 则该存储器中的所有位都应重置为0 但是我不知道如何以简洁的方式设置m的所有位 因为如果有数百个内存中有
  • Verilog 中的大括号是什么意思?

    我很难理解 Verilog 中的以下语法 input 15 0 a 16 bit input output 31 0 result 32 bit output assign result 16 a 15 a 15 0 我知道assign语句
  • VIM 高亮匹配开始/结束

    我正在尝试找到一个插件 它将突出显示与 Verilog 匹配的开始 结束语句 VIM 可以使用花括号 方括号 但不能使用它的开始 结束 我希望 VIM 突出显示正确的开始到正确的结束 在我看来 最好的选择是使用 matchit 该脚本是 v
  • 我怎样才能让我的verilog移位器更通用?

    这里我有一个移位器 但现在它最多只能工作 3 位 我一直在寻找 但不知道如何让它工作最多 8 位 module shifter a b out input 7 0 a b output 7 0 out wire 7 0 out1 out2
  • reg 声明中的位顺序

    如果我需要使用 4 个 8 位数字 我会声明以下 reg reg 7 0 numbers 3 0 我对第一个和第二个声明 7 0 和 3 0 之间的区别感到很困惑 他们应该按什么顺序来 第一个是保留数字的大小 而第二个是保留数字的数量 还是
  • 多处理时如何获取每个进程ID

    我有一些问题 因为我是 Python 和 Pyside 的新手 我有N个进程同时运行 由于这些进程需要一些时间才能完成其工作 因此最终用户可能想要取消特定进程 因此 我需要一种方法来了解进程的 ID 以便将此功能添加到程序中 有一个answ
  • 如何使用 bash 锁定文件

    我有一个任务从远程服务器同步目录 rsync av email protected cdn cgi l email protection srv data srv data 为了使其定期运行并避免脚本 reEnter 问题 我使用 rsyn
  • 如何在 Makefile 中获取 make 命令的 pid?

    我想使用此构建特有的临时目录 如何在 Makefile 中获取 make 命令的 pid I tried TEMPDIR tmp myprog 但这似乎存储TEMPDIR as tmp myprog 然后将 eval 作为每个引用此命令的新
  • 如何在 alpine 中使用 ps 命令获取特定 PID 的进程名称

    在基于 ubuntu 的 docker os 中 ps PID USER TIME COMMAND 1 postgres 0 00 postgres 47 postgres 0 00 postgres checkpointer proces
  • 如何从 Ruby 检查具有特定 pid 的进程是否正在运行?

    如果有多种方法 请列出 我只知道一个 但我想知道是否有一种更干净的 Ruby 方式 之间的区别Process getpgid and Process kill方法似乎是当 pid 存在但由另一个用户拥有时发生的情况 Process getp

随机推荐

  • C/C++语言 从日期格式字符串中提取年月日时分秒

    系列文章目录 文章目录 系列文章目录 前言 一 日期格式字符串 二 strftime函数 1 描述 2 声明 3 形参 4 返回值 三 strptime函数 1 形参 2 示例 3 函数封装 总结 前言 上一篇文章 C C 语言 获取系统时
  • MES管理系统如何帮助制造企业打造透明化工厂

    在制造型企业的运营中 车间现场管理至关重要 然而 面临着信息传递速度慢 跨部门协作困难 生产进度无法及时掌握 制造品质不良 设备故障不能及时处理等困境 企业需要寻求有效的解决方案 MES生产管理系统作为针对制造企业车间生产过程控制和管理的解
  • 如何在使用中文输入法的时候打出英文字符

    解决方法很简单 只需要按CTRL 就可以实现中英文字符切换 这样在按字母的时候还是会显示拼音 但输入字符时都是英文字符了 避免了频繁的shift切换
  • 【Qt学习】07:绘图与绘图设备

    OVERVIEW 绘图与绘图设备 一 QPainter 二 QPainterDevice 1 QPixmap 2 QBitmap 3 QImage 4 QPicture 绘图与绘图设备 一 QPainter Qt 的绘图系统允许使用API在
  • unsigned int用法

    注意使用unsigned int 无符号常数 正如我们所知道的 编程语句都有很多的基本数据类型 如char inf float等等 而在C和C 中还有一个特殊的类型就是无符号数 它由unsigned修饰 如unsigned int等 大家有
  • visible.sync 的作用

    我们在前端开发中经常看到 visible sync这种修饰符 很多人不知道这是干什么的 特别是在使用ElementUI的时候 里面有个弹窗el dialog组件的时候会有用到 visible sync
  • VUE项目中的全局格式化时间过滤器

    1 全局过滤器 https blog csdn net weixin 45054614 article details 105368776 全局时间格式化 对名称进行定义 提供一个function函数 originVal 为形参 Vue f
  • Chapter1 ROS概述与环境搭建

    一 序言 学习一个新的知识模块时 要先了解模块的相关概念 安装官方软件包 搭建其集成的开发环境 这些完成后 继续开始开创新领域的大门 二 ROS简介 2 1 ROS的概念 一个比喻 机器人的控制集成硬件设计 嵌入式软件设计 上层软件设计 l
  • Echart 之 timeline 时间组件 基本用法

    此处是基础版 大神请绕路 官方给的timeline组件太过于复杂 对于不熟悉的朋友 只是看逻辑就浪费很多时间 以下是基础版的示例 div style height 100 div
  • Altium AD20删除机械层MECH

    1 滥用MECH机械层导致的PCB过孔错误 前两天打样PCB时犯了个大毛病 导致送打样回来发现有多处网络对GND短路 更可气的是 这是在手贴了两块样板后才发现的 赔进去一下午时间和一堆元件 亏死 刚开始百思不得其解 因为在AD20中规则检查
  • 2021/9/29 TX POWER 蓝牙发射功率

    1 觉得挺奇葩 如果不是扩展包的话 就是TX power 设定 没法通过hci 下发 就是 在controller 设定好 也没法中途去变化 扩展包 有这个选项 能够改变 可以通过HCI 下发指令来更改 2 AD9361 设置发射功率 要注
  • (转)如何选择合适的射频模块

    要选择合适的射频模块 以下几个问题我们都需要搞搞清楚 无线设备该通多远 该用多大的功率 如何扩大通信距离 天线是否该重视 该选什么样的模块 初学者如何选择仪器 如何确定通信状况等等 无线设备该通多远 谈到这个话题 有必要想一想当年了 比如1
  • 一个panic bug的分析过程1

    一个panic bug的分析过程 一 2012 05 15 14 57 06 分类 LINUX 一个工作中遇到的bug的问题 分析一下流程 顺便把panic这种类似的bug流程做一些分析 环境 linux 3 0 arm 芯片平台 首先看出
  • 一台服务器部署多个tomcat

    参考文档 19条消息 一 linux部署多个tomcat 做测试的喵酱的博客 CSDN博客
  • 【算法】经典的八大排序算法

    点击链接 可视化排序 动态演示各个排序算法来加深理解 大致如下 一 冒泡排序 Bubble Sort 原理 冒泡排序 Bubble Sort 是一种简单的排序算法 它通过多次比较和交换相邻元素的方式 将最大 或最小 的元素逐步冒泡到数组的一
  • gradle更新snapshot的jar

    通常 gradle下载引用的jar文件的话 会缓存到本地 不会重复去下载 但是 我们引用的是snapshot的jar 这种jar文件一般是其他项目组的代码 这种jar一般都进行迭代开发 会重复更新上传到nexus代码仓库中 我们必须在每次启
  • linux-vmware workstation安装

    环境介绍 闲着没事测试下linux vmware workstation linux vmware workstation安装 1 搭建软件仓库 root gby mount dev sr0 mnt mount mnt WARNING de
  • TRON节点验证交易的时间容忍度

    这篇文章主要介绍深入分析TRON的节点配置文件中vm minTimeRatio和vm maxTimeRatio这两个标志的意义 这两个标志的表示的是节点 包括sr和fullnode 验证区块中智能合约交易的时间比例 时间容忍度 注 sr节点
  • 【java】swagger中api接口传多个参数 @ApiImplicitParam和@ApiImplicitParams

    目录 1 ApiImplicitParam 2 ApiImplicitParams 3 Spring Boot项目中集成Swagger knife4j并自定义访问路径 swagger常用注解使用说明 1 ApiImplicitParam 作
  • PI闭环的FPGA实现

    PID闭环的FPGA实现 1 原理分析 最近小张同学在做项目的时候发现PI闭环的FPGA学习资料很少 秉持着 既然没有轮子 那么自己就造一个的原则 于是乎自己写了个PI的Verilog程序 FPGA中实现PI闭环与DSP STM32 arm