直流充电桩程序源码拆解:从状态机到BMS通信的实战指南
2026/8/31 15:44:45 网站建设 项目流程

简介:本资源为基于STM32平台开发的直流充电桩嵌入式控制程序源码包,面向电力电子、新能源汽车充电设施研发工程师及嵌入式系统进阶学习者,解决直流快充系统中AC/DC与DC/DC变换控制、BMS通信对接、安全保护逻辑实现等核心工程问题。压缩包共206个文件,主体为82个C源文件与91个头文件(.h),构成完整的底层驱动、协议栈(含GB/T 27930通信框架)、充电状态机及故障处理模块;另有汇编启动文件(.s)、Keil工程配置(.uvprojx/.uvoptx)、固件镜像(.bin)及调试配置文件,便于直接编译烧录与调试验证。资源大小699KB,结构清晰,模块划分明确,涵盖电力转换控制、CAN通信适配、温度/电压/电流多维采样与保护算法等关键实现。目前已有213人学习下载,适合开展充电桩协议开发、嵌入式实时控制实践或高校新能源方向课程设计参考。 前几天接手了一个直流充电桩程序.zip,解压完我盯着目录看了半天。说真的,这类项目包在充电桩行业里很典型——看起来就是一堆嵌入式源码,实际上里面藏着一整套完整的业务逻辑:BMS通信、充电流程状态机、电表采集、故障管理、HMI界面、参数配置,甚至连现场调试用的模拟工具都有。今天这篇就把这个程序包从目录结构、核心状态机、三路通信、安全机制,一路拆到移植调试的实战经验,给准备入行充电桩软件开发、或者想在老方案上做改版移植的工程师做个参考。

1. 程序包整体设计思路:先看懂目录结构,再谈改代码

1.1 解压后的第一件事:工程目录怎么放

我拿到这个包之后没有急着去翻main函数,先把目录整个过了一遍。这套程序的结构大致是这样:

  • app/main:主程序入口,初始化外设、创建任务
  • app/charge_machine:充电流程状态机,这是核心业务逻辑
  • app/bms_protocol:BMS通信协议解析,基于GB/T 27930
  • app/fault_manager:故障检测、分级和上报
  • app/parameter_manager:参数存取,比如桩号、费率、最大输出功率
  • app/hmi_display:LCD界面刷新和按键处理
  • app/log_manager:运行日志和故障记录
  • components/:电表采集、绝缘检测、RTC、Flash存储、4G模块等组件
  • drivers/:MCU底层驱动,包括CAN、UART、GPIO、ADC、定时器等
  • bootloader/:固件升级引导程序

这种分层的做法最大的好处是业务逻辑和硬件驱动剥离开了。我原来见过一些充电桩程序,CAN收发和充电流程全写在一个文件里,改一个报文解析能牵出一堆问题。而这个包里,底层驱动只管收发、协议层只管拆包组包、状态机只管业务流转,各层之间通过接口函数交互,编译的时候也方便做条件编译排除掉不需要的模块。

1.2 主控平台选型与资源分配

看完整目录结构,可以推测这套程序是跑在MCU平台上的,内部用了一个轻量级RTOS来调度任务。直流充电桩的主控做选择时一般就看几个硬指标:CAN通道数够不够、Flash和RAM够不够跑协议栈和日志、有没有足够的定时器和ADC通道。

资源分配上,这套程序用了至少两路CAN:一路走BMS通信,另一路预留做充电桩内部设备互联,比如和功率模块通信。BMS那一路要求实时性比较高,一般在RTOS里优先级也会设置得比较高。电表通过RS485走Modbus-RTU,4G模块走UART,HMI屏幕走RGB接口或者SPI,这些外设资源在程序里都能对应得上。

1.3 拿到他人代码后的快速上手路径

如果你也收到一个没文档的充电桩程序包,我建议按这个顺序来:

  1. 先看README和版本记录,了解硬件平台、工具链版本和已经修过什么问题
  2. 检查工程文件后缀,判断是Keil、IAR还是CMake工程,把编译环境先搭起来
  3. 直接编译一遍,能产出固件说明代码基本完整,编译报错的话先解决工具链和芯片型号的配置问题
  4. 用代码搜索定位main函数,然后顺着“初始化—建任务—跑状态机”这条主线往下读
  5. 找一张原理图放旁边,把各个外设的GPIO、中断、DMA通道标出来,对照着看驱动配置

说实话,大多数充电桩程序包的时间成本都在“读代码”而不是“写代码”上。建立起这种读码路径,比闷头逐行翻代码效率高很多。

2. 充电流程状态机:直流充电桩程序的灵魂

2.1 为什么非要用状态机

直流充电桩的充电过程不是“插枪就充电、拔枪就结束”这么简单。一次完整的充电要经历物理连接确认、低压辅助上电、握手、辨识、参数配置、正式充电、结束统计这么多个阶段,每个阶段都有明确的进入条件、执行动作和超时处理,而且一旦某一步出现异常,还要能安全退出。

这种流程如果用“if-else套if-else”的方式写,后面一旦加需求,比如增加一种新的充电模式或者支持双枪互充,代码就乱了。状态机的好处在于,每个状态只做自己该做的事,状态之间的跳转条件明确写在表格里,查问题的时候对着状态表看当前停在哪里、缺什么条件,一下就能定位。

我见过不少现场问题,比如“为什么充电桩一直显示握手失败”,排查到最后其实就是BMS超时逻辑没写对。如果你把状态机拆清楚,这类问题的排查成本会低很多。

2.2 状态定义与跳转关系

这套程序里核心状态我整理了一下,大概是这样的:

状态进入条件主要动作正常退出
IDLE上电初始化完成界面待机、检测枪头连接枪头连接有效/收到启动指令
CONNECTED枪头连接确认低压辅助上电、启动CAN通信完成物理连接确认
HANDSHAKE低压上电完成发握手报文、等BMS握手响应收到BMS握手报文
IDENTIFICATION握手成功发辨识报文、等BMS辨识报文收到BMS辨识报文
PARAM_CONFIG辨识完成接收电池参数、判断是否可充参数匹配/收到充电准备就绪
CHARGING参数配置完成按BMS需求调整输出电压电流收到BMS中止充电或达到目标SOC
END收到结束报文/人工停止停止输出、断开接触器统计完成
FAULT任意阶段发生故障记录故障、切断输出人工复位/故障恢复

国标直流充电里,BMS和充电机之间先来一轮“自我介绍”:充电机发CHM握手报文,BMS回BHM;充电机再发CRM辨识报文,BMS回BRM辨识报文。之后BMS下发BCP电池充电参数,充电机判断自己的输出能力够不够,没问题就进入待充状态。整个流程非常像一个接待流程——先确认双方身份,再交换“合同条款”,最后才开工。

2.3 握手与参数配置阶段在程序里做了什么

握手阶段,充电机发出的报文中包含协议版本号和充电机编号。程序里对版本号做兼容判断,不匹配时不会直接退出,而是进入一个“依据低版本协议继续”的分支,这在旧车老桩互操作的场景里特别有用。

参数配置阶段就更实际了。BMS下发的电池参数中包括电池类型、单体最高电压、电池总电压上限、电池电流上限、SOC等信息。程序要把这些需求和充电桩自己的最大输出电压、最大输出电流做交集判断,比如BMS请求电压上限是750V,而桩的最大输出只有500V,那就要按通信协议反馈“中止充电”。这块判断写不对,轻则充电失败,重则触发保护。

调试这块时,如果手边没有真实车辆,可以用支持CAN发送的上位机模拟BMS,按协议周期发报文,这是我在实验室里用得最多的方法。

2.4 充电中的动态调节与结束流程

进入充电状态后,BMS会周期发送BCL电池充电需求,包含充电模式、电压需求、电流需求。充电桩程序要做的事是实时解析这些需求,然后通过底层控制接口对输出电压和电流做闭环调节。程序同时还要监控BCS和BSM报文,BCS是BMS上报的充电状态,BSM是电池状态信息,里面带有最高单体电压、最高温度这些关键量,一旦越限要立刻降功率甚至停机。

结束流程同样有讲究。BMS会发中止充电报文并携带中止原因,比如“电池已充满”或“达到需求电量”。充电机收到后要先停止功率输出,再断开直流接触器,最后做放电统计。如果程序在收到结束指令后没有正确执行接触器断开动作,下一次充电时可能直接报绝缘故障或继电器粘连故障,那种问题在现场很常见。

3. 三路通信解析:BMS、电表、后台各是什么套路

3.1 BMS通信(CAN):协议栈要做得又稳又兼容

直流充电桩最核心的通信就是和车辆BMS的CAN通信。国标协议GB/T 27930里对这些报文有明确定义,CAN帧ID、数据长度、发送周期都是标准的。程序里一般把报文解析做成独立模块,用结构体加解析函数的方式管理,收一帧、解一帧、存一帧。

报文格式解析要注意几个细节:数据字节通常是大端对齐,但有些老版本BMS会出现填位不一样的情况,所以解析时不能直接整段memcpy,建议逐个字节按位处理;另外每条报文都有固定的发送周期和超时时间,写一个“报文新鲜度”检查,超过时间没收到就算超时。我遇到过一些程序只在启动时检查一次报文,结果车辆在充电过程中突然停止响应,程序还在傻等,最后只能靠看门狗复位,这就比较被动了。

协议兼容性是另一个大坑。市面上存量车的BMS版本五花八门,有的握手报文先发、有的等充电机先发,有的参数配置阶段跳着报文走。成熟的程序会在协议层做一个“兼容模式”,比如握手超时后自动切换成对方先发报文的时序,这在现场能省掉大量时间。

3.2 电表采集(RS485 Modbus-RTU):数据要准,重试要稳

直流充电桩需要计量充电电量,这个是计费依据,数据必须可靠。程序里一般通过RS485接直流电能表,用Modbus-RTU协议读寄存器。

电表采集在程序里通常是一个独立任务,按固定周期轮询。Modbus-RTU读多个寄存器时,要注意CRC校验,十六进制解析的时候要把高低字节顺序处理好。读取失败要有重试机制,连续失败多次后把电表通信故障上报给故障管理模块。这套程序里电表寄存器的映射大概长这样:

数据项寄存器地址数据格式
直流电压0x0000有符号整型,单位0.1V
直流电流0x0002有符号整型,单位0.01A
瞬时功率0x0004有符号整型,单位0.1kW
充电电量0x0006BCD码,单位0.001kWh

这里有个细节:不同厂家的电表寄存器定义不完全一样,程序里最好单独做一个电表驱动适配层,把“读什么寄存器”和“业务层用什么数据”解耦开,换电表品牌的时候只改驱动,不动充电逻辑。

3.3 后台通信(4G/以太网):心跳、上报、远程控制

直流充电桩不是单机设备,充电数据要上送云端,也要能接受远程升级和远程停机指令。程序里通常用4G模块走TCP或者MQTT协议,数据格式用JSON,把桩号、状态、电压、电流、电量、故障码等信息周期上报。

后台通信在程序里最需要注意的是断网缓存策略。现场4G信号不稳定,网络中断在充电过程中经常发生。程序要把关键数据先存到本地Flash或者数据库,网络恢复后再补传。如果断电或者断网就直接丢数据,充电客人扫码充电后查不到记录,后台对账就会出问题。

3.4 通信超时与异常处理经验

三路通信各有各的奇葩故障。CAN通信可能因为线缆接触不良导致间歇性丢帧,Modbus可能因为干扰导致CRC错误,4G可能因为运营商网络波动导致连接断开。程序里统一的做法是:每个通信通道都维护一个“最近一次成功通信时间”,超时后按级别上报,一般通信超时给警告,允许重连和重试;涉及充电安全的超时,比如BMS报文超时,直接进入故障停机流程。这里千万不要把不同通道的超时时间写得一样,BMS报文超时可能要求几百毫秒内处理,电表和后台通信超时容忍几秒钟都行。

4. 故障管理:程序里的安全底线是怎么兜住的

4.1 故障分类分级:不是所有故障都要直接断电

我见过一些初版程序,把所有故障都当成“必须立刻停充”来处理,结果在现场频繁跳闸,用户体验很差。好的故障管理会按严重程度分级,这套程序里大概是分了三档:

  • 致命故障:急停被按下、绝缘检测失败、输出电压过压、接触器异常。这类故障必须立刻切断输出,且需要人工复位
  • 严重故障:BMS通信超时、充电温度过高、电表通信超时。这类故障先停止当前充电流程,排除问题后可以重新启动
  • 普通告警:某个采集值波动、单次CRC校验失败。这类故障记录日志,按策略重试或降功率,不中断充电

分级响应不仅能减少误停机,还能保护设备。高温告警可以先降功率让桩“喘口气”,而不是直接一刀切,设备利用率高很多。

4.2 绝缘检测与泄放回路怎么联动

直流充电桩涉及高电压直流输出,绝缘检测是必须要做的。程序里绝缘检测一般通过独立的绝缘检测模块完成,主控通过UART或CAN读取绝缘电阻值。绝缘电阻低于阈值时要闭锁输出,防止充电桩在绝缘异常的情况下对车辆送电。

泄放回路的逻辑更偏底层:充电桩在充电结束、断开接触器之后,直流母线上还会残留电荷,必须通过泄放电阻把母线电压降到安全范围。程序里通常会有一个“母线电压监测”任务,先闭合泄放回路,等到母线电压降到安全值以下,才允许进入待机状态。如果代码里只做断电、不管泄放,下次充电前可能因为母线带电直接导致预充失败或者其他设备损坏。

4.3 急停、看门狗、继电器粘连检测

急停是最直接的硬件安全链路。真正的急停信号一般不经过软件主流程,直接通过硬件逻辑联动接触器断开。程序要做的是一方面检测急停按钮的状态并记录,另一方面确保在急停复位后不能自动恢复充电,必须重新插枪或者人工确认。

看门狗这里要设计两层:软件看门狗和硬件看门狗。软件看门狗监控RTOS任务是否正常调度,硬件看门狗防止MCU跑飞。有一次我在现场遇到充电桩死机,查到最后是某个底层驱动在极端情况下阻塞了中断,软件看门狗本身也调度不了,只能靠硬件看门狗兜底复位。

继电器粘连检测是容易被忽略的细节。直流接触器在大电流下触点可能熔焊,程序在下一次上电前要做一个接触器状态确认,检测驱动端和反馈端的逻辑是否一致。如果已经粘连还继续送电,后续可能会造成更严重的事故。这个检查逻辑最好放在充电开始之前,不要等到充电中再通过异常电流来判断。

4.4 故障记录与追溯:日志要能支撑现场售后

看过很多充电桩程序只把故障码放在内存里,一断电就没了。现场售后最需要的是“这台桩在什么时间、什么条件下报了什么故障”。程序里最好做独立的日志模块,把故障码、充电状态、电压电流采样值、关键报文超时信息一并写入Flash存储区,并加上实时时间戳。

故障记录要有循环覆盖机制,按时间顺序存最近几十条记录,每条记录尽量完整。充电桩程序调试的时候,我通常先让现场拍一张故障码和关键参数的截图,很多时候问题已经能猜出七八成了,再配合日志基本就能定位。

5. HMI与参数管理:程序好不好用全看这里

5.1 界面刷新逻辑:界面别和充电状态抢资源

直流充电桩的HMI屏幕实时显示电压、电流、功率、电量、SOC等信息,但界面刷新只是“展示层”,优先级不能太高。程序里面用独立任务处理屏幕刷新,界面元素读到的都是共享内存中的最新数据,充电流程任务只负责更新数据、不直接操作屏幕。

界面跳转逻辑要和状态机保持一致:待机界面、插枪/启动界面、充电中界面、充电结束界面、故障界面。每切一次状态就通知界面任务切换页面,这样刷屏逻辑不复杂,也不容易出错。实际调试中最常见的问题是屏幕的RGB刷屏占用太多CPU时间,导致CAN中断处理延迟,造成BMS超时误报,遇到这种情况要把屏幕刷新放到DMA或者降低刷新频率。

5.2 参数配置与保存:Flash磨损和掉电保护

每台桩都有自己的“身份证”,包括桩编号、后台服务器地址、最大输出功率、费率、协议版本号、电压电流限值。这些参数不能写死在代码里,否则现场改一个服务器地址就得重新烧固件,维护成本太高。

程序里的参数管理模块提供默认值管理和读写接口,把参数存储到外部Flash或者EEPROM。掉电保存时要注意Flash写入次数,频繁擦写会磨损存储介质,所以要有版本号和校验值,并且采用“先写备份区、再更新主区”的方式,防止掉电导致参数区损坏。现场如果遇到“参数一保存就丢”的问题,九成是这里处理得不到位。

5.3 本地调试工具:没有真车也能测流程

充电桩程序开发最麻烦的是测试环境,不是随时都有真实车辆可以用。所以程序包里面一般会带一个PC端的CAN模拟工具,通过USB-CAN适配器模拟BMS,按协议周期发送握手、辨识、参数配置和充电需求报文,用来测试充电桩的完整充电流程。

我在测试时习惯先跑一遍“正常充满流程”,确认状态机走到位;再模拟各种异常,比如BMS突然断电、发送非法报文、电压需求超过桩的能力范围,逐一确认故障逻辑能兜住。这一套跑完,再到现场真车验证,能省很多现场调试时间。

6. 移植与现场调试:从能跑到能卖的实战经验

6.1 移植适配最常见的几个坑

公司内部如果把一套程序从老主控平台移植到新平台,最常见的坑是引脚配置没有根据原理图重新核对。CAN_TX和CAN_RX用错了引脚、继电器控制的GPIO初始电平不对、ADC采样通道接错,这类问题在代码编译时完全发现不了,只有上电测试才会暴露。

另外要注意不同MCU的CAN外设时钟配置不一样。有些芯片CAN需要独立的时钟源,时钟没配好,波特率计算出来就是偏差的,现场表现是CAN通信时好时坏。排查这类问题最有效的方法是看示波器上的CAN波形,对比波特率和位时序是否符合预期。

还有一点,如果新平台的主频变了,所有基于延时循环的代码都要重新检查。我之前把一个程序从72MHz平台移植到168MHz平台,延时函数没改,结果BMS握手报文的发送周期直接快了近一倍,BMS判定超时,充电一直启动不了。

6.2 现场问题快速排查思路

结合我处理过的现场售后问题,整理几个高频问题排查思路:

现象排查方向
充电桩显示绝缘故障检查绝缘检测模块是否正常、直流母线是否残留高压、充电枪线缆是否磨损
BMS握手失败检查CAN线A/B是否接反、波特率是否一致、对方是否兼容老版协议
充电功率上不去读取BMS请求值与桩输出能力是否匹配、电表采样值是否正确、功率模块限功率
拔枪后继电器不释放检查继电器驱动电路、程序状态机是否停在充电态、继电器是否粘连
屏幕显示正常但无法启动充电检查枪头CC/CP检测信号、急停状态、后台是否下发禁用指令

这些问题的共性是:先看程序当前停在哪个状态、缺哪个条件,再看硬件信号是否到位。状态机和日志模块做得好,这个过程会很快。

6.3 版本发布与固件升级:升级不是烧一次固件就结束了

充电桩装到现场之后,固件升级是躲不开的需求。程序包里带了bootloader引导程序,远程升级流程一般是:后台下发升级指令,主程序把升级包下载到存储区,校验通过后跳转到bootloader,bootloader负责把新固件写入应用区,写入完成后跳回应用。关键点在于升级过程中断电,要有恢复机制,常见的做法是保留两个应用区,一个当前版本、一个新版本,写入失败自动回滚。

我建议在发布前做一个“升级可靠性测试”,专门模拟升级过程中断电、断网、升级包损坏这些情况,确认设备还能正常启动并恢复到可用状态。如果这一步不测,现场升级失败后设备变砖,售后成本很高。

6.4 现场标定和实测记录:数据要留底

充电桩程序上线前最好做一轮系统的标定测试,用电子负载或者标准测试设备记录电压、电流采样误差,在程序里做线性补偿。不同温度下采样结果可能不一样,有条件的话把常温标定和高温标定都做一遍。

标定数据要留底。我发现现场很多“电压不准”“电量不准”的问题,最后对比出厂标定记录,往往能发现是电表型号换过但驱动没换,或者补偿系数没同步。程序里把标定日期、标定系数、硬件版本统一存进参数区,后续售后排查会方便很多。

说实话,这几年我改过的充电桩程序没有几十套也有十几套,最大的体会是:充电桩程序的核心不是堆功能,而是把状态机理清楚、把安全机制兜住、把现场数据留下来。代码写得再花哨,现场一个绝缘故障或者一次BMS握手失败,该暴露的问题全都会暴露。希望这篇拆解能帮你少走点弯路,也欢迎在评论区聊聊你遇到过的奇葩充电桩问题,我尽量知无不言。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询