
简介面向智能电表与AMI高级计量架构开发场景这套基于STM32的DLMS智能电表源码能帮助嵌入式开发者理解国际标准DLMS/COSEM如何在资源受限的MCU上落地适用于远程抄表、负荷控制及多厂商设备互操作等实际工程需求。压缩包共543个文件其中C实现与头文件占主体223个.c、252个.h并包含用于硬件初始化的启动文件、辅助测试的Lua脚本、IAR/Keil工程与调试配置以及说明文档整体仅1.85MB结构紧凑便于快速定位协议栈核心代码并对照学习。目前已有404人学习浏览。通过学习该工程可以掌握STM32的GPIO/UART驱动编写、COSEM对象模型的编码解码规则、事件上报机制以及AES加密和哈希校验等DLMS安全特性同时还能参考FreeRTOS多任务划分、Keil/J-Link调试流程以及协议分层实现思路。对于电力仪表、物联网计量及嵌入式协议栈方向的开发者这份源码提供了从底层硬件驱动到上层应用协议的完整范例具有很高的参考和复用价值。 三年前我接到一个跟智能电表相关的项目客户要求设备必须支持DLMS/COSEM协议配合主站做远程抄表和费率下发。当时最大的难点不是STM32的外设驱动而是这套DLMS协议栈在国内可参考的完整工程实在太少。后来我把协议栈、计量驱动、存储和显示全部整理到一起做成了一份基于STM32的DLMS智能电表源码打包成zip。今天把这份源码的架构、移植步骤、关键参数和调试经验写出来应该能帮到正在做智能电表、能源采集终端、充电桩计费模块或者想研究IEC 62056协议簇的朋友。这份源码能做的事包括远程抄读电压、电流、功率、电量、需量支持费率时段切换支持事件记录和负荷曲线还预留了红外和RS485两种通信接口。无论你是刚入行的嵌入式工程师还是已经从单片机转到电力物联网方向的老手都能从这套工程里拆出一部分直接使用。接下来我会按“协议理解—代码结构—移植联调—排错经验”这条路线来讲尽量把每个决定背后的原因说清楚。1. 项目背景与整体设计思路1.1 DLMS/COSEM到底解决了什么问题DLMS是Device Language Message Specification的缩写也就是设备语言报文规范COSEM是Companion Specification for Energy Metering即能源计量配套规范。两者合在一起被IEC 62056标准采用构成了智能电表领域最通用的通信协议族。用大白话讲DLMS负责“怎么说”COSEM负责“说什么”。COSEM把电表里的所有数据都定义成对象每个对象有唯一的OBIS编码DLMS则规定了主站和电表之间如何建立连接、如何读取对象属性、如何上报事件。我见过不少工程师对DLMS的第一反应是“不就是串口发数据吗我自己搞一套更简单”。这种想法能理解但放到实际项目里很容易翻车。智能电表生命周期长部署之后可能换主站系统、换集中器、换通信模块如果当初用了私有协议每次升级都要重新对接。DLMS把数据模型标准化之后设备只要把对象树注册好新主站通过扫描就能自动发现数据项这才是它最大的价值。1.2 为什么选择STM32搭配独立计量芯片芯片选型上STM32在这里面的地位很像“水电煤气都通的标准间”UART、SPI、I2C、DMA、定时器、RTC全都有生态资料多到可以闭眼开发关键是成本还压得住。智能电表是典型的成本敏感产品STM32F103系列一颗就几块钱性能也足够跑完整个DLMS协议栈加业务逻辑。电能计量部分我建议外挂专用计量芯片而不是直接用MCU内置ADC硬算。原因很简单计量精度不是提高ADC位数就行的。信号采样、功率计算、潜动检测、温度补偿、长期稳定性每一块都是专用计量芯片的强项。用独立芯片还能让MCU在低功耗模式下只保留通信和RTC这对电池供电或热插拔场景都很重要。我当时选的是SPI接口的国产计量芯片从寄存器直接读回电压、电流、功率、电量一周就调通了计量链路。1.3 系统整体架构与数据流整个系统可以分成五层来看物理层提供RS485、红外等通信介质链路层负责HDLC帧收发、定界和校验应用层解析APDU报文并维护DLMS关联再往上就是业务层包括电量累计、费率、需量、事件和存储最上层是显示和人机交互。分层设计的最大好处是调试的时候能按层定位问题。数据流的主干是主站先发来一个请求帧UART通过DMA把数据收进缓冲区HDLC层解帧后交给应用层应用层解析出对象OBIS和属性号去对象注册表里查对应回调回调函数去计量芯片或EEPROM读数据再把结果沿原路组装成响应帧发回去。这套流程只要理顺了后续加协议特性只是做加法不会让代码结构失控。2. 源码结构拆解协议栈与业务功能2.1 拿到zip后先看懂工程目录解压之后不要急着编译建议先花十分钟把目录结构过一遍。我这份工程大致是这样的smart-meter/ ├── APP/ // 主流程、任务调度、菜单按键 ├── DLMS/ // 协议栈COSEM对象、APDU编解码、关联管理 ├── HDLC/ // 链路层帧收发、转义、CRC ├── METER/ // 计量芯片驱动、电量累加、需量计算 ├── DRV/ // 底层外设UART、SPI、Flash、EEPROM、RTC ├── OS/ // 可选的轻量级时间片调度 ├── DOC/ // 原理图、接线图、协议说明 └── MDK-ARM/ // Keil工程文件如果你拿到的是别人给的源码建议先看APP里的main函数和DLMS目录下的对象注册表这两个文件基本能概括整个工程的入口和数据结构。协议栈代码和业务代码分开很重要我在早期版本里为了省事把计量逻辑直接写进了协议回调后面加新功能时每改一次都要担心弄坏通信后来痛下决心重构才解脱。2.2 协议栈核心COSEM对象树与OBIS码COSEM对象树是整个协议栈的心脏。每个对象由6字节OBIS编码、一个接口类Interface Class和若干属性Attribute组成。比如常用的几个OBIS值0.0.1.0.0.255 逻辑设备名主站连接后第一件事通常就是读它1.0.1.8.0.255 正向有功电能量1.0.2.8.0.255 反向有功电能量1.0.32.7.0.255 电压瞬时值1.0.31.7.0.255 电流瞬时值0.0.9.0.0.255 日期时间在源码里我维护了一张对象注册表把每个对象的OBIS、属性号、读写回调绑在一起typedef struct { uint8_t obis[6]; // OBIS编码例如 {1,0,1,8,0,255} uint8_t attr_id; // 属性号2通常表示“值” uint8_t ic; // 接口类标识不同接口类对应不同访问行为 int16_t (*read_cb)(uint8_t *buf); int16_t (*write_cb)(uint8_t *buf); } cosem_obj_t; const cosem_obj_t g_obj_table[] { {{0,0,1,0,0,255}, 2, 3, read_device_name, NULL}, {{1,0,1,8,0,255}, 2, 5, read_fwd_energy, NULL}, {{1,0,2,8,0,255}, 2, 5, read_rev_energy, NULL}, {{1,0,32,7,0,255}, 2, 3, read_voltage, NULL}, {{1,0,31,7,0,255}, 2, 3, read_current, NULL}, };协议层收到GET请求后就用OBIS在这张表里做匹配匹配到就调用对应的read_cb。这样新增一个计量项只需要加一行表项协议栈本身完全不用动。很多厂家的DLMS产品就是靠这张表来配置不同版本的维护成本低很多。2.3 三层协议协作关联、APDU、HDLCDLMS通信过程可以分成三层来理解。第一层是关联Association类似登录。主站发AARQ报文携带客户端地址、服务器地址和访问许可信息电表校验通过后回AARE报文这时才算建立起会话。第二层是APDU也就是应用层数据单元常用的是Get-Request/Get-Response、Set-Request/Set-Response以及EventNotification事件通知。每条APDU都对应一个具体的对象OBIS和属性号通信双方就是通过这种精确引用来完成数据读写的。第三层是HDLC链路层负责把APDU封装成帧帧以0x7E作为起始和结束标志数据里出现的0x7E要做转义整帧还要带HCS和FCS两处校验。新手最容易在HDLC这层翻车。我踩过最经典的一个坑是数据内容里恰好出现了0x7E没有做转义结果主站把一帧拆成了两帧整个解析全乱。后来在协议栈里加了标准转义0x7E转成0x7D 0x5E0x7D转成0x7D 0x5D才彻底解决问题。3. 从零跑通移植、联调与关键参数3.1 硬件平台与关键引脚分配这套源码在STM32F103RCT6上跑得最稳晶振8MHzRTC外接32.768kHz晶振。如果你的板子引脚不一样只需要改DRV目录下对应的初始化代码。我整理一份参考引脚功能外设/引脚说明计量SPISPI1PA5/PA6/PA7读计量芯片寄存器RS485UART1PA9/PA10PB12PB12控制收发方向红外通信UART2PA2/PA3本地抄表用EEPROM模拟I2CPB8/PB9保存参数和掉电数据LCD段码屏驱动芯片轮显电压电流电量接线时有一点要特别提醒RS485方向控制脚不能随便用普通IO必须在发送数据前拉高、最后一个字节发完后再延时一个字节时间拉低。这个时序错了会出现“对方能收到我的请求但我的收包全是坏的”这种诡异现象。3.2 移植步骤从解压到编译通过第一步解压zip。第二步安装Keil5并装好STM32F1系列Device Pack不然打开工程会报找不到芯片。第三步打开MDK-ARM目录下的uvprojx工程在Device选项里确认芯片型号。第四步如果自己画了板子把DRV目录下的引脚配置和串口参数改成实际值。第五步编译。第六步用ST-Link下载如果报“Error: no stm32 target found”先检查接线和供电再把芯片的SWDIO/SWCLK引脚是否被复位或外部电路占用排除掉。很多初学者卡在最后一步其实多半是ST-Link接触不良。换一根杜邦线、给板子单独供上电、把Boot0接地基本能解决九成问题。如果还不行就用STM32 ST-LINK Utility做一次整片擦除再重新下载效果立竿见影。3.3 几个必须抠的关键参数我把自己调试过程中最影响成败的几个参数整理出来HDLC收发缓冲区接收缓冲不要小于512字节。AARQ报文、事件上报帧都不小缓冲区太小会出现“收一半丢一半”的问题而且这种问题特别难查。波特率默认9600项目里常用2400到19200。修改时不能只改UART分频还要把DLMS连接配置里的波特率协商值一起改否则两边对不上。最大信息长度主站和电表在关联阶段会协商最大信息长度。两边设置不一致时长帧会被错误分片建议都设成128或都设成256。RTC时钟精度费率切换依赖时间必须用外部32.768kHz晶振。内部RC振荡器长时间跑会漂掉电后时间还可能丢失。晶振旁边的负载电容数值也影响起振稳定一般选12到22pF具体根据晶振规格书来。计量系数不同计量芯片的寄存器比例系数差别很大接互感器还要乘变比。换芯片或换互感器后读数不对第一件事查这个系数。3.4 与主站联调5步验证整条链路拿到源码后怎么验证它真的没问题我建议按这个顺序来先用USB转TTL接调试串口确认上电打印正常再接入RS485转USB打开DLMS主站软件Gurux Device或厂家提供的调试工具都可以连接读取0.0.1.0.0.255逻辑设备名这一步通了说明HDLC和关联都没问题然后读1.0.32.7.0.255电压和1.0.31.7.0.255电流确认应用层和对象树正常接着读1.0.1.8.0.255正向电量同时给计量芯片加信号源或按键模拟脉冲观察电量是否累加最后做一次校时Set断电重上电确认时间保存成功。这套顺序是从依赖关系出发的链路层通才能谈应用层应用层通才能谈计量存储相关要放在最后单独验证。任何一个环节出问题你都能准确缩小到某一层。4. 常见问题与调试排查实录4.1 高频问题速查先放一张排查表都是我实际遇到过的场景现象常见原因排查方向ST-Link烧录报no target接线接触、供电不足、引脚占用换线、单独供电、Boot0接地、整片擦除上电串口无打印晶振未起振、复位电路、UART映射示波器测晶振、核对引脚、查复位脚主站连接不上波特率不一致、HDLC缓冲太小抓UART原始帧检查0x7E和CRC电压电流读成0SPI时序、芯片复位脚被拉低查SPI寄存器、量芯片供电和复位电平电量不累计计量系数配错、寄存器地址错对比芯片手册、确认单位换算掉电丢参数EEPROM写保护、无掉电检测加掉电中断、保证掉电前完成写入这几类问题里最耗时的是“主站连接不上”。不要一上来就怀疑协议栈先拿逻辑分析仪抓UART引脚上的原始波形数一数0x7E标志的数量再看HCS和FCS校验是否通过。通常能迅速定位是链路层还是应用层的问题。4.2 独门排查技巧第一把DLMS协议栈里的调试开关打开让它在串口打印收发APDU的十六进制内容。联调时和主站日志对比两边内容一致但解析失败问题就在对象树如果内容都不一致问题在链路层或缓冲区。第二抓HDLC帧时先数0x7E的个数比预期多了就说明转义没做全数据里天然出现的0x7E把帧切开了。第三联调初期先把安全级别设成最低也就是不启用认证打通链路后再逐步加上密码和加密否则排查难度会翻好几倍。还有一个从实战中总结的技巧多项目复用这套源码时每换一个主站软件或集中器都要重新跑一遍逻辑设备名读取、当前电量读取、校时三个用例。这三个用例覆盖了链路、对象树、应用层和存储任何一个不通过都不建议直接上线。把这几个用例做成脚本之后回归测试成本基本为零。4.3 从这套源码还能扩展什么源码扩展空间很大。如果要做以太网抄表可以加IEC 62056-47的TCP/IP映射让DLMS跑在socket上如果做远程费控可以扩展远程拉合闸通过Set请求控制继电器如果要支持分布式能源接入可以增加四象限计量和多费率配置对光伏逆变器和充电桩这类设备DLMS也提供了对应的数据模型扩展。这套源码最大的价值不是把协议栈写完了而是把数据模型和回调机制理顺了后续接什么业务都只是往注册表里加对象的事。做这套源码前后花了三个月真正写协议栈的时间不到一半大部分时间都耗在排查奇奇怪怪的通信问题上。现在回过头看最值钱的经验反而都来自那些坑HDLC转义漏一次、收发缓冲配小一次、RS485方向时序错一次以后就再也不会犯了。如果你正准备在STM32上做DLMS智能电表或者类似设备建议先把这套工程跑通再按自己的业务去改对象表。先学会站在标准里看问题后面再按自己的需求去裁剪这个过程比直接用现成方案更能帮你吃透协议。本文还有配套的精品资源点击获取