首页 / 资讯中心 / 文章详情

CAN-FD选型与STM32G474 FDCAN消息RAM配置避坑

CAN-FD选型与STM32G474 FDCAN消息RAM配置避坑 ★ FEATURED ARTICLE
简介这份面向STM32嵌入式开发者的FDCAN应用笔记对应AN5348 Rev12021年7月适合已具备CAN基础、希望迁移到CAN-FD或调试FDCAN外设的工程师使用。全文围绕CAN-FD协议概述、相较传统CAN 2.0的改进、STM32G0/G4/H7/L5及STM32MP1中的FDCAN实现以及外设模式与特性展开可帮助读者厘清仲裁阶段与数据阶段的比特率切换、EDL/BRS/ESI等控制位含义。资源包共1个PDF文件约554KB篇幅紧凑便于随查随用。已有698人学习下载。文档内含标准CAN-FD帧结构图、CAN-FD与CAN 2.0帧架构对比、有效负载长度代码表以及17位/21位CRC校验和位填充机制说明并附适用产品表格对需要配置数据段波特率、将单帧扩展至最多64字节、排查错误状态与帧格式差异的开发者可作为对照寄存器配置和协议时序的速查资料。1. 一条8字节的旧账stm32 CANFD 在我看来改的不是速率我在一块 G474 的板子上跑过很长一段时间的传统 CAN一包 64 字节的标定数据要拆成 8 帧发接收端再拼回来中间还得自己维护序号和超时重传。后来切到 FDCAN同样一包数据一帧走完但真正让我觉得值回票价的不是带宽数字而是软件层整整一层分片重组代码连同它的边界 bug 一起消失了。这篇不讲协议教科书只讲两件事CAN-FD 相比 CAN 2.0 在什么条件下值得换、G474 上那 10KB 消息 RAM 该怎么规划才不浪费。正在做选型的、配寄存器被地址算错的、准备把老 CAN 节点往 FD 迁的都能直接抄结论。2. CAN-FD 还是 CAN 2.0四个维度给出选型结论2.1 有效负载与 DLC 编码一帧 64 字节省下的是整条链路选型第一个要看的从来不是「最高 8Mbit/s」这个宣传口径而是你的应用一包数据到底多长。传统 CAN 单帧有效负载被死死钉在 8 字节CAN-FD 把它抬到 64 字节代价只是 DLC 那 4 位从「线性编码」变成了「阶梯编码」。DLC(十进制)0123456789101112131415CAN 2.0 字节数0123456788888888CAN-FD 字节数01234567812162024324864这张表有个容易忽略的细节CAN-FD 的 9 到 15 不是连续增长而是 12、16、20、24、32、48、64 跳着来。所以你写代码时如果按「DLC 乘 4」去算长度从 DLC9 开始就全错。我一般会写一个查找表把 DLC 映射成字节数而不是用乘法。账面收益可以粗算一下。传 64 字节传统 CAN 要 8 帧每帧标准数据帧的固定开销约 47 位加 64 位数据合计约 888 位CAN-FD 一帧的仲裁段加控制段约 22 位数据 512 位CRC 21 位加界定符再加 ACK、EOF、IFS 约 13 位量级在 600 位上下。总线占用降了三成左右但更关键的是仲裁次数从 8 次降到 1 次——仲裁次数决定了你的最坏延迟抖动而不是平均带宽。做实时控制的人应该盯这个数而不是盯着 8Mbit/s。反过来说如果你的应用每周期就发两三个字节的状态量帧率也不高那 CAN-FD 的收益接近于零还要多付收发器和时钟精度的成本。这是我见过的第一个选型误判为了「先进」而上 FD结果总线上跑的全是 8 字节小包。2.2 帧结构里的三个新位EDL、BRS、ESI 各自管什么CAN-FD 的帧被切成三段第一仲裁阶段、数据阶段、第二仲裁阶段。第一仲裁阶段包含帧起始、ID 和格式位第二仲裁阶段包含 ACK 和帧结束中间的数据阶段才是 FAST 模式真正提速的地方。整帧里新增了三个控制位各自职责完全不同但在寄存器配置时最容易被一锅烩。EDL扩展数据长度是「这帧是不是 FD」的身份位。它在帧里是隐性时表示这是 CAN-FD 帧显性时对应传统 CAN 2.0 帧里的 R0。这一位决定了总线上所有节点能不能听懂这帧。BRS比特率切换是「数据段要不要换速率」的开关。置位时仲裁阶段用标称比特率数据阶段切到数据比特率最高可以到 8 倍。这里有个硬件前提常被漏掉收发器的环路延迟必须撑得住数据段的位宽时钟源的精度也必须够否则一切换速率就在采样点上翻车。我在第一版硬件上就吃过这个亏仲裁段跑得好好的一开 BRS 误码率就上去了最后查出来是晶振精度和收发器延迟共同作用的结果。ESI错误状态指示是「我处于错误主动还是错误被动」的广播位。它让总线上其他节点提前知道某个节点已经带病工作这个信息在功能安全场景里比多出来的 56 字节更有价值——你可以据此做降级策略而不是等它彻底掉线才发现。还有一个结构变化必须记住CAN-FD 只支持数据帧远程帧被砍掉了原来 RTR 的位置变成了显性的 RRS。如果你的老代码里还有「发远程帧请求数据」的逻辑迁到 FD 时这段逻辑要整个重写成请求-应答模式这是工作量估算里最容易漏的一块。2.3 CRC 与完整性17 位和 21 位的分界卡在哪CAN-FD 把 CRC 从 15 位扩到了 17 位或 21 位分界线是有效负载长度不超过 16 字节用 17 阶多项式16 到 64 字节用 21 阶多项式。同时 CRC 计算里包含了位填充位位填充机制传统 CAN 的 CRC 是不含填充位的。这两条改动的意义在于帧变长之后如果还用 15 位 CRC 且不含填充位漏检概率会明显上升。所以这不是「顺手加长」而是帧长增加后的必要补偿。做选型时如果你的总线环境恶劣、节点多、线束长这一条应该单独列一项来比。实际配置层面你几乎不用管 CRC 的计算过程硬件全包了。但有一个连带影响要知道数据段长度超过 16 字节之后帧在总线上的最坏传输时间会跳一档你的看门狗周期和通信超时阈值如果按 8 字节帧算的得重新核。2.4 兼容性与迁移成本混合总线上谁能听懂谁选型结论里最容易被跳过的一节恰恰是决定项目能不能按时交付的一节。CAN-FD 节点在协议上是兼容的它能收发传统 CAN 报文也能被配置成只跑传统格式。但反过来不成立一个只懂 CAN 2.0 的老节点收到 EDL 为隐性的 FD 帧会把它当成格式错误通常的反应是报错甚至进错误被动。也就是说同一条总线上不可能既有一堆老节点全速跑 2.0又有一个新节点发 FD 帧——除非所有 FD 帧都退化成 2.0 格式发。对比项CAN 2.0CAN-FD兼容 CAN-FD不支持支持 CAN 2.0 A/B仲裁比特率最高 1 Mbit/s最高 1 Mbit/s数据比特率同上最高 8 Mbit/s单帧最大数据8 字节64 字节BRS / EDL / ESI无有CRC 含填充位否是远程帧支持不支持我的结论是这样落的如果整条总线上的节点都能重刷固件且应用里存在大于 8 字节的周期性数据块就上 FD如果总线上有不可替换的老节点那就把 FDCAN 外设配置成传统模式跑先保住硬件平台的延续性等老节点自然退役如果数据全是短帧小包别为了参数好看硬上收益抵不上调试成本。3. G474 的 FDCAN 硬件账本10KB RAM 怎么花3.1 元素大小公式为什么这笔账必须自己算G474 上的 FDCAN 外设有一块约 10KB 的专用消息 RAM可访问的部分是 2560 个 32 位字。发送和接收的所有报文都不是放在寄存器里而是放在这块 RAM 里你在初始化时唯一要做的事就是把 2560 个字切给滤波区、接收区、发送区和触发区。切之前先得知道一个「元素」占多少字。公式很朴素元素大小字 2消息头 数据场字节数 / 4消息头固定 2 个字装的是 ID、DLC、控制位ESI、XTD、RTR、BRS、FDF和收发专用的位域数据部分按 4 字节一个字向上取整。注意那个隐含的向上取整如果数据场在 0 到 8 字节之间数据部分一律按 2 个字算。数据场字节元素大小RAM 字占字节数0 至 841612520166242072824832321040481456641872这张表是后面所有地址计算的底座。我见过不止一个人把 64 字节元素按 16 字去算结果最后一算总账发现挤出了几十个字回头看是数据场除法没向上取整。写个脚本先算比在寄存器里试错快得多# 按 FDCAN 元素大小规则估算占用输入是各区元素个数和每个元素的数据场字节数 import math def elem_words(data_bytes: int) - int: 元素大小32位字 2 字消息头 ceil(数据场/4) data_words math.ceil(data_bytes / 4) return 2 data_words # 数据场 8 字节时自动等于 4 字 def region_words(count: int, data_bytes: int) - int: return count * elem_words(data_bytes) # 一个典型配置128 个 11 位滤波器 64 个 64 字节 FIFO 元素 # 64 个 64 字节 Rx 缓冲区 32 个 8 字节 Tx 缓冲区 plan { 11位滤波器: 128 * 1, # 每个滤波器元素占 1 字 Rx FIFO0: region_words(64, 64), # 64 * 18 1152 Rx Buffer: region_words(64, 64), # 64 * 18 1152 Tx Buffer: region_words(32, 8), # 32 * 4 128 } total 0 for name, words in plan.items(): print(f{name:12} 起始偏移 {total:5} 占用 {words:5} 字) total words print(f合计 {total} 字 / 可用 2560 字剩余 {2560 - total} 字)跑完的结果是 128 1152 1152 128 2560刚好排满一个不剩。这个例子很适合拿来当模板因为它把四种区都用上了而且没有任何浪费。你换成自己的配置时只要保证各区的起点不重叠、总和不超 2560 就行。这里有个设计上的小陷阱CAD 消息 RAM 的分配是动态连续的硬件不会帮你检查重叠两个区起始地址配到同一处表现是数据互相踩踏而不是报错。3.2 滤波区128 个 11 位和 64 个 29 位怎么摆滤波区的起始地址由两个寄存器决定11 位标准 ID 滤波器区看 FDCAN_SIDFC 里的 FLSSA 字段29 位扩展 ID 滤波器区看 FDCAN_XIDFC 里的 FLESA 字段。每个滤波器元素占 1 个字标准 ID 最多 128 个扩展 ID 最多 64 个。滤波器元素有三种类型选择靠 SFT 两位范围滤波器匹配 ID 落在两个边界之内的所有报文双 ID 滤波器匹配一个或两个特定 ID传统位屏蔽滤波器第一个 ID 是匹配值第二个 ID 是掩码掩码为 0 的位不参与比较掩码全 0 就匹配所有报文。把这三种类型组合起来可以做出相当复杂的硬件过滤而且整个过程不占 CPU。举一组实际配置需求是拒绝 ID 在 0x16 到 0x20 之间的报文接受 0x15 或 0x120 并存进 FIFO 1接受 0x130 并存进 Rx 缓冲区索引 4接受形如 0b11100XX0000 的一族 ID 并存进 FIFO 0。顺序SFT 类型SFEC 配置SFID1SFID2第 1 个00 范围滤波器011 拒绝0x160x20第 2 个01 双 ID010 存入 FIFO 10x150x120第 3 个任意111 存入 Rx 缓冲区0x1300x04缓冲区索引第 4 个10 位屏蔽001 存入 FIFO 00x7000x7CF第四行的掩码 0x7CF 拆成二进制是 0b11111001111把低 4 位和高 2 位屏蔽掉正好匹配 0x700、0x710、0x720、0x730 这四个 ID。这就是位屏蔽滤波器的典型用法用一条规则吃掉一族 ID。第三个滤波器有一个必须记住的规则当 SFEC 配成「存入 Rx 缓冲区」时SFT 的配置会被忽略。所以表里那一格写「任意」是有依据的不是你少填了。3.3 接收侧两个 FIFO 加 64 个专用缓冲区怎么选接收侧有两种机制先说清楚它们的本质差别选型才好落地。Rx FIFO 0 和 Rx FIFO 1 各自最多存 64 个元素元素大小通过 FDCAN_RXESC 里的 F0DS、F1DS 字段配置起始地址和元素个数分别由 FDCAN_RXF0C 的 F0SA/F0S 和 FDCAN_RXF1C 的 F1SA/F1S 决定。FIFO 的位置由硬件用获取索引和放入索引自动管理你只管读。专用 Rx 缓冲区最多 64 个每个只存一个元素起始地址由 FDCAN_RXBC 的 RBSA 配置元素大小由 FDCAN_RXESC 的 RBDS 配置。它的特点是位置由你指定的索引决定滤波器直接引用缓冲区编号。特征专用 Rx 缓冲区Rx FIFO可配置区数量64 个独立缓冲区2 个 FIFO每区元素数固定 1 个最多 64 个RAM 中的位置用户按索引指定硬件用索引自动管理满了以后缓冲区被锁定丢弃新报文拦截模式丢弃或覆盖模式挤掉最早的选择依据我给一条如果报文是「按 ID 分工况」的每个 ID 在软件里有独立的处理上下文用专用缓冲区索引天然就是上下文编号读的时候不需要判断是谁的数据如果是「同一类报文高频突发」长度不可预测用 FIFO让硬件帮你排队。我自己的项目里通常是两者混用控制指令走专用缓冲区保证不丢状态上报走 FIFO 消化突发。读取流程要按顺序走少一步就拿不到数据。以 FIFO 1 为例读 FDCAN_RXF1S 拿到状态和获取索引 F1GI算地址消息 RAM 基址 F1SA F1GI × FIFO 元素大小从该地址把元素读出来读完最后一个元素后把它的缓冲区索引写进 FDCAN_RXF1A 做应答。第四步最容易被忘。忘了应答FIFO 的获取索引不动你会一直读到同一个元素表现是「数据卡住不更新」。专用缓冲区的对应规则是读完之后要把 FDCAN_NDAT1 或 FDCAN_NDAT2 里的对应位清零否则那个缓冲区处于锁定状态后续匹配的报文进不来。3.4 发送侧专用缓冲区、FIFO、队列三选一发送侧的结构比接收侧复杂一点因为它有三种机制专用 Tx 缓冲区、Tx FIFO、Tx 队列。关键是这句专用 Tx 缓冲区可以和后面两者之一共存但 Tx FIFO 和 Tx 队列之间只能选一个不能混搭。专用 Tx 缓冲区的数量由 FDCAN_TXBC 的 NTDB 字段配置最多 32 个起始地址由同一寄存器的 TBSA 配置。每个专用缓冲区对应一条报文发送请求通过 FDCAN_TXBAR 的置位触发之后由硬件按 ID 优先级参与总线仲裁。注意最后这句多个专用缓冲区如果配了相同的 ID先请求的先发但不同 ID 之间还是按优先级走。Tx FIFO 和 Tx 队列通过 FDCAN_TXBC 的 TFQM 位选择写 0 是 FIFO写 1 是队列。这是两者最本质的差别所在——FIFO 按你写入的顺序往外发谁先进谁先走跟 ID 优先级无关队列按 ID 优先级出队急的报文可以插到前面。所以如果你的报文里有安全相关的紧急帧别用 FIFO它会老老实实排在一条 64 字节的大包后面。Tx FIFO 的空闲水平可以从 FDCAN_TXFQS 的 TFFL 字段读出来它是获取索引和放入索引之间的差值也就是还能塞几个元素。下一个可写位置的地址这样算下一个空闲 Tx FIFO 缓冲区地址 消息 RAM 基址 TBSA TFQPI × Tx FIFO 元素大小发送还有个可选件Tx 事件 FIFO最多 32 个元素起始地址由 FDCAN_TXEFC 的 EFSA 配置。每次发送成功后如果该 Tx 缓冲区元素里的 EFC 位为 1硬件就把消息 ID 和本地时间戳写进事件 FIFO。要读的时候用 FDCAN_TXEFS 的 EFGI 算地址读完写 FDCAN_TXEFA 的 EFAI 应答。它满了之后新事件会被直接丢弃所以要么开个水印中断提前取要么干脆别开——只在需要做发送时序分析的场景才值得占这几十个字。4. 把 RAM 规划落到寄存器一次从 0 到 2560 字的完整演算4.1 一个排满 2560 字的配置样例拿 3.1 节算过的那个配置动手配一遍。需求是128 个 11 位滤波器、Rx FIFO 0 收 64 条 64 字节报文、专用 Rx 缓冲区 64 个各装 64 字节、专用 Tx 缓冲区 32 个各 8 字节。区起始偏移字元素数元素大小字占用字11 位滤波器01281128Rx FIFO 012864181152Rx 缓冲区128064181152Tx 缓冲区2432324128合计———2560落到寄存器上大致是这样/* 消息RAM基址以器件参考手册为准这里用宏占位不写死 */ #define FDCAN_MSG_RAM_BASE (0x0000UL) /* 实际基址查手册对应章节 */ /* 各区起始偏移单位是 32 位字直接对应寄存器里的 SA 字段 */ #define OFF_SIDF 0U /* 11位滤波器区 128 字 */ #define OFF_RXF0 128U /* Rx FIFO 0 1152 字 */ #define OFF_RXBUF 1280U /* Rx 缓冲区 1152 字 */ #define OFF_TXBUF 2432U /* Tx 缓冲区 128 字 */ void fdcan_ram_init(void) { /* 1. 滤波器区起始地址 */ FDCAN_SIDFC (OFF_SIDF 2); /* FLSSA 字段低两位保留 */ /* 2. Rx FIFO 0先定元素大小再定元素数与起始地址 */ FDCAN_RXESC ~(0x7U 0); /* 清 F0DS */ FDCAN_RXESC | (0x5U 0); /* 数据场 64 字节对应的编码查手册 */ FDCAN_RXF0C (OFF_RXF0 2) | (64U 16); /* F0SA F0S64 */ /* 3. 专用 Rx 缓冲区元素大小 RBDS 起始地址 RBSA */ FDCAN_RXESC ~(0x7U 8); FDCAN_RXESC | (0x5U 8); FDCAN_RXBC (OFF_RXBUF 2); /* 4. 专用 Tx 缓冲区数量 NTDB32起始地址 TBSA */ FDCAN_TXBC (OFF_TXBUF 2) | (32U 16); /* NTDB 在 [21:16] */ }几处参数说一下。F0DS、RBDS 这类「元素大小编码」不是直接写字节数而是写一个编码值具体取值必须查对应型号的参考手册我这里用 0x5 只是占位照抄会翻车。起始地址字段一般是左移两位后写入因为低两位保留。NTDB 和 F0S 这类计数字段在寄存器的高半部分写入时要按位域位置左移对齐。最后一步永远是把外设的初始化请求置位等硬件把分配好的区域清零后再退出初始化这一步不做读出来的全是脏数据。4.2 滤波器顺序为什么决定成败硬件做接受过滤时永远从 0 号滤波器元素开始逐个比对在第一个匹配的元素处就停下后面的元素不再评估。这条规则决定了一件事滤波器元素的先后顺序本身就是逻辑。上面那组配置里拒绝 0x16 到 0x20 的范围滤波器排在第 1 个是因为它必须先拦住这批报文如果它排在最后前面任何一条能匹配 0x18 的规则都会先命中拒绝规则永远执行不到。反过来如果两条规则都会命中同一个 ID你希望哪条生效就把它排前面。这个机制在扩展成 128 个滤波器之后会变得很难维护因为顺序一改行为就变。我的做法是把滤波器配置写成一张静态表每条规则带一个显式的优先级序号初始化时按序号排序后依次写入硬件而不是靠代码里的书写顺序。顺便把一个常见误配说清楚给某个滤波器元素把 SFEC 配成 0 就是禁用这个元素。禁用之后它仍然占着 1 个字的位置但不会参与匹配。所以你觉得「我明明配了 128 个滤波器怎么只生效了 30 个」的时候先数一下有多少是禁用的。4.3 多实例共享 RAM2/3 与 1/3 的划分法G474 这类器件通常不止一个 FDCAN 实例而所有实例共享同一块消息 RAM。划分靠起始偏移地址第一个实例从偏移 0 开始占一部分第二个实例从一个新的偏移开始占剩下的部分。假设两个实例第一个用三分之二第二个用三分之一总量 2560 字也就是 1706 和 854。第一个实例从 0 开始各区的起始偏移都按上一节的方式在 1706 字之内排布第二个实例的起始偏移从 1707 附近取整开始各区的 FxSA 字段全部相对于这个新起点计算内部再二次分配。这里有个我踩过的坑两个实例的地址是相对各自的起点算的但 RAM 是同一块所以你必须自己保证第一个实例的所有区加起来不越过第二个实例的起点。硬件不会拦你越界了就是两个实例互相踩数据现象是「偶尔」丢帧或者收到别人的报文非常难查。我的习惯是在初始化代码里做一个静态断言把每个实例的规划总量加起来编译期就报错。5. 排查与避坑五条把我按在调试台上的具体记录5.1 配置期三个不到最后不显形的坑坑一所有区都配了就是一条报文都收不到。现象初始化返回成功总线上的波形用示波器看也正常但接收中断一次都没进。 原因滤波器区的起始地址配了但滤波器元素本身没使能——SFEC 字段还是 0等于 128 个元素全部禁用。也有另一种可能是把标准 ID 滤波器的起始地址写进了扩展 ID 滤波器的字段硬件按你的配置去一个空区域找规则永远不匹配。 解决先把滤波器数量减到 1 个SFEC 配成「存入 FIFO 0」确认能收到再往上加。永远从最小可工作配置开始不要一上来就配 128 条。坑二帧收到了数据长度和内容对不上。现象能进中断读出来的 ID 是对的但数据只取到前 8 个字节后面全是 0 或乱码。 原因元素大小配置和数据场长度不匹配。接收侧按 64 字节元素存了 18 个字你按 8 字节元素去读只读了前 4 个字剩下的当然读不到。反过来更隐蔽元素按 8 字节配报文来 64 字节硬件截断你读到的数据永远是残缺的而且不报错。 解决把 RXESC 里的元素大小编码、实际报文的最大 DLC、读取代码里的结构体长度这三者写在同一条注释里对齐改一个就改三个。坑三发送请求置位了报文永远发不出去。现象TXBAR 写进去了Tx 状态寄存器里的请求位一直不消失总线上一条报文都没有。 原因Tx 缓冲区数量和实际配置不匹配。NTDB 写了 32但 TBSA 指向的地址区间已经被 Rx 缓冲区占了写入的报文内容覆盖了别人的数据硬件读到的是一条非法报文。还有一种情况是 TBSA 的起始偏移和 Rx FIFO 重叠表现同样是「发不动」。 解决先把 NTDB 改成 1TBSA 指到一段绝对空闲的地址确认单帧能发出去再逐个往上加。每次加数量都重新算一遍总账。5.2 运行期两个静默丢数据的坑坑四跑了十几分钟一切正常然后接收突然停了重启就好。现象长时间运行后不再进接收中断但总线上的报文照常其他节点工作正常。 原因Rx FIFO 默认是拦截模式满了之后新报文直接被丢弃而 FIFO 满的标志位没有进你的轮询或中断所以你完全感知不到。等你终于读到数据时获取索引已经落后很多看上去像是「卡死」。 解决把 FIFO 的水印中断打开在还剩几个空位的时候就提前取或者在主循环里定期读一次 FIFO 状态寄存器把使用率当成健康指标打印出来。要求高吞吐又允许丢旧数据的场景可以考虑开覆盖模式但要想清楚覆盖掉的报文业务上能不能忍。坑五专用 Rx 缓冲区只收到第一帧之后再也不更新。现象某个缓冲区里第一次的数据是对的之后该 ID 再来报文内容一直是老的。 原因读完之后没有把 FDCAN_NDAT1/NDAT2 里的对应位清零。只要这一位置着对应的 Rx 缓冲区就是锁定状态硬件会拒绝往里面写新报文也不会通知你。 解决读数据的函数和清标志的动作绑在一起写读一个清一个别靠「等会儿统一清」。我后来干脆把这两步封在同一个函数里返回值就是数据调用方没有机会忘。6. 收在一个上线前必须走完的核对动作上上面这些内容摊开写了不少但真到项目里能救命的往往不是记住多少个公式而是每次改完 FDCAN 配置都强制走一遍固定的核对动作。我现在的习惯是把下面这张表贴在调试记录的最前面改一个参数就从头对一遍哪怕只改了一个元素大小。核对项检查方式出问题的典型表现各区起始偏移不重叠手算一遍累计偏移和规划表对数据互相踩踏偶发丢帧各区总和不超 2560 字用脚本把每个区加起来越界写到别人的区多实例划分不越过边界静态断言编译期报错两个实例互相覆盖元素大小与实际 DLC 匹配结构体长度和编码写在同一处注释数据截断或读不全滤波器顺序与优先级一致静态表带序号排序后写入拒绝规则不生效FIFO 读完有应答读和应答写在同一个函数一直读到同一帧NDAT 位读后清零封装成单一函数缓冲区只更一次Tx 事件 FIFO 溢出水印开中断或直接不用事件静默丢失这张表里有一行是我交了学费才加进去的就是「多实例划分不越过边界」。当时两个 FDCAN 实例共用一块 RAM第一个实例的 Rx 缓冲区规划大了悄悄侵入了第二个实例的滤波区现象是第二个实例偶尔收到一些莫名其妙的 ID重启一次又好查了整整两天。从那以后我每次配 FDCAN不管是单实例还是多实例都强制先把所有区的偏移和大小写成一张表用编译期断言把总账卡死然后才动寄存器。慢十分钟省两天。如果你手上也正拿着一份 FDCAN 的配置改来改去建议先别碰寄存器把那张偏移表填完、脚本跑通、把 2560 这个数对上再开始写代码。这份应用笔记里那些 RAM 映射图和元素大小表价值也正在这里——它不是让你读懂协议而是让你在动手之前就能把账算平。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站