AD9361 NO-OS驱动原理与实时射频控制实战
1. 项目概述AD9361的NO-OS代码命令到底在解决什么问题AD9361 是一款由某国际半导体厂商推出的高性能、宽带宽、可编程射频收发器芯片广泛应用于软件定义无线电SDR、无线通信原型验证、雷达信号处理、认知无线电测试平台等对实时性、低延迟和硬件可控性要求极高的场景。它本身不内置处理器必须通过外部主控如Zynq SoC的ARM核、STM32、或FPGA软核进行寄存器配置、数据通路控制与状态监控。而“NO-OS”——即“No Operating System”指的是一种轻量级、无内核依赖的驱动开发范式它绕过Linux内核驱动框架如IIO子系统直接在裸机bare-metal或RTOS如FreeRTOS、Zephyr环境下通过底层寄存器读写、DMA控制器操作和中断管理实现对AD9361的全功能控制。你看到的“AD9361的NO-OS代码命令”本质上不是一组终端里敲进去就能执行的Shell指令而是一套结构清晰、模块化组织的C语言函数接口集合。它封装了从芯片上电初始化、时钟树配置、RF前端校准、增益控制、采样率设置到数据收发缓冲区管理、中断触发逻辑、状态查询等全部底层操作。比如ad9361_init()并非简单地“启动芯片”而是按严格时序执行先复位PHY层再配置参考时钟源内部晶振 or 外部差分时钟接着加载PLL环路滤波参数以锁定TX/RX本振频率然后逐级使能LNA/VGA/PA路径并运行DCXO校准最后才打开数据链路。整个过程涉及上百个寄存器地址、数十种校准算法状态机、以及对时序精度达纳秒级的等待点。这些细节绝不会出现在Linux IIO驱动的sysfs节点里但恰恰是做高速跳频、窄脉冲雷达回波捕获、或超低延迟闭环反馈系统时工程师必须亲手掌控的关键。为什么需要NO-OS举个真实场景某高校实验室在搭建一个2GHz~3.8GHz频段的实时频谱感知系统要求从检测到异常信号到完成参数重配置如切换中心频点、调整带宽、改变增益的端到端延迟必须低于50微秒。若走标准Linux IIO驱动一次write()系统调用内核上下文切换驱动状态机调度轻松突破200微秒而NO-OS下同一操作可压缩至12~18微秒——差距不是优化百分比而是能否满足物理层实时约束的生死线。所以“AD9361的NO-OS代码命令”真正的价值不在于“能用”而在于“可控、可测、可裁剪、可确定性调度”。它面向的是嵌入式射频工程师、FPGA协同开发者、以及需要把硬件性能榨干到最后一纳米的算法验证者。如果你正在调试一个ZynqAD9361的板卡发现IIO驱动总在某个采样率下丢包或者想在ARM核上跑一个轻量级的AGC闭环而不引入Linux调度抖动那么这套代码就是你拆解问题、定位瓶颈、甚至重构控制逻辑的原始地图。2. 整体架构与设计思路为什么是这套结构而不是别的2.1 分层抽象从寄存器到API的三级跃迁NO-OS代码并非平铺直叙的一堆spi_write()调用而是构建了清晰的三层抽象模型这是其可维护性与可移植性的根基最底层硬件抽象层HAL这一层完全与主控平台强绑定负责将通用SPI/I2C/GPIO操作翻译为具体MCU的寄存器操作。例如在Zynq平台上ad9361_spi_init()会调用Xilinx SDK提供的XSpiPs_CfgInitialize()和XSpiPs_PolledTransfer()而在STM32平台上则会映射到HAL库的HAL_SPI_TransmitReceive()。关键点在于HAL层不包含任何AD9361业务逻辑只做“把字节发出去”和“把字节读回来”这件事。这意味着只要重写HAL层整套NO-OS驱动就能无缝迁移到新平台——我曾在一个基于GD32E503的定制板上仅用半天就完成了HAL适配后续所有AD9361功能调用零修改。中间层设备驱动层Device Driver这是NO-OS的核心智力所在。它定义了struct ad9361_dev结构体囊括芯片所有可配置状态rx_lo_freq,tx_lo_freq,rf_bandwidth,gain_control_mode,calib_mode等。所有寄存器操作都被封装为内联函数或静态函数如ad9361_set_rx_rf_bandwidth()。这个函数内部不是硬编码地址而是先查表获取对应带宽值所需的RX_BW寄存器值该表由ADI官方数据手册提供再调用ad9361_spi_write()写入。这种设计让业务逻辑与硬件细节彻底解耦——当你需要支持一个新的带宽值时只需更新查表数组无需触碰任何SPI传输代码。最上层应用接口层API面向最终用户暴露简洁函数如ad9361_setup()一键完成全流程初始化、ad9361_set_tx_attenuation()设置发射衰减、ad9361_get_rx_rssi()读取接收信号强度。这些API内部会自动处理依赖关系调用ad9361_set_tx_attenuation()前会隐式检查TX通道是否已使能若未使能则返回错误码而非静默失败。这种“防御性编程”极大降低了误用概率也是区别于野鸡代码的关键。提示很多初学者试图跳过HAL层直接在main函数里写SPI裸操作。实测下来这会导致两个严重后果一是不同平台代码无法复用每次换板子都要重写80%驱动二是寄存器时序错误频发——AD9361某些寄存器写入后必须等待特定周期如REG_0x004写入后需usleep(10)裸写极易遗漏造成校准失败或锁相环失锁。2.2 状态机驱动校准不是“一键”而是“一串动作”AD9361最令人头疼又最体现NO-OS价值的部分是其复杂的校准流程。官方文档明确指出芯片上电后必须依次执行“TX LO Leakage Calibration”、“RX IQ Balance Calibration”、“TX IQ Balance Calibration”等多达7种校准且部分校准存在严格先后依赖如RX IQ校准必须在TX LO泄漏校准之后。NO-OS代码没有用一个ad9361_run_all_calibrations()粗暴打包而是将每种校准实现为独立的状态机函数例如ad9361_rx_iq_calibrate()int32_t ad9361_rx_iq_calibrate(struct ad9361_dev *dev) { // 步骤1配置校准模式寄存器 ret ad9361_spi_write(dev, REG_0x01A, 0x0001); // 启动RX IQ校准 if (ret 0) return ret; // 步骤2轮询状态寄存器等待完成超时保护 for (i 0; i 10000; i) { ret ad9361_spi_read(dev, REG_0x01B, val); if ((val 0x0001) 0x0001) break; // bit0置1表示完成 usleep(100); } if (i 10000) return -ETIMEDOUT; // 步骤3读取校准结果并写入补偿寄存器 ret ad9361_spi_read(dev, REG_0x01C, iq_gain); ret ad9361_spi_read(dev, REG_0x01D, iq_phase); ret ad9361_spi_write(dev, REG_0x020, iq_gain); ret ad9361_spi_write(dev, REG_0x021, iq_phase); return 0; }这种显式展开的状态机带来的好处是你可以精准插入调试点比如在步骤2加个LED闪烁直观看到校准耗时、可以动态跳过某项校准如已知环境稳定跳过温度敏感的LO Leakage校准以节省200ms、还可以在失败时获取具体卡在哪一步——而Linux IIO驱动通常只返回一个笼统的-EIO让你对着日志抓瞎。2.3 内存与DMA数据通路的确定性保障NO-OS对数据收发的处理彻底抛弃了Linux的buffer cache和page fault机制采用预分配双缓冲ping-pong策略。在初始化阶段ad9361_init_dma()会调用平台HAL申请两块连续物理内存如各1MB并将其地址写入AD9361内部DMA控制器的RX_BUFFER_ADDR和TX_BUFFER_ADDR寄存器。数据流如下ARM核将待发送的IQ样本填入Buffer A触发DMA控制器将Buffer A内容通过AXI-Stream推给AD9361的TX PHY当Buffer A传完DMA自动切换到Buffer B并触发中断ARM核在中断服务程序中处理Buffer A的接收数据此时Buffer B正被TX使用同时准备下一帧发送数据到Buffer A。整个过程无内存拷贝、无系统调用、无调度延迟。我实测过在Zynq-7020上使用NO-OS DMA245.76 MSPS采样率下连续接收10秒数据CPU占用率稳定在3.2%且无单次中断延迟超过5微秒而同等条件下用Linux IIO的configfs方式CPU占用飙升至68%且出现多次100微秒的中断延迟抖动直接导致FFT分析结果出现频谱泄露。3. 核心命令解析与实操要点从初始化到校准的完整链路3.1 初始化命令链ad9361_init()背后的17个关键动作ad9361_init()是NO-OS的入口函数但它绝非黑盒。理解其内部动作序列是调试一切问题的起点。以下是该函数执行的17个不可跳过的步骤按实际执行顺序排列并标注每个步骤的“为什么”和“踩坑点”ad9361_reset()拉低RESETB引脚至少100ns再释放。这是硬件复位确保所有寄存器回到PORPower-On Reset默认值。坑点某些定制板RESETB引脚未接上拉电阻释放后电平浮动导致芯片处于亚稳态。必须用万用表实测复位引脚电压是否稳定在3.3V。ad9361_spi_init()初始化SPI外设设置CPOL0, CPHA0, 波特率≤10MHzAD9361 SPI最大支持10MHz。坑点Zynq PS端SPI时钟源为50MHz若不分频直接设为10MHz实际波特率可能是12.5MHz导致通信错乱。必须查Zynq TRM确认分频系数。ad9361_check_id()读取CHIP_ID寄存器地址0x000验证返回值是否为0x9361。这是软件握手确认SPI链路连通且芯片供电正常。经验若此处读回0x000090%是SPI线序接反MOSI/MISO互换或CS片选未正确拉低。ad9361_set_clkout()配置CLKOUT引脚输出通常设为REF_CLK参考时钟的1/2分频用于给FPGA提供同步时钟。注意此步必须在PLL配置前完成否则FPGA可能因无时钟而无法配置。ad9361_set_ref_clk()设置参考时钟源内部20MHz晶振 or 外部差分时钟。关键寄存器REF_CLK_DIV0x003决定分频比。实测心得外部时钟输入时必须确保REF_CLK_N引脚有有效差分信号峰峰值≥500mV否则PLL无法锁定。ad9361_set_rx_lo_freq()/ad9361_set_tx_lo_freq()分别设置RX/TX本振频率。内部调用PLL配置函数计算N、R、M等参数。核心原理AD9361 PLL采用分数N合成器目标频率f_out f_ref × (N F/M)其中F/M为小数部分。NO-OS代码内置了最优参数搜索算法在保证相噪的前提下优先选择M值较小的组合以减少计算误差。ad9361_set_rx_rf_bandwidth()/ad9361_set_tx_rf_bandwidth()设置RF通道模拟带宽。这不是数字滤波器带宽而是LNA和VGA前端的模拟滤波器——它决定了芯片能无失真通过的最大信号带宽。重要RX带宽必须≥数字采样带宽的1.2倍否则高频分量被截断引发混叠。例如采样率122.88 MSPS数字带宽61.44 MHz则RX RF带宽至少设为73.728 MHz。ad9361_set_rx_sampling_freq()/ad9361_set_tx_sampling_freq()设置数字采样率。这会联动配置内部DDC数字下变频和DUC数字上变频的抽取/插值因子。计算示例若ADC原始采样率为1000 MSPS目标RX采样率为122.88 MSPS则DDC抽取因子 1000 / 122.88 ≈ 8.137NO-OS会自动选择最接近的整数因子8并微调PLL使ADC采样率精确匹配122.88×8983.04 MSPS。ad9361_set_gain_control_mode()设置增益控制模式Manual, AGC, Hybrid。Manual模式下ad9361_set_rx_gain()直接写入VGA增益索引AGC模式则需配置AGC_ATTACK_DELAY、AGC_DECAY_STEP等十余个寄存器。避坑AGC模式下若忘记配置AGC_THRES_HIGH高门限会导致增益在弱信号时疯狂跳变。ad9361_set_tx_attenuation()设置发射衰减单位0.25dB。范围0~127对应0~31.75dB。注意此值与PA输出功率非线性相关实测中当衰减值10时PA可能进入饱和区产生谐波失真。建议工作区设为20~100。ad9361_set_rx_rf_port_input()选择RF输入端口RX1A, RX1B, RX2A, RX2B。关键AD9361的RX1和RX2是独立的接收通道但共享同一套LNA/VGA因此不能同时启用RX1A和RX2A。NO-OS代码在此处做了互斥检查。ad9361_set_tx_rf_port_output()选择RF输出端口TX1A, TX1B, TX2A, TX2B。同理TX1和TX2共享PA需避免冲突。ad9361_set_rx_lo_powerdown()/ad9361_set_tx_lo_powerdown()关闭未使用的LO通道以降低功耗。经验即使只用RX也应显式调用ad9361_set_tx_lo_powerdown(1)否则TX LO仍在耗电板子温升明显。ad9361_init_dma()如前所述初始化DMA缓冲区和控制器。实操要点缓冲区大小必须是2的幂次如1024, 2048且大于等于一次DMA传输的最小单元通常为128字节否则DMA控制器拒绝启动。ad9361_enable_rx()/ad9361_enable_tx()使能RX/TX数据链路。此步会配置RX_EN和TX_EN寄存器并启动数据流。警告必须在所有校准完成后执行否则未校准的通道会输出大量噪声。ad9361_run_tx_lo_leakage_calib()执行TX LO泄漏校准。这是首个校准耗时约80ms。现象校准期间TX输出端会短暂出现宽频噪声属正常。ad9361_run_rx_iq_calib()执行RX IQ平衡校准耗时约120ms。至此芯片进入可工作状态。注意以上17步是典型流程实际代码中还穿插着数十次寄存器读写验证和超时判断。NO-OS的价值正在于你能随时在任意一步加printf(Step X done\n)亲眼看到哪一步卡住——而Linux驱动日志只会告诉你“ad9361 spi0.0: failed to initialize”。3.2 校准命令详解如何读懂并干预校准过程校准是AD9361 NO-OS中最易出错也最需深度理解的部分。官方提供了一个ad9361_run_all_calibrations()函数但生产环境中我们几乎从不直接调用它而是分步执行、逐项验证。以下是三个最常干预的校准命令及其深层逻辑3.2.1ad9361_run_tx_lo_leakage_calib(): 解决“发射底噪高”的根因TX LO泄漏校准的目标是消除本振信号通过PCB走线耦合到TX输出端造成的“虚假发射”。其原理是在校准模式下TX PA被强制关闭但LO信号仍工作芯片内部一个专用检测电路测量TX输出端的LO功率并通过调节TX_LO_LEAKAGE_I和TX_LO_LEAKAGE_Q寄存器0x02A, 0x02B注入反向电流来抵消泄漏。何时必须重做更换PCB、更换天线、环境温度变化10℃、或发现TX频谱中在LO频率处有一个尖锐的单音幅度-40dBm即表明泄漏超标。如何验证成功校准后读取TX_LO_LEAKAGE_I和TX_LO_LEAKAGE_Q的最终值。正常范围应在0x7FFF±0x2000即32767±8192之间。若值接近0x0000或0xFFFF说明校准失败可能原因TX输出端未接50欧姆负载开路或短路、PA损坏、或LO信号本身失真。手动干预技巧若自动校准失败可尝试“半自动”先运行校准记录下失败时的I/Q值然后手动将I值设为0x7FFFQ值设为0x7FFF再微调I值每次±100用频谱仪观察LO处单音幅度找到最小值点。NO-OS允许你直接调用ad9361_spi_write()写入这些寄存器这是Linux驱动绝对禁止的操作。3.2.2ad9361_run_rx_iq_calib(): 拯救“镜像抑制比差”的关键RX IQ校准旨在补偿I路和Q路模拟电路的增益和相位不匹配从而提升镜像抑制比IRR。理想情况下IRR应60dB若40dB接收信号会出现严重镜像干扰。校准原理简析芯片内部生成一个纯正弦波频率RX_LO/4同时注入I路和Q路。由于硬件不匹配两路输出的幅度和相位不同。校准算法通过迭代测量I/Q输出的矢量误差计算出最佳的增益补偿因子RX_GAIN_CORR和相位补偿角度RX_PHASE_CORR写入寄存器0x020~0x023。失败典型现象校准后用信号源输入一个-20dBm、偏离RX_LO 1MHz的单音用另一台频谱仪观察AD9361的RX输出若在RX_LO1MHz和RX_LO-1MHz处均能看到等幅信号即IRR≈0dB说明校准完全失效。排查三步法检查RX_LO_FREQ是否准确用示波器测CLKOUT引脚计算PLL输出误差需100Hz检查RX_RF_BANDWIDTH是否足够带宽过窄会滤掉校准信号的谐波导致算法误判检查RX_GAIN_CONTROL_MODE必须设为GAIN_MODE_MANUALAGC模式下增益动态变化校准无法收敛。3.2.3ad9361_run_tx_iq_calib(): 让发射信号“干净”的最后防线TX IQ校准与RX类似但目标是让TX输出的I/Q基带信号在上变频后正交性误差最小化从而降低EVM误差矢量幅度和ACLR邻道泄漏比。与RX校准的本质区别RX校准是“被动测量”TX校准是“主动激励”。它需要RX通道处于工作状态作为“接收机”来测量TX发出的校准信号。因此ad9361_run_tx_iq_calib()内部会临时配置RX参数如增益、带宽并启动RX数据采集。实操禁忌绝对禁止在TX IQ校准期间连接天线因为校准信号功率虽低约-30dBm但若天线阻抗不匹配反射功率可能损坏PA。必须使用50欧姆终端负载。结果解读校准成功后TX_IQ_GAIN_CORR0x024和TX_IQ_PHASE_CORR0x025会被写入。理想值增益补偿在0x800032768附近相位补偿在0x00000度附近。若相位值0x2000对应45度说明I/Q路径存在严重布局不对称需检查PCB。4. 实操过程与核心环节实现从零开始搭建一个可工作的NO-OS工程4.1 环境准备Zynq-7020平台上的最小可行系统我以Xilinx ZC702评估板Zynq-7020 SoC为例演示如何从零构建一个能点亮AD9361的NO-OS工程。该板载有AD-FMCOMMS3-EBZ子板集成了AD9361。整个过程不依赖Vivado IP Integrator的自动驱动生成而是手动集成NO-OS源码。第一步硬件连接确认ZC702的FMC-LPC接口与FMCOMMS3正确连接固定螺丝拧紧FMCOMMS3的REF_CLK跳线设为ON使用板载20MHz晶振TX_EN和RX_EN跳线设为ON用万用表确认FMCOMMS3的12V和5V供电正常实测值应在标称值±5%内。第二步Vivado工程配置关键在Vivado中创建Block Design必须手动添加以下IP并精确配置AXI Quad SPI IPData Width: 16-bitAD9361 SPI是16位宽高8位为寄存器地址低8位为数据Mode: Standard SPISCK Frequency: 10 MHz在Clock Configuration中将PS端SPI0时钟源设为50MHz分频系数5CS Polarity: Active LowConnects_axi_aclktoFCLK_CLK0100MHzConnectio0_io1_io2_io3tospi_mosi,spi_miso,spi_sclk,spi_ss_b注意AD9361的SPI_SS_B是低电平有效必须接IP的ss_i[0]。AXI DMA IPEnable Scatter Gather Engine: UncheckedNO-OS用简单模式Include Buffer Length Register: CheckedSetS2MM_DMACR寄存器的RS位Run/Stop由软件控制而非自动Connectm_axi_mm2s_aclkandm_axi_s2mm_aclktoFCLK_CLK0。AXI GPIO IP用于RESETB和ENABLEWidth: 2 bitsBit 0:RESETB输出低电平复位Bit 1:ENABLE输出高电平使能AD9361连接到Zynq PS的EMIO引脚确保在SDK中能用XGpioPs_WritePin()控制。提示Vivado自动生成的system.hdf文件必须在SDK中正确导入。若HDF路径错误SDK编译时会报#include xparameters.h not found这是新手最高频错误。第三步SDK工程创建与NO-OS集成在SDK中创建Hello World模板工程下载ADI官方NO-OS源码GitHub仓库analogdevicesinc/no-OS解压后将drivers/ad9361/目录下的所有.c/.h文件以及hal/zynq/下的ad9361_hal.c、xil_spi.c等全部复制到SDK工程的src/目录在src/下新建main.c内容如下#include xil_printf.h #include ad9361.h #include ad9361_api.h #include ad9361_config.h struct ad9361_dev *ad9361_phy; int main() { int32_t ret; xil_printf(AD9361 NO-OS Demo Start...\r\n); // 1. 初始化HAL层 ret ad9361_spi_init(ad9361_phy, XPAR_PS7_SPI_0_BASEADDR); if (ret 0) { xil_printf(SPI init failed: %d\r\n, ret); return ret; } // 2. 执行芯片复位 ret ad9361_reset(ad9361_phy); if (ret 0) { xil_printf(Reset failed: %d\r\n, ret); return ret; } usleep(10000); // 等待10ms // 3. 检查芯片ID ret ad9361_check_id(ad9361_phy); if (ret 0) { xil_printf(Chip ID check failed: %d\r\n, ret); return ret; } xil_printf(AD9361 detected successfully.\r\n); // 4. 配置参考时钟使用内部20MHz晶振 ret ad9361_set_ref_clk(ad9361_phy, 20000000ULL, 1); if (ret 0) { xil_printf(Ref clk setup failed: %d\r\n, ret); return ret; } // 5. 设置RX/TX中心频点为2.4GHz ret ad9361_set_rx_lo_freq(ad9361_phy, 2400000000ULL); ret ad9361_set_tx_lo_freq(ad9361_phy, 2400000000ULL); // 6. 设置采样率122.88 MSPS ret ad9361_set_rx_sampling_freq(ad9361_phy, 122880000ULL); ret ad9361_set_tx_sampling_freq(ad9361_phy, 122880000ULL); // 7. 设置RF带宽100MHz ret ad9361_set_rx_rf_bandwidth(ad9361_phy, 100000000ULL); ret ad9361_set_tx_rf_bandwidth(ad9361_phy, 100000000ULL); // 8. 手动增益模式设置RX增益为20dB ret ad9361_set_gain_control_mode(ad9361_phy, GAIN_MODE_MANUAL); ret ad9361_set_rx_gain(ad9361_phy, 2000); // 单位0.1dB // 9. 初始化DMA ret ad9361_init_dma(ad9361_phy); if (ret 0) { xil_printf(DMA init failed: %d\r\n, ret); return ret; } // 10. 使能RX/TX ret ad9361_enable_rx(ad9361_phy); ret ad9361_enable_tx(ad9361_phy); // 11. 运行TX LO泄漏校准 ret ad9361_run_tx_lo_leakage_calib(ad9361_phy); if (ret 0) { xil_printf(TX LO calib failed: %d\r\n, ret); return ret; } xil_printf(TX LO leakage calibration done.\r\n); // 12. 运行RX IQ校准 ret ad9361_run_rx_iq_calib(ad9361_phy); if (ret 0) { xil_printf(RX IQ calib failed: %d\r\n, ret); return ret; } xil_printf(RX IQ calibration done.\r\n); xil_printf(AD9361 is READY!\r\n); while(1) { // 主循环可在此添加数据收发逻辑 usleep(1000000); } return 0; }第四步编译与下载在SDK中右键工程 →Build Project确保无编译错误右键工程 →Run As→Launch on Hardware (System Debugger)观察UART输出若看到AD9361 is READY!则基本成功。此时用频谱仪探头轻触FMCOMMS3的TX1A焊盘应能看到一个纯净的2.4GHz单音信号功率约-10dBm探头触RX1A接入一个2.4GHz信号源应能在UART中看到RSSI值随输入功率变化。4.2 数据收发实战用NO-OS实现一个简单的IQ回环测试NO-OS的终极价值体现在数据通路上。下面是一个完整的IQ回环Loopback测试代码它将RX收到的数据不做任何处理直接送入TX发送出去用于验证整个数据链路的完整性。// 定义双缓冲区 #define BUFFER_SIZE 8192 static int16_t rx_buffer[BUFFER_SIZE] __attribute__ ((aligned(64))); static int16_t tx_buffer[BUFFER_SIZE] __attribute__ ((aligned(64))); // DMA完成中断回调函数 void dma_rx_callback(void *param) { // 将RX缓冲区数据原样拷贝到TX缓冲区 memcpy(tx_buffer, rx_buffer, BUFFER_SIZE * sizeof(int16_t)); // 启动TX DMA传输 ad9361_start_tx_dma(ad9361_phy, tx_buffer, BUFFER_SIZE); } int main() { // ... 前面的初始化代码省略... // 注册RX DMA完成中断回调 ad9361_register_rx_callback(ad9361_phy, dma_rx_callback, NULL); // 启动RX DMA接收 ad9361_start_rx_dma(ad9361_phy, rx_buffer, BUFFER_SIZE); xil_printf(Loopback test started. Press any key to stop.\r\n); while(1) { if (inbyte() ! 0) break; // 按任意键退出 usleep(10000); } // 停止DMA ad9361_stop_rx_dma(ad9361_phy); ad9361_stop_tx_dma(ad9361_phy);