ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

S32K3 FlexCAN Enhanced FIFO + EDMA 实现高速 CAN FD 批量接收

S32K3 FlexCAN Enhanced FIFO + EDMA 实现高速 CAN FD 批量接收 最近在调 S32K3 的 CAN FD 接收发现很多从 S32K1 或者其他 MCU 转过来的朋友还在沿用老思路要么在主循环里轮询 FIFO 状态要么让每一条 CAN 报文都触发一次接收中断。CAN FD 数据段跑到 5 Mbps、一帧又带 64 字节负载的时候总线利用率稍微高一点这两种做法都会让 CPU 疲于奔命还会给控制环路引入不确定抖动。FlexCAN 的 Enhanced FIFO 配合 EDMA是解决这类高速接收问题很成熟的一套组合拳报文到达后由硬件直接写入接收 FIFOEDMA 自动把 FIFO 里的数据搬到内存缓冲区攒够一批才打断 CPU 一次。这篇文章就把我实际配置 S32K3 FlexCAN Enhanced FIFO DMA 接收 CAN FD 的完整流程、寄存器操作要点和踩坑记录写出来给同样在做高速 CAN FD 接收的朋友做个参考。1. 为什么推荐 Enhanced FIFO DMA 这套组合1.1 轮询和每帧中断在高速 CAN FD 下的问题先算一笔账。CAN FD 数据段跑到 5 Mbps 时一帧 64 字节负载的报文总线占用时间大概在 100~200 微秒这个量级。如果总线上有几个节点同时在发数据1 毫秒内收到几十帧是很正常的事。传统轮询方式看起来代码最简单主循环里不断查接收 FIFO 状态位有数据就一帧帧取出来。但问题在于轮询间隔很难卡准。查得太频繁CPU 大部分时间都空转在查询操作上查得太慢FIFO 深度有限数据一来一多就直接溢出丢帧。高速 CAN FD 场景下轮询基本是吃力不讨好。每帧中断的方式比轮询好些但同样有瓶颈。一条报文进一次中断每次中断都要做现场保存、状态判断、从 CAN RAM 拷贝数据、清标志、恢复现场。报文密集时中断频率可能到几十 kHzCPU 大量时间消耗在中断上下文切换上。更麻烦的是高优先级实时任务比如电机控制环、安全监控任务的响应延迟会被这批高频中断拉大系统的实时性反而变差。1.2 Enhanced FIFO 到底解决了什么问题S32K3 的 FlexCAN 支持两种报文接收存储方式传统 Message Buffer 方式和 Receive FIFO 方式。Message Buffer 方式下每个要接收的 ID 基本都要占用一个 MB软件接收时要遍历所有 MB 的 CODE 标志去判断哪些 MB 有有效报文。报文 ID 一多MB 资源紧张遍历代码也啰嗦。Receive FIFO增强 FIFO不一样。报文进入 FlexCAN 后先经过接收过滤表ID Filter Table匹配匹配成功的报文由硬件按顺序写入 FIFO 队列。软件不需要关心具体哪个 MB 收到了数据只要按 FIFO 顺序读取即可。在 CAN FD 模式下FlexCAN 的每个 FIFO 条目占 32 字节正好容纳一条最大 64 字节负载的完整报文。这意味着高速多 ID 接收场景下一个 FIFO 就能搞定一大堆报文的接收缓冲不用为每个 ID 单独分配 mailbox。1.3 DMA 把“搬运”和“处理”彻底解耦FIFO 解决了数据存储问题但读取 FIFO 仍然需要 CPU 参与。如果 CPU 在中断里一帧帧读还是会陷入高频中断的窘境。EDMA 的价值就在于把“搬运”这件事从 CPU 手里接过去。FlexCAN 在 FIFO 非空时会拉高 DMA 请求信号EDMA 收到请求后按照预先配置好的传输描述符TCD把 FIFO 数据搬到内存缓冲区。整个搬运过程不消耗 CPU 指令周期CPU 只在 DMA 搬运完一个批次后通过 DMA 完成中断处理一次数据。这相当于从“每来一个快递就打电话喊你下楼取件”变成了“快递柜自动收货攒满一箱才给你发一条取件码”。我用一个更直观的对比表总结下这几种方式接收方式CPU 开销丢帧风险实现复杂度适用场景主循环轮询 FIFO高空转严重高容易溢出低低速、极少量报文MB 每帧中断较高中断频繁较低中传统 CAN、报文量不大FIFO 每帧中断中中断仍频繁低中CAN FD 中低负载FIFO EDMA 批量搬低攒批唤醒低较高CAN FD 高负载、网关、刷写1.4 这套组合适合哪些场景不是所有场景都需要上 DMA。如果你只是低速 CAN 总线比如 125 kbps 或 500 kbps上收几个 ID传统 MB 加中断完全够用。但当你有下面这些需求时FIFO DMA 的优势就非常明显CAN FD 数据段速率 2 Mbps 以上且报文负载通常较大多路 CAN 同时工作CPU 还要跑控制算法或复杂协议栈诊断刷写、OTA 这类高效传输场景数据量大且要求接收稳定中央网关、域控制器这种需要把多路 CAN 数据汇聚处理的场合。一句话总结越是总线繁忙、报文量大、CPU 还有别的活要干越应该把接收路径从软件手里释放出来。2. FlexCAN FIFO 与 CAN FD 参数配置2.1 打开 Enhanced FIFO 和 DMA 请求S32K3 的 FlexCAN 寄存器名字和 S32K1 基本一脉相承。要让 FIFO 工作关键是 MCRModule Configuration Register里的几个位RFENReceive FIFO Enable置 1 使能接收 FIFODMA配合 RFEN 使用置 1 后 FlexCAN 会在 FIFO 非空时向 EDMA 发出 DMA 请求CANFDENCAN FD 功能使能位。配置这些位时最好让 FlexCAN 处于 Freeze Mode 下操作避免配置过程中总线还在跑寄存器值被硬件状态干扰。进入 Freeze Mode 的方式是先配置 MCR 的 FRZ 位然后置 HALT 请求确认 FREEZE 状态位生效后再改配置。配置完成后退出 Freeze ModeFIFO 和 DMA 请求才会真正开始工作。这里特别注意DMA 位只有在 RFEN 为 1 时才有意义配好 FIFO 再开 DMA顺序反了容易出一些莫名其妙的异常。2.2 确定 FIFO 规模和 RAM 空间分配FlexCAN 内部有专门的 RAM 区域同时存放 Message Buffer、接收过滤表和 FIFO 数据区。FIFO 数据区占用的空间会从 MB 区域“借”过去所以 FIFO 条目配得越大留给普通 MB 的数量就越少。S32K3 的 FlexCAN 支持通过 CTRL2 寄存器的 RFFNReceive FIFO Number以及 CAN FD 相关的 CFFNCAN FD Filter Number字段来配置 FIFO 深度和过滤表规模。以我实际用的 S32K3 系列芯片为例RFFN 配置成较大值时FIFO 可以容纳 64 条甚至更多报文条目普通 MB 会被压缩。具体 RFFN/CFFN 取值与 FIFO 条目数的对应关系不同衍生型号略有差异务必以你手头芯片参考手册里那张 RAM 分配表为准。我踩过的一个坑就是一开始想当然按 S32K1 的某个 RFFN 值配置结果发现 MB 区域被截断原本分配的 mailbox 空间被 FIFO 覆盖收到的数据出现错位。后来老老实实翻开手册对照 RAM 布局表重新分配问题才解决。所以这里强调一句初始化之前先把 RAM 布局搞清楚。2.3 配置 ID 接收过滤表FlexCAN 的接收 FIFO 不是所有报文都收它依赖 ID 过滤表。每个过滤表项是 32 位格式分 A/B/C 三种。格式 A 是一个完整 ID 加一个掩码格式 B 是双 32 位 ID 匹配格式 C 是四个 8 位掩码。实际用下来格式 A 最直观一个表项里同时包含期望接收的 ID 和掩码掩码为 0 的位不参与比较。最简单粗暴的配置是全接受设置一个过滤表项ID 写 0掩码写 0表示任何 ID 都能进 FIFO适合前期调试。等到功能稳定了再按需改成选择性接收比如只接收诊断相关的 0x7E0、0x7E8或某个控制器的 0x18FF50E5 等扩展帧 ID。需要提醒的是CAN FD 报文在 FlexCAN 中除了匹配 ID还可以匹配帧格式经典 CAN 帧还是 FD 帧。如果你的系统里经典 CAN 和 CAN FD 混跑过滤表配置时要把帧类型匹配考虑进去否则可能出现“ID 对但帧类型不对也收进来”的干扰帧。2.4 CAN FD 位时序与采样点设置CAN FD 的位时序配置是很多朋友容易忽略、但直接影响通信质量的环节。CAN FD 有仲裁段和数据段两套位时序两者波特率可以不同。仲裁段速率通常保持和经典 CAN 兼容比如 500 kbps数据段速率可以提高到 2 Mbps 甚至 5 Mbps。采样点这个参数至关重要。采样点太靠前信号还没稳定就采样容易采到跳变沿上的毛刺太靠后可能来不及覆盖传播延迟。实际工程经验仲裁段采样点建议设在 80% 左右数据段采样点建议设在 75% 左右。我这边常用的 500 kbps / 5 Mbps 配置就是按这个原则来。以一个外设时钟 80 MHz 的系统为例仲裁段 500 kbps 需要每个位时间有 160 个时钟周期。如果预分频 PRESDIV 设为 15则每个位时间由 10 个 Time Quantum 组成。分配方式可以是 SyncSeg 1、PropSeg 3、PhaseSeg1 4、PhaseSeg2 2这样采样点位置在 (1 3 4) / 10 80%。数据段 5 Mbps 时每个位时间只需要 16 个时钟周期按 75% 采样点做类似分配。注意这里只是示例实际分配还要看 FlexCAN 位时序寄存器里各字段的取值范围和约束。NXP 的配置工具或者 RTD 驱动里的位时序计算接口能帮我们自动生成合法参数比自己手动试安全得多。不要觉得采样点差不多就行。CAN FD 速率上来之后位时间非常短收发器延迟、线缆长度、节点时钟偏差都会被放大。采样点设置不合理的直接表现就是总线上大量错误帧、节点频繁进入 Bus Off。调这类问题时先用 CAN 总线分析仪看实际波形和采样点位置再回头改位时序参数。3. EDMA 配置批量接收的核心难点3.1 DMA 通道与 DMA MUX 源映射S32K3 的 EDMA 通道请求通过 DMA MUX 模块做外设映射。FlexCAN 的 FIFO DMA 请求信号需要连接到某个 EDMA 通道这个连接关系不是固定的而是通过 DMA MUX 通道配置寄存器的 SOURCE 字段来选择。配置时先查阅参考手册里的 DMA MUX 请求映射表找到对应 CAN 实例的 RX FIFO DMA 请求源编号比如 CAN0_RXFIFO、CAN1_RXFIFO。然后在 DMA MUX 的对应通道上使能 ENBL 位把 SOURCE 填成这个编号。不同 S32K3 型号、不同封装映射编号可能不一样抄代码的时候一定要对一下手册。这个环节出错的表现很典型FlexCAN 那边 FIFO 明明已经收到报文DMA 却纹丝不动或者 DMA 一直在搬别的外设数据。调试时先确认 MUX 映射是否对比直接啃 TCD 参数更省时间。3.2 TCD 参数详解minor loop 与 major loopEDMA 的传输描述符 TCD 是配置的核心。对于 CAN 接收我的做法是把一次 minor loop 定义为“搬运一条 CAN FD 报文”即 32 字节major loop 定义为“攒够一批报文”比如 32 条。FlexCAN 每来一条报文就触发一次 DMA 请求EDMA 执行一个 minor loop32 字节搬运CITER 减一。当 CITER 减到 0major loop 完成产生 DMA 完成中断CPU 一次性处理 32 条报文。关键配置如下tcd.SADDR (uint32_t)flexcanFifoBaseAddr; // FlexCAN FIFO 数据区映射地址 tcd.DADDR (uint32_t)rxBufferLarge; // 内存中的大缓冲区 tcd.ATTR.SSIZE EDMA_ATTR_SSIZE_32BIT; tcd.ATTR.DSIZE EDMA_ATTR_DSIZE_32BIT; tcd.ATTR.SMOD EDMA_ATTR_SMOD_NONE; tcd.ATTR.DMOD EDMA_ATTR_DMOD_NONE; tcd.NBYTES 32U; // 一次 minor loop 1 条报文 tcd.SOFF 0U; // 源地址固定由硬件 FIFO 出帧 tcd.DOFF 4U; // 目的地址每 32 位递增 4 字节 tcd.SLAST 0U; tcd.DLASTSGA -(int32_t)(sizeof(rxBufferLarge)); tcd.BITER 32U; // major loop 次数 32 帧 tcd.CITER 32U; tcd.CSR.INTMAJ 1U; // major loop 完成产生中断如果你是从 STM32 这类平台转过来可以把 minor loop 理解成 DMA 一次传输的最小数据块major loop 理解成整个传输任务。S32K3 的 EDMA 和 STM32 DMA 的区别在于它把一次传输拆分成了可配置的多级循环灵活性更高但也更容易配错。3.3 大缓冲半满中断避免乒乓切换的麻烦有些资料会推荐乒乓双缓冲DMA 先填 Buffer A填满后软件在中断里把 DADDR 改成 Buffer BCPU 处理 A。双缓冲确实能降低处理延迟但代码上需要频繁修改 TCD 的 DADDR 并重新使能通道稍不注意就会在切换瞬间丢帧。我更推荐的做法是分配一块比较大的缓冲区比如能装 64 条报文TCD 配置为 major loop 64 次同时打开 EDMA 的半程中断INTMAJ 和 INTHALF。这样 DMA 搬运完前 32 条报文时触发半程中断CPU 处理前 32 条继续搬运后 32 条完成后再触发一次中断CPU 处理后 32 条。缓冲区地址通过 DLASTSGA 在 major loop 完成后自动回到起始地址软件完全不用改 DADDR。这个方案的延迟和乒乓差不多但代码量小很多。实际项目里如果要进一步降低延迟可以把 major loop 调小比如 16 条报文一次中断代价是中断频率相应上升。3.4 别忘了 Cortex-M7 的 D-CacheS32K3 内核是 Cortex-M7带有 D-Cache。DMA 写内存是不经过 Cache 的如果 DMA 把数据写进了一块被 CPU 标记为 Cacheable 的内存区域CPU 读取时可能直接从 Cache 里拿到旧数据看到的现象就是“收到了数据但内容不对”或者“永远是第一帧数据”。处理方案有两种一是在 MPU 里把 DMA 缓冲区配置成 Non-cacheable Normal Memory最简单直接二是在每次 DMA 中断处理前调用 SCB_InvalidateDCache_by_Addr 对相应缓冲区做无效化操作。我实际项目里优先用第一种省心也不会带来明显的性能损失。缓冲区本身注意 32 字节对齐这也是 DMA 传输效率和安全性的要求。4. 手把手初始化与接收流程4.1 初始化 FlexCAN含 FIFO 与过滤表初始化流程我习惯按下面顺序走配置引脚复用和时钟让 FlexCAN 进入 Freeze Mode配置 FlexCAN 工作模式和 CAN FD 位时序配置 FIFO 深度、使能 FIFO 和 DMA 请求填写接收过滤表退出 Freeze Mode等待总线恢复。核心代码大致如下void flexcan_fd_fifo_init(void) { /* 1. 进入 Freeze Mode */ CAN0-MCR | CAN_MCR_FRZ_MASK | CAN_MCR_HALT_MASK; while (!(CAN0-MCR CAN_MCR_FRZACK_MASK)) { } /* 2. 软复位并设置 FD 使能 */ CAN0-MCR | CAN_MCR_SOFTRST_MASK; while (CAN0-MCR CAN_MCR_SOFTRST_MASK) { } CAN0-MCR ~CAN_MCR_CANFDEN_MASK; CAN0-MCR | CAN_MCR_CANFDEN_MASK; /* 3. 使能 Enhanced FIFO 与 DMA 请求 */ CAN0-MCR | CAN_MCR_RFEN_MASK | CAN_MCR_DMA_MASK; /* 4. 配置 FIFO 深度这里按自己的 RAM 布局表确认 */ CAN0-CTRL2 ~CAN_CTRL2_RFFN_MASK; CAN0-CTRL2 | CAN_CTRL2_RFFN(4U); /* 5. 配置 CAN FD 位时序仲裁段 500k数据段 5M */ /* 具体寄存器位段以参考手册为准建议用 RTD 计算接口填充 */ flexcan_set_nominal_bitrate_500k(); flexcan_set_data_bitrate_5m(); /* 6. 配置过滤表全接受方便调试 */ uint32_t *filterTable (uint32_t *)flexcan_filter_base_addr(); filterTable[0] 0U; /* ID 0 */ filterTable[1] 0U; /* Mask 0全接受 */ /* 7. 退出 Freeze Mode */ CAN0-MCR ~CAN_MCR_HALT_MASK; while (CAN0-MCR CAN_MCR_FRZACK_MASK) { } }必须再次强调RFFN(4)对应多少条 FIFO 条目以你实际芯片的手册为准。我用的这颗芯片配成这个值后 FIFO 深度满足需求但不同型号不能照搬。4.2 初始化并启动 EDMAEDMA 这边需要配置 DMA MUX 通道、TCD 和中断然后启动通道。核心代码片段如下void edma_flexcan_rx_init(void) { /* 1. 配置 DMA MUX源为 FlexCAN 的 RX FIFO 请求 */ DMA_MUX0-CHCFG[FLEXCAN_EDMA_CH] DMA_MUX_CHCFG_ENBL_MASK | DMA_MUX_CHCFG_SOURCE(FLEXCAN_RX_DMA_SOURCE); /* 2. 配置 TCD参考上一节 */ tcd.SADDR (uint32_t)flexcanFifoBaseAddr; tcd.DADDR (uint32_t)rxBufferLarge; tcd.NBYTES 32U; tcd.BITER 32U; tcd.CITER 32U; tcd.CSR EDMA_CSR_INTMAJ_MASK | EDMA_CSR_INTHALF_MASK; tcd.DLASTSGA -(int32_t)sizeof(rxBufferLarge); /* 3. 把 TCD 写入 EDMA 通道 */ EDMA-TCD[FLEXCAN_EDMA_CH] tcd; /* 4. 使能 EDMA 中断并启动通道 */ EDMA_EnableChannelInterrupts(EDMA, FLEXCAN_EDMA_CH); EDMA_StartChannel(EDMA, FLEXCAN_EDMA_CH); }启动通道后FlexCAN FIFO 一旦有报文DMA 就会自动开始搬运。这里注意 EDMA 的通道优先级、仲裁机制如果系统里同时跑着多路 DMA比如 ADC、SPI、串口要给 CAN 接收通道一个合理的优先级避免高负载时被其他通道饿死。4.3 中断处理与 CAN FD 报文解析DMA 中断里做的第一件事不是解析数据而是先把缓冲区索引切到另一段如果用了半满/全满机制就判断是前半段还是后半段然后再去处理数据。这样能最大限度减少 DMA 搬运和 CPU 处理之间的互锁等待。半满中断处理前半段缓冲区的示例void EDMA_IRQHandler(void) { uint32_t intr; intr EDMA_GetChannelInterruptFlags(EDMA, FLEXCAN_EDMA_CH); if (intr EDMA_INTHALF_MASK) { process_canfd_frames(rxBufferLarge[0], 32); EDMA_ClearChannelInterruptFlags(EDMA, FLEXCAN_EDMA_CH, EDMA_INTHALF_MASK); } if (intr EDMA_INTMAJ_MASK) { process_canfd_frames(rxBufferLarge[32], 32); EDMA_ClearChannelInterruptFlags(EDMA, FLEXCAN_EDMA_CH, EDMA_INTMAJ_MASK); } }解析单条 CAN FD 报文时缓冲区里每个条目是 32 字节第 0 个 word控制/状态字包含 DLC、IDE、BRS、FD 标志等信息第 1 个 wordID 和接收时间戳等信息第 2~7 个 word报文数据最多 64 字节。解析代码大致如下void parse_canfd_frame(const uint32_t *entry, canfd_frame_t *out) { uint32_t cs entry[0]; uint32_t id entry[1] CAN_ID_MASK; out-id id; out-dlc (cs DLC_SHIFT) 0xFU; out-fd (cs FD_FLAG_SHIFT) 0x1U; out-brs (cs BRS_FLAG_SHIFT) 0x1U; uint32_t data_len dlc_to_bytes(out-dlc); memcpy(out-data, entry[2], data_len); }DLC 是 4 位编码2 的幂次映射关系0~8、12、16、20、24、32、48、64 字节自己列一张表就行。注意 CAN FD 的 DLC 大于 8 时实际字节数不是线性递增的解析时别拿 DLC 直接当长度。4.4 兜底溢出、超时和异常处理DMA 批量接收模式下如果一段时间内总线上报文特别少攒满规定条数可能耗时较长CPU 拿到数据的延迟会变大。这种情况可以通过一个周期任务比如 1~10 ms 的定时中断检查 EDMA 当前 CITER 值如果发现已经接收了若干条但没触发中断就主动把已收到的部分处理掉。这算是 DMA 接收在低负载场景下的补充策略。另一个必须处理的异常是 FIFO 溢出。FlexCAN 的接收 FIFO 深度虽然可以配得比较大但 DMA 异常或者 CPU 忙碌导致数据没及时搬走时FIFO 还是会溢出。溢出后 FlexCAN 会置起 OVRIF 中断标志。建议单独开一个错误中断入口在中断里读取状态、清除溢出标志、记录错误计数。这个错误计数对评估总线负载和系统健康度非常有价值。5. 实测中的常见问题与排查技巧5.1 一帧都收不到DMA 完全不动这种问题十有八九出在 DMA MUX 映射或 FlexCAN 的 DMA 使能位上。先确认 FlexCAN 的 MCR 里 RFEN 和 DMA 位都已经置 1再确认 DMA MUX 通道的 SOURCE 编号和使能位正确。还可以在调试器里直接看 EDMA 通道的 CITER 是否被修改如果 CITER 一直不变说明 DMA 请求根本没到 EDMA。5.2 收到几帧之后就停了典型原因是 TCD 的 major loop 完成后通道没有再被重新使能。有些 EDMA 配置下 major loop 完成会把通道置为 inactive需要软件在中断里重新写入 CITER 并启动通道。如果不想每批都手动处理可以研究一下 TCD 的通道链接功能把通道链接到自身实现硬件上的自动重复传输。5.3 数据解析出来是乱的先核对 FlexCAN FIFO 数据区基地址是否算对。FIFO 深度配置不同数据区在 CAN RAM 里的起始地址也不同直接用固定的基地址很容易读到错误区域。其次检查 DMA 源地址是否固定、目的地址递增方向是否正确。CAN FD 帧是 32 字节对齐的结构如果 DMA 按 16 字节或者别的长度搬运解析结果必然错位。5.4 开了 D-Cache 后数据不更新这就是前面说的 Cache 一致性问题。先看缓冲区是否在 MPU 里配置成了 Non-cacheable如果不是改成 Non-cacheable或者在每次中断处理前对缓冲区做 D-Cache Invalidate。5.5 总线上大量错误帧大概率是位时序或采样点配置有问题。用 CAN 分析仪抓一下实际波形确认仲裁段和数据段的波特率是否符合预期采样点是否落在合理位置。另外检查 CAN FD 收发器的支持速率有些收发器并不支持 5 Mbps 的数据段速率硬件选型不对也会导致通信不稳定。5.6 问题速查表现象排查方向DMA 完全不动DMA MUX 源映射、FlexCAN DMA 使能位、TCD 是否启动收几帧就停major loop 完成后通道状态、CITER 是否重置数据错乱FIFO 基地址计算、DMA 传输宽度/地址递增数据不更新D-Cache 一致性、MPU Non-cacheable 配置总线错误帧多位时序、采样点、收发器速率支持FIFO 溢出计数增长DMA 处理不及时、FIFO 深度不足、总线负载过高调试时强烈建议先用 FlexCAN 的 Loopback 自环模式打通整条通路再接入真实总线。Loopback 模式下报文自己发自己收排除了总线物理层和收发器的干扰非常适合验证 FIFO DMA 的软件链路。等自环正常了再接外部节点联调定位问题会快很多。我个人的体会是S32K3 的 FlexCAN Enhanced FIFO DMA 这套东西真正困难的不是某一个寄存器怎么配而是把 FlexCAN 的接收机制、EDMA 的传输模型和 M7 内核的 Cache 特性融合在一起理解。配置过程中多参考参考手册里的 RAM 分布表和 DMA MUX 映射表大部分坑都能提前避掉。最后再分享一个我常用的验证方法在 DMA 中断里放一个计数器观察单位时间内的中断次数是否和你预期的批次数一致。如果中断次数明显偏多或偏少说明 DMA 请求和 TCD 的传输配置还没完全匹配这时候回头排查 TCD 的 NBYTES 和 BITER 往往能找到问题。
返回列表