☰
Windows环境下AD9361 no-os工程移植与编译实战指南
2026/10/5 8:04:23 网站建设 项目流程

去年做一个小型无人机的数据链项目,甲方现场只提供了一台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、修改驱动代码
WSL(Windows 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/ └── Makefile

drivers/ad9361下是芯片驱动本体,platforms/xilinx是平台适配层,projects/ad9361下面按不同开发板细分。以ZC706+FMCOMMS3为例,工程目录是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=快速攻击AGC,1=手动
tx_attenuation_md发射衰减控制模式0=手动,1=自动
freq射频中心频率2400000000(2.4GHz)
rx_bandwidth / tx_bandwidth基带带宽20000000(20MHz)
tx_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 PLATFORM=zync CROSS_COMPILE=arm-none-eabi- all

注意这里的PLATFORM=zync是我这台机器的实际值,具体值要到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 make
fatal 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-gcc
line 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,就说明需要转回LF:

sed -i 's/\r$//' drivers/ad9361/ad9361.c

4.3 生成产物和确认编译成功的标准

编译成功后,会在工程目录下生成build子目录,里面是各个模块的目标文件,以及最终的ELF文件。以zc706_fmcomms3为例,生成的可执行文件通常叫ad9361.elf。

确认编译成功的标准不只是"没有error",还要确认生成了完整ELF:

ls -l build/ad9361.elf

用编译器自带的工具查看ELF的CPU架构和入口信息:

arm-none-eabi-readelf -h build/ad9361.elf

在ELF头信息里,Machine字段应当显示ARM,Entry 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 con

dow是下载到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_r2,Vitis版本建议用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下搭这套工程,反倒逼着我养成了检查版本、记录日志的习惯,后面在做其他平台移植时省了不少力气。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询