基于 HLW8012 的电能计量演示板程序

基于 HLW8012 的电能计量演示板程序

基于 UART 通信方式 的 HLW8012 电能计量演示板程序。此方案适用于 STM32、ESP32 等主流微控制器,代码简洁高效,可直接集成到您的工程中。

一、 硬件连接说明

HLW8012 芯片通过 UART 接口与主控制器通信,无需复杂的外部中断脉冲捕获,仅需连接 TX、RX 引脚即可。

HLW8012 引脚 主控制器引脚 说明
VDD 3.3V / 5V 电源正极
RX MCU TX 接收来自 MCU 的指令
TX MCU RX 向 MCU 发送计量数据
SEL GND 选择电流通道(通常接地)
CF / CF1 悬空 UART 模式下无需连接

二、 HLW8012 通信协议格式

HLW8012 默认波特率为 9600 bps,8 位数据位,无校验位,1 位停止位
芯片会主动上报数据,或根据指令返回数据。典型的数据包格式如下:

字节位置 名称 内容 (十六进制) 说明
Byte 0 帧头 55 固定帧头
Byte 1 长度 09 数据长度
Byte 2 命令字 01 / 02 / … 01: 读取电能数据,02: 读取电压电流等
Byte 3 ~ N 数据域 可变 具体的计量参数
Byte N+1 校验和 可变 从长度字节到数据域最后一个字节的累加和

(注:不同厂家的 HLW8012 模块固件指令可能微调,请以实际模块手册为准,以下代码以读取标准电能参数为例)


三、 演示板程序代码 (基于 C 语言)

1. HLW8012 驱动头文件 (hlw8012.h)

#ifndef __HLW8012_H__
#define __HLW8012_H__

#include "stm32f1xx_hal.h" // 根据实际MCU修改头文件

// 定义串口句柄 (以STM32 HAL库为例)
extern UART_HandleTypeDef huart2; 
#define HLW8012_UART huart2

// HLW8012 数据接收缓冲区大小
#define HLW8012_RX_BUFFER_SIZE 64

// HLW8012 电能数据结构体
typedef struct {
    float voltage;       // 电压 (V)
    float current;       // 电流 (A)
    float active_power;  // 有功功率 (W)
    float energy;        // 累计电量 (kWh)
    uint8_t update_flag; // 数据更新标志
} HLW8012_Data_t;

// 函数声明
void HLW8012_Init(void);
void HLW8012_UART_IRQHandler(void);
void HLW8012_ReadData(HLW8012_Data_t *data);
uint8_t HLW8012_CalculateChecksum(uint8_t *data, uint8_t len);

#endif

2. HLW8012 驱动源文件 (hlw8012.c)

#include "hlw8012.h"
#include <string.h>

HLW8012_Data_t hlw_data;
static uint8_t rx_buffer[HLW8012_RX_BUFFER_SIZE];
static uint8_t rx_index = 0;

/**
 * @brief HLW8012 初始化
 */
void HLW8012_Init(void) {
    memset(rx_buffer, 0, HLW8012_RX_BUFFER_SIZE);
    rx_index = 0;
    
    // 启动UART中断接收
    HAL_UART_Receive_IT(&HLW8012_UART, &rx_buffer[0], 1);
}

/**
 * @brief 计算校验和 (从Byte1累加到Byte N)
 * @param data 数据指针
 * @param len 长度
 * @return 校验和
 */
uint8_t HLW8012_CalculateChecksum(uint8_t *data, uint8_t len) {
    uint32_t sum = 0;
    for(uint8_t i = 0; i < len; i++) {
        sum += data[i];
    }
    return (uint8_t)(sum & 0xFF);
}

/**
 * @brief 解析HLW8012返回的数据包
 * @param data 指向接收缓冲区的指针
 */
static void HLW8012_ParseData(uint8_t *data) {
    // 校验帧头
    if(data[0] != 0x55) return;
    
    uint8_t len = data[1];
    uint8_t *payload = &data[2];
    
    // 校验和检查
    uint8_t checksum = HLW8012_CalculateChecksum(&data[1], len);
    if(checksum != data[1 + len]) {
        return; // 校验失败,丢弃数据
    }

    // 根据命令字解析数据 (示例:假设返回的是电压、电流、功率、电量)
    // 实际解析需根据模块的具体协议调整偏移量
    if(payload[0] == 0x01) { 
        // 假设数据格式为: [Cmd] [V_H] [V_L] [I_H] [I_L] [P_H] [P_L] [E_H] [E_L]
        uint16_t voltage_raw = (payload[1] << 8) | payload[2];
        uint16_t current_raw = (payload[3] << 8) | payload[4];
        uint16_t power_raw   = (payload[5] << 8) | payload[6];
        uint16_t energy_raw  = (payload[7] << 8) | payload[8];
        
        // 转换为实际物理单位 (系数需根据硬件分压/分流电阻校准)
        hlw_data.voltage = voltage_raw * 0.1f;      // 例如: 0.1V/LSB
        hlw_data.current = current_raw * 0.01f;     // 例如: 0.01A/LSB
        hlw_data.active_power = power_raw * 0.1f;   // 例如: 0.1W/LSB
        hlw_data.energy = energy_raw * 0.001f;      // 例如: 0.001kWh/LSB
        hlw_data.update_flag = 1;
    }
}

/**
 * @brief UART接收中断回调函数
 */
void HLW8012_UART_IRQHandler(void) {
    static uint8_t rx_byte;
    
    // 读取接收到的字节
    if(HAL_UART_GetState(&HLW8012_UART) == HAL_UART_STATE_READY) {
        rx_byte = rx_buffer[rx_index - 1];
        
        // 简单的帧同步: 如果收到0x55且当前不在收包中,则认为是新的一帧开始
        if(rx_byte == 0x55 && rx_index == 0) {
            rx_buffer[0] = rx_byte;
            rx_index = 1;
        } 
        else if(rx_index > 0) {
            rx_buffer[rx_index++] = rx_byte;
            
            // 防止缓冲区溢出
            if(rx_index >= HLW8012_RX_BUFFER_SIZE) {
                rx_index = 0; 
            }
            
            // 如果接收到足够的数据 (帧头 + 长度 + 数据 + 校验和)
            // 这里简单判断,实际应用应根据长度字节动态判断
            if(rx_index >= 10) { 
                HLW8012_ParseData(rx_buffer);
                rx_index = 0; // 重置接收索引
            }
        }
        
        // 重新启动接收中断
        HAL_UART_Receive_IT(&HLW8012_UART, &rx_buffer[rx_index], 1);
    }
}

/**
 * @brief 读取最新的电能数据
 */
void HLW8012_ReadData(HLW8012_Data_t *data) {
    if(hlw_data.update_flag) {
        memcpy(data, &hlw_data, sizeof(HLW8012_Data_t));
        hlw_data.update_flag = 0;
    }
}

3. 主程序演示 (main.c)

#include "main.h"
#include "hlw8012.h"
#include <stdio.h>

// 重定向printf到串口
int _write(int fd, char *ptr, int len) {
    HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, HAL_MAX_DELAY);
    return len;
}

int main(void) {
    HLW8012_Data_t current_data;
    
    // HAL库初始化
    HAL_Init();
    SystemClock_Config();
    
    // 初始化GPIO和UART
    MX_GPIO_Init();
    MX_USART1_UART_Init();  // 用于printf打印
    MX_USART2_UART_Init();  // 用于HLW8012通信
    
    // 初始化HLW8012
    HLW8012_Init();
    printf("HLW8012 Electric Energy Meter Demo Start!\r\n");
    
    while (1) {
        // 读取电能数据
        HLW8012_ReadData(&current_data);
        
        if(current_data.update_flag) {
            printf("====== HLW8012 Data ======\r\n");
            printf("Voltage: %.2f V\r\n", current_data.voltage);
            printf("Current: %.3f A\r\n", current_data.current);
            printf("Power:   %.2f W\r\n", current_data.active_power);
            printf("Energy:  %.3f kWh\r\n", current_data.energy);
            printf("=========================\r\n\r\n");
        }
        
        // 延时1秒
        HAL_Delay(1000);
    }
}

// UART接收中断回调函数注册
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
    if(huart->Instance == USART2) {
        HLW8012_UART_IRQHandler();
    }
}

参考代码 基于HLW8012的电能计量演示板程序 www.youwenfan.com/contentcsu/60377.html

四、 关键注意事项与校准

  1. 系数校准:代码中的 0.1f0.01f 等转换系数是示例值。实际应用中,必须根据您的演示板原理图中的分压电阻(电压采样)锰铜电阻(电流采样)的阻值进行校准。通常厂家会提供校准公式或上位机工具。
  2. 校验和算法:部分 HLW8012 模块的校验和可能是“累加和后取反加1”(补码),具体请查阅您手中模块的数据手册。
  3. 中断优先级:如果使用 STM32,请确保 UART 接收中断的优先级高于主循环中的任务,以免丢失数据。

专注于matlab/simulink,电子电路,编程