基于51单片机的智能电表设计(Keil C51实现)

一、系统概述

以STC89C52RC单片机(8位,11.0592MHz)为核心,结合电能计量芯片、LCD显示、EEPROM存储和RS485通信,实现智能电表的基本功能:电能计量(电压、电流、功率、累计电量)、数据显示、费率管理、数据存储与远程抄表。系统支持手动/自动抄表,具备掉电保护和低功耗特性,适用于家庭或小型商业用电监测。

二、硬件设计

2.1 核心组件选型

模块 型号/参数 功能说明
主控 STC89C52RC(8KB Flash,512B RAM) 数据处理、逻辑控制、外设驱动
电能计量 HLW8032(单相电能计量芯片) 采集电压、电流、有功功率,输出脉冲频率(与电能成正比)
显示 LCD1602(16×2字符型液晶) 显示电压、电流、功率、累计电量(格式:U=XXX.XV I=XX.XA P=XXXW E=XXX.XkWh)
存储 AT24C02(I2C EEPROM,2KB) 掉电保存累计电量、费率参数(尖/峰/平/谷)
通信 MAX485(RS485收发器) 远程抄表(Modbus RTU协议,波特率9600bps)
按键 4个轻触按键(设置/加/减/确认) 设置费率、清零电量、切换显示界面
电源 5V/1A DC电源(或220V AC-DC模块) 为系统供电(HLW8032需隔离电源)

2.2 硬件连接图

+-------------------+          +-------------------+          +-------------------+  
|    HLW8032        |          |    STC89C52RC      |          |    LCD1602         |  
|  VCC → 5V          |          | P1.0 → HLW8032 SEL |          | VCC → 5V           |  
|  GND → GND         |          | P1.1 → HLW8032 CLK |          | GND → GND          |  
|  CF → P3.2(INT0)  |◄---------| P1.2 → HLW8032 DO  |          | RS → P2.0          |  
|  UART_TX → P3.0(RX)|◄---------| P2.0~P2.7 → LCD DB0~DB7 |          | RW → P2.1          |  
+-------------------+          | P3.3 → AT24C02 SCL |          | E → P2.2           |  
                                 | P3.4 → AT24C02 SDA |          +-------------------+  
                                 | P3.5 → MAX485 RO   |          +-------------------+  
                                 | P3.6 → MAX485 DI   |          |    AT24C02         |  
                                 | P3.7 → MAX485 DE   |          |  SCL → P3.3         |  
+-------------------+          +-------------------+          |  SDA → P3.4         |  
|    MAX485         |                                       |  VCC → 5V           |  
|  A → RS485总线A    |                                       |  GND → GND          |  
|  B → RS485总线B    |                                       +-------------------+  
+-------------------+                                       +-------------------+  
                                                           |    按键(4个)    |  
                                                           |  SET → P3.0        |  
                                                           |  UP → P3.1          |  
                                                           |  DOWN → P3.2        |  
                                                           |  OK → P3.3          |  
                                                           +-------------------+

三、软件设计(Keil C51)

3.1 开发环境

  • IDE:Keil μVision5(C51编译器)

  • 编程语言:C51(兼容ANSI C,扩展51单片机特有语法)

  • 调试工具:STC-ISP下载器、串口助手(调试通信)

3.2 核心模块实现

3.2.1 电能计量芯片(HLW8032)驱动

HLW8032通过UART接口输出电能数据(脉冲频率与有功功率成正比),或通过寄存器读取电压、电流值。本设计采用脉冲计数法计量累计电量:

  • 原理:HLW8032的CF引脚输出脉冲(频率\(f=\frac{P×N}{3600}\),P为功率(W),\(N\)为脉冲常数,如3200imp/kWh),单片机通过外部中断0(INT0)计数脉冲,累计电量\(E=\frac{N}{N_{imp}}×1kWh\)\(N_{imp}\)为脉冲常数)。
#include <reg52.h>
#include <intrins.h>

// HLW8032引脚定义
sbit HLW_SEL = P1^0;  // 片选(低有效)
sbit HLW_CLK = P1^1;  // 时钟
sbit HLW_DO = P1^2;   // 数据输出
sbit HLW_CF = P3^2;   // 脉冲输出(INT0)

// 全局变量
unsigned long pulse_count = 0;  // 脉冲计数(累计电量)
unsigned int voltage = 0;       // 电压值(mV)
unsigned int current = 0;       // 电流值(mA)

// 外部中断0初始化(脉冲计数)
void INT0_Init() {
    IT0 = 1;  // 下降沿触发
    EX0 = 1;  // 允许INT0中断
    EA = 1;   // 开总中断
}

// INT0中断服务函数(脉冲计数)
void INT0_ISR() interrupt 0 {
    pulse_count++;  // 每来一个脉冲,计数+1
}

// 读取HLW8032电压/电流(串行接口)
void HLW8032_Read() {
    unsigned char i, dat[4];
    HLW_SEL = 0;  // 选中芯片
    for (i=0; i<4; i++) {  // 读取4字节数据(示例)
        dat[i] = 0;
        for (char j=0; j<8; j++) {
            HLW_CLK = 0; _nop_(); HLW_CLK = 1; _nop_();
            dat[i] = (dat[i] << 1) | HLW_DO;
        }
    }
    HLW_SEL = 1;  // 取消选中
    voltage = (dat[0] << 8) | dat[1];  // 电压(mV)
    current = (dat[2] << 8) | dat[3];  // 电流(mA)
}

3.2.2 LCD1602显示驱动

// LCD引脚定义
sbit LCD_RS = P2^0;
sbit LCD_RW = P2^1;
sbit LCD_E  = P2^2;
#define LCD_DB P0  // 数据总线

// LCD延时(1ms@11.0592MHz)
void LCD_Delay(unsigned int t) {
    unsigned int i, j;
    for (i=t; i>0; i--)
        for (j=110; j>0; j--);
}

// LCD写命令
void LCD_WriteCmd(unsigned char cmd) {
    LCD_RS = 0; LCD_RW = 0;
    LCD_DB = cmd; LCD_Delay(1);
    LCD_E = 1; LCD_Delay(1); LCD_E = 0;
}

// LCD写数据
void LCD_WriteData(unsigned char dat) {
    LCD_RS = 1; LCD_RW = 0;
    LCD_DB = dat; LCD_Delay(1);
    LCD_E = 1; LCD_Delay(1); LCD_E = 0;
}

// LCD初始化
void LCD_Init() {
    LCD_WriteCmd(0x38);  // 8位数据,2行显示
    LCD_WriteCmd(0x0C);  // 开显示,关光标
    LCD_WriteCmd(0x06);  // 光标右移
    LCD_WriteCmd(0x01);  // 清屏
}

// 显示电能数据
void Display_Data() {
    char buf[16];
    // 第一行:电压、电流
    LCD_WriteCmd(0x80);
    sprintf(buf, "U=%3.1fV I=%2.1fA", voltage/1000.0, current/1000.0);
    for (char i=0; buf[i]; i++) LCD_WriteData(buf[i]);
    // 第二行:功率、累计电量
    LCD_WriteCmd(0xC0);
    unsigned int power = (voltage * current) / 1000;  // 功率=U*I/1000(W)
    float energy = pulse_count * 0.001;  // 假设3200imp/kWh,0.001kWh/脉冲
    sprintf(buf, "P=%3dW E=%4.1fkWh", power, energy);
    for (char i=0; buf[i]; i++) LCD_WriteData(buf[i]);
}

3.2.3 AT24C02 EEPROM存储(I2C驱动)

// I2C引脚定义
sbit SCL = P3^3;
sbit SDA = P3^4;

// I2C延时
void I2C_Delay() { _nop_(); _nop_(); }

// I2C起始信号
void I2C_Start() {
    SDA = 1; I2C_Delay(); SCL = 1; I2C_Delay();
    SDA = 0; I2C_Delay(); SCL = 0;
}

// I2C停止信号
void I2C_Stop() {
    SDA = 0; I2C_Delay(); SCL = 1; I2C_Delay();
    SDA = 1; I2C_Delay();
}

// I2C写字节
void I2C_WriteByte(unsigned char dat) {
    for (char i=0; i<8; i++) {
        SDA = (dat & 0x80) >> 7; dat <<= 1;
        SCL = 1; I2C_Delay(); SCL = 0;
    }
    SDA = 1; I2C_Delay(); SCL = 1; I2C_Delay(); SCL = 0;  // 等待ACK
}

// I2C读字节
unsigned char I2C_ReadByte() {
    unsigned char dat = 0;
    SDA = 1;
    for (char i=0; i<8; i++) {
        SCL = 1; I2C_Delay();
        dat = (dat << 1) | SDA;
        SCL = 0; I2C_Delay();
    }
    return dat;
}

// AT24C02写数据
void AT24C02_Write(unsigned char addr, unsigned char dat) {
    I2C_Start();
    I2C_WriteByte(0xA0);  // 器件地址+写
    I2C_WriteByte(addr);  // 存储地址
    I2C_WriteByte(dat);   // 数据
    I2C_Stop();
    LCD_Delay(10);  // 等待写入完成
}

// AT24C02读数据
unsigned char AT24C02_Read(unsigned char addr) {
    unsigned char dat;
    I2C_Start();
    I2C_WriteByte(0xA0);  // 写地址
    I2C_WriteByte(addr);
    I2C_Start();
    I2C_WriteByte(0xA1);  // 读地址
    dat = I2C_ReadByte();
    I2C_Stop();
    return dat;
}

3.2.4 主程序流程

void main() {
    LCD_Init();       // LCD初始化
    INT0_Init();      // 脉冲计数中断初始化
    TMOD = 0x01;      // 定时器0模式1(1ms定时,用于按键扫描)
    TH0 = 0xFC; TL0 = 0x66;
    ET0 = 1; EA = 1; TR0 = 1;

    // 从EEPROM读取累计电量
    pulse_count = AT24C02_Read(0x00) << 8 | AT24C02_Read(0x01);

    while (1) {
        HLW8032_Read();  // 读取电压、电流
        Display_Data();  // 显示数据
        // 每10秒保存一次累计电量到EEPROM
        static unsigned int save_timer = 0;
        if (++save_timer >= 10000) {  // 10秒(1ms定时)
            save_timer = 0;
            AT24C02_Write(0x00, (pulse_count >> 8) & 0xFF);
            AT24C02_Write(0x01, pulse_count & 0xFF);
        }
    }
}

// 定时器0中断(1ms,按键扫描)
void T0_ISR() interrupt 1 {
    TH0 = 0xFC; TL0 = 0x66;  // 重装初值
    // 按键扫描逻辑(略,可扩展设置费率、清零等)
}

参考代码 智能电表,利用51单片机作为控制器和keil C51编程 www.youwenfan.com/contentcnt/182698.html

四、关键问题与解决方案

4.1 电能计量精度

  • 问题:HLW8032脉冲计数受干扰导致累计电量误差。

  • 解决

  • 硬件:CF引脚加10kΩ上拉电阻,并联0.1μF滤波电容;

  • 软件:脉冲计数采用软件滤波(连续3次检测到下降沿才计数)。

4.2 低功耗设计

  • 问题:51单片机空闲时功耗较高(约2mA)。

  • 解决

  • 空闲时进入掉电模式PCON |= 0x02),通过外部中断(按键或脉冲)唤醒;

  • 关闭未用外设(如LCD背光、MAX485电源)。

4.3 通信干扰

  • 问题:RS485总线长距离传输易受干扰。

  • 解决

  • 总线两端加120Ω终端电阻;

  • 通信数据加CRC校验(Modbus RTU协议自带CRC16)。

五、测试与验证

  1. 功能测试:接入220V/5A负载,观察LCD显示电压(220V)、电流(5A)、功率(1100W),累计电量随负载运行时间线性增加。

  2. 精度测试:对比商用电流表,误差<±2%(HLW8032自身精度±1%)。

  3. 掉电测试:断电后重新上电,累计电量从EEPROM正确恢复。

六、总结

基于51单片机和Keil C51实现了智能电表的基本功能,核心是电能计量芯片驱动、数据显示与数据存储。通过模块化设计,可扩展多费率计费、远程抄表和预付费功能,满足家庭或小型商业用电监测需求。

posted @ 2026-04-10 10:50  w199899899  阅读(50)  评论(0)    收藏  举报