带红外抄板和LCD显示的单相电能表设计

一、系统概述

基于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通信),满足电力计量与远程抄表需求。

相关推荐
wggmrlee3 小时前
GD32 vs STM32
单片机·嵌入式硬件
czhaii3 小时前
STM32 F103 Altium一键下载PCB图
stm32·单片机·嵌入式硬件
雾削木3 小时前
基于STM32F411RET6 + 双路MB85RS2MT的铁电U盘
stm32·单片机·嵌入式硬件
笨笨饿3 小时前
33_顺序表(待完善)
linux·服务器·c语言·嵌入式硬件·算法·学习方法
点灯小铭4 小时前
基于单片机的多路温湿度采集与WIFI智能报警控制系统设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
嵌入式×边缘AI:打怪升级日志4 小时前
MX6ULL 的 GPIO 操作方法(保姆级教程)
stm32·单片机·嵌入式硬件
点灯小铭4 小时前
基于单片机的球类比赛专用计分与暂停管理系统设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
自小吃多5 小时前
TMC220X芯片 串口工具连接交互
笔记·嵌入式硬件
笨笨饿6 小时前
34_数据结构_栈
c语言·开发语言·数据结构·人工智能·嵌入式硬件·算法