1. 从零开始理解ZYNQ PS与PL的“双核”对话大家好我是老张一个在嵌入式领域摸爬滚打了十多年的工程师。今天咱们不聊那些虚的就聊一个非常具体、非常实用的场景如何用LabVIEW让ZYNQ芯片里的ARMPS端和FPGAPL端真正“聊”起来实时捕捉几个按键的状态。听起来是不是有点复杂别怕我刚开始接触ZYNQ时也觉得它像个“黑盒子”ARM和FPGA各干各的怎么让它们协同工作是个大难题。但后来我发现一旦掌握了它们之间通信的“桥梁”事情就变得异常简单。这个桥梁就是我们今天要重点玩的寄存器Reg通道。你可以把ZYNQ芯片想象成一个“双核”大脑。PS端的ARM处理器就像大脑的“逻辑思考”部分运行着Linux RT实时系统负责复杂的决策和任务调度。而PL端的FPGA则像大脑的“条件反射”部分由无数个可编程的逻辑单元组成能对硬件信号比如按键按下做出纳秒级的即时反应。我们做的就是在这两者之间搭一座高速数据桥让“条件反射”的结果能瞬间传递给“逻辑思考”中心。这个实验的目标非常明确用LabVIEW写一个跑在FPGA上的小程序实时读取开发板上三个物理按键的电平同时再写一个跑在ARM上的Linux RT程序通过寄存器通道把FPGA读到的按键状态“抓”过来并在电脑上显示出来。整个过程你不需要写一行Verilog或C代码全程图形化编程像搭积木一样直观。我实测下来这套方法特别适合做嵌入式控制、设备状态监测、快速原型验证。比如你可以用它来做一个智能控制面板FPGA负责毫秒不差地扫描所有按钮和传感器ARM则负责处理复杂的UI交互和网络通信。两者分工合作效率和稳定性远超传统的单片机方案。2. 硬件准备与核心原理你的“积木”和“桥梁”工欲善其事必先利其器。在动手写代码之前咱们得先把家伙事儿准备好并搞清楚它们是怎么连在一起的。2.1 硬件清单与接线图这次实验你需要准备以下几样东西正点原子领航者ZYNQ开发板这是我们的主战场。Xilinx JTAG下载器主要用于在线调试和观察FPGA VI的前面板。这里有个小技巧如果你只关心ARM端程序运行结果不打算实时看FPGA的界面这个下载器其实可以不接因为FPGA程序可以由ARM动态加载后面会详细说。一根千兆网线这是连接你的电脑上位机和ZYNQ开发板下位机的生命线ARM端的程序全靠它来部署。接线非常简单用网线连接电脑和开发板的PS端网口如果需要观察FPGA运行再用JTAG下载器连接电脑和开发板的JTAG口。给开发板通上电硬件准备就完成了。2.2 关键引脚与原理找到那三个“开关”我们的目标是读取底板上的三个按键PL_RESET、PL_KEY0、PL_KEY1。别看名字里带“RESET”在ZYNQ的PL端FPGA部分它就是个普通的IO引脚按下去并不会复位FPGA。这里插一句从7系列FPGA开始就没有传统MCU那种专用的复位引脚了FPGA的可靠性极高程序烧进去就能一直跑除非芯片物理损坏。真正的PS端ARM复位引脚是核心板上的PS_RESET。通过查看原理图我们可以找到这三个按键分别连接到了FPGA的哪几个脚PL_RESET-N16PL_KEY0-L14PL_KEY1-K16这三个引脚都属于BANK 35。请务必记住这三个引脚编号等会儿在LabVIEW里配置FPGA I/O时我们就要把它们“召唤”出来。2.3 通信核心寄存器Reg通道详解这是本实验的灵魂所在。PS和PL之间有很多种通信方式比如高速的DMA、FIFO但对于按键这种简单的开关量信号用寄存器通道是最简单、最直接的。你可以把寄存器通道想象成连接ARM和FPGA的一组共享变量。这组变量被划分成了很多“格子”每个格子都有固定的地址和方向。PS2PL_Reg方向是ARM - FPGA。ARM可以把数据写到这些格子里FPGA来读。好比ARM给FPGA下命令。PL2PS_Reg方向是FPGA - ARM。FPGA可以把数据写到这些格子里ARM来读。好比FPGA向ARM汇报状态。我们封装好的通道里有32路专门用于传输布尔Bool类型数据的PL2PS寄存器。这就完美契合我们的需求FPGA把3个按键的“高/低”1/0状态放进其中任意3个“格子”里ARM端的程序定时去读取这3个“格子”就知道按键是按下还是松开了。这种方式的优势是极低的延迟和确定的时序。数据直接在芯片内部通过AXI总线传递没有操作系统调度开销非常适合对实时性要求高的控制场景。3. 动手搭建PL端FPGA程序开发理论说再多不如动手做一遍。咱们先从FPGA端程序开始它的任务就是持续读取引脚电平并把状态“塞”进寄存器通道。3.1 创建FPGA VI与配置I/O首先打开或创建一个LabVIEW项目.lvproj里面应该包含ZYNQ的FPGA终端和Linux RT终端。在FPGA终端下新建一个VI我习惯命名为“实验5-读取FPGA按键状态(ZYNQ PL端).vi”。接下来是关键一步添加FPGA I/O资源。在项目浏览器中右键点击FPGA终端选择“New” - “FPGA I/O”。在弹出的窗口中找到BANK 35展开IN文件夹从列表里精准地找到N16、L14、K16这三个引脚把它们添加到右侧并分别重命名为我们熟悉的PL_RESET、PL_KEY0、PL_KEY1。点击确定后这三个I/O节点就会出现在项目浏览器中等着被我们调用。3.2 编写FPGA程序框图打开VI的程序框图开始“搭积木”设置引脚为输入从项目浏览器中把刚才创建的三个I/O的ENA使能节点拖到程序框图中。右键点击每个ENA节点将其转换为“写入”模式。然后创建一个常量False假连接到这三个ENA端口。这个操作相当于把这三个FPGA引脚配置为输入模式高阻态准备读取外部信号。创建主循环从函数选板放置一个定时循环。这里有个非常重要的细节定时循环的时钟源必须选择lv_FCLK_CLK0_PS2PL。这个50MHz的时钟是由PS端ARM产生并提供给PL端FPGA的是两者协同工作的“心跳”。只有使用这个时钟FPGA和ARM之间的寄存器通信才能同步。读取引脚并显示把三个I/O的IN输入节点也拖进定时循环。为每个IN节点创建一个指示灯显示控件这样编译后我们就能通过JTAG在线看到这三个指示灯的状态直观地调试。连接寄存器通道这是打通“任督二脉”的一步。在项目浏览器中找到并展开PS_System_Reg Data这个Socket CLIP。里面有一组lv_PL2PS_Reg_Bool_xx从1到32的节点这些就是FPGA向ARM发送布尔数据的通道。我们随便选三个比如1、2、3拖到程序框图中。完成连接将三个IN节点按键状态的数据线分别连接到三个lv_PL2PS_Reg_Bool_x节点上。这样按键的电平状态就会在每个时钟周期被送入对应的寄存器通道。至此一个简洁而完整的FPGA端程序就完成了。它的逻辑非常清晰在一个由ARM提供的50MHz时钟驱动下循环读取三个物理引脚的电平一方面送到前面板指示灯用于本地调试另一方面实时写入到PL2PS寄存器通道等待ARM来取。3.3 编译与生成BIT文件程序写好了得把它变成FPGA能执行的“机器码”——也就是BIT文件。首先确保FPGA终端的执行模式是“FPGA Target”而不是仿真模式。在编译前我强烈建议你运行一个我们提供的辅助VILicense-ID-Bitfile-ZYNQ.vi。把它放在“我的电脑”下运行。它的作用是拦截LabVIEW编译过程中生成的原始BIT文件并保存到你指定的路径比如E:\ZYNQ_FPGA_Bit_Files\。路径不要太深且不能有中文我吃过亏因为路径有中文导致后续加载失败排查了半天。点击VI的运行箭头LabVIEW会询问使用哪种编译服务器。对于个人开发选择“本地编译服务器”即可它会调用你电脑上安装的Xilinx Vivado进行编译。在编译配置中务必勾选“Run when loaded to FPGA”加载至FPGA时运行。这个选项决定了生成的BIT文件一旦被加载到FPGA程序是自动运行还是处于挂起状态。不勾选的话你会发现加载后程序没反应。点击确定开始编译。等待几分钟Vivado会完成综合、布局布线。编译成功后在你刚才设置的路径里就能找到新鲜的.bit文件了比如我的是5-PL2PS-KEY.bit。踩坑提醒编译过程中如果报错找不到引脚请回头检查I/O配置的引脚编号是否正确。如果编译时间异常长可以查看“Estimated device utilization”报告如果资源占用超过80%可能需要优化代码。4. ARM端Linux RT程序开发FPGA部分准备就绪现在该ARM端的Linux RT程序上场了。它的任务就是初始化系统、加载FPGA程序、然后不断地从寄存器通道里读取按键状态。4.1 程序流程与核心函数ARM端程序的流程是标准化的对于任何需要与FPGA交互的Linux RT程序几乎都遵循以下几步我把它总结为一个“六步法”PS_Load_FPGA_bit.vi动态加载FPGA比特流。这是第一步也是必不可少的一步。它把刚才编译好的.bit文件加载到ZYNQ的PL端让FPGA程序跑起来。这是ZYNQ设计最精妙的地方之一PS可以随时重置和加载PL的硬件逻辑灵活性极高。PSLoadGPIOKO(SubVI).vi加载GPIO内核模块KO。我们要操作寄存器本质上是在操作GPIO。这个VI负责把必要的Linux内核驱动动态加载到内存中。PS_reg_Open.vi打开寄存器通道。相当于给PS和PL之间的寄存器“桥梁”通电做好通信准备。PL_Reg_Read.vi读取PL端寄存器数据。这就是我们的核心操作从这个VI里我们能拿到FPGA传过来的按键状态。它位于PS2PL_Reg_Data函数选板中注意图标上有“PL2PS”字样表示方向。PS_Reg_Close.vi关闭寄存器通道。程序退出前礼貌地“关闭桥梁”释放资源。PSUnloadGPIOKO(SubVI).vi卸载GPIO内核模块。与第二步对应程序退出前卸载驱动保持系统干净。这六个VI像一套组合拳打下来就完成了从初始化到读取再到清理的全过程。在LabVIEW里编程就是按顺序把这些图标用数据线连起来中间加个While循环实现持续读取。4.2 编写Linux RT VI在项目的Linux RT终端下新建一个VI命名为“实验5-读取PL端FPGA寄存器映射的按键状态(ZYNQ PS端).vi”。摆放函数从“PowerGod-RIO-RT”函数选板中依次找到上述六个VI拖到程序框图中。连线与配置将PS_Load_FPGA_bit.vi的bitfile path输入控件连接到前面板的一个路径控件上。这个路径需要指向ZYNQ Linux RT系统内的一个固定位置通常是/home/lvuser/natinst/bin/data/。我们只需要把文件名改成自己的BIT文件名即可比如5-PL2PS-KEY.bit。PL_Reg_Read.vi需要配置参数。它有一个Reg_Channel输入是一个下拉枚举框里面列出了所有可用的寄存器通道如PL2PS_Reg_Bool_1、PL2PS_Reg_Bool_2等。这里必须和FPGA程序里选择的通道严格对应如果FPGA用的是1、2、3通道这里也要选择1、2、3。为PL_Reg_Read.vi的Reg_Data_Read输出创建三个布尔指示灯用于显示按键状态。构建主循环将PL_Reg_Read.vi放入一个While循环中并添加一个等待函数例如50ms以避免CPU占用率过高。这样程序就会以20Hz的频率不断查询按键状态。错误处理良好的习惯是为整个VI链添加简单的错误处理连线这样一旦某一步出错程序能有序停止并报错。程序框图搭建完成后前面板就包含了BIT文件路径输入框、三个状态指示灯以及一个可能用于显示循环次数的数值框。整个ARM端程序结构清晰逻辑直接。5. 系统联调与现象观察最激动人心的时刻到了让整个系统跑起来看看PS和PL是不是真的“对上话”了。5.1 部署与运行PS端程序首先我们需要把FPGA的BIT文件和Linux RT程序部署到ZYNQ开发板上。网络配置确保你的电脑和ZYNQ板子用网线直连。将电脑的以太网IP地址设置为静态例如192.168.2.10子网掩码255.255.255.0。这是因为ZYNQ Linux RT系统默认有一个静态IP192.168.2.99。添加BIT文件在LabVIEW项目的Linux RT终端下通常有一个名为“ZYNQ PL FPGA bit files”的文件夹。右键点击它选择“Add File”把之前编译好的5-PL2PS-KEY.bit文件添加进来。这一步是为了在部署时LabVIEW能自动将这个文件传输到板载系统的指定目录。创建应用程序右键点击Linux RT终端下的“Build Specifications”新建一个“Real-Time Application”。在属性设置中将我们写好的PS端VI添加到“Startup VIs”并将BIT文件添加到“Always Included”文件列表里。这样编译部署时会一并打包。连接与部署右键点击Linux RT终端选择“Connect”。如果网络设置正确LabVIEW会连接到板子。然后右键点击刚创建的应用程序生成规范选择“Deploy”。稍等片刻程序就会被下载到ZYNQ的ARM中并运行。此时你应该能在电脑上看到Linux RT程序的前面板“活”了Running_PS的数值开始增加说明循环正在运行。但按键指示灯可能还没反应因为FPGA逻辑可能还没加载。5.2 动态加载FPGA逻辑与联合调试接下来是关键一步让PS端程序加载FPGA逻辑。确保PS端程序前面板上BIT文件路径是正确的指向/home/lvuser/natinst/bin/data/5-PL2PS-KEY.bit。点击PS端VI的运行箭头。程序会执行PS_Load_FPGA_bit.vi将BIT文件加载到PL端。此时FPGA硬件逻辑开始运行。可选观察FPGA前面板如果你想同时观察FPGA端的运行情况可以打开之前写的FPGA VI。但这里有个重要设置需要修改一个配置文件通常是ini.txt将其中的某个标志位从1改为0。这个操作是告诉LabVIEW不要尝试通过JTAG下载BIT文件因为已经由PS端加载了而是直接进入“在线前面板”模式通过JTAG仅进行通信和调试。然后运行这个FPGA VI你就能看到它的前面板也“活”了Running_FPGA的数值飞速增长50MHz时钟驱动。5.3 验证交互效果现在整个系统已经准备就绪FPGA端正在以50MHz的速度实时扫描PL_KEY0、PL_KEY1、PL_RESET三个引脚的电平并将状态写入PL2PS_Reg_Bool_1/2/3通道。ARM端正在以约20Hz的速度从PL2PS_Reg_Bool_1/2/3通道读取数据并显示在前面板上。动手实验找到开发板上的三个PL端按键。按下其中一个键比如PL_KEY0。你会立刻看到FPGA VI前面板上对应的PL_KEY0指示灯熄灭因为按键按下接地为低电平。Linux RT程序前面板上对应的Reg_Data_Read_Bool-1指示灯也同步熄灭。同时按下三个按键则两边的三个指示灯会全部熄灭。松开按键所有指示灯恢复点亮因为原理图上拉默认高电平。这种两边前面板状态实时同步变化的现象就是PS和PL通过寄存器通道进行实时、可靠交互的最直接证明。整个过程没有复杂的协议没有繁琐的驱动就是简单的读写共享变量但效率却极高。6. 深度优化与避坑指南按照上面的步骤你应该已经成功实现了功能。但作为实战指南我还想分享一些更深度的优化技巧和常见问题的排查方法这些都是我踩过坑后总结的经验。6.1 性能优化与稳定性提升读取频率的权衡在PS端的While循环里我加了50ms的等待。对于按键检测这绰绰有余。但如果你需要检测更快的脉冲信号这个间隔就需要缩短。不过要注意读取频率越高ARM的CPU占用率也会上升。你需要根据实际信号的频率和系统负载找到一个平衡点。对于微秒级的信号建议在FPGA端做边沿检测或脉冲计数然后通过寄存器传递结果而不是让ARM去轮询高速信号。使用中断替代轮询高级话题本例用的是ARM不断查询轮询寄存器的方式。对于多个信号或低功耗应用更好的方式是使用中断。我们封装的寄存器通道也支持中断功能。可以在FPGA端配置当某个寄存器值变化时向ARM发送一个中断信号ARM收到中断后再去读取数据。这样可以极大降低ARM的负载实现事件驱动。这部分内容会在更高级的实验中涉及。错误处理与资源释放在最终的产品化程序中一定要完善错误处理。确保即使加载BIT文件失败、打开寄存器失败程序也能给出明确的提示并安全退出调用PS_Reg_Close和Unload_GPIO_KO来释放资源。否则驱动模块残留可能会导致下次程序运行异常。6.2 常见问题与解决方案PS端程序部署失败连接超时检查网线确保是千兆网线且连接稳定。检查IP设置确认电脑IP如192.168.2.10和ZYNQ目标IP192.168.2.99在同一网段且子网掩码一致255.255.255.0。关闭防火墙有时电脑的防火墙会阻止LabVIEW与目标的通信尝试暂时关闭防火墙。重启服务在LabVIEW中尝试断开目标连接然后重新扫描或连接。PS端加载FPGA BIT文件失败检查文件路径和名称确保路径是ZYNQ Linux RT系统内的有效路径且文件名没有中文和特殊字符最好全是英文和数字。检查BIT文件确认BIT文件是通过我们提供的License-ID-Bitfile-ZYNQ.vi导出的原始文件并且编译时勾选了“加载时运行”。权限问题确保BIT文件已被成功包含在“Always Included”中并部署到了目标板。可以尝试通过LabVIEW的终端窗口用命令行查看/home/lvuser/natinst/bin/data/目录下文件是否存在。PS端读取到的寄存器值始终不变或不对通道号不匹配这是最常见的问题。百分百确认FPGA程序里使用的lv_PL2PS_Reg_Bool_x通道编号例如1,2,3与PS端PL_Reg_Read.vi中Reg_Channel下拉框选择的通道编号完全一致。时钟不同步确保FPGA程序中的定时循环使用的是lv_FCLK_CLK0_PS2PL这个由PS提供的时钟。如果用了其他时钟寄存器的读写可能无法同步。硬件连接问题用万用表测量一下按键按下和松开时对应FPGA引脚N16, L14, K16的电压是否在0V按下和3.3V左右松开之间变化。FPGA VI在线前面板无法连接检查JTAG连接和驱动确认Xilinx JTAG下载器已连接且电脑设备管理器中能识别到“Xilinx USB Cable”。检查INI文件配置如果希望通过PS端动态加载FPGA那么在用JTAG进行在线调试时需要将ini.txt文件中的相应标志位设为0以跳过下载步骤直接进行通信。PL端已由PS加载如果PS端程序已经成功加载了BIT文件那么FPGA硬件正在运行我们的逻辑。此时再用LabVIEW FPGA VI去连接是可以正常通信并显示前面板的。如果PS端没加载FPGA里是空的或者别的程序连接自然会失败。把这个实验吃透你就掌握了ZYNQ异构系统协同开发中最基础、最核心的一环——数据交互。它就像你学会了如何让大脑的左右半球开始对话。基于这个基础后续无论是传输数组、图像数据还是使用更高效的DMA、FIFO都会变得有章可循。