Windows下搭建AD9361 no-os master工程:从环境到编译

发布时间:2026/10/5 9:54:35
Windows下搭建AD9361 no-os master工程:从环境到编译 AD9361 这块射频收发器这几年在软件无线电和射频前端开发里确实太常见了。但很多人一拿到 ADI 官方的 no-os 代码第一反应就是得去 Linux 下鼓捣要么装虚拟机要么换台机器光是环境折腾就能耗掉大半天。其实在 Windows 下把 AD9361 的 no-os master 工程搭起来并跑通并没有想象中那么麻烦而且对于只是想读代码、改配置、验证寄存器流程的人来说Windows 环境反而更顺手。这篇文章我就围绕“Windows 下 AD9361 的 no-os master 工程搭建”这条主线把从环境准备、源码拉取、工程结构拆解到 Visual Studio 编译运行的全过程讲清楚。适合刚接触 AD9361 的嵌入式软件工程师、用 FPGA 做主控但想先纯软件验证驱动的开发者以及手头没有 Linux 机器却要评估 AD9361 初始化流程的朋友。1. AD9361 和 no-os 模式先搞清楚你到底在搭建什么1.1 AD9361 是一块怎样的射频收发器AD9361 是 ADI 推出的一款高集成度射频捷变收发器名字里的“捷变”两个字很关键它支持 70 MHz 到 6 GHz 的频率范围覆盖了从 VHF 到 C 波段的大部分常用频段接收带宽和发射带宽都可以在 200 kHz 到 56 MHz 之间配置。一片芯片内集成了 12 位 ADC 和 12 位 DAC、完整的射频收发链路、可编程滤波器和数字接口基本上一颗芯片就能顶上一整块传统射频板卡的工作。我经常跟刚入行的朋友说AD9361 本质上就是一个“射频侧的可编程外设”。你要做的不是去焊接一堆分立器件搭射频链路而是通过 SPI 接口往它的寄存器里写配置告诉它工作在哪个频点、用多宽的带宽、增益控制走什么策略、数据接口是 CMOS 还是 LVDS。之后它就会按你的配置把天线端的射频信号变成数字 IQ 数据送出来或者把你送进来的 IQ 数据变成射频信号发射出去。正因为 AD9361 的高度可编程性它的配置和校准流程非常复杂。寄存器数量上千涉及收发通道、锁相环、滤波器、增益表、校准引擎等众多模块如果靠手工逐寄存器配置几乎是不可能完成的任务。所以 ADI 提供了完善的驱动层代码来处理这些底层细节让上层软件只需要调用几个接口就能完成射频链路的初始化。1.2 no-os 模式解决什么问题与操作系统驱动有哪些区别ADI 为 AD9361 提供了两套主流的软件支持方案一套是 Linux 下的 IIOIndustrial I/O驱动另一套就是我这里要说的 no-os 驱动。Linux 下的 IIO 驱动适合在嵌入式 Linux 系统里使用它把 AD9361 抽象成了标准的 IIO 设备上层可以通过 /sys 节点和 libiio 库来操作好处是有系统调度、内存管理、DMA 框架帮忙适合跑大型应用。但缺点也很明显依赖 Linux 内核体积大移植周期长调试一个寄存器行为可能还要翻内核日志。no-os 则是完全不同的思路。它是一套纯 C 语言编写的裸机驱动库代码里不依赖任何操作系统接口不涉及进程调度、虚拟内存、内核框架只是老老实实地通过 SPI 读写寄存器、执行校准流程、维护器件状态。这样一套代码既可以直接跑在 ARM Cortex-M 这类单片机上也可以跑在 FPGA 内部的软核处理器上甚至可以在没有真实硬件时用模拟的 SPI 接口在 PC 上编译运行用来做流程验证和调试。在有操作系统的环境里应用层和驱动之间隔了一层系统调用而在 no-os 环境下你的主循环直接调用驱动 API能清晰地看到每一步执行了什么。这种“所见即所得”的特性是很多人选择 no-os 来做底层验证和理解器件行为的原因。1.3 master 工程里的“master”具体指什么在 ADI 的 no-os 仓库里每个参考设计都对应一个软件工程命名里通常会带“master”这个词。比如 ad9361 这个核心驱动目录之外往往还会有针对 FMCOMMS2/FMCOMMS3/FMCOMMS4 等评估板的工程而这些板级工程一般会以某个“master”工程为模板拉起整个项目。这里的 master 并不是指 git 里的主分支而是在 ADI 参考设计语境下的“主控工程”或“主工程”。它把平台初始化代码、驱动源码、应用示例代码组织在一起形成一个完整可编译、可烧录、可运行的程序框架。对使用者来说master 工程就是整套 no-os 代码的入口你只需要在这个框架基础上修改器件配置参数、增加自己的信号处理逻辑就能快速完成原型验证。因此“搭建 master 工程”实际上包含了几件事把驱动源码正确组织起来、把平台相关的接口实现好、让工程能成功编译出可执行文件以及在编译通过后能正确完成 AD9361 的初始化流程。下文我会逐一拆解。2. Windows 搭建环境先把家伙事备齐2.1 硬件准备评估板与连接方式先说硬件。如果你手里有 ADI 的评估套件典型的组合是AD-FMCOMMS2-EBZ / AD-FMCOMMS3-EBZ / AD-FMCOMMS4-EBZ 射频子卡加上Xilinx ZedBoard或者ADI 自家的 PlutoSDR。AD9361 在板卡上通过 SPI 接口与主控连接同时用 CMOS 或 LVDS 并行接口传输 IQ 数据。不过在 Windows 下搭建 master 工程并编译这一步其实和真实硬件的关系不大。哪怕你手头没有板卡也完全可以先把代码工程搭好、编译通过再用一个简单的 SPI 模拟器或日志回调来验证驱动调用流程。我见过不少同事是先在纯软件环境下把初始化代码调通再上板验证这样能把“软件问题”和“硬件问题”彻底分开。如果你确实想连真实硬件Windows 下通常可以这样接用USB 转 SPI 适配器比如基于 FTDI FT2232H 的调试器连接 AD9361 的 SPI 引脚驱动层通过 D2XX 库进行 USB 读写用串口连接板卡上的调试串口打印驱动日志用JTAG连接 Zynq 或 ARM 主控把编译好的可执行文件加载进去跑。这个过程中示波器或逻辑分析仪也很有用。调 SPI 时序时把 CLK、MOSI、MISO、CS 几个信号挂在探头下一眼就能看出时钟极性、相位、片选时序对不对。我在实际调试中就遇到过因为 SPI 模式配置错误导致寄存器写不进去的案例最后就是用逻辑分析仪看到数据根本没被采样才定位到问题。2.2 软件工具链选择Windows 下编译 no-os 工程最省事的选择是Visual Studio。我用的是 VS2019 和 VS2022社区版免费装的时候勾选“使用 C 的桌面开发”工作负载就够用了。no-os 代码是纯 C 语言不需要任何图形库或额外依赖VS 对 C99 和 C11 的支持经过简单配置就能满足。除了 VS还有一条路线是用Eclipse MinGW-w64或者MSYS2。Eclipse 的好处是跟很多嵌入式 IDE 的操作习惯一致适合那些从 Linux 开发转过来的人。但说实话在 Windows 上搭建 master 工程Eclipse 的工程文件配置反而比 VS 繁琐所以我自己更推荐 VS。另一条值得提的路线是交叉编译。如果你的目标平台是 Zynq 这类带 ARM 处理器的芯片那么最终要在硬件上跑的其实是 ARM 版本的可执行文件。这时你可以在 Windows 上装一个arm-none-eabi-gcc工具链用 CMake 或 Makefile 编译出 ARM 固件再通过 JTAG 或 U-Boot 烧到板子上。但这属于从“验证工程”迈向“部署工程”的下一步本文先不展开。还需要准备一个Git 客户端。拉取 ADI no-os 源码用的推荐用 Git for Windows或者直接用 VS 自带的 Git 功能也行。虽然 ADI 的代码放的是 GitHub 仓库但国内网络拉取 GitHub 的速度偶尔很慢可以用镜像仓库或者等网络状况好的时候再拉这部分不影响工程搭建的核心流程。我做了一个简单的工具推荐表方便你对照选择用途推荐工具备注IDEVisual Studio 2019/2022社区版免费纯 C 项目足够交叉编译arm-none-eabi-gcc目标平台是 ARM 时使用版本管理Git for Windows拉取 no-os 源码和日常版本管理SPI 调试FTDI FT2232H 适配器配合 D2XX 库模拟 SPI 主机波形观察逻辑分析仪 / 示波器排查 SPI 时序问题必备3. no-os 工程结构拆解与启动流程分析3.1 源码目录与关键文件从 ADI 的 GitHub 仓库拉下来的 no-os 工程目录结构乍看有点杂但核心内容其实集中在几个地方。以no-OS主仓库为例你会看到ad9361目录这就是 AD9361 驱动的核心源码还有platform目录承载各种硬件平台的适配代码以及projects或app这类目录放着具体的应用工程示例。在ad9361驱动目录里有这几个文件是你必须重视的ad9361.c / ad9361.h驱动主逻辑。包括器件探测、初始化、校准、参数配置的全部实现也是大多数 API 函数体的所在地。这个文件很大代码量非常夸张光是初始化流程就能写几百行但它对使用者是透明的正常情况下你并不需要改它。ad9361_api.c / ad9361_api.h对外 API 封装层。像ad9361_init()、ad9361_set_rx_gain()、ad9361_set_tx_gain()这些上层函数都是定义在这里的。做应用开发的主要跟这一层打交道。ad9361_conv.c与数据转换相关的辅助函数处理 IQ 数据格式转换、增益表换算等操作。ad9361_util.c一些内部的工具函数比如寄存器位域的读写。ad9361_reg_map.h寄存器映射表的头文件定义了每个寄存器地址和位域名称。手动查寄存器时会经常用到。这些核心文件组成了一套完整的器件驱动层。在 Windows 下搭建工程时你需要把整个ad9361目录加入项目并保证编译器能找到所有头文件。platform目录则是平台适配层。ADI 的 no-os 驱动为了跨平台运行抽象出了ad9361_spi、ad9361_gpio、ad9361_timer等操作结构体。移植到 Windows 时你实际上是在实现这些结构体对应的回调函数比如 SPI 的读写回调、GPIO 的读写回调、时间延迟回调。3.2 初始化链路与数据通路理解 no-os master 工程的运行时行为最关键的是看懂程序从进入到完成 AD9361 初始化的整条链路。我用最简单的方式向你描述这个过程第一步调用platform_init()。这个函数是平台相关的初始化入口在嵌入式平台上会配置时钟、GPIO、SPI 控制器、DMA 等硬件资源在 Windows 平台上它至少要完成 SPI 适配器的打开和 GPIO 引脚的初始化。第二步调用ad9361_init(ad9361_phy)。这是整个初始化流程的核心。传入的参数是ad9361_init_param结构体里面保存着 RF 频率、带宽、采样率、增益模式、数据接口模式等所有配置项。ad9361_init内部会按顺序执行器件 ID 读取、BBPLL 配置、RF PLL 配置、基带滤波器配置、增益表加载、数字校准等步骤任何一个关键步骤失败都会直接返回错误码。第三步根据应用需求配置数据通路。如果只是做验证你可能还需要打开 RX/TX 使能开关启动数据搬运。在 no-os 架构下这一步通常通过 API 接口完成比如ad9361_set_rx_enable()、ad9361_set_tx_enable()。搞清楚这条链路之后你在 Windows 上看到的就不仅仅是一个编译任务而是一个可以在没有硬件时通过打印日志逐条观察寄存器读写行为的完整流程。4. Windows 实操从拉代码到编译通过4.1 拉取源码与版本选择第一步找一个工作目录比如D:\ad9361_work打开 Git Bash 或者 VS 的终端执行git clone https://github.com/analogdevicesinc/no-OS.git仓库体积不算小如果网络情况不理想等一等或者用代理镜像。克隆完成后进入目录看一下分支和标签git branch -a git tag -l通常建议拉取最新的 release 标签对应的代码而不是直接使用 master 分支的头部提交。因为 ADI 的 no-os 仓库活跃度非常高主分支经常有正在开发中的改动稳定性不一定有保证。我常用的方式是git checkout 2024_R1或者你查阅一下当前最新的稳定标签再决定。选择版本的一个原则是与你的 AD9361 器件版本Rev. A/B/C/D匹配。不同版本的 AD9361 在部分寄存器默认值和校准流程上有细微差异ADI 在驱动里通常做了自动识别但选新版驱动总归更稳。4.2 创建 VS 工程并导入源码打开 Visual Studio新建一个空项目。这里有几个关键配置点我会逐个说清楚。第一项目类型选择“空项目”语言选择 C 没关系因为你加入的 .c 文件会被 VS 当作 C 代码编译只是要注意后续配置“编译为 C 代码”。第二项目属性里需要设置C/C - 常规 - 附加包含目录把no-OS仓库的ad9361目录、platform目录、util目录等所有可能包含头文件的路径加进去。一个不够就加多个直到编译时所有#include xxx.h都能被找到。C/C - 语言 - 符合模式设为“否”。no-os 这套驱动写得很奔放大量使用了 C99 的特性VS 的默认符合模式过严会报一堆语法错误。C/C - 预处理器 - 预处理器定义加上_CRT_SECURE_NO_WARNINGS否则 VS 会把fopen、strcpy这些函数当成不安全函数告警或报错如果用到 Windows 底层 API可能还需要_WINSOCK_DEPRECATED_NO_WARNINGS类似的宏具体情况具体处理。C/C - 预编译头选择“不使用预编译头”。no-os 工程不使用pch.h如果保留默认的预编译头选项会报“无法打开包含文件 pch.h”。第三把需要的源文件添加到项目里。右键项目 - 添加 - 现有项然后一次性选中ad9361目录下的所有 .c 文件。如果你只是做 AD9361 初始化和参数配置验证不需要把整个仓库的几千个文件都加进去只加驱动核心文件就够了这样编译速度快很多也不会把无关的平台适配错误牵扯进来。我在ad9361目录下用到的核心源文件大致是这几个spirw.c / spi_platform.h 对应的源文件如果驱动内使用了独立 SPI 文件的话ad9361.cad9361_api.cad9361_conv.cad9361_util.c自己写的 main.c4.3 平台层适配与 SPI/GPIO 模拟这是整个搭建过程中工作量最大的环节也是很多人在 Windows 下卡住的根本原因no-os 驱动默认面向嵌入式平台它调用的是硬件 SPI 和 GPIO而你在 Windows 主机上默认没有这些接口。解决思路有两种。第一种模拟实现。不连真实硬件只为了实现流程验证和调试。此时你要做的是实现platform抽象层中 SPI 读写和 GPIO 读写的回调函数。最简单的实现方式是让每个 SPI 写操作打印一条日志让每个 SPI 读操作返回一个预设的模拟值这样就能完整跑通ad9361_init的调用链路观察驱动发送的寄存器地址序列和期望值。下面是一个极简的模拟 SPI 回调示意int32_t spi_read_reg(struct spi_device *spi, uint16_t addr, uint8_t *val) { printf([SPI RD] addr0x%04X\n, addr); *val 0x00; // 模拟读到 0x00实际场景根据需求返回 return 0; } int32_t spi_write_reg(struct spi_device *spi, uint16_t addr, uint8_t val) { printf([SPI WR] addr0x%04X val0x%02X\n, addr, val); return 0; }这样的实现虽然不能真正初始化 AD9361但足够让你验证 master 工程的代码路径、函数调用顺序和参数传递是否符合预期。第二种通过 USB 转 SPI 适配器连真实硬件。这时你要实现的是真实的 SPI 读写函数底层调用 FTDI 的 D2XX API。这里需要额外链接 FTDI 的驱动库比如在 VS 里添加ftd2xx.lib并在预处理宏中加入USE_FTDI_D2XX之类开关。SPI 适配器的接线方式要注意AD9361 的 SPI 支持 Mode 0 或 Mode 3这点要看你的适配器默认模式如果两者不一致数据读写会完全失败。在实际项目里我更推荐大家从模拟实现开始先确保代码逻辑和工程配置正确再考虑接入真实硬件。这样环境问题和代码问题不会混在一起。4.4 编译、运行与结果验证工程配置完成后直接 F7 编译。第一次编译大概率会报错不过大部分错误集中在头文件路径缺失、语法标准不匹配、宏定义缺失这几类。根据编译输出窗口的提示逐个修正就行。编译通过后生成一个 .exe 可执行文件。直接运行如果你用的是模拟 SPI 实现就会看到终端里刷出一串串寄存器读写日志。这些日志的顺序和内容就是 AD9361 初始化流程的真实执行轨迹。你可以把日志输出到文件方便用 diff 工具对比两次修改配置后的差异。我在调试时习惯这样运行ad9361_noos_init.exe init_log.txt然后打开init_log.txt重点检查几个关键点驱动是否先读取了器件 ID。AD9361 的寄存器REG_PRODUCT_ID地址 0x037读回来应该是 0x21Rev.C 版本读回来可能是 0x22。如果读到的是 0xFF 或 0x00说明 SPI 通信有问题。BBPLL 和 RF PLL 锁定流程是否走到位。日志里通常会打印_ad9361_bbpll_set_rate或_ad9361_rfpll_set_rate这类关键字。是否有任何返回非零的错误码。如果某个初始化子步骤返回错误驱动会立即中止此时就要根据错误码反查是哪个配置参数越界。如果你接了真实硬件验证就更加直观运行后直接用频谱仪看发射端口是否有预期载频或者切到 RX 模式用一个信号源注入特定频率的信号再用 API 读取接收功率看数值是否合理。5. 常见问题与排查技巧实录5.1 编译阶段头文件路径、C 标准、宏定义三大坑我把实际搭建工程时最常遇到的编译问题整理成一张速查表方便你对照排查报错信息原因解决办法无法打开包含文件 ad9361.h附加包含目录没配全把ad9361和platform目录加入 “C/C - 常规 - 附加包含目录”“xxx”未声明的标识符C 标准不匹配或头文件顺序不对关闭“符合模式”保证在不使用预编译头时也声明了标准头文件宏定义冲突例如EN或GPIO这类通用宏与 Windows 头文件冲突给 AD9361 相关宏加前缀或在包含 Windows 头文件之前先定义私有宏链接时找不到某些函数源文件没加全检查 ad9361_api.c 和 ad9361.c 是否都加入项目错误 LNK2019无法解析的外部符号平台层回调函数未实现检查是否实现了spi_write_reg、spi_read_reg、gpio_set等回调并确认导出符号与驱动声明一致如果遇到.obj: error LNK2019这类链接错误绝大多数情况都是平台回调没有实现或源文件没有引入。可以打开“生成 - 重新生成解决方案”看输出窗口里的未解析符号名字然后在代码里全局搜索这个名字就能定位到需要实现哪个函数。5.2 运行阶段SPI 模式、字节序和寄存器写入失败编译通过只是第一步运行起来才是真正的开始。我在调试 AD9361 no-os 工程时遇到过几个非常典型的运行时问题第一个是 SPI 模式不对。AD9361 的 SPI 支持 CPOL0/CPHA0 或 CPOL1/CPHA1也就是 Mode 0 或 Mode 3。很多 USB 转 SPI 适配器默认是 Mode 0如果驱动里配置的是 Mode 3或者反过来那么所有寄存器读写都会失败表现为读回的数据全 0xFF 或全 0x00而初始化流程会在读取器件 ID 这一步直接卡住。第二个是字节序问题。AD9361 的寄存器地址是 16 位数据是 8 位。SPI 协议中地址部分分为两个字节驱动在构造地址帧的时候会区分 MSB 在前还是 LSB 在前。如果你用通用的 SPI 接口发送数据需要确认适配器是否按大端方式发送地址。很多 FTDI 适配器的默认行为是小端发送这会导致地址完全错位。遇到这种情况就需要在 SPI 适配层做字节交换。第三个是 GPIO 初始状态不正确。AD9361 的复位引脚、ENSMB 引脚、ENABLE 引脚等 GPIO 如果在初始化时没有正确的电平会导致器件无法进入配置模式或者无法正常上电。在 Windows 下用适配器控制 GPIO 时容易忽略 GPIO 引脚的初始方向设置。建议在初始化开始前先确保所有 GPIO 引脚方向正确、初始电平符合数据手册要求。5.3 几条值得反复强调的个人心得在这些年的项目经历里我总结了几条调试 AD9361 no-os 工程的经验写在这里供你参考日志是最好的老师。在 Windows 模拟环境下你唯一能依赖的调试手段就是日志。不要嫌日志刷屏建议在 SPI 读写回调里把寄存器地址和值都打印出来然后跟数据手册的寄存器描述逐条对照。很多神秘的配置问题一查日志就能发现是某个寄存器位被写反了。一个参数一个参数地试。修改配置时不要一次改好几个参数。比如你把采样率从 5M 改成 10M又把工作在 FDD 模式改成 TDD又在增益模式上动了手脚一旦出问题你根本不知道是哪一个改动导致的。每次只改一个参数观察日志变化这样定位问题最快。先用模拟环境再上真硬件。如果你第一次接触 AD9361强烈建议先在 Windows 模拟环境下把初始化流程跑通搞清楚有哪些阶段、每个阶段大致做什么再连接真实硬件。这样上了硬件以后如果初始化失败你至少知道是 SPI 通信问题、时钟问题还是配置参数问题不会一头雾水。根据我个人经验搭建 Windows 下的 AD9361 no-os master 工程真正的难点不在于写多少新代码而在于理解驱动代码的组织方式、弄清楚平台抽象层需要实现哪些接口以及掌握在缺少 Linux 工具链时如何用 VS 把工程跑起来。这套思路不仅适用于 AD9361ADI 的 AD9371、ADRV9009 等器件的 no-os 驱动也是高度相似的架构学会了搭一个后面再搭其他的就会快很多。最后再分享一个小技巧在你把工程跑通、日志验证无误之后建议用 git 单独拉一个分支把你对工程的所有修改固化下来写清楚每一步的配置和踩过的坑。这类工程如果你一段时间不碰再回头时很容易忘干净哪些宏必须定义、哪些目录必须添加、哪个回调函数整整改过。有一份自己的“搭建备忘”比翻论坛帖子高效得多。