ARTICLE DETAIL

资讯详情

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

S32K3双核CANFD配置实战:EB tresos中断与轮询方案详解

S32K3双核CANFD配置实战:EB tresos中断与轮询方案详解 CANFD这几年的普及速度比很多人想的要快。我手头这个项目最开始只是在CAN2.0上跑UDS刷写后来诊断报文和标定数据量一上来带宽立刻成了瓶颈。换到CANFD后数据场从8字节跳到64字节通信速率从500k提升到2M甚至5M整包刷写时间直接砍掉一大截。但问题也随之而来主控从S32K1系列换到S32K3系列原来的裸机库不再通用工程也要迁到EB tresos做AUTOSAR MCAL配置。如果你第一次用S32K3的EB配置CAN与CANFD还要考虑双核外设怎么分、中断和轮询到底该用哪个这篇文章就是给你准备的。这篇文章会围绕一套实际模板展开在EB tresos里把CAN0、CAN1分别配成Classic CAN和CANFD并分配到双核上同时给出中断与轮询两种收发代码写法最后对比它们在实际项目里的适用场景。内容偏工程向不会花太多篇幅讲CAN总线基础适合已经做过CAN开发、正在向S32K3迁移的工程师如果你想了解CANFD的收发机制或者双核MCAL工程怎么搭建这篇也能当参考。1. 为什么用S32K3加EB tresos做CANFD1.1 从CAN到CANFDS32K3解决的核心痛点CANFD和经典CAN最大的区别一句话就能说清仲裁段保留原来的可靠性和兼容性数据段直接把速率和长度拉高。经典CAN一帧最多8字节CANFD一帧可以到64字节经典CAN最高1Mbps左右CANFD数据段在S32K3上轻松跑到5Mbps。对整车通信来说这意味着同样一批标定数据、诊断响应传输时间可以由秒级降到百毫秒级。S32K3的CAN模块由S32K1的FlexCAN演进而来但内部邮箱机制、RAM分配和中断设计都变了。最直观的感受是寄存器思路上还留着FlexCAN的影子但配置深度已经完全不同。直接用寄存器开发不是不行工作量会非常难受尤其是CANFD的收发时钟、采样点、BRS位时序、错误状态恢复这些细节用寄存器逐个调太费劲。这也是我最终选择EB tresos做MCAL配置的原因它把底层寄存器封装成可视化的控制器、邮箱、中断选项生成代码后再看API调用逻辑会清晰很多。此外S32K3的多核架构让“一颗芯片同时管理多个CAN总线”成了常态。以前一颗单核MCU要在一个中断函数里处理所有CAN报文CPU负载和响应延迟很难平衡。现在把不同CAN控制器分配给不同核各核独立运行复杂度从应用层下放到了系统初始化层。1.2 双核资源分配的实际意义S32K3的双核不等于两个核完全对等。实际工程中核心A通常跑控制逻辑、状态机核心B跑通信协议、诊断服务或者反过来。这样分不是为了炫技而是为了隔离故障域通信栈出问题不应该拖垮核心控制逻辑。在EB里做双核分配的思路不是“一个CPU核上跑两个CAN驱动”而是“把CAN外设实例归属到某个核心”。也就是说CAN0分配给Core0CAN1分配给Core1那Core0的代码可以访问CAN0的寄存器和中断Core1的代码访问CAN1。更细颗粒度的做法是同一个CAN控制器内部的接收FIFO、 mailbox也可以按核分配但实际项目里很少会这么做因为核间访问外设会引入缓存一致性和仲裁问题收益小于风险。我这次模板里用了最典型的方案Core0控制Classic CAN通道做UDS诊断Core1控制CANFD通道做标定数据收发。两个核之间只通过IPC核间通信交换少量状态比如“数据帧已收到”“标定任务开始”。这个架构对新手很友好因为每个核的逻辑都相对独立调试的时候能明显减少“这个变量被另一个核改了”的玄学问题。1.3 适合直接参考这份方案的读者这个模板适合三类人。第一类正在做S32K3平台预研想快速跑通CANFD基本收发链路第二类从S32K1或STM32迁移过来想知道EB配置和裸机驱动到底怎么衔接第三类项目已经有双核分工的雏形但不确定外设归属和中断资源怎么处理。如果你是这三类之一后面几节可以直接照抄配置思路。2. 环境准备与EB工程搭建2.1 工具链版本对不齐后面全是玄学错误开始配CAN之前先把工具链装好。我在这个项目里使用的工具包括EB tresos Studio、S32 Design Studio for S32 Platform以及和芯片对应的MCAL插件包。这里强调一句MCAL插件包的版本和EB主程序版本必须严格匹配否则生成的代码经常出现函数声明有、定义没有的诡异编译错误。另外一个容易忽略的是S32K3的启动代码。EB负责生成MCAL驱动但不负责生成完整的启动文件和链接脚本。S32 Design Studio里新建S32K3工程时会带一套启动代码和链接器配置EB生成的MCAL代码要作为静态库或源码工程导入到S32DS里一起编译。我习惯的做法是在S32DS里先建立一个空工程确认能够编译、下载、点亮LED再导入EB生成代码。这样能把环境问题提前暴露后面不会分不清是CAN配置错还是工程配置错。2.2 新建EB工程与选择双核型号打开EB tresos后新建工程时要选芯片型号。双核S32K3的具体型号可以在Part Number里看到核心数。选择芯片之后EB会生成基础工程结构左侧Project Explorer里能看到MCU、Mcu、Port、Can等模块。这里要注意CAN模块并不是一进来就有的需要导入对应的MCAL插件包。导入插件以后推荐按“Mcu - McuClockReferencePoint - Can - CanTrcv - Port”的顺序配置。先配时钟和引脚再配CAN控制器否则你配置CAN波特率时明明正确但就是没有波形输出多半是时钟源没给对。2.3 时钟树和引脚复用是第一个拦路虎S32K3的时钟树比普通MCU复杂。在EB的Mcu模块里你需要先把FIRC、PLL、AON等时钟源配置好明确CAN外设的时钟源来自哪个PLL分支。CANFD数据段要跑到5Mbps时CAN模块的参考时钟至少需要20MHz以上实际建议直接给40MHz或者80MHz这样计算出的Bit Time步长更细采样点才能调准。引脚配置也要注意。S32K3的CANTX/CANRX引脚有多个可选复用功能在Port模块里找到对应引脚后要把它设置为CAN模块的复用功能同时配置输入使能。常见的坑是只配了输出复用、没使能输入缓冲结果自己发的帧自己都收不到还会在总线上产生错误应答。3. EB中CAN与CANFD的配置细节3.1 CAN控制器参数的“四板斧”不管经典CAN还是CANFD在EB的Can模块里一个CAN控制器都要配四个核心参数波特率、采样点、位时间分段、唤醒/错误处理选项。其中前三个直接影响物理层通信错误处理影响协议栈行为。Classic CAN我一般配500kbps采样点87.5%。这个采样点是从S32K1时代沿用下来的经验值在车身网络中兼容性比较好。如果你的总线上有不同供应商的节点建议先确认对方采样点要求再在EB里微调。位时间分段上S32K3的CAN模块会把一个Bit Time切成SyncSeg、PropSeg、PhaseSeg1、PhaseSeg2。这些值不用手算可以在EB里直接填“采样点百分比”工具会自动反算。但如果你的波特率不是整数分频出来的比如时钟源80MHz分频到500kbps刚好每bit160个时钟周期那配置起来很舒服一旦遇到外部晶振29.5MHz这类非整分频时钟就必须把时钟源改到PLL否则采样点误差会直接导致总线错帧。3.2 CANFD的FD使能与数据段波特率CANFD配置的核心是“两套位时间”。控制器以某个速率发送仲裁字段等到BRS位发出后切换到另一套更快的速率发送数据字段。所以EB里需要分别配置Arbitration Bit Rate和Data Bit Rate。我推荐仲裁段设为1Mbps数据段设为5Mbps。这个组合在整车上最常用一方面1M仲裁段和经典CAN兼容调试工具都能识别另一方面5Mbps数据段已经能让64字节报文在几十微秒内发完。数据段采样点建议设置在75%到80%之间比仲裁段的87.5%低一点因为5Mbps下信号上升沿和传播延迟影响更大采样点太靠后容易采到跳变沿。需要注意CANFD的Payload长度配置和DLC对应关系在EB里也有选项。64字节意味着DLC15但EB里有些版本会把MaxDLC配置成64有些只支持到8。要确认你选中了CANFD模式否则最大报文长度会被限制在8字节发64字节的PduInfo会直接被Can_Write拒绝。3.3 中断与轮询在EB里的底层开关EB配置里CAN模块收发方式的选择不是“选中断就不能轮询”而是“选哪些硬件信号可以产生中断”。中断模式需要在Can模块中使能每个Mailbox或者FIFO的RX/TX Interrupt Enable还要把中断优先级配置好轮询模式则不需要使能这些中断直接在主循环里周期调用轮询函数去查标志位。我的建议是Rxd中断使能可以开但初始化阶段先不开等Can_Init之后手动调用一次中断使能接口这样能避免芯片上电瞬间总线上乱帧触发误中断。发送中断可以只在需要确认发送完成的报文上开比如UDS诊断应答普通周期报文用轮询写缓冲区即可。3.4 双核外设归属配置S32K3的外设归属是通过Resource Domain模块配置的。在EB里找到Resource Domain或类似的模块把Can0实例分配到Core0域Can1实例分配到Core1域。除了模块本身中断控制器也要对应分配。Core0的中断由INTC0管理Core1的中断由INTC1管理CAN0/1的中断号要注册在对应核心上。如果配置后两个核同时跑但某个核的CAN中断永远不触发十有八九是外设归属对了、中断归属没对。我在第一次调试时就犯过这个错Core1的CAN1寄存器访问正常但中断响应一直没进查了半天才发现中断响应模块里没把CAN1中断映射到Core1。4. 中断模式代码实现4.1 MCAL中断初始化的正确姿势EB生成代码后CAN驱动初始化只需要调用Can_Init然后把控制器状态切换到Started。但中断使能一般不是Can_Init自动完成的需要显式调用一次中断使能接口或者在Can_Init之前使能NVIC中断。这里给出一个实用的初始化模板void Can_DriverInit(void) { Can_Init(Can_ConfigData); Can_SetControllerMode(0u, CAN_CS_STARTED); Can_SetControllerMode(1u, CAN_CS_STARTED); /* 使能CAN0/CAN1的中断中断号以EB实际生成的为准 */ Intc_Ip_EnableIrq(CAN0_RX_IRQn); Intc_Ip_EnableIrq(CAN1_RX_IRQn); }很多初学者会问既然EB里已经把中断使能勾上了为什么代码里还要再开一次。因为MCAL驱动为了初始化阶段的安全通常不会在Can_Init里直接开总中断防止扫描总线时收到大量错误帧把中断烧起来。等总线状态稳定后再打开更可控。4.2 TX/RX中断回调函数S32K3 MCAL的中断服务函数里驱动会处理硬件标志位然后调用用户注册的回调函数。回调函数里不能做耗时操作我一般只做置位和消息复制真正业务逻辑放在主循环里。static volatile uint32_t g_can0_rx_flag 0u; static uint8_t g_can0_rx_data[64]; void Can0_RxIndication(const Can_HwType *Mailbox, const Can_PduType *Pdu) { memcpy(g_can0_rx_data, Pdu-SduDataPtr, (size_t)Pdu-SduLength); g_can0_rx_flag 1u; } void Can0_TxConfirmation(const Can_HwType *Mailbox) { g_can0_tx_done 1u; }回调里的Pdu结构体指针在中断结束后可能失效所以必须先把数据拷贝到自己的缓冲区。另外CANFD长报文达到64字节时SduLength类型要能容纳如果用的是uint8最大255当然够但如果你自己定义结构体别定义成uint8_t legacyLength这是低级错误但确实会出现在各种移植代码里。4.3 中断优先级和双核响应差异S32K3的Cortex-M7中断优先级分组要统一。如果Core0用FreeRTOSCore1裸机两个核的中断优先级分组必须一致否则核间通信或共享内存访问会出现优先级反转的坑。我这次Core0跑的是裸机状态机Core1跑的是调度内核但两个核的NVIC分组都设成了4位抢占优先级整体上就很好管理。从实测效果看CANFD 5Mbps收64字节时中断响应延迟在几微秒到几十微秒之间。因为CAN控制器收到完整帧后数据已经在接收缓冲区里中断里取数就是做一次SRAM到SRAM的拷贝速度很快。但如果把很长一帧的数据解析逻辑也放在中断里主循环就被拖垮所以中断里“复制数据、置位标志、马上退出”的原则必须坚持。5. 轮询模式代码实现5.1 周期轮询框架轮询模式并不比中断Low它只是换一种方式处理事件。在不需要高实时性、或报文频率很低的通道上轮询反而能省掉一套中断资源管理逻辑。比如Classic CAN通道处理UDSUDS请求本身频率低用轮询完全没问题。void Can_PollTask(void) { Can_PduType rxpdu; Can_HwType hwType; if (Can_Read(CAN0_RX_HRH, rxpdu) CAN_OK) { /* 处理CAN0收到的报文 */ } }这里Can_Read每次调用会读取接收缓冲区里已经完成接收的最新报文。如果缓冲区里没有新报文返回CAN_NO_DATA或类似状态轮询框架只需要周期性调用就行。周期建议设在5ms以内这样对500k波特率的经典CAN来说不会因为轮询间隔太长而漏掉报文对5Mbps的CANFD来说如果一帧64字节报文在忙时连续到达轮询间隔就不太够用所以FD通道我通常用中断而不是轮询。5.2 FD帧的发送流程发送CANFD报文时需要指定CANID、DLC、数据指针并且要把FD标志和BRS标志带到发送缓冲区。MCAL底层的Can_Write接口在发送时会读取这些标志决定是否以FD格式发送、是否发送BRS切换位。Can_PduType pdu; uint8_t fd_data[64]; pdu.CanId 0x123u; pdu.CanIdType CAN_ID_STD; pdu.CanDlc 15u; /* 64字节对应DLC15 */ pdu.SduDataPtr fd_data; pdu.SduLength 64u; Can_Write(CAN1_HTH_FD_TX, pdu);轮询模式下发送完成后最好通过读取发送缓冲区状态来确认缓冲区已经释放。如果上一帧还没发完就继续调用Can_Write驱动会返回BUSY这时候不要重试太频繁否则会把CAN控制器的内部状态搞乱。我一般做一个简单的重试机制BUSY则等1ms再发连续3次BUSY就上报发送超时。5.3 错误状态轮询与恢复轮询模式天生适合做错误状态监控。Can_MainFunction_Write和Can_MainFunction_Read这一类周期函数由MCAL内部调用后底层会更新错误计数。想读总线错误状态可以调用Can_GetControllerErrorState把返回值映射成ERROR_ACTIVE、ERROR_PASSIVE、BUS_OFF。我建议在轮询任务里加一个“慢周期”分支每100ms读一次错误状态。一旦发现BUS_OFF不要直接自动恢复而是先等待一段时间确认总线上没有持续干扰再把控制器切到STOPPED再切回STARTED这样能避免总线上还有问题就反复恢复、反复错帧的情况。6. 中断与轮询模式对比6.1 关键指标实测对比我在同一个工程里分别用中断和轮询跑过同一路CANFD收发整理了一份对比结果。这里的“CPU占用率”是粗略统计不严谨但能反映趋势。对比项中断模式轮询模式报文响应延迟几微秒到几十微秒取决于轮询周期周期5ms时最大5msCPU实时占用低中断处理完就释放固定占一部分执行时间代码复杂度偏中需要回调函数和标志管理偏低主循环轮询即可适合报文频率高频、突发性强低频、周期固定风险点中断风暴、临界区保护漏包、轮询任务被其他任务延迟实测下来中断模式更适合CANFD的5Mbps突发数据因为64字节在5Mbps下的传输时间本身很短中断处理开销相对报文间隔来说很小。轮询模式对于UDS这种一问一答的场景完全够用而且不用考虑多个外设中断同时触发导致的嵌套问题。6.2 双核项目里的推荐组合双核项目里我推荐做“一中断一轮询”的混合使用主控核的实时控制CAN采用中断通信核的标定和诊断通道采用轮询或低优先级中断。这样两个核的中断负载错开调试时也可以根据通道行为快速定位是哪一侧的调度出了问题。如果你的两个CAN通道都跑在同一个核上那就别把两个通道都设成高频中断否则中断嵌套和临界区保护会变得很复杂。一个通道中断收发另一个通道在主循环里轮询实际工程上更好写也更好维护。6.3 影响选择的隐藏因素除了延迟和CPU占用还有两个隐藏因素一是可预测性轮询模式的任务耗时是可计算的但中断模式在极端报文频率下可能“踩踏”主循环二是调试难度中断模式下断点容易打断时序关系轮询模式下用仿真器查看变量状态更方便。所以如果你的项目调试资源有限优先让CANFD跑轮询先跑通基本通信再优化成中断模式能省很多排错时间。这个顺序在开发阶段非常实用。7. 实操中容易踩的坑7.1 EB配置和生成代码不一致EB里配置好Can模块后一定要重新生成代码并且确保S32DS里的源码引用路径指向新生成的文件夹。我遇到过“配置改了一整天代码跑起来还是老逻辑”的事最后发现是生成代码没有覆盖到工程引用目录导致链接的还是旧库。所以每次改完EB配置先看生成文件的时间戳再编译。7.2 CANFD波特率采样点没配对把一个CANFD节点接到已有总线时最容易出现的问题是“自己发自己收没问题但和外部节点通信全是错误帧”。这通常是两端的数据段采样点差距过大。比如链路上一端采样点是70%另一端是80%在5Mbps下一个位时间只有200ns采样点差10%就是20ns的误差已经足以导致采样位置漂移。排查方法不用复杂的分析仪先用示波器看正常报文波形再在总线上挂一个自环测试发送FD帧并接收回显观察错误计数器增长情况。如果自环正常而外部通信异常优先检查两端采样点。7.3 DMA和Mailbox分配错误S32K3的接收缓冲区概念比较抽象。EB里配置的接收邮箱一般是固定映射接收FIFO又是一个独立缓冲区组。如果你一边开着FIFO一边又按邮箱方式读数据得到的报文序号和ID可能全是乱的。我的经验是CANFD高频数据通道用FIFO中断模式读取UDS诊断通道用独立邮箱轮询模式读取。两条路分开互不干扰。7.4 双核调试时两个核同时跑飞双核调试和单核完全是两种体验。刚上电时两个核都会执行自己的启动代码但如果你在S32DS的调试配置里只启动了一个核另一个核可能还在复位状态导致它负责的CAN外设没有初始化总线上看不到数据。建议先分别单核下载和运行确认每个核各自的CAN通道都能独立通信后再做双核联合调试。联合调试时在两个核各自的主循环里加一个软件计数变量周期翻转一个调试IO。这样运行时用示波器就能直观看到两个核是否都在正常工作。这个土办法比看日志可靠得多。7.5 代码移植时DLC和长度别混用AUTOSAR的Can_PduType里有CanDlc和SduLength两个字段。CanDlc用于表示CAN协议层DLCSduLength表示实际数据字节长度。经典CAN下两者可以互相换算但CANFD下64字节数据对应DLC15如果按“DLC减4”的老逻辑去算长度就会出问题。凡是涉及CANFD报文解析的代码都要用SduLength作为真实长度CanDlc只用来填发送缓冲区。我在实际调试中的体会是S32K3的CAN模块配置确实比S32K1复杂但用EB把参数可视化之后很多问题反而变成了“回到数据手册查一个字段含义”的事而不是靠猜寄存器。这篇模板里的双核分配和中断/轮询代码是我在项目里跑通并验证过的版本你可以直接作为起点再根据自己的通信矩阵去微调波特率、采样点和报文过滤规则。最后再分享一个小技巧每次改完EB配置后顺手把生成的.c/.h文件用版本管理工具提交一次这样排查问题时可以快速回退到某个能工作的配置。别让“上次还能跑这次突然不行了”浪费你一下午。
返回列表