ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Windows环境下AD9361 no-os工程移植与编译实战指南

Windows环境下AD9361 no-os工程移植与编译实战指南 去年做一个小型无人机的数据链项目甲方现场只提供了一台Windows工控机而手上这块AD9361评估板平时都是在Linux主机上编译、调试的。被逼无奈之下我花了两天时间把ADI的no-os master工程完整搬到了Windows环境下从交叉编译工具链到JTAG烧录把整条路走通了一遍。这篇文章就是这次迁移的完整记录包括工具选型、工程结构拆解、编译过程和排坑经验给同样只能在Windows下开发AD9361的朋友一份能直接照着做的参考。如果你是刚拿到FMCOMMS3-EBZ这类评估板想快速把AD9361跑起来做数据收发实验又不打算为了编译一个工程专门装一台Linux机器那这篇文章应该能帮你省下大量试错时间。1. AD9361 no-os master工程到底是个什么工程1.1 AD9361芯片与no-os软件的搭配逻辑AD9361是ADI推出的一款高性能射频收发器覆盖70MHz到6GHz频段支持200kHz到56MHz的信号带宽内部集成了完整的零中频收发链路。这颗芯片在软件无线电、点对点通信、无人机数据链、雷达模拟器这些领域用得非常多。芯片本身的模拟性能只是一半另一半在于它内部的数字接口和配置机制这些都要靠软件来驱动。ADI针对AD9361提供了几条软件路线一是Linux内核驱动程序适合跑嵌入式Linux的系统二是本文要讲的no-os裸机驱动三是libiio用户空间库配合Linux或跨平台使用。no-os这个名字就是字面意思不依赖操作系统跑直接在硬件上操作外设通过SPI接口配置AD9361的寄存器通过并口或LVDS接口传输采样数据。它的优势在于启动快、没有操作系统开销适合对实时性有要求、又想完全控制硬件行为的场景。1.2 master工程的版本包袱这里要先说明一个容易让人困惑的点ADI的no-OS仓库其实迭代过好几轮目录结构。早些年仓库根目录下会有一个projects/ad9361这样的工程目录再配合drivers/文件夹下的驱动源码整体被称为master工程。后来ADI做了工程重构目录改成projects/ad9361并引入了更灵活的构建变量但很多老工程师和教程里仍然习惯叫它master工程。所以你现在去GitHub上拉代码看到的仓库结构和老教程可能不一致。这种版本差异是新手第一个坑拿一份旧教程去套新代码或者拿新脚本去跑老版本都会遇到文件找不到Makefile规则不存在这类问题。我在后面的章节里会具体讲如何选择版本、如何对应教程。1.3 no-os工程不是只靠软件就能跑的需要特别强调一点AD9361的no-os工程运行的前提是FPGA里已经烧录了配套的HDL比特流。AD9361和处理器之间的数据接口通常是LVDS或CMOS并行数据线需要FPGA做桥接处理器内部的SPI控制器也往往是通过FPGA逻辑映射到AD9361的SPI引脚。ADI提供了完整的HDL参考设计对应不同的开发板比如ZC702、ZC706配合FMCOMMS2/3/4/5评估板都有现成的比特流。软件工程师容易忽视的方向就是我的程序没跑起来首先应该怀疑FPGA里的逻辑对不对。这一点在Windows下开发特别明显因为Vivado和SDK经常是分开安装的有人甚至只装了SDK没装Vivado然后发现没法生成比特流。记住这条依赖链FPGA比特流 no-os裸机代码 正确的硬件连接 能跑起来的AD9361系统。这三者缺一不可任何一环不对都会导致程序看起来编译通过、跑起来没反应。2. Windows下环境准备工具链和终端的正确打开方式2.1 方案选型MSYS2、WSL和虚拟机该怎么选开始之前先解决一个方向性问题在Windows下构建no-os工程到底用什么运行环境我试过几种方案各有各的坑。方案优点缺点适合场景MSYS2/AUR原生终端轻量、和Windows文件系统交互方便、包管理简单有些路径转换要手动处理日常编译no-os、修改驱动代码WSLWindows Subsystem for Linux接近真实Linux环境、交叉编译工具链好装访问Windows文件系统路径麻烦、USB/JTAG透传需要额外配置需要经常执行Linux专用脚本的场景虚拟机跑Ubuntu最接近官方开发环境资源开销大、启动慢、共享文件夹偶发兼容问题需要完整Linux开发环境且不介意重启切换我最终选择的是MSYS2。原因很直接no-os工程本质上就是一套Makefile加C代码只需要一个符合POSIX风格的环境来执行make脚本。MSYS2提供了bash、make、git、perl这些工具而且和Windows文件系统交互非常自然编译生成的elf文件可以直接被Windows下的XSCT工具读取烧录不需要做虚拟机网络映射。如果你的开发流程还涉及到Linux内核驱动移植、或者经常要跑Python脚本那WSL可能更合适。但对于纯no-os裸机工程MSYS2的轻量优势非常明显。2.2 ARM交叉编译工具链安装与验证no-os工程的运行目标通常是Zynq-7000系列SoC或者ADI自家的微控制器平台我这次用的是Zynq平台CPU是ARM Cortex-A9架构所以要用ARM的裸机GCC工具链。建议去Arm官方站点下载gcc-arm-none-eabi-10.3-2021.10-win32.exe这个安装包。安装的时候有个细节要注意默认路径是C:\Program Files (x86)\Arm GNU Toolchain arm-none-eabi\10.3 2021.10路径里带着空格和括号这会给后续Makefile调用编译器带来麻烦。我的做法是改安装路径为C:\arm-gcc直接把版本优先级降低图个清静。装完以后需要把编译器bin目录加入系统PATH环境变量。验证方式arm-none-eabi-gcc --version如果能看到类似arm-none-eabi-gcc (GNU Arm Embedded Toolchain 10.3-2021.10)的输出说明编译器已经可用。注意这里装的是ARM裸机GCC和Windows本地GCC完全不是一回事。不要试图用你装了Python或者搞Web开发时装的MinGW GCC来编译ARM代码它们是给不同目标平台用的。2.3 获取no-OS源码与版本选择ADI的no-OS源码托管在GitHub上仓库名就是analogdevicesinc/no-OS。直接用git拉代码git clone https://github.com/analogdevicesinc/no-OS.git cd no-OS git tag -l拉下来以后建议先看一下有哪些版本标签。我的经验是不要直接拿最新main分支因为有些新提交还没来得及补充完整文档可能存在已知问题。建议选择稳定的发布版本比如2021_R2或者2022_R2这两个版本对AD9361的支持都很完整结构也相对清晰。这里有一个关键对应关系no-OS的版本需要跟HDL的版本尽量对齐。ADI的release note里会标注某个no-OS版本配套哪个HDL版本如果软件和硬件版本差距太大有可能出现寄存器配置不一致、数据接口时序对不上的问题。我这次用的组合是no-OS 2021_R2搭配HDL 2021_r2。2.4 辅助工具的安装make、perl和sh在MSYS2环境下安装辅助工具非常方便打开MSYS2终端执行pacman -Syu pacman -S make git perl diffutils findutils这里有个容易踩的坑MSYS2默认会安装一个make但它的实际命令可能是make而不是mingw32-make。某些环境下如果同时安装了MinGW的make会出现版本冲突。我的建议是只用MSYS2自带的make不要额外安装或改变PATH顺序否则后面执行make时会报一些莫名其妙的老毛病。验证make是否正常make --version如果看到GNU Make 4.3这类版本号说明环境基本可用。另外MSYS2终端和Windows命令提示符的路径表示不一样。MSYS2里C:\my_project写作/c/my_project这个细节虽然小但是在导入工程路径时需要特别注意。你可以在MSYS2终端里直接cd到Windows路径对应的目录也可以把源码直接放在C:\msys64\home\你的用户名\下面这样访问起来最顺。3. no-os master工程结构拆解与关键文件修改3.1 目录结构总览以2021_R2版本的no-OS仓库为例拉下来以后主要的目录结构如下no-OS/ ├── drivers/ │ ├── ad9361/ │ │ ├── ad9361.c │ │ ├── ad9361_api.c │ │ ├── ad9361_api.h │ │ ├── ad9361.h │ │ └── ad9361_conv.c │ ├── axi_ad9361/ │ └── ... ├── platforms/ │ ├── xilinx/ │ │ ├── xilinx_spi.c │ │ ├── xilinx_gpio.c │ │ ├── xilinx_timer.c │ │ ├── xilinx_platform.c │ │ └── ... ├── projects/ │ └── ad9361/ │ ├── common/ │ ├── zc706_fmcomms3/ │ │ ├── Makefile │ │ ├── main.c │ │ └── ... ├── include/ └── Makefiledrivers/ad9361下是芯片驱动本体platforms/xilinx是平台适配层projects/ad9361下面按不同开发板细分。以ZC706FMCOMMS3为例工程目录是projects/ad9361/zc706_fmcomms3。3.2 平台适配层的核心价值很多初学者打开工程后一头扎进ad9361.c看AVT配置和收发寄存器但其实真正决定工程能否运行的是platforms/目录下的那几组函数。no-os对平台的抽象非常简洁platform.h里定义了SPI读写、GPIO控制、定时器这些基础接口。Xilinx平台的实现就在xilinx_spi.c、xilinx_gpio.c这些文件里面。理解平台层的关键在于AD9361的配置是通过SPI接口发送寄存器读写命令来实现的而SPI的底层实现完全由平台决定。在Zynq平台SPI控制器可能直接映射到ARM的SPI外设也可能通过AXI接口连接FPGA内部的SPI IP核。不同板子对应不同的寄存器地址这些地址在平台初始化代码里写死。如果你换了开发板或者在自定义板卡上跑最需要改的就是这一层。调试过程中如果发现写寄存器没反应、回读全是0xFF八成是平台层的SPI基地址配置不对而不一定是芯片的问题。3.3 驱动初始化流程和关键结构体main.c里做的事情通常很简单初始化平台、初始化DDR、调用ad9361_init。ad9361_init是整颗芯片的配置入口它接收一个ad9361_init_param结构体里面包含了参考时钟频率、RF端口配置、采样率、带宽、滤波器配置等一大堆参数。部分关键参数参数含义典型值reference_clk_rate参考时钟频率40MHz取决于板载晶振rx_gain接收增益模式0快速攻击AGC1手动tx_attenuation_md发射衰减控制模式0手动1自动freq射频中心频率24000000002.4GHzrx_bandwidth / tx_bandwidth基带带宽2000000020MHztx_fir_enableTX FIR滤波器使能1这些参数定义在ad9361.h的ad9361_init_param结构体里。初次上手时建议先照着数据手册和参考设计把参考时钟和频率带宽设对其他高级参数先维持默认跑通了再逐步优化。3.4 针对你自己的板卡需要改什么如果你用的不是ADI官方评估板而是自己画的板子那主要改动集中在三个地方第一参考时钟频率。AD9361需要一个外部参考时钟通常是40MHz或38.4MHz晶振这个值必须和电路板实际使用的晶振一致否则PLL锁不住频率配置全部会失败。第二SPI读写时序参数。在芯片手册的SPI Interface章节会明确支持CPOL/CPHA的两种组合模式。no-os的驱动里默认配置可能只要一种如果你的硬件设计导致SPI模式和默认不一致需要在平台层调整。第三GPIO映射。AD9361的使能引脚、复位引脚、TX/RX切换相关的GPIO编号在平台代码里定义不同板卡的连接可能完全不同。最典型的就是复位引脚的GPIO号一些评估板上连到了FPGA的某个用户GPIO有的板子连到了ARM处理器的MIO改错了会导致芯片一直处于复位状态代码跑半天毫无反应。4. Windows下编译的完整过程与实测排坑记录4.1 编译命令详解在MSYS2终端里进入工程目录执行make PLATFORMzync CROSS_COMPILEarm-none-eabi- all注意这里的PLATFORMzync是我这台机器的实际值具体值要到projects/ad9361/zc706_fmcomms3/Makefile里去确认。不同的硬件平台对应不同的PLATFORM取值常见的有xilinx、altera、versal等。老版本统一的PLATFORM是xilinx新版可能细化到了zync或versal。如果发现PLATFORM名称不对编译会在平台文件搜索阶段就报错。编译过程会先编译drivers和platforms下的各个模块最后链接出ELF文件。第一次编译时间可能比较长之后增量编译就快很多。4.2 编译报错对照表我最常遇到的几个问题这里把我在Windows环境下实际踩过、且具有代表性的编译错误整理成表错误现象根本原因解决办法arm-none-eabi-gcc: command not found编译器路径没在PATH中或安装路径含空格把C:\arm-gcc\bin加入PATH重新打开MSYS2终端make: command not foundMSYS2里make未安装pacman -S makefatal error: stdint.h: No such file or directoryGCC的sysroot路径被环境变量干扰检查是否设置了错误的CPATH或C_INCLUDE_PATH清掉后重试unknown type name uint32_t头文件搜索路径缺include目录检查Makefile的CFLAGS里是否有-Iinclude或者对应心跳的-I路径/c/Program Files (x86)/...: No such file or directory路径里有空格导致Makefile解析异常将工具链安装在无空格路径如C:\arm-gccline endings are not consistent或编译器诡异报错Windows换行符CRLF和Linux换行符LF混用执行git config core.autocrlf false重新拉取代码或用dos2unix批量转换undefined reference to main链接时找不到入口函数检查是否把main.c排除在编译列表外或链接脚本入口设置错误其中CRLF的问题是Windows下最具迷惑性的坑。no-OS源码仓库里文件默认是LF换行但如果你用某些Windows文本编辑器动过源文件它可能自动转成CRLF后续git再拉代码时就会混行。GCC对LF换行的要求很严格一旦源码里混了CRLF编译器报错位置往往和实际错误风马牛不相及。我最严重的一次是报错指向ad9361.c第300行实际问题是另一个文件被改了换行符。排查方法很简单file drivers/ad9361/ad9361.c如果输出里带with CRLF line terminators就说明需要转回LFsed -i s/\r$// drivers/ad9361/ad9361.c4.3 生成产物和确认编译成功的标准编译成功后会在工程目录下生成build子目录里面是各个模块的目标文件以及最终的ELF文件。以zc706_fmcomms3为例生成的可执行文件通常叫ad9361.elf。确认编译成功的标准不只是没有error还要确认生成了完整ELFls -l build/ad9361.elf用编译器自带的工具查看ELF的CPU架构和入口信息arm-none-eabi-readelf -h build/ad9361.elf在ELF头信息里Machine字段应当显示ARMEntry point address应当是链接脚本指定的入口地址。如果入口地址是0x00000000而链接脚本里定义的DDR地址不在那里说明链接脚本加载位置可能不对启动阶段会有问题。5. 下载、运行与第一轮验证5.1 烧录路径JTAG直连最稳妥Zynq平台的启动方式很灵活可以从SD卡启动、从QSPI Flash启动也可以通过JTAG把程序直接加载到DDR里运行。对于调试阶段我强烈建议用JTAG直连省去格式化SD卡、做BOOT.BIN的流程改代码重编译直接加载调试周期短。Windows环境下常用的两个工具是Xilinx Vitis/SDK自带的XSCT命令行工具和OpenOCD。我这边用的XSCT因为它对Zynq的原生支持最好不需要额外写板级配置。XSCT工具在Vitis安装目录下比如C:\Xilinx\Vitis\2021.2\bin\xsct.bat。启动之前需要确保USB-JTAG驱动已经安装好通常Vitis安装的时候会提示安装Cable Drivers。在XSCT终端里连接目标板并加载程序connect targets -set -filter {name ~ *A9*} rst -system after 500 dow build/ad9361.elf condow是下载到DDR的命令con是继续运行。如果一切正常程序会从ad9361的初始化函数开始执行。5.2 上电后第一件事观察串口和电平状态程序跑起来以后首先要确认的是CPU是否在正确执行。最简单的方式是串口打印main.c里初始化完之后一般都有printf输出或者至少会配置一个UART打印初始化信息。如果串口终端上什么都看不到先别急着调AD9361要确认是不是程序根本没跑到main。确认方式在main函数开头加一个GPIO翻转或者点亮LED的操作。我就遇到过编译下载都正常但程序根本没执行原因是DDR没有完成初始化CPU一跳到DDR地址就死了。Zynq平台跑外部程序之前必须要先运行FSBL或者用XSCT的dow命令下载并运行FSBL来初始化DDR。直接下载裸机ELF到DDR地址会因为没有初始化DDR控制器而崩溃。正确顺序是connect targets -set -filter {name ~ *A9*} rst -system dow -data ../fsbl.elf con # 等FSBL跑完初始化DDR之后 rst -processor dow build/ad9361.elf con或者更简单的方法在XSCT里用dow -range 0x00100000这种命令把程序放到OCM片上内存里跑OCM不需要DDR初始化但空间有限。调试阶段的替代方式就是编译时把链接脚本的加载地址改到OCM里。5.3 寄存器回读验证比频谱仪更快的确认方式如果程序已经跑过ad9361_init此时通过调试器或者再编写一段回读代码读取AD9361的寄存器是验证芯片与CPU通信是否正常的最快路径。AD9361有一个SPI配置寄存器地址是0x00芯片的版本信息可以从这个寄存器读出来。以AD9361为例0x00寄存器中的低6位是REVISION ID常见的版本包括0x82表示Rev2等。如果你用的是no-os提供的API直接在main.c里加一段uint8_t reg_val; ad9361_spi_read(ad9361_phy, 0x00, reg_val); printf(AD9361 REG0x00 0x%02X\r\n, reg_val);能够读到非0xFF的值基本可以断定SPI链路、芯片上电和复位状态都是正常的。这一步比拿频谱仪看信号快得多建议每次调整硬件连接后都先做寄存器回读。继续往后验证可以调用API配置一个小信号输出然后用频谱仪观察。典型配置是设单音ad9361_set_tx_lo_freq(ad9361_phy, 2400000000); ad9361_set_tx_attenuation(ad9361_phy, 10000);实际测试中设完频率后频谱仪中心频率调到2.4GHz应该能看到一个明显的单音信号。把这个简单流程跑通说明从SPI配置到射频链路都已经可以工作后面再做调制解调实验就有了基础。6. 从能编译到能开发几个过来人的忠告6.1 版本对齐HDL、no-OS和Vitis的配套关系开发AD9361最忌讳的就是版本随便搭。ADI每一代release都做了严格的配套验证比如no-OS2021_R2对应HDL2021_r2Vitis版本建议用2021.2。这个配套关系在ADI官方Wiki的Release Notes页面写得很清楚开始项目前务必花十分钟对一遍。我见过一个案例有人拿着no-OS最新代码配合Vivado 2019.2生成的比特流结果HDL里的AXI寄存器映射变了no-OS程序读写的SPI地址对不上AD9361根本配置不进去排查了好几天。根源就是软件和硬件设计版本不匹配。Windows开发环境下这个问题更容易发生因为各种版本的工具链装得多同一个工程被拿到不同版本的Vitis里编译链接脚本可能都会变。6.2 调试工具的补充建议纯Windows环境下调试裸机程序在线的调试器终端是主要手段但我推荐再配合两个工具一是AD936x Filter Design软件ADI官方推出的滤波器设计工具。no-os驱动内部有FIR滤波器配置你可以用这个软件根据你的采样率和带宽要求生成滤波器系数然后填入驱动代码里。这个工具是GUI界面Windows下直接跑极大降低了配置RF前端的门槛。二是逻辑分析仪或者示波器调试SPI时序时看波形。Windows下配合国产的逻辑分析仪很便宜几百块就能买到看到SPI片选、时钟和数据线的实际波形比起靠猜要高效得多。6.3 关于Windows下开发的心态和习惯最后聊点实际的。很多嵌入式工程师天然抗拒Windows下开发觉得正规做法就该用Linux。但这两年我越来越觉得工具只是工具能解决问题的就是好工具。在Windows下用MSYS2做编译用XSCT做下载调试用ADI的Windows工具做滤波器设计反而在某些场景下比Linux更顺。关键是养成几个习惯所有工程文件放在一个不包含空格和中文的短路径下比如C:\work\no-OS源码统一用LF换行用git管理时把core.autocrlf设为false每次换电脑或者换网络环境先花十分钟检查PATH顺序和工具链版本编译报错先看是不是环境问题工具链路径、换行符、路径含空格再怀疑代码我当时踩完这些坑以后把整套流程写成了内部文档后来团队里其他同事在Windows上也都能半小时内把AD9361跑起来了。这套流程如果你照着走一遍第一次可能还是要花点时间但走通之后Windows环境下开发射频前端这件事真的没有想象中那么难。现在想起来当初被迫在Windows下搭这套工程反倒逼着我养成了检查版本、记录日志的习惯后面在做其他平台移植时省了不少力气。
返回列表