C2000 DMA通道优先级与寄存器配置实战指南

📅 2026/7/19 16:51:59
C2000 DMA通道优先级与寄存器配置实战指南
1. 项目概述与DMA核心价值在嵌入式实时控制系统的开发中尤其是在电机控制、数字电源、新能源并网这些对实时性要求极高的领域CPU的每一微秒都异常宝贵。当你的ADC以1MHz的频率采样或者SPI总线以50Mbps的速率与外部传感器通信时如果每一次数据搬运都需要CPU通过软件指令来执行那么CPU将深陷于数据搬运的泥潭无暇顾及核心的控制算法。这时直接内存访问DMA就成了解放CPU算力的“幕后英雄”。简单来说DMA就是一个硬件搬运工。它独立于CPU拥有自己的地址总线和数据总线能够在外设如ADC结果寄存器、SPI接收缓冲器和内存如RAM之间直接搬运数据。整个过程由硬件自动完成CPU只需在初始化时告诉DMA“从哪里搬”、“搬到哪里”、“搬多少”然后就可以去执行其他任务等待DMA搬运完成后通过中断通知即可。这种机制极大地减少了CPU的中断开销和数据搬运延迟是实现高效、实时系统的基石。德州仪器TI的C2000系列微控制器作为实时控制领域的明星产品其内置的DMA控制器功能强大且灵活。它支持多达6个独立通道每个通道都可以配置不同的源地址、目的地址、传输量以及触发方式。但功能强大也意味着配置复杂尤其是通道优先级和寄存器配置这两个核心环节如果理解不透彻或配置不当轻则导致数据传输效率低下重则引发数据丢失、时序错乱等严重问题。本文将结合官方技术手册和实际项目经验为你深入解析C2000 DMA的优先级机制与关键寄存器配置让你能真正驾驭这个强大的数据搬运引擎。2. DMA通道优先级机制深度解析C2000的DMA控制器支持两种优先级模式轮询Round-Robin模式和通道1高优先级Channel 1 High-Priority模式。理解这两种模式的工作机制是合理规划多通道DMA任务、确保关键数据流实时性的前提。2.1 标准轮询Round-Robin模式这是默认的优先级模式。在此模式下所有6个DMA通道CH1-CH6具有完全相同的优先级。DMA内部维护一个状态机按照固定的顺序通常是CH1 - CH2 - ... - CH6 - CH1...循环检查每个通道。只有当某个通道被使能RUNSTS位为1且收到了有效的事件触发PERINTFLG位被置1时它才会被加入服务队列。工作流程如下状态机从当前通道开始按顺序检查下一个通道。如果检查到的通道处于“待服务”状态使能且有待处理事件则开始服务该通道执行一次突发Burst传输。该通道的突发传输完成后状态机移动到下一个通道继续检查。如果检查到的通道没有待处理事件则直接跳过继续检查下一个。这种模式非常公平保证了所有活跃通道都能获得服务机会不会出现某个通道长期霸占总线的情况。它适用于多个外设数据流重要性相当、带宽要求不高的场景例如同时搬运多个低速SPI传感器的数据。2.2 通道1高优先级模式及其精妙之处这是C2000 DMA一个非常关键的特性。当通过PRIORITYCTRL1寄存器的CH1PRIORITY位使能此模式后通道1CH1将获得绝对优先权而通道2至通道6CH2-CH6则继续保持轮询模式且彼此优先级相同。这个“绝对优先权”的运作机制比想象中更精细我们通过手册中的例子来剖析假设CH1、CH4、CH5在通道1高优先级模式下被使能且CH4正在处理其突发传输。此时CH1和CH5都从各自的外设收到了中断触发信号变为待处理状态。当CH4当前的字Word传输完成时注意不是整个突发传输完成无论CH4的整个突发传输是否结束其执行都会被立即挂起Suspended。DMA状态机会转而服务最高优先级的CH1。只有在CH1的整个突发传输完成后被挂起的CH4才会恢复Resume执行。CH4完成后才会轮到CH5。这里有三个至关重要的细节是手册没有明说但实践中必须牢记的抢占粒度是“字传输”而非“突发传输”高优先级通道CH1的抢占点发生在低优先级通道完成一个字16位或32位数据的搬运之后而不是等待低优先级通道完成整个突发可能包含多个字。这极大地减少了高优先级任务的响应延迟。状态保存与恢复当一个低优先级通道如例中的CH4被挂起时DMA硬件会自动保存该通道的当前状态如已传输的字数、当前地址指针等。当CH1完成后硬件能精确地恢复CH4的现场从中断点继续执行保证数据搬运的完整性。影子寄存器Shadow Register的作用在高优先级通道服务期间低优先级通道的配置如地址、计数是安全的。因为DMA使用了两套寄存器影子寄存器和活动寄存器。软件配置的是影子寄存器当传输启动时影子寄存器的值被一次性拷贝到活动寄存器中供DMA硬件使用。因此即使在CH4被挂起期间软件去修改CH4的影子寄存器如更新目的地址也不会影响当前被挂起的传输也不会造成冲突。待CH4恢复时它使用的仍是之前从影子寄存器加载到活动寄存器的那套参数。为什么通常将ADC分配给CH1在电机控制中ADC采样是控制环路的核心其数据必须被及时、确定性地搬运到内存中进行处理任何延迟都可能导致控制性能下降甚至失稳。通道1的高优先级模式确保了即使系统中有其他DMA活动如搬运UART日志、SPI通信数据ADC数据的搬运请求也能被立即响应为快速电流环、速度环的计算争取了宝贵时间。配置注意事项模式互斥手册明确指出通道1的高优先级模式CH1PRIORITY1和单次模式ONESHOT1不能同时使用。这是因为单次模式要求通道在完成一次完整传输后自动停止这与高优先级模式下可能被多次触发、需要持续响应的场景相冲突。其他通道CH2-CH6则不受此限制它们可以在CH1为高优先级时使用单次模式。配置时机PRIORITYCTRL1.CH1PRIORITY位只能在所有DMA通道都被禁用RUNSTS0时修改。修改后建议通过DMACTRL.PRIORITYRESET位对优先级状态机进行一次复位以确保状态机从一个干净的状态开始工作。3. 核心寄存器配置详解与实战指南理解了优先级机制我们还需要通过配置一系列寄存器来“教会”DMA如何工作。C2000的DMA每个通道都有近20个寄存器看似繁杂但可以归纳为几个功能组。下面我们以一次典型的ADC数据搬运为例详解关键寄存器的配置。3.1 控制与模式寄存器组设定行为框架这是DMA通道的“大脑”决定了通道的基本工作模式。1. MODE寄存器偏移地址 0h此寄存器定义了通道的全局行为。CHINTE(位15)通道中断使能。置1后当满足CHINTMODE条件时DMA会向CPU产生中断。建议在传输完成后需要CPU处理的场景如处理一批数据下使能。DATASIZE(位14)数据大小。0表示每次传输16位1表示32位。注意此设置影响每次读/写操作的数据宽度但所有其他寄存器如BURST_SIZE中定义的长度和步进仍以16位字为单位。若选择32位模式地址步进值需要相应调整例如想每次地址1个32位字则需设置步进为2。CONTINUOUS(位11)连续模式。置1后当一次传输TRANSFER_COUNT减到0完成后通道会自动用影子寄存器的值重新初始化并等待下一个事件触发。否则传输完成后通道会自动停止RUNSTS清零。适用于需要循环缓冲区的场景如持续ADC采样。ONESHOT(位10)单次模式。置1后每个外设事件触发会导致通道完成整个传输所有突发。否则每个事件触发只导致一次突发传输。注意与CH1高优先级模式的互斥性。CHINTMODE(位9)中断产生模式。0在新传输开始时产生中断1在传输结束时产生中断。通常选择1在数据就绪后通知CPU。PERINTE(位8)外设事件触发使能。必须置1否则DMA不会响应硬件事件或软件强制触发。PERINTSEL(位4-0)传统位通常忽略。在C2000 F2838x等新型号中外设触发源的选择是通过独立的DMACHSRCSELn寄存器组属于DMA_CLA_SRC_SEL_REGS来配置的这里只需设置为通道号本身。2. CONTROL寄存器偏移地址 1h此寄存器包含状态标志和控制位很多位是“写1置位”W1S。RUNSTS(位13)运行状态标志只读。为1表示通道已使能正在等待或处理事件。BURSTSTS(位12)突发状态标志只读。为1表示通道正在执行一次突发传输。TRANSFERSTS(位11)传输状态标志只读。为1表示通道正处于一次传输可能包含多个突发过程中。PERINTFLG(位8)外设事件触发标志只读。当有效触发事件到来时硬件将其置1。当DMA开始服务该通道开始一次突发时硬件自动将其清零。RUN(位0)运行控制位W1S。这是启动通道的关键。在配置完所有参数后向此位写1通道即进入使能状态RUNSTS变为1等待触发。HALT(位1)暂停控制位W1S。写1可暂停正在进行的传输暂停后可通过再次写RUN位来恢复。SOFTRESET(位2)通道软复位W1S。写1将复位该通道到默认状态所有状态位清零计数寄存器清零但不会影响配置寄存器如MODE,BURST_SIZE等。在重新配置通道前应先进行软复位。PERINTFRC(位3)强制外设事件触发W1S。当PERINTE使能时向此位写1会手动设置PERINTFLG从而触发一次DMA传输。常用于软件启动DMA或测试。3.2 传输参数寄存器组定义搬运细节这组寄存器定义了“搬多少”和“怎么搬”。1. 大小与计数寄存器BURST_SIZE(偏移地址 2h)定义一次突发Burst传输包含多少个字Word。实际大小 BURST_SIZE 1。例如设置为7则一次突发传输8个字。突发传输是DMA响应一次事件触发所执行的最小不可分割单元。TRANSFER_SIZE(偏移地址 6h)定义一次完整的传输Transfer包含多少个突发。实际大小 TRANSFER_SIZE 1。例如设置为15则一次完整传输包含16个突发。BURST_COUNT(偏移地址 3h)和TRANSFER_COUNT(偏移地址 7h)分别是剩余突发字数和剩余突发数的计数器只读。用于监控传输进度。2. 地址步进寄存器这是DMA地址自动管理的核心能实现灵活的数据排列。SRC_BURST_STEP/DST_BURST_STEP(偏移地址 4h/5h)定义在一个突发内部每搬运一个字后源/目标地址的增量。这是一个16位有符号数二进制补码范围-4096到4095。例如ADC结果寄存器地址固定则SRC_BURST_STEP设为0目标地址是内存中的数组希望数据连续存放则DST_BURST_STEP设为2假设DATASIZE1即32位模式地址按字节计算时一个字是2个字节所以2。SRC_TRANSFER_STEP/DST_TRANSFER_STEP(偏移地址 8h/9h)定义在完成一个突发后源/目标地址的增量。这用于在内存中跳过一段间隔。例如ADC的突发大小为8个字我们希望每8个采样点存放到一个数组下一个8个点存放到另一个不连续的数组就可以通过设置DST_TRANSFER_STEP为一个较大的值来实现。3. 地址环绕Wrap寄存器组用于实现循环缓冲区在实时流处理中极其有用。SRC_WRAP_SIZE/DST_WRAP_SIZE(偏移地址 Ah/Dh)定义在多少次突发之后地址环绕到起始地址。实际值 WRAPSIZE 1。若要禁用环绕将其设置为大于TRANSFER_SIZE的值即可通常设为0xFFFF。SRC_WRAP_STEP/DST_WRAP_STEP(偏移地址 Ch/Fh)定义当地址环绕发生时在起始地址上增加的偏移量。这可以用来实现“乒乓缓冲区”或更复杂的缓冲区链。例如设置DST_WRAP_SIZE为3即每4个突发环绕一次DST_WRAP_STEP为0则目标地址会在4个数组元素间循环。若DST_WRAP_STEP设为数组长度的偏移则可以实现双缓冲。4. 地址寄存器组分为影子Shadow和活动Active两组这是实现“无干扰”动态配置的关键。SRC_BEG_ADDR_SHADOW/DST_BEG_ADDR_SHADOW(偏移地址 10h/18h)软件可读写的起始地址影子寄存器。传输开始时其值被加载到活动寄存器。SRC_ADDR_SHADOW/DST_ADDR_SHADOW(偏移地址 12h/1Ah)软件可读写的当前地址影子寄存器。传输开始时其值被加载到活动寄存器。在连续模式下一次传输结束后此寄存器会被更新为下一次传输的起始地址考虑步进和环绕软件可以读取它以知悉下一次DMA将写入的位置。SRC_BEG_ADDR_ACTIVE/DST_BEG_ADDR_ACTIVE(偏移地址 14h/1Ch)只读的起始地址活动寄存器。反映DMA硬件当前使用的起始地址。SRC_ADDR_ACTIVE/DST_ADDR_ACTIVE(偏移地址 16h/1Eh)只读的当前地址活动寄存器。实时反映DMA硬件下一次读/写操作的地址。关键技巧由于存在影子寄存器软件可以在DMA传输进行中的任何时刻安全地更新影子寄存器中的地址、大小等参数。这些更新不会影响正在进行的传输。当下一次传输启动时对于连续模式是当前传输结束后的下一次对于非连续模式是下一次软件启动新的配置会自动生效。这为实现双缓冲、动态调整缓冲区等高级功能提供了硬件支持。3.3 全局控制寄存器DMACTRL(全局偏移地址 0h)包含HARDRESET硬复位整个DMA模块和PRIORITYRESET复位优先级状态机位。PRIORITYCTRL1(全局偏移地址 4h)如前所述CH1PRIORITY位在此配置。PRIORITYSTAT(全局偏移地址 6h)ACTIVESTS位指示当前正在服务的通道号。ACTIVESTS_SHADOW位在CH1高优先级模式下特别有用它记录了被CH1中断的那个低优先级通道号当CH1服务完成后状态机知道该恢复哪个通道。4. 实战配置以ADC双缓冲连续采样为例假设我们需要用DMA通道1高优先级搬运ADC结果目标是在内存中实现一个大小为SAMPLE_BUFFER_SIZE的双缓冲Ping-Pong Buffer供CPU处理。步骤1定义缓冲区与参数#define BURST_SIZE_WORDS 8 // 每个突发传输8个字16位ADC结果 #define TRANSFER_SIZE_BURSTS (SAMPLE_BUFFER_SIZE / 2 / BURST_SIZE_WORDS) // 传输大小半缓冲区大小/突发大小 volatile uint16_t pingBuffer[SAMPLE_BUFFER_SIZE / 2]; volatile uint16_t pongBuffer[SAMPLE_BUFFER_SIZE / 2]; volatile uint16_t *currentDmaDest; // 指向DMA当前正在写入的半区步骤2初始化DMA通道1寄存器// 假设使用TI的DriverLib库代码更清晰 // 1. 先停止并复位通道 DMA_disableChannel(DMA_BASE, DMA_CH1); DMA_softResetChannel(DMA_BASE, DMA_CH1); // 2. 配置模式寄存器 (MODE) DMA_setChannelMode(DMA_BASE, DMA_CH1, DMA_MODE_CHINTE_ENABLE | // 使能通道中断 DMA_MODE_DATASIZE_16BIT | // ADC结果为16位 DMA_MODE_CONTINUOUS_ENABLE | // 连续模式实现循环缓冲 DMA_MODE_ONESHOT_DISABLE | // 禁用单次模式与高优先级冲突 DMA_MODE_CHINTMODE_END_OF_TRANSFER | // 传输完成产生中断 DMA_MODE_PERINTE_ENABLE); // 使能外设触发 // 3. 配置传输参数 DMA_setBurstSize(DMA_BASE, DMA_CH1, BURST_SIZE_WORDS - 1); // 寄存器值实际值-1 DMA_setTransferSize(DMA_BASE, DMA_CH1, TRANSFER_SIZE_BURSTS - 1); // 4. 配置地址步进 // 源地址ADC结果寄存器固定突发内和突发间步进均为0 DMA_setSrcBurstStep(DMA_BASE, DMA_CH1, 0); DMA_setSrcTransferStep(DMA_BASE, DMA_CH1, 0); // 目标地址内存突发内步进为1连续存放突发间步进为0直到环绕 DMA_setDestBurstStep(DMA_BASE, DMA_CH1, DMA_ADDR_STEP_INCR1); DMA_setDestTransferStep(DMA_BASE, DMA_CH1, 0); // 5. 配置地址环绕以实现双缓冲 // 目标地址每传输完半缓冲区TRANSFER_SIZE_BURSTS个突发就环绕一次 DMA_setDestWrapSize(DMA_BASE, DMA_CH1, TRANSFER_SIZE_BURSTS - 1); // 环绕时目标起始地址跳转到另一个半缓冲区的起始 DMA_setDestWrapStep(DMA_BASE, DMA_CH1, (SAMPLE_BUFFER_SIZE / 2) * sizeof(uint16_t)); // 6. 配置起始地址影子寄存器 currentDmaDest pingBuffer; // 初始指向Ping缓冲区 DMA_setDestBeginAddrShadow(DMA_BASE, DMA_CH1, (uint32_t)currentDmaDest); DMA_setDestAddrShadow(DMA_BASE, DMA_CH1, (uint32_t)currentDmaDest); DMA_setSrcBeginAddrShadow(DMA_BASE, DMA_CH1, (uint32_t)AdcaResultRegs.ADCRESULT0); // ADC结果寄存器地址 DMA_setSrcAddrShadow(DMA_BASE, DMA_CH1, (uint32_t)AdcaResultRegs.ADCRESULT0); // 7. 配置触发源通过DMACHSRCSEL寄存器非MODE.PERINTSEL DMA_setPeripheralSelection(DMA_BASE, DMA_CH1, DMA_TRIGGER_ADCA1); // 假设由ADC序列1触发 // 8. 使能通道1高优先级模式必须在所有通道禁用时配置 DMA_disableAllChannels(DMA_BASE); DMA_setChannel1Priority(DMA_BASE, DMA_PRIORITY_CH1_HIGH); // 设置CH1为高优先级 DMA_resetPriority(DMA_BASE); // 复位优先级状态机 // 9. 清除可能存在的旧中断标志使能DMA通道中断 DMA_clearInterruptStatus(DMA_BASE, DMA_CH1); Interrupt_register(INT_DMA_CH1, dmaCH1Isr); // 注册中断服务函数 Interrupt_enable(INT_DMA_CH1); // 10. 启动通道 DMA_enableChannel(DMA_BASE, DMA_CH1);步骤3编写DMA中断服务程序ISR__interrupt void dmaCH1Isr(void) { // 1. 清除DMA通道中断标志 DMA_clearInterruptStatus(DMA_BASE, DMA_CH1); // 2. 判断DMA当前正在写入哪个半区通过读取目标影子地址 // 注意在连续模式下传输完成后DST_ADDR_SHADOW会自动更新为下一次传输的起始地址 uint32_t nextDestAddr DMA_getDestAddrShadow(DMA_BASE, DMA_CH1); // 3. 处理刚刚被填满的半区 if (currentDmaDest pingBuffer nextDestAddr (uint32_t)pongBuffer) { // DMA刚刚写满了Ping区并已跳转到Pong区 processBuffer(pingBuffer, SAMPLE_BUFFER_SIZE / 2); // 处理Ping区数据 currentDmaDest pongBuffer; // 更新当前指针 } else if (currentDmaDest pongBuffer nextDestAddr (uint32_t)pingBuffer) { // DMA刚刚写满了Pong区并已跳转回Ping区 processBuffer(pongBuffer, SAMPLE_BUFFER_SIZE / 2); // 处理Pong区数据 currentDmaDest pingBuffer; // 更新当前指针 } // 如果地址不符合预期可能是配置错误或缓冲区溢出应进入错误处理 // 4. 如果需要可以在这里动态更新DMA配置例如改变目标缓冲区 // 因为操作的是影子寄存器所以是安全的。 // DMA_setDestBeginAddrShadow(DMA_BASE, DMA_CH1, (uint32_t)newBuffer); // 5. 中断应答 Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9); // DMA中断属于ACK Group 9 }5. 高级主题溢出检测与错误处理DMA的CONTROL寄存器中有一个关键的OVRFLG溢出标志位14。这是保障数据可靠性的重要机制。溢出是如何发生的当DMA通道收到一个外设事件触发时会将PERINTFLG位置1表示有一个传输请求待处理。当DMA开始服务该通道即开始一个突发传输时硬件会自动清除PERINTFLG位。但是如果在PERINTFLG被置1之后、被清除之前的这个时间窗口内同一个外设又产生了第二个事件触发那么第二个触发信号将会丢失。此时硬件会设置OVRFLG位表示发生了溢出Overrun。溢出意味着什么意味着你丢失了一次数据搬运的触发事件。在ADC连续采样场景下这就可能丢失一个或一组采样点导致控制环路的数据不连续严重影响系统性能。如何配置和处理溢出使能溢出中断在MODE寄存器中设置OVRINTE位为1。在中断服务程序中检查在DMA通道中断服务程序中除了检查传输完成中断还应检查OVRFLG位。清除标志与恢复一旦检测到溢出应通过写CONTROL.ERRCLR位来清除OVRFLG标志。更重要的是你需要评估数据丢失对系统的影响并采取恢复措施。这可能包括重置数据缓冲区指针。丢弃当前可能不完整的数据块。在关键应用中甚至需要触发一个系统级的安全状态如故障停车。配置建议 对于高速、关键的数据流如电机相电流采样务必使能溢出中断并将其视为一种严重的运行时错误进行处理。同时在系统设计时要确保DMA的吞吐能力与总线带宽、突发大小有关高于外设的数据产生速率并留有一定余量从根本上避免溢出的发生。6. 调试技巧与常见问题排查在实际开发中DMA配置出错往往表现为数据错乱、传输不触发或系统卡死。以下是一些实用的调试方法和常见问题1. DMA根本不触发检查触发源配置确认DMACHSRCSELn寄存器是否正确映射到了目标外设如ADC、SPI、ePWM。这是新手最常出错的地方。检查PERINTE和RUN位MODE.PERINTE必须为1以允许触发CONTROL.RUN必须为1以启动通道。检查外设端确认外设本身已正确配置并产生了中断事件。例如ADC的SOCStart-of-Conversion和中断是否使能软件强制触发测试尝试在初始化后手动向CONTROL.PERINTFRC位写1。如果DMA能正常搬运一次说明DMA配置基本正确问题出在外设触发信号上。2. 数据搬运到了错误地址检查地址寄存器确认SRC_BEG_ADDR_SHADOW和DST_BEG_ADDR_SHADOW设置的是否是32位绝对地址。在C2000中对于全局变量数组直接使用(uint32_t)array即可。检查步进寄存器特别是DATASIZE设置为32位时DST_BURST_STEP应为2按字节地址计算而不是1。理解“所有长度和步进以16位字为单位”这句话至关重要。检查环绕配置如果使能了环绕计算WRAP_SIZE和WRAP_STEP时务必小心。WRAP_SIZE定义的是“多少次突发后环绕”WRAP_STEP是环绕时加到起始地址的偏移而不是当前地址。3. 传输次数或数据量不对理解“大小”与“计数”BURST_SIZE和TRANSFER_SIZE寄存器写入的值是“实际值-1”。设置BURST_SIZE0意味着突发大小为1个字。连续模式与单次模式混淆在CONTINUOUS1的模式下一次传输完成后会自动重载影子寄存器开始下一次除非你手动停止。如果只想搬运一次应使用CONTINUOUS0或使用ONESHOT1注意CH1的互斥。4. 系统在DMA使能后卡死或行为异常内存区域冲突确保DMA的源地址和目的地址所在的内存区域是DMA可访问的并且没有与正在执行的代码区域重叠。例如试图用DMA写入正在执行的Flash区域会导致不可预知的结果。总线仲裁问题DMA与CPU、CLA等主设备共享内存总线。如果DMA以最高优先级持续请求总线例如在极高带宽下可能会暂时阻塞CPU访问内存导致CPU取指或数据访问变慢看起来像“卡顿”。需要合理规划DMA带宽和使用HALT控制。中断嵌套与优先级DMA中断的优先级需要合理设置。如果DMA中断服务程序执行时间过长或者被更高优先级中断频繁打断可能导致无法及时响应新的DMA请求甚至造成溢出。5. 利用调试器Debugger监视关键寄存器实时查看CONTROL寄存器中的RUNSTSBURSTSTSTRANSFERSTSPERINTFLG状态位。监视地址活动寄存器查看SRC_ADDR_ACTIVE和DST_ADDR_ACTIVE可以实时看到DMA正在读写的地址是排查地址错误最直接的方法。设置数据观察点Watchpoint在目标缓冲区设置观察点当缓冲区被修改时暂停CPU可以确认DMA是否正常工作以及何时工作。DMA是提升C2000系统性能的利器但其配置的灵活性也带来了复杂性。深入理解其优先级仲裁机制和每个寄存器的细微含义结合系统需求进行精心设计才能让它稳定、高效地服务于你的实时控制系统。记住在修改任何DMA配置尤其是优先级模式之前先禁用相关通道多利用影子寄存器实现动态、安全的配置更新并对溢出等错误状态做好预案。