一、系统概述
基于STM32F103C8T6微控制器,集成专用电能计量芯片、LCD显示模块与红外抄表模块,实现单相电能高精度计量、实时数据显示、红外远程抄表功能,符合DL/T 645-2007《多功能电能表通信协议》 标准。系统支持正向/反向电能累计、电压/电流/功率实时监测、历史数据存储,适用于家庭、商铺、工业用电计量场景,具备高精度(±0.5级)、低功耗、易抄表特点。
二、系统架构与硬件设计
1. 系统架构
电压/电流采样
UART/ SPI
I2C
GPIO/红外调制
I2C
供电
供电
供电
供电
供电
市电 220V/50Hz
电能计量芯片 HLW8032
STM32F103C8T6主控
LCD显示 SSD1306 OLED
红外收发模块 HS0038+发射管
EEPROM AT24C02, 存储电能数据
电源模块 220V→3.3V
2. 关键硬件组件
| 模块 | 型号/参数 | 功能 |
|---|---|---|
| 主控 | STM32F103C8T6(72MHz,64KB Flash,20KB RAM) | 计量数据处理、LCD控制、红外协议解析 |
| 计量芯片 | HLW8032(UART接口,±0.5级精度) | 电压/电流/功率采样,电能累计(kWh) |
| 显示 | SSD1306 OLED(128×64,I2C) | 显示电压、电流、功率、电能、时间 |
| 红外模块 | HS0038(接收头)+ 940nm发射管 | 红外抄表(DL/T 645协议,38kHz载波) |
| 存储 | AT24C02(I2C,2KB EEPROM) | 存储累计电能、用户参数(防掉电) |
| 电源 | 220V→5V(阻容降压)+ AMS1117-3.3V | 系统供电(低功耗设计,待机<10mA) |
3. 核心电路设计
(1)电能计量电路(HLW8032)
-
HLW8032连接 :UART_TX接STM32的PA10 (USART1_RX),UART_RX接PA9(USART1_TX),VCC=3.3V,GND接地;
-
采样原理:通过内部ADC采样电压(220V经电阻分压)和电流(锰铜片采样),计算有功功率 P=UIcosϕ,累计电能 E=∫Pdt。
(2)红外通信电路
-
接收头HS0038 :OUT接STM32的PA0 (外部中断0,下降沿触发),VCC=3.3V,GND接地,需加38kHz带通滤波;
-
发射管 :通过STM32的PA1(PWM输出,38kHz载波)驱动,基极串1kΩ电阻,集电极接5V电源。
(3)LCD显示电路
- SSD1306 OLED :SCL接PB6 (I2C1_SCL),SDA接PB7(I2C1_SDA),VCC=3.3V,GND接地,通过I2C协议通信。
三、软件设计(C语言实现)
1. 开发环境
-
IDE:STM32CubeIDE(V1.13.0)
-
库:STM32 HAL库(V1.11.0)、HLW8032驱动(UART)、SSD1306驱动(I2C)、DL/T 645协议栈(自定义)
-
协议:DL/T 645-2007(红外抄表帧格式:起始符0x68+地址域+控制码+数据域+校验码+结束符0x16)
2. 主程序流程
c
#include "stm32f1xx_hal.h"
#include "hlw8032.h"
#include "ssd1306.h"
#include "infrared.h"
#include "eeprom.h"
#include "dl645.h"
// 系统状态
typedef struct {
float voltage; // 电压(V)
float current; // 电流(A)
float power; // 功率(W)
float energy; // 累计电能(kWh)
uint8_t lcd_update; // LCD更新标志
} SystemState;
int main(void) {
// 1. 初始化硬件
HAL_Init();
SystemClock_Config(); // 72MHz主频
HLW8032_Init(&huart1); // 计量芯片初始化(UART1,4800bps)
SSD1306_Init(); // OLED初始化(I2C1)
Infrared_Init(); // 红外模块初始化(PA0外部中断,PA1 PWM)
EEPROM_Init(); // AT24C02初始化(I2C1)
DL645_Init(); // DL/T 645协议初始化
SystemState sys_state = {0};
sys_state.energy = EEPROM_ReadEnergy(); // 从EEPROM读取累计电能
// 2. 主循环
while (1) {
// 2.1 读取计量数据(HLW8032通过UART主动上报)
if (HLW8032_DataReady()) {
HLW8032_ReadData(&sys_state.voltage, &sys_state.current, &sys_state.power);
sys_state.energy += (sys_state.power * 0.001f); // 1秒累计0.001kWh(示例)
EEPROM_WriteEnergy(sys_state.energy); // 存储累计电能(防掉电)
sys_state.lcd_update = 1; // 标记LCD需更新
}
// 2.2 处理红外抄表请求(DL/T 645协议)
if (Infrared_ReceivedFrame()) {
DL645_Frame frame = Infrared_GetFrame();
if (DL645_Verify(&frame)) { // 校验帧合法性
DL645_ProcessFrame(&frame, &sys_state); // 处理读/写请求
Infrared_SendResponse(&frame); // 发送响应帧
}
}
// 2.3 更新LCD显示
if (sys_state.lcd_update) {
SSD1306_Clear();
SSD1306_ShowString(0, 0, "U:%.1fV I:%.2fA", sys_state.voltage, sys_state.current);
SSD1306_ShowString(0, 2, "P:%.1fW E:%.2fkWh", sys_state.power, sys_state.energy);
SSD1306_Update();
sys_state.lcd_update = 0;
}
// 2.4 低功耗管理(空闲时进入Stop模式)
if (!sys_state.lcd_update && !Infrared_Busy()) {
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
SystemClock_Config(); // 唤醒后恢复时钟
}
}
}
3. 关键模块实现
(1)电能计量驱动(HLW8032,C语言)
HLW8032通过UART主动上报数据帧(格式:0x55+电压高8+电压低8+电流高8+电流低8+功率高8+功率低8+校验和),STM32解析后转换为实际值:
c
// HLW8032数据帧结构
typedef struct {
uint8_t header; // 0x55
uint8_t vol_h; // 电压高8位(整数部分)
uint8_t vol_l; // 电压低8位(小数部分,0.1V/bit)
uint8_t cur_h; // 电流高8位
uint8_t cur_l; // 电流低8位(0.01A/bit)
uint8_t pow_h; // 功率高8位
uint8_t pow_l; // 功率低8位(0.1W/bit)
uint8_t checksum; // 校验和(前7字节异或)
} HLW8032_Frame;
// 读取HLW8032数据
void HLW8032_ReadData(float* voltage, float* current, float* power) {
uint8_t rx_buf[8];
if (HAL_UART_Receive(&huart1, rx_buf, 8, 100) == HAL_OK) {
// 校验和验证
uint8_t sum = 0;
for (int i=0; i<7; i++) sum ^= rx_buf[i];
if (sum == rx_buf[7]) {
// 解析数据(示例:电压= (vol_h<<8 | vol_l) * 0.1V)
*voltage = (float)((rx_buf[1] << 8) | rx_buf[2]) * 0.1f;
*current = (float)((rx_buf[3] << 8) | rx_buf[4]) * 0.01f;
*power = (float)((rx_buf[5] << 8) | rx_buf[6]) * 0.1f;
}
}
}
(2)DL/T 645协议解析(红外抄表,C语言)
DL/T 645帧格式(读电能请求示例):0x68 AA AA AA AA AA AA 68 01 02 00 00 00 00 00 00 16
- 解析逻辑:提取地址域、控制码(0x01=读数据)、数据域(0x0000=当前电能),返回响应帧(含电能值,BCD码格式)。
c
// DL/T 645帧结构
typedef struct {
uint8_t start; // 0x68
uint8_t addr[6]; // 地址域(6字节BCD码)
uint8_t start2; // 0x68
uint8_t ctrl; // 控制码(0x01=读,0x04=写)
uint8_t data_len; // 数据域长度
uint8_t data[16]; // 数据域
uint8_t cs; // 校验和(地址域+控制码+数据域异或)
uint8_t end; // 0x16
} DL645_Frame;
// 处理读电能请求
void DL645_ProcessReadEnergy(DL645_Frame* req, SystemState* state, DL645_Frame* resp) {
// 1. 构造响应帧(地址域+控制码+数据域)
resp->start = 0x68;
memcpy(resp->addr, req->addr, 6); // 原样返回地址域
resp->start2 = 0x68;
resp->ctrl = req->ctrl | 0x80; // 响应控制码(原控制码最高位置1)
resp->data_len = 4; // 电能值4字节(BCD码,0.01kWh/位)
// 2. 电能值转为BCD码(示例:123.45kWh→0x12 0x34 0x50 0x00,小数点后2位)
uint32_t energy_int = (uint32_t)(state->energy * 100);
resp->data[0] = (energy_int / 1000000) % 10; // 十万位
resp->data[1] = (energy_int / 100000) % 10; // 万位
resp->data[2] = (energy_int / 10000) % 10; // 千位
resp->data[3] = (energy_int / 1000) % 10; // 百位
resp->data[4] = (energy_int / 100) % 10; // 十位
resp->data[5] = (energy_int / 10) % 10; // 个位
resp->data[6] = energy_int % 10; // 小数位1
resp->data[7] = 0; // 小数位2(补0)
// 3. 计算校验和
resp->cs = 0;
uint8_t* p = (uint8_t*)resp;
for (int i=1; i<(4+resp->data_len+1); i++) resp->cs += p[i]; // 地址域+控制码+数据域
resp->end = 0x16;
}
(3)红外通信(38kHz载波调制,C语言)
通过STM32的PWM输出38kHz载波,调制DL/T 645帧数据(0=载波关,1=载波开):
c
// 红外发送函数(调制38kHz载波)
void Infrared_SendFrame(DL645_Frame* frame) {
// 1. 帧数据转为二进制流(含起始/结束符)
uint8_t bit_stream[256];
uint16_t bit_len = 0;
AddBitStream(0x55); // 引导码(简化,实际用38kHz载波开/关表示0/1)
// 2. PWM输出38kHz载波(载波开=1,关=0)
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // TIM2_CH1(PA1,38kHz)
for (int i=0; i<bit_len; i++) {
if (bit_stream[i]) {
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 13); // 50%占空比(38kHz/2=19μs高/19μs低)
} else {
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 0); // 载波关
}
HAL_Delay(1); // 每位1ms(示例,实际需按DL/T 645时序)
}
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);
}
参考代码 带红外抄板和LCD显示的单相电能表设计,附原理图源码设计说明 www.youwenfan.com/contentcst/160985.html
四、系统测试与优化
1. 测试指标
| 参数 | 指标 | 测试方法 |
|---|---|---|
| 电能计量精度 | ±0.5%(1-100A负载) | 用标准功率源输入,对比标准表读数 |
| 红外抄表距离 | ≥5米(无障碍) | 用红外抄表器发送请求,统计响应成功率 |
| LCD显示刷新率 | 1Hz(1秒/次) | 示波器测量数据更新间隔 |
| 待机功耗 | <10mA(3.3V) | 万用表串联测量电源电流 |
2. 优化方向
-
低功耗 :STM32空闲时进入Stop模式,HLW8032和LCD仅在数据更新时唤醒;
-
抗干扰 :红外通信增加重传机制 (3次重发),DL/T 645帧加CRC校验;
-
数据存储 :用FRAM(铁电存储器) 替代EEPROM,提高擦写次数(100亿次 vs 10万次);
-
多费率 :扩展时钟模块(DS3231),实现峰谷电价计量(需修改DL/T 645数据域)。
五、总结
本设计基于STM32实现了带红外抄板和LCD显示的单相电能表,通过HLW8032计量芯片保证精度,DL/T 645协议实现标准化抄表,SSD1306 OLED实时显示数据,具备高精度、低功耗、易维护特点。系统可扩展为三相电能表或智能电表(增加GPRS/4G通信),满足电力计量与远程抄表需求。