介绍
在本实验中,我们使用FPGA使用激光和一组称为振镜(“检流计”)的电机控制镜来家用投影矢量图像文件。要做到这一点,就需要我们使振镜移动得足够快,以至于激光所描绘出的路径似乎可以为人类观察者生成图像。具有强大信号处理和I / O功能的FPGA非常适合此高速控制任务。
尽管对于FPGA而言,控制振镜是一项很好的任务,但是从内存中读取矢量图像文件并将其解析为易于绘制的形式是一台复杂的顺序问题,可以在微控制器上更好地完成。除FPGA外,我们使用的片上系统还包括一台基于ARM的微控制器,称为HPS或硬处理器系统。我们在该系统上运行了一台嵌入式Linux发行版,为我们提供了一台干净的操作系统环境来运行代码。使用此HPS,我们能够包含一台C组件,该组件完成所有预处理工作,然后将路径转发到FPGA进行绘制。
高级设计
背景-Galvo汽车
检流计是一种基于施加的电压旋转到特定位置的设备。我们使用两个带反射镜的振镜来控制激光束的路径。考虑图1,该图显示了一组振镜的操作。该设置包括两个沿y轴成直角固定的反射镜,它们的绕x轴(对于y 振镜)和z轴(对于x振镜)旋转,由附加的振镜控制。激光先退出x振镜,然后再退出y振镜,然后退出设备。x振镜的旋转控制激光家用投影位置的x位置,而y振镜的旋转类似地控制y位置。通过使用附带的电机调节反射镜,控制器可以高速移动激光束的家用投影。
图1:一组振镜电动机的操作[1]
整体结构
考虑图2,它显示了我们系统的整体结构。为了使用振镜绘制矢量图像文件,我们首先将其通过在HPS上运行的C代码传递,该C代码从内存中读取文件,对其进行解析,然后将其传递给FPGA。然后,FPGA沿向量路径内插一系列位置,并使用一对DAC将这些位置作为模拟信号发送至振镜。它还使用通用I / O引脚将数字开/关信号发送到激光器。这些信号通过驱动电机和激光器的电气驱动电路。最后,物理系统通过用激光绘制图像来响应电信号
图2:系统图
软件设计-HPS
背景-SVG规范
SVG(可缩放矢量图形)规范描述了一种用于对矢量图形文件进行编码的有效文件格式。为了能够显示以这种格式创建的许多现有矢量图像,我们选择使用SVG标准作为我们项目的基础。
SVG格式基于XML(可扩展标记语言)的结构。也就是说,SVG文件由树形数据结构组成,其中每个节点都以<nodeName> 标签开始,并以</ nodeName> 标签结束。包围在节点的开始和结束标记之间的节点是其子节点。节点也可以携带以<nodeName attributeName =“ attributeValue”>形式编码的属性。SVG标准描述了可在此树数据结构中使用的一组特定的XML标记及其含义。
尽管SVG标准非常大,但与我们的项目相关的部分是<path> 元素。这是用来描述实际矢量路径本身的元素。路径数据以类似于G代码的样式编码为列出命令的字符串。每个命令都包含一台字母来标识它,然后是所需的数字值。该字符串存储为路径元素的d 属性。表1显示了可用的命令及其编码。
Command | Letter (Absolute) | Letter (Relative) | Data | Move To | M | m | X, Y | Line To | L | l | X, Y | Vertical Line To | V | v | Y | Horizontal Line To | H | h | X | Cubic Bezier Curve To | C | c | X1, Y1, X2, Y2, X, Y | Smooth Cubic Bezier Curve To | S | s | X2, Y2, X, Y | Quadratic Bezier Curve To | Q | q | X1, Y1, X, Y | Smooth Quadratic Bezier Curve To | T | t | X, Y | Elliptical Arc To | A | a | RX, RY, X-Axis-Rotation, Large-Arc-Flag, Sweep-Flag, X, Y | Close Path | Z | z | 表1:SVG路径命令
路径始终从绝对移动开始,以定位所有将来的坐标。从那里,命令定义了一系列路径段,这些路径段可以是直线,二次曲线,三次曲线或椭圆曲线。该规范还包括常用段类型的简写形式-水平线,垂直线和平滑贝塞尔曲线(它们会自动推断出它们的第一台控制点)。close path命令生成一条返回到子路径开头的行。
处理SVG文件
由于SVG文件基于极其常见的XML格式,因此有许多开放源代码库可以从内存中读取它们并将它们解析为树数据结构。我们选择使用libxml2库是因为它的界面简洁,许可的MIT许可证以及可靠性方面的普遍声誉。
在使用libxml2解析SVG文件之后,我们的下一台任务是提取所需的信息,并丢弃其余信息。我们首先从根节点的属性中拉出宽度和高度。然后,我们在树上行走,寻找任何路径元素,以便提取它们的路径数据。
不幸的是,由于SVG路径只是编码为字符串,因此在从文件中提取路径数据后,我们仍然需要将其传递给另一台解析器,以将其转换为可用形式。为了实现这一点,我们使用flex lexer生成器和bison解析器生成器编写了一台小型解析器。该解析器扫描路径字符串并吐出段命令的链接列表。解析文件中的所有路径后,我们将这些列表链接在一起以创建单个大路径。
在将此路径发送到FPGA之前,我们先对其进行了多次预处理,这有助于降低FPGA代码的复杂性。我们应用了以下转换
- 将所有相对坐标转换为绝对坐标
- 将所有速记命令转换为其等效的速记形式
- 用与命令等效的行替换关闭路径命令
- 缩放并偏移所有坐标,使其在[0 + 20,255-20]范围内
- 从路径的末端到中心添加最后的移动
缩放和偏移转换是对我们硬件现实的一种让步。我们使用8位DAC输出每个轴的模拟信号,因此每个轴只有255个位置。而且,由于镜对准困难,我们不愿意一直走到振镜的最远位置。因此,我们选择在比例尺的任一端刮掉20个位置障碍。这导致转换到范围[20,235]
由于可以将椭圆弧转换成等效的三次贝塞尔曲线,而误差很小,因此我们决定避免在FPGA上实现椭圆弧,而改为在软件中进行处理。但是,在项目结束时我们用光了时间,从没有真正实现此功能。
QSys界面
QSys是专有的Altera互连,可处理基础结构以允许HPS与FPGA进行通信。Altera开发环境中内置了将模块放置在总线上并为其分配地址的工具。为了从我们的C代码与FPGA对话, 我们在QSys总线上使用了轻量级的主机。我们首先将主机的物理基地址映射到C代码的虚拟地址空间中。然后,我们为QSys总线上的每个组件添加了适当的地址偏移量,以计算可用于与之通信的指针。图3显示了QSys总线的地址映射。
图3:QSys地址映射
为了控制FPGA上的求解器,我们使用了并行端口,使HPS可以同时访问复位线和使能线。将复位线保持为高电平会完全使系统复位,而将使能线保持为低电平会使FPGA完成绘制图像,然后停止而不是无限期地重复图像。当FPGA达到此结束状态时,我们使用了从FPGA到HPS的另一台并行端口,向我们的C代码发出信号。
此启用/完成界面允许我们在加载新图像之前干净地完成图像绘制。由于所有图像路径都在中心位置开始和结束,因此不会导致振镜位置出现不连续的跳跃,从而平滑了它们的行进路径。由于振镜很脆弱,对尖峰或跳跃的响应不佳,因此这是理想的属性。
除了并行端口接口,我们还使用了一组RAM块来保存路径数据。它们由一台path_type RAM块和一台X0,X1,X2,X3,Y0,Y1,Y2,Y3 RAM块组成,该块保存每个段的控制点。给定块中的第n个条目对应于所有其他条目中的第n个条目。由于并非所有命令类型都使用所有四个控制点,因此我们将未使用的条目留为空白。我们还包括一台特殊的END 命令作为RAM块中的最后一台条目,向状态机发出信号,告知它应返回到开头并重复映像路径。
应用旋转
使家用投影图像旋转是涉及正弦和余弦的复杂转换。由于这种旋转的时间尺度要比用于控制振镜的高速模拟信号慢得多,因此我们选择在HPS上进行数学运算,并定期将新帧发送到FPGA。我们发现缓慢的旋转速度(每旋转> 3秒)与每秒10帧的更新速率相结合,可以创建相对平稳的运动。
硬件设计-FPGA
定点格式
我们选择以带符号的二进制补码10.18定点格式进行所有数学运算。由于我们的输出需要是一台无符号的8位数字,因此选择10个整数位使我们能够省事地检测上溢(设置第二高的位)和下溢(设置最高的两个位)。18个小数位使我们可以沿路径进行高精度插值。
背景-Bresenham的线算法
尽管比许多世纪甚至几千年的纯数学领域还年轻,但计算机图形学如今已成为60多年来引起人们关注的领域。这意味着几乎所有简单的图形任务都具有经典且完善的算法来执行它们。其中包括Bresenham的Line Algorithm,它提供了一种优雅的方法来在像素网格上的两个点之间绘制一条线。
布雷森汉姆(Bresenham)的线算法将这项任务分为两种情况:一种情况是直线斜率的大小小于或等于一种,另一种情况是斜率的大小大于或等于一。我们将从考虑第一种情况开始。
plotLineLow(x0,y0, x1,y1)
dx = x1 - x0
dy = y1 - y0
yi = 1
if dy < 0
yi = -1
dy = -dy
end if
D = 2*dy - dx
y = y0
for x from x0 to x1
plot(x,y)
if D > 0
y = y + yi
D = D - 2*dx
end if
D = D + 2*dy清单1:小于或等于1的坡度的布雷森汉姆线算法[2]
考虑清单1,该清单显示了该算法的伪代码。通过循环,我们的增量在每次迭代X 和更新d 。每当 D 超过0时,我们也会使y递增。这使我们可以逐像素逐行地移动。
plotLineHigh(x0,y0, x1,y1)
dx = x1 - x0
dy = y1 - y0
xi = 1
if dx < 0
xi = -1
dx = -dx
end if
D = 2*dx - dy
x = x0
for y from y0 to y1
plot(x,y)
if D > 0
x = x + xi
D = D - 2*dy
end if
D = D + 2*dx清单2:斜率大于或等于1的布雷森汉姆线算法[3]
目前考虑清单2,该清单显示了大于1的斜率的伪代码。该算法与第一种情况完全对称:我们 在每次迭代中使y递增,而 当D 超过0时使x递增。 使用Bresenham算法仅需要确定要应用哪种情况,然后委派给相关子例程。
线插补
为了允许我们为直线插值点并移动svg路径命令,我们在FPGA的硬件中构建了Bresenham的Line Algorithm的实现。我们设计了同时实现lineLow 和lineHigh的模块 ,然后将它们包装在一台更高级别的模块中,该模块将二者复用在一起,以便将适当的模块发送到输出。
图4:lineLow 模块的状态机图
图4显示了lineLow 模块的状态机。系统最初处于状态0,等待有效输入。当in_val 线变为高电平时,它转换为状态1,并将所有值从输入线复制到寄存器中。在下一台循环中,它将初始化x ,y和D, 并转换为状态2,在此开始运行循环。在每次循环中,它递增x ,更新D , 如果D> 0则递增 y 。然后,它过渡到状态3,在该状态下它会发出out_val 并等待out_rdy 线走高,表明另一边已准备好接受交易。发生这种情况时,它将返回到状态2并计算下一台点。当x 最终到达末尾时,它将转换回状态0并引发done ,表示顶级模块已完成该行。然后,它等待in_val 再次变高,这时它将开始计算新行。
lineHigh的实现 与x 和y的角色完全对称 。
为了防止输出上溢和下溢,包装模块检查x 和y的高两位。如果设置了最高位(指示下溢),那么它将返回0。如果设置了第二个最高位而不是最高位,则表明溢出,然后它返回255。这样可以防止溢出错误传播到输出。
二次插值
与具有简单的不需要插值的像素插值算法的直线不同,实现与Bresenham直线算法等效的二次曲线非常复杂。因此,我们决定根据二次贝塞尔曲线的控制点对简单参数化进行插值。
图5显示了实现该方程式的电路框图。我们首先计算
,
和
。然后,我们将每个项与 每个控制点的
和
输入相乘,并将它们相加在一起以创建
和
输出。第二项的乘以2是使用左移实现的。在将结果发送到输出之前,我们将其通过与线路相同的溢出保护电路。这种设计导致电路的两倍输出延迟。
图5:二次贝塞尔曲线框图
像这样沿参数方程插值会产生一些问题。根据的步长
,我们可能会跳过一些像素,并在振镜位置产生不连续的跳跃。或者,输出图像中的某些像素可能比其他像素具有更多的内插点别名。由于我们的物理设置的性质,这将导致激光在这些像素上花费更多的时间,因此这些位置看上去比它们的邻居更亮。
我们通过选择较小的步长来解决第一台问题(我们选择了1/512,这对于我们的255 x 255输出网格已经足够了)。为了处理第二个问题,我们在状态机中内置了逻辑,以便仅在像素别名发生变化时才输出点。这导致需要花费可变的周期,并且可能需要大量的周期来计算曲线中的下一台点。但是,由于我们的求解器运行的时钟速度比用于将输出馈入振镜的时钟快得多,因此这种不规则的生产率不会造成任何问题,并且在需要时我们总是准备好了点。
图6:用于二次贝塞尔插值模块的状态机
图6显示了我们的二次Bézier插值模块的状态机。我们使用与线路模块相同的接口来使通信统一。最初,我们处于状态0,等待有效输入。当 in_val 变高时,我们将输入复制到寄存器中,并转换到状态1。在状态1中,我们增加t。我们将插值电路的输出与last_x和last_y进行 比较, 后者存储我们发送到输出的最后一台像素的值,以确定该像素是否已更改。如果像素改变了,或者它是第一点,那么我们进入状态2,提高out_val, 并通过提高out_rdy等待顶层模块接受我们的输出。发生这种情况时,我们完成交易并返回状态1以继续进行插值。如果像素没有改变,那么我们将保持状态1。当t最终达到1时,我们将返回状态0并完成操作,并告知顶层模块我们已经完成了曲线。
三次插值
像二次Bézier曲线一样,三次Bézier曲线也没有简单的Bresenham样式像素插值算法。因此,我们再次选择通过 以参数化形式从0步进到1来实现它们。
二次贝塞尔曲线的参数化形式是线的加权和,而三次贝塞尔曲线的参数化形式是二次贝塞尔曲线的加权和。这使我们可以省事地重用二次贝塞尔插值模型中的计算代码。
图7:三次贝塞尔曲线框图
图7显示了实现三次插值的逻辑框图。我们取 两条二次贝塞尔曲线的x 和y输出,并将它们乘以适当的权重,然后将它们求和以创建输出。这为该序列增加了一台额外的乘法,从而为整个电路创造了三个乘法延迟。
三次贝塞尔插值模块使用与二次贝塞尔插值模块相同的接口,并具有相同的状态机。唯一的区别是,它使用更复杂的电路为x和内插新值y。
顶级求解器
创建模块以沿三种不同的路径段类型对像素进行插值后,我们创建了一台顶层求解器模块,以从RAM中读取命令并将其分配给适当的插值器。我们创建了9个独立的RAM块-一台用于段类型,八个用于保存四个控制点的x和y坐标。每个RAM中的第n个条目与其他所有RAM中的第n个条目相对应,这意味着不需要所有四个控制点的命令仅留在某些RAMS中。
图8:顶级SVG求解器状态机图
图8显示了顶级求解器的状态机。我们从状态0开始,在该状态下我们并行读取9个RAM块。我们进入状态1,然后进入状态2,这时读取的结果已准备好,我们将RAM的输出传输到我们的寄存器中,并继续进入状态3。然后,我们根据状态寄存器的内容调度一台命令。类型寄存器。如果是移动命令,我们将关闭激光器,并将控制点发送到电源模块,而对于线路命令,我们将使用电源模块,但保持激光器开启。对于二次贝塞尔曲线,我们使用二次曲线模块,对于三次贝塞尔曲线,我们使用三次曲线模块。在这两种情况下,激光都保持开启状态。对于所有这些命令,我们进入状态4。如果相反,类型寄存器将保留特殊的END。 命令,然后我们到达了路径的尽头。如果启用了求解器,则我们将读取地址重置为0并返回到状态0以从头开始重新开始路径。如果不是,那么我们进入状态7,提升完成的 并行端口,然后等待求解器再次启用,以便我们返回状态0。
在状态4中,我们降低in_val 线,然后进入状态5,在该状态中,我们等待输出fifo中有空间,并等待 相关插值器的out_val 或完成线上升。如果完成 了,则我们增加读取地址并返回状态0,以从RAM中读取下一台命令。在另一方面,如果OUT_VAL 升高然后我们提高out_rdy 完成交易,写点和激光器的输出FIFO的状态,并移动到状态6.最后,在状态6中,我们完成了FIFO写入和回归状态5等待下一台点或线段的完成。
驱动输出
我们使用的廉价振镜无法以我们的求解器运行时的高速准确响应控制信号。为了处理消耗点的速率与产生点的速率之间的不匹配,我们使用了一组fifo队列-一台用于x通道,一台用于y通道,另一台用于激光器的状态。这些队列的写端在求解器时钟上,只有在fifo中有足够的空间来存储它们时,求解器才会生成点。队列的读取端的时钟要慢得多,它驱动振镜。
为了生成此读取时钟,我们使用在50 MHz参考时钟的上升沿递增的计数器作为时钟分频器。然后,我们将该计数器的几个不同的高阶位混合在一起,并使用板子的某些开关作为选择线。这使我们能够通过拨动一些开关来控制振镜时钟的速度。
然后将从fifo读取的x和y数据发送到板上的高速8位DAC。该硬件最初旨在用于VGA端口上的模拟彩色通道,但是通过禁用常规VGA功能,我们能够将它们重新用作振镜的驱动器。我们将激光器的控制信号输出到通用I / O引脚上,因为它是由数字信号而不是模拟信号控制的。
尽管存在一些非线性家用投影效果,我们可以通过将输出通过反正切函数进行补偿,但是在我们的角度下,系统处理的小角度近似值会使它们过小而无法使用。
硬件设计-电气
激光电路
在这个项目中,我们使用了从亚马逊购买的经过分解的廉价激光笔。代替了原来设计的AA电池,我们使用了一台很小的5V DC电源。
我们需要根据正在执行的命令类型来打开或关闭激光器。为了正确渲染图像,需要在移动和结束命令期间以及在直线,二次曲线和三次曲线命令期间关闭激光。为此,我们使用FPGA的GPIO引脚之一来输出 生成的laser_on信号由求解器。
图9显示了我们的激光驱动器电路。我们使用了由laser_on在基极驱动的TIP31 NPN晶体管来形成低侧开关。我们使用1kΩ电阻R5限制了从I / O引脚汲取的电流,以免损坏FPGA引脚,并且使用50Ω电阻R6限制了激光器电流,以防止过热并降低激光器的亮度。一台舒适的水平。经过一些测试后,我们添加了电容器C1来减少由激光电路引起的电压纹波,因为它在检流计放大器电路中引起失真。
图9:激光驱动器电路。
振镜巡回赛
最终,我们最终订购了最便宜的振镜装置,该装置来自Seeed Studio [4]。但是,由于价格低廉,因此无法提供清晰的文档。当我们收到订单时,它包含两个振镜,一台驱动器板和一台电源的组件。在对文档和电缆进行了一些摸索之后,我们确定了如何将它们正确地钩在一起。
所提供的电源输出+ 15V,地和-15V,均已馈送到驱动器板上。然后,驱动器板获取了介于-12V至+ 12V范围内的两个模拟控制信号,以控制X和Y振镜的角度。为了足够快地移动振镜以生成可识别的图像,我们需要高速数模转换器(DAC)。幸运的是,我们的FPGA板随附了一组高速8位DAC,打算由VGA模块使用,因此我们为此选择了VGA模块。
尽管VGA DAC的速度足以控制振镜,但输出电压范围为 0V至0.7V,因此我们需要在将VGA输出发送到振镜驱动器电路之前对其进行偏移和缩放。我们使用两个差分运算放大器来完成此操作。
考虑图10,它显示了我们的振镜驱动器电路。差分放大器放大两个输入之间的差异。运算放大器的反相输入是直流电压,因此输出是被直流电压补偿然后被放大的同相输入信号。电位器R4和两个R3电阻在左侧形成分压器,用于调节失调电压。放大器的增益由R2与R1之比确定。我们选择R1和R2来获得10的增益,因为它将振镜输入安全地保持在范围的中心。
图10:放大电路,用于将VGA DAC X和Y输出(0V至+ 0.7V)转换为振镜的输入范围(-12V至+ 12V)。
硬件设计
为了使激光与检流计镜机械对准,我们设计并3D打印了一台小的安装部件。我们将振镜拧紧到安装座上,并使用对准标签使激光器将其粘在正确的位置。最后,我们在支脚的底部增加了支脚,以使其可以水平放置。附录E 提供了下载CAD文件的链接。图11显示了机械设置。
图11:激光和振镜的机械对准
我们将电路放在坚固的纸板箱内,将安装好的振镜和激光器放在顶部,并在盒子上切孔,以允许电缆在适当的地方进出。图12提供了机箱的后视图,图13显示了其中的物品。
图12:机箱的后视图
图13:机箱内部
测验
在Verilog中进行任何工作之前,我们的第一步是确保可以驱动订购的振镜。我们使用直接数字合成将VGA DAC设置为在y通道上输出正弦波,在x通道上输出余弦波,并构建了放大器电路。在确认DAC输出正确,放大后的DAC输出正确之后,我们将系统挂接到振镜驱动板上,然后将激光笔照在镜子上。很难保持稳定,但是我们仍然能够在输出中看到一台非常清晰的圆圈-一台很好的第一台起点。
接下来,在继续编译FPGA代码之前,我们花了一段时间测试仿真中的求解器设计,以确保其性能正确。我们分别测试了每个不同的插值器模块,然后一起测试了顶级求解器模块。在每种情况下,我们都导出了仿真的输出波形,并在MATLAB中将其绘制出来,以验证我们是否正确地沿路径插值了点。
在设法使求解器在FPGA上工作之后,我们将示波器探头链接到DAC通道以查看波形。当我们对它们是正确的感到满意时,我们将它们通过了放大器电路,然后从那里进入了振镜驱动器板。在这一点上,我们设法使电动机正确运行,但是由于某些原因,我们的设计从未打开激光器。为了诊断此问题,我们使用SignalTap(一种Altera逻辑分析器工具)重新编译了我们的FPGA代码,该工具使我们能够将波形记录在板载RAM中并通过USB查看。使用SignalTap调试我们的系统,我们能够找到问题所在并使整个系统正常工作。
一旦获得了合理的激光输出,我们将通过目视确认结果来完成大部分测试。我们尝试家用投影几个不同的svg文件-一组四个箭头,一台大箭头,一台生物危害标志和一台灯泡。基于这些问题,我们能够诊断设计问题并进行更改以改进系统。我们面临的一台主要问题是进入振镜的模拟信号失真。通过减少承载VGA中模拟信号的链接线的长度,使激光电路远离放大器电路,并在电源上增加一台大电容,我们能够减少(但不能消除)失真。激光。
结果
图14:激光家用投影仪输出示例
图15:生成图14的输入文件
图14显示了我们的激光家用投影仪输出的示例,图15显示了产生它的输入文件。尽管非常清楚和连贯,但路径却失真了,应该将直线渲染为曲线。在我们的实验过程中,我们发现提高时钟驱动振镜的速度可以使输出更清晰并减少闪烁,但同时也使失真严重恶化。此外,我们发现随着系统运行时间的延长和振镜驱动器板开始发热,失真变得越来越严重。通过为振镜驱动器电路提供更好的通风,可以部分缓解此问题,尽管很大一部分失真似乎是我们所使用的硬件所固有的。
结论
我们对我们的结果感到满意,尽管我们当然希望他们可以做得更好。总体而言,我们完成了我们要做的工作,即创建一台工作的矢量激光家用投影仪以及一台与之配套的SVG解析器。我们渲染的图像的质量并不理想,但是尚不清楚是否可以用我们捡到的便宜振镜来做得更好。如果我们有更多的时间来处理项目,我们将花费更多的时间来尝试改善图像质量。我们还希望实现对椭圆弧的支持,或者增加对更多类型的SVG动画的支持,而不仅仅是旋转。考虑到项目时间和预算的限制,我们认为这是一次成功。
就知识产权方面的考虑,我们使用了具有开放许可的几种开源工具来构建项目的HPS部分。我们使用了在GPL许可下发布的linux内核,在MIT许可下发布的libxml2库,在BSD许可下发布的flex lexer生成器和在GPL许可下发布的bison解析器生成器。我们还使用了一些不可再发行的Altera IP来构建我们的FPGA代码。
总而言之,我们在这个项目上度过了愉快的时光,我们为所做的工作感到自豪。感谢布鲁斯和所有技术支持人员的帮助!
<hr/>Appendix C - HPS Code
Appendix D - FPGA Code
Appendix E - CAD Files
References
<hr/>[1]
[2]
[3]
[4]
<hr/>希望大家能够分享点赞打赏,这将会是我继续分享的动力。
随着我的成长,与理论、经验和技术的积累,我将会分享给大家实用和精彩的FPGA+CPU系列的各种知识,希望为我国的集成电路事业贡献一些自个微薄的力量。 |