AUTOSAR 通信栈 + Bootloader 刷写
真实量产 ECU 不会手写 bxCAN 寄存器,而是运行在 AUTOSAR 标准化软件架构之上。本章从应用层一路追到 CAN 控制器,看一个信号如何被打包成帧;再拆解 CAN Bootloader——车辆 4S 店"刷程序"背后的 UDS 0x34/0x36/0x37 流程、Flash 驱动、校验与双 Bank 回滚。
真实量产 ECU 不会手写 bxCAN 寄存器,而是运行在 AUTOSAR 标准化软件架构之上。本章从应用层一路追到 CAN 控制器,看一个信号如何被打包成帧;再拆解 CAN Bootloader——车辆 4S 店"刷程序"背后的 UDS 0x34/0x36/0x37 流程、Flash 驱动、校验与双 Bank 回滚。
一个应用写下"车速 = 120",到它变成总线上的电平,中间穿过整个通信栈。下图是信号自上而下的打包路径:
注意上图两条路径:普通信号报文走 COM → PduR → CanIf;而 UDS 诊断/刷写这类长报文,在 PduR 处分流进 CanTp 做分段(一帧 8 字节装不下),再汇入 CanIf。理解这个分叉,就能读懂为什么诊断请求要用 ISO-TP 而不是裸 CAN 帧。
AUTOSAR 不写代码而是"配置"。所有模块的参数(报文列表、信号布局、CanTp 通道、Dcm 服务表)都存在 ARXML(AUTOSAR XML)文件里,由 DaVinci Configurator、EB tresos 等工具生成 BSW 代码。DBC 通常先转成 ARXML 的 ECU 提取 (System Extract) 再导入。开发者面对的是配置界面,而非手写寄存器。
| 模块 | 层级 | 职责 | 关键概念 |
|---|---|---|---|
| COM | 服务层 | 信号打包/解包 | Signal / I-PDU / 发送模式 |
| PduR | 服务层 | PDU 路由/网关 | 路由表 / 转发 |
| CanTp | 服务层 | 长报文分段 | FF / CF / FC / STmin |
| Dcm / Dem | 服务层 | 诊断/故障码 | UDS 服务 / DTC |
| CanIf | ECU 抽象 | 硬件无关接口 | HOH / L-PDU 映射 |
| CanDrv | MCAL | 操作 CAN 外设 | 邮箱 / FIFO / 中断 |
Bootloader 与应用共享 Flash,但各占独立区域。上电后 Reset 向量指向 Bootloader,由它决定去留:
跳转的本质:关外设与中断、重定位向量表、加载应用的初始栈指针 (MSP),再跳到应用的 Reset_Handler。
/* bootloader.c — 从 Bootloader 跳转到应用程序 */ #include "stm32f4xx.h" #define APP_BASE_ADDR 0x08008000UL /* 应用区起始地址 */ #define APP_MAGIC_ADDR 0x080FC000UL /* 有效标志所在地址 */ #define APP_VALID_MAGIC 0xA5A5A5A5UL typedef void (*app_entry_t)(void); /* 校验应用是否有效:栈顶合法 + 标志位正确 */ static int app_is_valid(void) { uint32_t sp = *(volatile uint32_t *)APP_BASE_ADDR; uint32_t magic = *(volatile uint32_t *)APP_MAGIC_ADDR; /* 栈指针应落在 SRAM 区间 (0x2000_0000 ~ 0x2002_0000) */ if ((sp & 0x2FFE0000) != 0x20000000) return 0; return (magic == APP_VALID_MAGIC); } void jump_to_application(void) { uint32_t app_sp = *(volatile uint32_t *)(APP_BASE_ADDR); uint32_t app_reset = *(volatile uint32_t *)(APP_BASE_ADDR + 4); app_entry_t app_entry = (app_entry_t)app_reset; /* ① 关闭所有中断、复位外设时钟到默认态 */ __disable_irq(); HAL_RCC_DeInit(); HAL_DeInit(); SysTick->CTRL = 0; SysTick->LOAD = 0; SysTick->VAL = 0; /* ② 重定位中断向量表到应用区 */ SCB->VTOR = APP_BASE_ADDR; /* ③ 设置主栈指针为应用的初始 SP,再跳转 */ __set_MSP(app_sp); __enable_irq(); app_entry(); /* 跳入应用 Reset_Handler,不再返回 */ } int main(void) { HAL_Init(); SystemClock_Config(); CAN_Bootloader_Init(); /* 初始化 bxCAN + UDS 会话 */ /* 无刷写请求且应用有效 → 直接进应用 */ if (!reprogram_requested() && app_is_valid()) { jump_to_application(); } /* 否则停在 Bootloader,等待 UDS 刷写 */ while (1) { CanTp_MainFunction(); /* 处理分段 */ Dcm_ProcessRequest(); /* 处理 UDS 服务 */ } }
刷写不是一条命令,而是一套严格的 UDS 服务序列。诊断仪(Tester)与 Bootloader 之间的完整握手如下:
/* uds_flash.c — 处理 0x36 TransferData:把数据块写入 Flash */ #include "stm32f4xx_hal.h" static uint32_t flash_write_addr; /* 由 0x34 协商得到的起始地址 */ static uint8_t expected_bsc; /* 期望的块序号 blockSequenceCounter */ /* UDS 0x36 回调:data 已由 CanTp 重组为完整块 */ uint8_t Uds_TransferData(uint8_t bsc, const uint8_t *data, uint16_t len) { /* ① 校验块序号连续,防丢包/重发 */ if (bsc != expected_bsc) return 0x73; /* wrongBlockSequenceCounter */ /* ② 解锁 Flash 并按字 (32bit) 写入 */ HAL_FLASH_Unlock(); for (uint16_t i = 0; i < len; i += 4) { uint32_t word = *(const uint32_t *)(data + i); if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, flash_write_addr, word) != HAL_OK) { HAL_FLASH_Lock(); return 0x72; /* generalProgrammingFailure */ } flash_write_addr += 4; } HAL_FLASH_Lock(); /* ③ 回读校验:Flash 内容应与源数据一致 */ if (memcmp((void *)(flash_write_addr - len), data, len) != 0) return 0x72; expected_bsc++; /* 期望下一个块序号 */ return 0x00; /* 肯定响应 */ }
刷写过程若在擦除后、写完前掉电,应用区会残缺,ECU 变砖。量产方案的三道防线:① 双 Bank——新固件写空闲 Bank,校验通过前不动旧 Bank,掉电后旧 Bank 仍可启动;② 完整性校验——传完用 0x31 例程算 CRC32 或验数字签名,只有通过才把"有效标志"写入,Bootloader 靠它判断能否跳转;③ 有效标志最后写——先擦标志、再写固件、最后写标志,任何中断都不会误跳一个半成品固件。
刷写前必须过 0x27 SecurityAccess:ECU 发 Seed,Tester 用只有授权方掌握的算法(AES/自定义)算出 Key 回传,匹配才解锁。这防止未授权刷写恶意固件——ISO 21434 信息安全要求的一环。真正量产还会叠加固件签名 (RSA/ECDSA),Bootloader 用公钥验签,仅接受厂商签名的固件。
量产 ECU 跑在 AUTOSAR 上:应用层 SWC / RTE / BSW 三层解耦;通信栈自上而下为 COM(信号↔PDU 打包)→ PduR(PDU 路由与网关)→ CanIf(硬件无关接口,HOH/ID 映射)→ CanDrv(操作 bxCAN 寄存器),换 MCU 只改 CanDrv。长诊断报文在 PduR 处分流进 CanTp(ISO-TP:FF/CF/FC/STmin 分段),诊断由 Dcm(UDS 服务)+ Dem(DTC 故障码)处理;一切参数用 ARXML 配置生成而非手写。CAN Bootloader 常驻 Flash 首扇区,上电判断刷写请求与应用有效性(栈指针+CRC/签名+标志位),无请求且有效则关外设、重定位 VTOR、设 MSP 后跳转应用。UDS 刷写序列:0x10 会话 → 0x85/0x28 关 DTC 与通信 → 0x27 安全解锁 → 0x31 擦除 → 0x34 RequestDownload 协商 → 0x36 TransferData 循环写块(校验块序号+回读)→ 0x37 结束 → 0x31 校验 CRC/签名 → 0x11 复位。刷写安全靠双 Bank 回滚、完整性校验、有效标志最后写三道防线,加上 0x27 安全访问与固件签名。