Bootloader上位机,基于UDS实现,DoCAN(包括源码和发布的小程序). 可根据自己的程序扩展和重新设计升级流程.
先扔个硬核的:我撸的这套Bootloader上位机底层基于SocketCAN搞DoCAN通信,支持ISO15765-2的分包重组。上位机界面用PyQt5糊的,核心就三个模块------协议栈、文件解析器、状态机。

看这个处理UDS正响应的代码片段:
python
def handle_positive_response(self, service_id, data):
if service_id == 0x31: # 例程控制
if self.current_routine == ROUTINE_ERASE_MEMORY:
self._send_request(0x31, [0x01, 0xFF, 0x00]) # 触发下载
elif service_id == 0x34: # 下载请求
self.block_counter += 1
self._send_next_data_block()
elif service_id == 0x36: # 退出传输
self._verify_flash()
注意那个block_counter自增的位置,必须在收到正确响应后才更新。之前翻车过在发送时就计数,结果网络丢包直接导致数据错位,烧了ECU的boot区(幸亏提前焊了测试座)。
文件传输用了个骚操作------动态分块算法。根据当前通信延迟自动调整块大小,实测从32字节到4096字节弹性变化。看这个自适应逻辑:
python
def _calculate_optimal_block_size(self, last_transfer_time):
if last_transfer_time < 0.1:
return min(self.max_block_size, self.current_block_size * 2)
elif last_transfer_time > 0.5:
return max(self.min_block_size, self.current_block_size // 2)
else:
return self.current_block_size
别小看这几行,配合硬件在环测试能提升30%刷写速度。但要注意ECU端的buffer容量,有次给某国产芯片设了2048的分块,结果把CAN控制器缓冲区撑爆了,后来加了个预设参数表才解决。

说到坑点,时间参数必须死磕ISO14229:
- P2Server超时默认50ms
- P2*Server延长超时需要协商
- S3Server保活间隔建议3000ms
我在代码里搞了个时间守护线程,用优先级队列管理定时任务:
python
class TimingWheel(Thread):
def __init__(self):
super().__init__()
self.tasks = PriorityQueue()
def add_task(self, delay, callback):
deadline = time.time() + delay/1000
self.tasks.put((deadline, callback))
def run(self):
while not self.exit_flag:
now = time.time()
if not self.tasks.empty():
deadline, callback = self.tasks.queue[0]
if now >= deadline:
self.tasks.get()
callback()
time.sleep(0.001)
这玩意儿比传统定时器省资源,特别是在同时处理多个ECU会话时。但要注意Python的GIL锁问题,回调函数里千万别搞阻塞操作。
最后安利下我的升级流程验证方案------三步校验法:
- 传输后立即CRC32校验
- 重启ECU后回读比对
- 执行目标程序的自我签名验证
源码里有个贼好用的debug模式,开启后会记录所有交互报文。有次客户现场出问题,直接拿日志文件喂给回放脚本,十分钟就定位到网关的报文过滤问题。

代码仓库里还藏了个彩蛋:在关于页面连续点击版本号三次,会弹出CAN总线压力测试工具。这个本来是给自己调试用的,结果好几个客户当正式功能在用了(笑)。