
1. 项目概述为什么我们需要μDMA在嵌入式开发尤其是对实时性和功耗有严苛要求的无线MCU应用中CPU的时间是极其宝贵的资源。想象一下你的MCU正在处理一个复杂的无线通信协议栈同时还需要响应按键、刷新屏幕、并从一个高速ADC读取传感器数据。如果每次ADC转换完成都需要CPU停下手中的工作去执行一条“从ADC数据寄存器读取一个值然后存放到内存指定位置”的指令那么CPU大部分时间可能都浪费在了这种简单、重复的“搬运工”角色上。这不仅会导致主任务响应迟缓还会因为CPU频繁被唤醒而增加系统功耗。这就是直接内存访问DMA技术登场的核心场景。DMA的本质是在系统内部设立一个“专职的快递员”。这个快递员DMA控制器知道货物的源头如外设的数据寄存器、目的地如内存中的数组以及运送的规则一次运多少、怎么运。当外设准备好数据或需要数据时它只需给这个快递员发个信号快递员就会自动完成数据的搬运工作全程无需CPU插手。CPU只需要在快递员开始工作前告诉它任务详情配置DMA通道并在整批货物运送完毕后接收一个“任务完成”的通知中断即可。这样一来CPU就被解放出来可以专注于更复杂的计算和决策任务。德州仪器TI在其CC13x2和CC26x2系列无线微控制器中集成的μDMA控制器便是这样一个高度专业化、可配置的“超级快递系统”。它不仅仅是简单的数据搬运更提供了一套完整的物流管理方案支持32个独立的快递通道Channel每个通道可以服务一个特定的外设如UART、SPI、ADC等支持多种运输模式Mode从简单的单次送货Basic Mode到复杂的、不间断的流水线作业Ping-Pong Mode再到能根据一张“任务清单”自动跑多个不同地址的智能配送Scatter-Gather Mode。理解并熟练运用μDMA是榨干CC13x2/CC26x2这类高性能MCU潜力的关键一步尤其对于需要处理连续数据流如音频采样、无线数据包收发或复杂内存操作的应用而言它是实现高性能、低功耗设计的基石。2. μDMA核心架构与工作原理解析要驾驭μDMA不能只停留在“配置寄存器”的层面必须深入理解其内部架构和运作逻辑。这就像开车知道油门和刹车在哪固然重要但了解发动机和变速箱如何协同工作才能让你开得更稳、更省油。2.1 核心组件与数据通路μDMA控制器是一个独立于Cortex-M4F CPU的硬件模块它通过系统总线与内存、外设相连。其核心可以看作一个高度智能化的“传输调度中心”。这个中心的核心组件包括通道仲裁器这是调度中心的总指挥。当多个外设同时发出传输请求时比如UART收到数据ADC转换完成仲裁器会根据预设的优先级规则决定哪个通道的请求先被服务。CC13x2/CC26x2的μDMA提供两级优先级高优先级和默认优先级。高优先级通道总是优于默认优先级通道。在同优先级内通道号越小优先级越高通道0最高。通道控制结构表这是μDMA的“任务指令库”位于系统内存中。每个DMA通道在表中都对应一个或多个“控制结构”Control Structure每个结构体占16字节包含四个关键信息源结束指针数据搬运的起始地址。对于外设这通常是一个固定的寄存器地址对于内存这是一个指向数据缓冲区的指针。目的结束指针数据搬运的目标地址。控制字这是任务的“操作手册”定义了数据大小8/16/32位、地址增量字节、半字、字或不增量、本次传输的总项目数、仲裁大小一次突发传输多少数据以及传输模式。未使用空间保留字段。 这个表分为两半前半部分是所有通道的“主控制结构”后半部分是对应的“备用控制结构”。在简单模式下只用前半部分在乒乓或分散-聚集模式下需要用到完整的表。外设请求接口每个支持μDMA的外设都有专门的请求信号线连接到控制器。请求分为两种单次请求外设准备好传输一个数据项时发出。突发请求外设准备好传输多个数据项时发出例如UART的发送FIFO空出4个位置。突发请求的优先级高于单次请求。2.2 传输流程的微观视角一次完整的μDMA传输其微观流程可以拆解为以下几个步骤理解它们对调试和优化至关重要初始化与配置CPU在内存中设置好通道控制结构表配置好源/目的地址、数据大小、传输模式等。然后通过写UDMA:SETCHANNELEN寄存器使能目标通道。请求与仲裁外设产生数据事件如RX FIFO非空向μDMA控制器发出请求信号。控制器内的仲裁器检查所有已使能且未屏蔽请求的通道选出优先级最高的一个。取指与执行μDMA控制器根据获胜的通道号找到其在控制结构表中的主或备用条目读取其中的“操作手册”控制字和地址指针。数据传输控制器根据仲裁大小发起一次“突发”传输。例如仲裁大小设为8数据大小为8位那么控制器会一次性从源地址读取8个字节连续写入目的地址。在此期间CPU的总线访问可能会被暂时挂起取决于总线仲裁但CPU内核仍可继续执行缓存中的指令。更新与判断一次突发传输后控制器会更新控制结构中的“剩余传输项数”。然后判断如果项数未减到0且传输模式为Basic则等待外设的下一个请求如果项数减到0则本次传输任务完成。完成与中断当控制字中的“剩余传输项数”字段变为0控制器会自动将通道模式标记为“停止”并可选地产生一个传输完成中断通知CPU。CPU在中断服务例程中可以处理接收到的数据或为下一次传输重新配置DMA通道。注意一个常见的误解是“DMA传输期间CPU完全停止”。实际上只要CPU访问的指令和数据在缓存中或者访问的不是DMA正在占用的总线资源CPU是可以继续运行的。真正的瓶颈在于系统总线带宽。如果DMA在进行大量内存到内存的拷贝确实会显著占用总线影响CPU访问内存的速度。2.3 仲裁大小的艺术平衡吞吐量与实时性仲裁大小Arbitration Size是μDMA中一个关键且微妙的参数它直接影响系统性能和响应时间。是什么仲裁大小定义了一个通道一旦获得总线使用权连续传输多少数据项后才会释放总线并重新进行通道仲裁。范围是1到1024。为什么重要设置较大的仲裁大小如256或1024可以最大化传输效率。因为每次仲裁都有开销一次性传输大量数据可以减少仲裁次数提高平均数据传输速率特别适合大块数据搬运如图像帧传输。带来的风险高优先级通道的延迟Latency。如果一个低优先级通道的仲裁大小设置得很大一旦它开始传输就必须完成整个“突发”传输比如256个数据项后控制器才会重新仲裁。在此期间即使有高优先级通道发出请求也必须等待。这对于需要快速响应的实时任务如处理一个紧急的按键中断或通信超时是致命的。配置建议高实时性通道如外部中断触发的传输、对延迟敏感的通信外设如某些SPI命令响应应将仲裁大小设置为较小值如1, 2, 4, 8。高带宽通道如ADC连续采样数据到内存、LCD帧缓冲区刷新可以设置较大的仲裁大小如128, 256以提升吞吐量。默认值TI的驱动库通常为每个外设提供了一个合理的默认仲裁大小但在设计混合关键性系统时必须手动评估和调整。3. μDMA传输模式深度剖析与选型指南μDMA提供了多种传输模式远不止简单的“从A搬到B”。选择正确的模式是构建高效数据流处理链的核心。3.1 基础模式Basic与Auto模式这两种模式适用于最简单的单次传输场景。Basic模式这是最符合直觉的“请求-响应”模式。外设每准备好一个或一组数据就发一次请求μDMA传输一次然后停止等待下一个请求。它要求外设的请求信号在传输完成前必须保持有效。如果请求是脉冲式的比如某些GPIO边沿中断可能无法完成整个传输。Basic模式完成后通道会自动禁用。典型应用非周期性的、数据量不固定的传输。例如一个低速传感器通过UART不定时上报数据包。Auto模式与Basic模式类似但关键区别在于只要传输被启动就会一直进行到完成即使中途外设撤回了请求信号。这听起来有点奇怪但它主要是为软件触发的传输设计的。CPU通过写UDMA:SOFTREQ寄存器来模拟一个请求脉冲启动传输后DMA就会自动完成全部数据的搬运。典型应用内存到内存的数据块拷贝、初始化一段内存区域。你可以在代码中手动启动一次DMA传输来搬运数据而不必依赖任何外设事件。3.2 乒乓模式实现零延迟连续数据流乒乓模式是处理连续、实时数据流的利器比如音频采集、麦克风阵列、或高速ADC流。工作原理它需要两个内存缓冲区Buffer A和Buffer B以及对应的两个控制结构主结构和备用结构。初始时DMA使用主结构指向Buffer A进行传输。当Buffer A填满或取空时传输完成产生中断。关键点来了此时DMA控制器不会停止而是自动切换到备用结构指向Buffer B继续传输。同时CPU在中断服务程序中处理刚刚满的Buffer A的数据并重新配置主结构例如指向Buffer A准备下一轮。当Buffer B满时DMA又切换回主结构此时已被CPU重配为指向Buffer A如此循环往复。优势实现了双缓冲。DMA在向一个缓冲区写入数据时CPU可以安全地处理另一个缓冲区的数据两者互不干扰消除了数据搬运和处理之间的等待时间实现了数据流的“零缝拼接”。配置要点必须在内存中分配完整的控制结构表包含主和备用结构。初始化时分别配置主结构和备用结构指向两个不同的缓冲区。设置传输模式为Ping-Pong。在中断服务程序中必须及时处理数据并重载刚刚用完的那个控制结构为下一次切换做好准备。一个坑如果CPU处理数据的速度慢于DMA填充缓冲区的速度会发生“缓冲区溢出”。虽然DMA会继续切换但CPU来不及处理的数据会被新数据覆盖。因此缓冲区大小和中断处理例程的效率必须仔细设计。3.3 分散-聚集模式高级内存管理大师这是μDMA最强大的模式它允许你定义一张“任务列表”让DMA自动执行一系列不连续地址的传输。它又分为内存分散-聚集和外围分散-聚集。内存分散-聚集模式此模式由单个软件请求启动然后DMA会自动、连续地执行任务列表中的所有传输直到最后一个任务完成。任务列表中的最后一个任务必须设置为Auto模式作为结束标志。应用场景数据打包从多个分散的源缓冲区如不同的协议包负载收集数据到一个连续的目的缓冲区。数据解包将一个连续源缓冲区的数据分发到多个分散的目的缓冲区。复杂初始化一次性配置多个不同地址的外设寄存器。工作流程CPU在内存中创建一个“任务列表”列表中的每一项都是一个完整的控制结构包含源/目的指针、控制字且控制字的模式字段设为“内存分散-聚集”。CPU配置通道的主控制结构将其设置为一个“搬运工”它的任务是把任务列表中的条目一个一个地拷贝到备用控制结构中。CPU触发软件请求。μDMA使用主结构将第一个任务从列表拷贝到备用结构。μDMA执行备用结构定义的这个具体传输任务。完成后μDMA再次使用主结构拷贝列表中的下一个任务到备用结构然后执行……如此循环直到遇到一个模式为Auto的任务执行完后整个流程停止。外围分散-聚集模式此模式与内存模式的核心区别在于每个子任务的启动都依赖于外设的一次请求。它适用于外设数据就绪事件触发一系列复杂传输的场景。应用场景处理来自同一外设、但需要存放到不同内存区域的数据块。例如一个图像传感器输出一帧数据但YUV分量需要分别存放到三个不同的缓冲区中。每个缓冲区填满都需要一次外设请求。工作流程与内存模式类似但控制器每完成一个子任务就会停下来等待外设的下一个请求信号收到请求后才从任务列表中加载并执行下一个子任务。实操心得分散-聚集模式极大地增强了DMA的灵活性但它的配置也最为复杂。在调试时务必在内存中仔细检查你构建的“任务列表”的每一个字段是否正确特别是源/目的指针和传输模式。一个常见的错误是忘记将最后一个任务的模式设置为Auto导致DMA无法正常停止。4. 基于CC13x2/CC26x2的μDMA配置实战理论说得再多不如一行代码。下面我们以TI的SimpleLink SDK为例拆解一个UART通过DMA接收数据的完整配置流程。这里我们选择更实用的乒乓模式以实现稳定的串口数据流接收。4.1 环境与资源准备首先确保你已安装好Code Composer Studio (CCS)或IAR Embedded Workbench以及对应版本的SimpleLink CC13x2/CC26x2 SDK。我们假设使用CC26x2 LaunchPad开发板并启用UART0的RX通道对应μDMA通道1。核心步骤与代码解析定义缓冲区与控制结构表#include ti/drivers/dma/UDMACC26XX.h #include ti/drivers/dma/UDMACC26XX.h // 1. 定义乒乓缓冲区 #define BUFFER_SIZE 128 uint8_t gPingBuffer[BUFFER_SIZE]; uint8_t gPongBuffer[BUFFER_SIZE]; // 2. 声明DMA控制结构表。 // 注意此表必须1024字节对齐编译器属性 __attribute__((aligned(1024))) 或 #pragma DATA_ALIGN 是关键。 #if defined(__TI_COMPILER_VERSION__) #pragma DATA_ALIGN(dmaControlTable, 1024) static tDMAControlTable dmaControlTable[64]; // 64个条目对应32个通道的主备用结构 #elif defined(__IAR_SYSTEMS_ICC__) static tDMAControlTable dmaControlTable[64] 0x20004000; // 使用IAR的定位语法地址需按1024对齐 #else static tDMAControlTable dmaControlTable[64] __attribute__((aligned(1024))); #endif这里tDMAControlTable是SDK定义的结构体类型对应一个16字节的控制结构。分配64个是为了容纳所有32个通道的主结构和备用结构。初始化DMA驱动与通道// DMA驱动参数配置 UDMACC26XX_Object udmaCC26XXObject; UDMACC26XX_HWAttrs udmaCC26XXHWAttrs { .controlTableBaseAddr (void *)dmaControlTable, // 告诉驱动控制表的位置 }; UDMACC26XX_Config UDMACC26XX_config { .object udmaCC26XXObject, .hwAttrs udmaCC26XXHWAttrs, }; // 打开DMA驱动 DMA_Handle dmaHandle DMA_open(BOARD_DMA0, NULL); // BOARD_DMA0 通常在板级配置中定义 // 配置UART略使用TI Driver的UART配置 UART_Handle uartHandle; UART_Params uartParams; UART_Params_init(uartParams); uartParams.readMode UART_MODE_BLOCKING; // 实际上将由DMA接管 uartParams.readDataMode UART_DATA_BINARY; uartParams.readReturnMode UART_RETURN_FULL; uartParams.baudRate 115200; uartHandle UART_open(BOARD_UART0, uartParams);配置DMA传输参数乒乓模式DMA_ControlTable entry; DMA_Params dmaParams; // 初始化DMA参数 DMA_Params_init(dmaParams); dmaParams.mode DMA_MODE_PINGPONG; // 设置为乒乓模式 dmaParams.triggerSource UDMA_CH1_UART0_RX; // 触发源UART0 RX通道通道1 dmaParams.transferMode DMA_TRANSFER_SINGLE; // 每个缓冲区传输模式为单次 dmaParams.transferSize BUFFER_SIZE; // 每个缓冲区大小 dmaParams.transferUnitSize DMA_SIZE_8; // 传输单元8位字节 dmaParams.srcInc DMA_INC_NONE; // 源地址UART数据寄存器不递增 dmaParams.dstInc DMA_INC_8; // 目的地址内存缓冲区按字节递增 dmaParams.srcAddr (void *)UART0_BASE UART_O_DATA; // 源地址UART数据寄存器地址 dmaParams.dstAddr (void *)gPingBuffer; // 初始目的地址Ping缓冲区 dmaParams.altDstAddr (void *)gPongBuffer; // 备用目的地址Pong缓冲区 // 注意在乒乓模式下需要配置备用控制结构这里通过altDstAddr指定了备用缓冲区的地址。 // SDK内部会帮我们设置好主和备用控制结构。 // 创建DMA传输控制块 DMA_Handle transferHandle DMA_create(dmaHandle, entry, dmaParams); if (transferHandle NULL) { // 错误处理 }设置回调函数与启动传输// 定义传输完成回调函数 void dmaPingPongCallback(DMA_Handle handle, int_fast16_t status, void *userArg) { // status 可以判断是主传输完成还是备用传输完成 // userArg 可以用来传递自定义参数比如标识是哪个缓冲区满了 static bool isPingBufferReady false; uint8_t *fullBuffer; if (status DMA_TRANSFER_COMPLETED) { // 主结构传输完成Ping Buffer满 fullBuffer gPingBuffer; isPingBufferReady true; // 注意此时DMA已经在用备用结构指向Pong Buffer接收新数据了 } else if (status DMA_TRANSFER_ALT_COMPLETED) { // 备用结构传输完成Pong Buffer满 fullBuffer gPongBuffer; isPingBufferReady false; // 此时DMA已切换回主结构指向Ping Buffer接收新数据 } // 在这里处理满的缓冲区数据 processUartData(fullBuffer, BUFFER_SIZE); // 重要在乒乓模式下我们不需要手动重新提交传输。 // DMA会在两个缓冲区之间自动切换。我们的任务只是及时处理数据。 } // 将回调函数关联到DMA传输 DMA_registerCallback(transferHandle, dmaPingPongCallback, NULL); // 最后启动DMA传输 DMA_startTransfer(transferHandle);一旦调用DMA_startTransferDMA通道就被使能并等待UART的RX FIFO达到触发阈值产生突发请求或非空产生单次请求随后自动开始数据传输。4.2 关键寄存器操作解析底层视角虽然SDK封装了大部分细节但了解底层寄存器操作对调试和深入优化有帮助。以配置通道1UART0 RX的Basic模式为例设置控制表基地址通过写UDMA:CTRL寄存器的CTLBASE字段告诉μDMA控制器我们定义的控制结构表dmaControlTable在内存中的起始地址。SDK的初始化函数会完成这一步。配置通道控制字我们需要在控制表中找到通道1的主控制结构偏移量0x10并填充其控制字。这通常通过uDMAChannelControlSet()函数完成它内部会设置数据大小、地址增量、仲裁大小等。// 伪代码示意实际使用SDK的DMA_Params配置更安全 uint32_t controlWord UDMA_CHCTL_DSTINC_8 | // 目的地址按字节递增 UDMA_CHCTL_SRCINC_NONE | // 源地址不递增 UDMA_CHCTL_DSTSIZE_8 | // 目的数据大小8位 UDMA_CHCTL_SRCSIZE_8 | // 源数据大小8位 UDMA_CHCTL_ARBSIZE_4 | // 仲裁大小4个项 UDMA_CHCTL_XFERSIZE_128 | // 总传输项数128 UDMA_CHCTL_MODE_BASIC; // 基础模式 // 将controlWord写入 dmaControlTable[1].control (地址: dmaControlTable 0x10 0x08)设置源和目的指针同样在主控制结构中设置。dmaControlTable[1].srcEnd (void *)(UART0_BASE UART_O_DATA); // 源结束指针 dmaControlTable[1].dstEnd (void *)(gRxBuffer BUFFER_SIZE - 1); // 目的结束指针注意是结束地址使能通道写UDMA:SETCHANNELEN寄存器的第1位从0开始计数为1。清除请求掩码写UDMA:CLEARREQMASK寄存器的第1位为1允许UART外设的请求信号传递到μDMA控制器。完成以上步骤后当UART收到数据DMA传输便会自动开始。5. 常见问题排查与调试技巧实录在实际项目中DMA配置出错往往会导致一些难以直接定位的诡异问题比如数据错位、系统卡死、中断不触发等。以下是我在多个项目中总结的排查清单和调试技巧。5.1 典型问题速查表问题现象可能原因排查步骤与解决方案DMA传输完全不启动1. 通道未使能。2. 外设请求被屏蔽 (REQMASK)。3. 控制表地址未对齐或设置错误。4. 外设本身未配置为产生DMA请求。1. 检查UDMA:SETCHANNELEN寄存器对应位。2. 检查UDMA:REQMASK寄存器确保对应位为0未屏蔽。3. 使用调试器查看UDMA:CTRL.CTLBASE寄存器值确认其指向的地址是否正确且是1024字节对齐的。检查内存中控制结构内容。4. 查阅外设章节的DMA配置部分例如UART需要使能DMA接收 (UART:DMACTL.RXDMACTL)。数据传输不完整或丢失1. 传输总项数 (XFERSIZE) 设置错误。2. 仲裁大小 (ARBSIZE) 大于外设FIFO深度。3. 目的地址指针递增方式 (DSTINC) 与数据大小不匹配。4. 在Basic模式下外设请求信号过早撤销。1. 核对XFERSIZE值它表示要传输的项目数不是字节数除非数据大小为8位。2. 确保仲裁大小不超过外设FIFO的触发阈值。例如UART RX FIFO触发等级设为4则仲裁大小不应大于4。3. 若数据大小为16位 (HALFWORD)地址增量至少应为2INC_2。4. 考虑使用Auto模式软件触发或检查外设请求信号时序。系统进入HardFault1. 源或目的指针指向非法地址如NULL或未映射区域。2. 控制表内存区域被其他代码意外修改。3. DMA传输期间访问了已释放的内存。1. 在调试器中检查dmaControlTable中配置的srcEnd和dstEnd指针值是否有效。2. 将控制表所在的内存区域如.dmaTable段在链接器文件中设置为非缓存、非共享区域并确保其不被其他变量覆盖。3. 确保DMA传输的生命周期覆盖对缓冲区的访问。乒乓模式中断只触发一次1. 备用控制结构未正确配置。2. 中断服务程序中没有正确处理状态或错误地停止了DMA通道。3. 缓冲区处理超时导致DMA等待。1. 确认dmaParams.altDstAddr已正确设置且SDK成功配置了备用结构。查看内存中通道的备用控制结构内容。2. 在回调函数中不要调用DMA_stopTransfer除非你想终止传输。只需处理数据即可。3. 优化数据处理函数确保其执行时间远小于DMA填满一个缓冲区的时间。分散-聚集模式卡在某个任务1. 任务列表中的控制结构格式错误。2. 最后一个任务的模式未设置为AUTO。3. 任务指针计算错误导致加载了错误的内存内容作为控制字。1. 将任务列表在内存中的内容打印或通过调试器查看逐一核对每个控制结构的字段。2. 确保列表末尾条目的控制字模式字段为UDMA_MODE_AUTO。3. 检查用于计算下一个任务地址的指针算术确保步长是sizeof(tDMAControlTable)16字节。5.2 高级调试技巧使用调试器观察控制表这是最直接的调试手段。在CCS或IAR的Memory Browser中直接查看你定义的dmaControlTable数组的内存。找到对应通道的条目偏移量 通道号 * 0x10检查其中的srcEnd、dstEnd和control字段的值是否符合预期。在传输过程中control字段中的XFERSIZE传输剩余项数和模式位会被硬件自动更新观察其变化可以判断传输进度。利用DMA状态寄存器UDMA:STATUS寄存器可以显示哪些通道当前处于活动状态 (ACTIVE位)。这在排查多个DMA通道竞争问题时非常有用。总线分析仪对于极其复杂或时序要求苛刻的问题逻辑分析仪或芯片内置的总线跟踪模块如ARM的ETM/ITM是终极武器。你可以捕获DMA请求信号、总线访问序列精确分析传输是否发生、延迟是多少、是否被高优先级任务打断。软件模拟与日志在关键位置如DMA回调函数、中断入口添加轻量级的日志输出通过ITM或备用UART记录缓冲区索引、数据长度等信息。这有助于理解DMA与CPU的交互时序。优先级与仲裁大小压测如果你怀疑是高优先级任务被低优先级DMA阻塞可以设计一个压测场景让一个低优先级、大仲裁大小的DMA通道如内存拷贝持续运行同时用一个高优先级、小仲裁大小的通道如GPIO触发来测量其响应延迟。通过调整仲裁大小直观地观察其对实时性的影响。5.3 性能优化要点缓冲区对齐确保源和目的缓冲区在内存中按照数据大小对齐8位数据按字节对齐16位按半字对齐32位按字对齐。不对齐的访问在某些架构上会导致额外的总线周期降低性能。善用数据大小在总线带宽允许的情况下使用更大的数据大小如32位可以减少传输次数提高效率。例如从32位宽的SPI FIFO读数到32位对齐的内存使用32位传输是最优的。谨慎使用高优先级不要轻易将所有通道设为高优先级。如果大家都高那就等于大家都不高。只将真正对延迟敏感的关键通道设为高优先级。理解总线拓扑CC13x2/CC26x2内部有多条总线。了解你的外设和内存位于哪条总线上DMA访问是否会与CPU访问产生冲突。有时将频繁访问的DMA缓冲区放到SRAM中更快的bank如果存在可以提升性能。配置μDMA就像在为一个复杂的交通系统制定规则初看繁琐但一旦掌握就能让你的嵌入式系统从“单车道乡村公路”升级为“智能立交枢纽”各个数据流井然有序、高效通行。它带来的性能提升和功耗降低是实实在在的尤其是在电池供电的无线物联网设备中这份投入绝对物超所值。