
搞AD9361的同行应该都懂ADI官方那套no-OS工程在GitHub上clone下来默认分支就叫master它跟Linux驱动保持同步更新直接操作SPI寄存器不挂操作系统特别适合做板卡验证和协议调试。以前我都是在Linux下编译这套工程换到Windows工作机之后反而折腾了好几天因为官方文档默认你手里是一台Ubuntu什么make、gcc、git都是现成的而在Windows下光是把工具链拼起来就够喝一壶。这篇就把我在Windows下搭建AD9361 no-OS master工程的完整过程写出来包括环境选择、工具链安装、工程结构梳理、编译踩坑和运行验证给同样被这套工程困住的朋友做个参考。先交代一下背景我手头用的板卡是Zynq系列搭配AD-FMCOMMS2-EBZ也就是AD9361评估板最终目标是编译出能在ARM Cortex-A9上运行的裸机固件。整个过程中我试过Cygwin、试过Xilinx Vitis里直接导源码编译最后稳定下来的方案是MSYS2加ARM交叉工具链配合git命令行整套流程在Windows下跑得很顺。如果你也打算这么搞这篇文章可以帮你省下至少两天的踩坑时间。1. 项目概述与核心思路拆解1.1 这个工程解决什么问题AD9361是一颗集成度很高的宽带收发器芯片频率范围覆盖70MHz到6GHz支持TDD和FDD两种模式带宽从200kHz到56MHz可配置内部有完整的射频收发链路、模拟滤波、数字滤波和校准逻辑。要把它跑起来最核心的工作就是通过SPI接口完成几千个寄存器地址的初始化配置包括本振频率锁定、滤波器配置、增益控制模式、数据接口格式等。这些寄存器配置手工写根本不现实所以ADI官方提供了一套驱动代码在Linux下有成熟的工业级驱动在裸机场景下就是这套no-OS工程。从工程角度讲no-OS master分支的价值不只是初始化寄存器而已。它包含了完整的API层比如配置LO频率、配置采样率、配置增益、触发校准等都封装成了简单函数调用。你拿到一块新的AD9361板子先跑这套工程把寄存器环境调通后续无论是自己做协议栈还是对接FPGA数据通路都从这个基础上展开。跟Linux驱动相比no-OS版本没有进程调度、没有内核抽象逻辑更直接非常适合用来理解AD9361的工作原理也适合放在资源紧张的MCU上做轻量化控制。这里顺便解释一下标题里的“master”。ADI的no-OS仓库在GitHub上默认分支历来叫master对应最新开发主线。很多人一上来不知道clone哪个分支其实直接clone默认分支就行它会持续同步最新的寄存器映射和API改动。工程内部又有多个子项目各自针对不同器件AD9361相关的代码就集中在对应的目录下面后面我会详细拆。1.2 为什么要在Windows下搭建可能有人会说嵌入式开发老老实实装个Linux虚拟机不就行了我之前也这么想但实际工作中Windows是主力办公系统很多芯片厂商的配置工具、串口调试助手、FPGA下载工具都是在Windows下更顺手Xilinx的Vitis开发套件跑在Windows下也最稳定。如果每次编译一个小固件都要切到虚拟机里来回拷贝文件非常影响效率尤其是调代码改一行就要编一次的场景切换成本太高。所以现实需求就是源码下载、编辑、编译、生成固件这一整套流程最好都在Windows下完成只有烧录和运行调试才需要连目标板。这个诉求看起来简单但难点在于no-OS工程的构建系统默认是Linux生态下的make加gcc而Windows原生不带这些工具。解决方案无非几条装Cygwin、装WSL、装MSYS2或者直接用Xilinx Vitis自带的编译环境。我在对比之后选择了MSYS2加ARM交叉工具链的命令行方案原因后面会细说。1.3 整体方案选型先说我试过的几条路线给大家一个直观对比路线一Cygwin加mingw工具链 这种方案最接近Linux体验Cygwin提供了一个POSIX模拟层很多Linux命令都能跑。但Cygwin的包管理比较老旧ARM交叉工具链装起来要手动折腾而且Cygwin的fork机制在Windows上效率很低make编译时经常卡顿项目一大就难受。路线二Windows Subsystem for LinuxWSL WSL2基本就是一个轻量Linux虚拟机装Ubuntu之后跟真实Linux差别不大编译no-OS很顺利。缺点是我个人不太喜欢为了编译个固件再维护一层Linux环境而且WSL访问Windows磁盘效率一般来回倒腾文件也不划算。路线三Xilinx Vitis里直接导入源码工程 Vitis本质上就是Eclipse加一堆Xilinx插件它自带的Cortex-A9交叉编译器、链接脚本和BSP库都现成理论上最贴合Zynq平台。实际用下来发现Vitis的工程管理太重了每次换源码版本都要重新同步编译速度也不快还经常因为BSP更新引发莫名其妙的问题。路线四MSYS2加ARM交叉编译器 这是我最终定下来的方案。MSYS2自带一个很顺手的软件包管理器pacmanmake、git、diffutils这些工具一条命令就能装好交叉编译器从ARM官网下Windows版本解压就行Path配置好之后整个流程就跟Linux下几乎一样。后面我所有的操作都是基于这条路线稳定性实测很好。工具链选型这块核心就是两条编译环境要轻量、可复现编译命令要跟官方文档保持一致。MSYS2恰好满足这两点所以不管你是新手还是老手我都建议直接抄这条路的作业。2. Windows环境准备与工具链选型2.1 工具清单与版本建议在做任何操作之前先把工具列表整理清楚。我用的配置如下工具版本建议用途WindowsWindows 10/11 64位宿主机系统MSYS2最新版提供make、git、shell环境GNU ARM Embedded Toolchain10.3或更高ARM交叉编译Git for Windows最新版下载源码也可用MSYS2内git串口工具SecureCRT / MobaXterm看目标板运行日志JTAG调试器Xilinx Platform Cable / 其他烧录和调试特别提醒一下GNU ARM Embedded Toolchain在ARM官网的下载页面是直接给的一个Windows zip压缩包下下来解压就能用不需要安装这点跟很多人的习惯不太一样别傻等安装向导。版本号方面我用的是10.3-2021.10兼容性最好后面的链接脚本和标准库都没有问题。如果你手里的no-OS源码版本比较老可能需要配合老一点的编译器比如gcc-arm-none-eabi-7-2018-q2-update否则个别源码会报未定义类型的错误。不过以master主线当前的状态用新版工具链完全没问题。2.2 MSYS2环境部署MSYS2的安装没什么特殊的官网下载exe安装包一路Next就行装到默认路径C:\msys64即可尽量不要改路径后面很多脚本和工具会默认引用这个位置。安装完之后打开MSYS2终端首先要做的就是更新系统包pacman -Syu这个命令可能需要执行两次第一次更新核心包后终端会提示关闭窗口重新打开再执行一次直到提示系统已是最新。这就是MSYS2的典型操作不熟悉的容易卡在第一步。接着安装编译no-OS需要的车间级工具一条命令搞定pacman -S make git diffutils ncurses-devel这里make是编译核心git用来拉代码diffutils提供diff等相关命令某些Makefile脚本和shell脚本会用到。ncurses-devel也是顺手装的有些配置界面需要它。装好之后验证一下make --version git --version能正常输出版本号就说明环境可用。这里有一个MSYS2的小细节它的文件系统把Windows盘符映射为/c、/d这样的路径所以C盘根目录就是/c用起来跟Linux的根目录逻辑一致后续切换目录时要习惯这种路径写法。2.3 ARM交叉编译工具链安装GNU ARM Embedded Toolchain下载好之后是个zip包比如文件名类似gcc-arm-none-eabi-10.3-2021.10-win32.zip解压到一个没有中文和空格的路径比如C:\arm-gcc解压完成后你会看到一个bin目录里面就是arm-none-eabi-gcc等一系列工具。然后在MSYS2里把这个bin目录加入PATH。我用的是修改用户配置文件的方式编辑~/.bashrc添加一行export PATH/c/arm-gcc/bin:$PATH保存之后执行source ~/.bashrc让配置生效然后验证交叉编译器arm-none-eabi-gcc --version看到版本信息就说明交叉编译工具已就位。这里有一个容易踩的坑Windows环境变量里的Path和MSYS2自身的PATH是两回事只改Windows系统环境变量有时候在MSYS2里不生效必须写到bash的配置文件里或者用MSYS2系统级配置。我自己一开始就是只改了Windows环境变量在cmd里能用切到MSYS2终端里就报command not found折腾了好一会儿。2.4 源码获取与版本选择源码下载推荐直接用git clone不要下zip包原因很简单后续想切换分支、查看历史、重置到某个旧版都需要完整的git信息。命令行操作如下cd /c/work git clone https://github.com/analogdevicesinc/no-OS.git cd no-OSclone完成之后默认就在master分支可以用下面命令确认一下git branch输出结果会显示当前分支已经是master跟标题里的“master工程”刚好对上。这里多说一句ADI no-OS仓库的master分支是开发主力分支功能最新但偶尔也有不稳定的时候如果你追求稳定可以考虑切到某个release tag具体可以从官方Wiki或者GitHub的tag列表里选。但既然标题强调的是master我们就按master来搞。另外为了防止Windows下的换行符问题搞坏脚本建议clone之前执行一句git config --global core.autocrlf false这句命令的意思是让git不要自动把LF换行转成CRLF否则很多Makefile脚本和shell脚本在Windows下会出现奇怪的编译错误这个问题在后面章节还会再提。3. no-OS master工程结构解析3.1 顶层目录与Master分支的关系拿到源码之后别急着make先把工程结构看清楚。我在代码根目录执行ls看到的目录比想象中要多这里挑跟AD9361关系最密切的说明。master分支的顶层核心目录大致包括drivers、include、platform、projects以及若干芯片相关的子目录。不同版本结构会有差异早期版本直接在顶层放ad9361目录新版可能把器件驱动统一收到drivers目录下但核心模块划分是一致的。看懂这套结构之后无论版本怎么变你都能很快找到自己需要的东西。这里想强调一个思路no-OS不是一个单一固件工程而是一个驱动库加大量的平台适配工程。你要编译的是某个具体平台上、基于这套驱动库的应用程序而不是直接在根目录make生成一个全局固件。搞清楚这一点后构建思路就清晰了。3.2 芯片驱动层芯片驱动层是整个工程的核心AD9361相关代码主要负责把寄存器配置封装成高层的API。比如你调用一个函数去设置本振频率驱动内部会自动计算寄存器参数触发VCO校准检查锁定状态整个过程对使用者透明。这层代码从功能上分成这样几块寄存器映射定义每个寄存器的地址、位域、读写属性代码里用宏或者常量表表示。初始化序列芯片上电后的默认配置流程包括射频端口模式、时钟分频、基带滤波器配置。API接口层提供adi_ad9361_开头的一系列函数比如配置频率、带宽、增益模式、数据格式等。校准例程TDD校准、LO泄漏校准、基带滤波器校准等这些是射频芯片跑起来之后必须执行的操作。你不需要把每个寄存器都背下来但至少要理解API调用的内部逻辑否则后面调板子时出了问题不知道从哪里排查。我个人喜欢边看API实现边对照寄存器手册把关键寄存器的地址和位域记在笔记里调试时能省不少事。3.3 platform层与硬件适配platform层是这套工程在Windows下编译时需要特别留意的部分。它提供的是硬件抽象接口包括SPI读写函数、GPIO控制函数、延时函数、串口打印函数等。不同平台实现不同比如针对Zynq平台的实现可能包含对Xilinx库的调用而针对某些STM32平台的实现可能用HAL库。你在选型时要确保当前平台目录下的实现跟你手里的硬件匹配。如果目标平台是Zynq且使用Vitis SDK的BSP那么platform层很多函数会调用xil_printf、Xil_ICacheEnable这类Xilinx提供的库函数这意味着纯命令行编译时这些库需要单独加进来否则链接阶段会报undefined reference。这一点是Windows命令行编译最大的坑后面我会展开讲。反过来如果你的目标平台是裸机片机或者FPGA软核不需要依赖Xilinx库那么platform层通常有自包含的SPI和GPIO模拟实现这样命令行编译就简单很多。所以编译之前一定要看清platform具体用的是什么实现别等链接报错才回头看。3.4 工程示例与Makefile变量在projects目录下有多个针对不同芯片的工程示例。AD9361对应的工程一般是以ad9361打头或者存在于一个功能演示目录下里面包含了应用层示例main函数会调用API做初始化、配置、收发数据等操作。通常这个示例就是你要编译的目标工程。Makefile是整个编译过程的指挥棒不同项目的Makefile风格不完全一样但核心变量是差不多的。我用的版本里你需要重点关注的变量如下变量作用典型值PLATFORM指定目标硬件平台zed、zc706、fmcomms2等CROSS_COMPILE交叉编译前缀arm-none-eabi-CC编译器命令arm-none-eabi-gccARCH目标架构armDEBUG是否编译调试模式0或1不同版本的Makefile可能用不同的方式传递这些参数有的直接在Makefile里写死有的通过命令行传。我的建议是先看一遍Makefile开头部分的说明注释再试着执行一次make根据错误信息反向调整通常两次就能摸清套路。这也是我跟新手说的一个通用调试思路不要怕看Makefile很多时候编译失败的原因都在里面写着。4. 编译实操从源码到ELF固件4.1 进入工程目录与首次编译假定你的目标板是Zynq AD-FMCOMMS2-EBZ我们进入对应的工程目录先看Makefile关键内容。我当时的操作路径是这样的cd /c/work/no-OS/projects/ad9361 head -50 Makefile cat Makefile | grep PLATFORM看完Makefile确认变量名后执行第一次编译。第一次编译通常会暴露各种问题不用慌按报错逐步解决。我这边最终用的命令大致是make PLATFORMzed CROSS_COMPILEarm-none-eabi-如果你的平台不是zed替换成你自己板卡对应的平台名即可。编译过程中工具链会先编译驱动库源码再编译应用层main函数最后调用链接器生成elf文件。整个过程在MSYS2下跑得挺快几百个源文件大概一两分钟就能编完取决于机器配置。相比Vitis那个动不动卡半天的编译速度命令行这条路确实清爽太多。编译成功之后你会在输出目录下看到生成的ad9361.elf文件这就是后续要烧录到Zynq的固件镜像。有些工程的Makefile还会顺带生成.hex或者.bin文件取决于平台要求。4.2 链接脚本与Xilinx库依赖的处理先说链接脚本。Zynq裸机程序的链接脚本一般由Vitis SDK自动生成用来定义内存布局把代码放到DDR或者OCM的指定地址。在命令行编译时如果Makefile没有自带链接脚本你需要从SDK里导出一个lscript.ld把它放到工程目录下并在编译时指定。具体方法是在Makefile里找到LDFLAGS或者-L参数加上类似这样的内容arm-none-eabi-gcc -T lscript.ld -Wl,--start-group ...链接脚本加工之后生成的elf地址段才符合Zynq实际内存布局。否则即使编译通过烧进去也跑不起来。这是命令行路线跟IDE路线差异最大的一点IDE帮你做完了这一步命令行则需要手动指定。再说Xilinx库依赖。如果你的platform层代码里引用了xil_printf、Xil_DCacheFlush这些函数那么在链接时必须有xil库。这块的处理方式有两种方式一从Xilinx SDK安装目录里找到对应的libxil.a加入Makefile的链接参数。方式二改用platform层里不依赖Xilinx库的实现比如自己写SPI读写和延时函数。方式一最省事但要求你机器上装了Vitis或者SDK能把库文件路径导出。方式二最干净适合想彻底脱离IDE的人但前期适配工作量稍大。我当时项目里平台代码依赖xil库所以我采用了方式一在Makefile里把库路径写死实测没有问题。4.3 产物验证与后续运行编译通过后先别急着接目标板先在本地做两个验证。第一用file命令看一下生成的elf文件架构信息file ad9361.elf正常输出应该包含ARM、32-bit、EABI等信息确认不是x86架构否则说明交叉编译配置有问题。第二可以用arm-none-eabi-objdump看一下入口地址确认链接脚本起作用了arm-none-eabi-objdump -f ad9361.elf运行环节具体烧录方式取决于你的调试器。Zynq下常见的操作是把elf通过Xilinx的xsdb工具下载到DDR运行或者用FSBL做一个启动镜像从SD卡启动。运行起来之后重点观察串口日志AD9361的初始化流程会打印很多关键信息比如SPI通信是否正常、VCO是否锁定、校准是否通过等。如果串口有正常输出说明Windows下编译的固件跟Linux下编译的固件行为一致整个搭建过程就算成功了。5. 常见问题与调试技巧实录5.1 环境类问题速查环境层面的问题最多也是新手最容易被卡住的环节我整理了一张速查表按我实际遇到的可能性排序问题现象可能原因解决办法make: command not found未安装make或PATH没配置pacman -S makearm-none-eabi-gcc: command not found交叉工具链未加入MSYS2 PATH修改~/.bashrc加入工具链路径git clone速度很慢网络原因用镜像或代理这里不展开Makefile换行符报错Windows下CRLF导致git config core.autocrlf falseMSYS2安装报缺少包未先执行pacman -Syu先完整更新系统到最新编译中fork失败或卡死MSYS2下make的并发配置问题改用make -j1测试或用winpty优化这里想重点说一句遇到“command not found”不要慌先确认是不是PATH问题在终端里echo $PATH看一下工具路径是否包含这个排查速度最快。很多朋友一看到报错就怀疑源码有问题实际上八成是环境没配好。5.2 编译错误实战排查编译层面的错误千奇百怪但归类下来无非三类头文件找不到、类型未定义、链接符号缺失。头文件找不到通常是include路径没加全看一下Makefile的CFLAGS变量里有没有把你需要的库源码目录加进去。类型未定义大多是因为某个源代码依赖另一个目录的头文件但Makefile里的依赖关系没写充分手动补上即可。链接符号缺失是重灾区典型报错就是undefined reference to xxx。遇到这种情况我的排查思路是先确认这个符号是哪个库提供的再用arm-none-eabi-nm在生成的.a文件里搜。比如arm-none-eabi-nm /path/to/libxil.a | grep xil_printf搜得到说明库路径正确搜不到说明库版本不对或者根本没链接进来。这个方法比盲目改Makefile高效得多实测排查错一次链接错误不超过十分钟。还有一个容易被忽略的细节是编译告警。no-OS工程里很多老代码会触发编译告警比如隐式函数声明之类通常不影响功能但如果你开了-Werror就会变成编译错误。建议先确认Makefile的CFLAGS里没有-Werror有的话先去掉确保工程能顺利编过再考虑是否要严格编译。5.3 烧录与运行时排查如果固件烧进Zynq但串口没有任何输出优先级最高的是检查SPI通信是否正常。AD9361的初始化第一步就是通过SPI读取芯片ID寄存器如果读不出合法ID后续所有配置都不会执行这通常是硬件连接问题比如SPI的片选信号接错、MISO没焊好等。如果ID能读出来但后续某个配置断言失败那就需要对照寄存器手册看API调用过程中的参数是否合法。比如配置本振频率超出了芯片支持范围或者滤波器带宽超出了当前时钟约束都会触发驱动里的参数失效检查。这种时候不要急驱动打印的错误信息里一般带了哪个函数出的问题顺着源码往下看就能定位到具体寄存器。还有一种情况是固件能启动但射频指标不对比如输出功率异常、星座图发散。这种问题就要检查AD9361的校准流程有没有正常执行尤其是TDD模式下的突发校准。no-OS工程提供的API里相关校准函数建议在初始化流程中按官方示例顺序调用不要自己颠来倒去。5.4 避坑总结把这次搭建过程中一些零碎的经验写在最后。第一点MSYS2尽量保持最新版老版本在处理某些Makefile语法时会有兼容性问题。第二点交叉编译器能在ARM官网下就尽量在官网下别用第三方打包的版本那些版本经常改默认参数编译出来的固件行为跟预期对不上。第三点遇到看不明白的Makefile变量就用make -p打印所有内置变量和默认规则比翻文档直观得多。第四点Windows下操作路径不要带空格和中文否则某些编译脚本会挂掉。我个人的体会是搭建这套工程最花时间的其实不是编译本身而是理解工程结构以及配套的链接环境。只要你把Makefile变量、platform适配、链接脚本这三块搞明白Windows下编译AD9361 no-OS master工程就是一条顺畅的直线流程。后续你不管换平台还是换芯片这套经验都能复用因为ADI的no-OS工程体系内在设计是高度统一的。