三相智能电能质量分析仪设计
项目开发背景
随着现代电力系统的复杂化和对能源效率要求的不断提高,电能质量问题日益凸显。电压暂升、暂降、谐波污染以及短时中断等事件不仅影响电气设备的正常运行,还可能导致生产中断、设备损坏和能源浪费,因此对电能质量进行实时监测和分析已成为保障电力系统稳定运行和优化能源管理的关键环节。
传统的电能质量分析仪往往功能受限,存在采样率低、计算能力不足或缺乏友好用户界面等问题。许多设备还不支持高效的数据存储和远程通信功能,难以满足工业4.0和智能电网背景下对数据集成与实时监控的迫切需求。市场亟需一款高性能、多功能且易于集成的智能电能质量分析仪,以提升监测精度和操作便捷性。
针对这些需求,本项目设计了三相智能电能质量分析仪,旨在通过先进硬件和软件集成解决现有挑战。该设备采用高性能STM32F407IGT6单片机进行快速浮点运算和FFT分析,确保精确计算有功功率、无功功率、功率因数、频率及谐波含量等参数;结合同步采样ADC和隔离互感器实现高精度信号采集,并通过LCD触摸屏实时显示波形、参数表格及矢量图。此外,设备集成SD卡存储和RS485通信接口,支持Modbus协议,便于分钟级数据记录和接入工业监控系统,实现全面的电能质量管理。
该分析仪可广泛应用于工业生产线、电力变电站、商业建筑和新能源发电场等场景,帮助用户及时发现和处理电能质量问题,提升系统可靠性并降低运维成本。其模块化设计和标准接口也使其易于定制和扩展,为未来智能电网的深化发展提供可靠的技术支持。
设计实现的功能
(1) 同步采集三相电压、电流信号,计算有功/无功功率、功率因数、频率及谐波含量(THD)。
(2) 实时判断并记录电压暂升/暂降、短时中断等电能质量事件。
(3) 通过LCD屏显示实时波形、参数表格及矢量图。
(4) 将分钟级数据存入SD卡,并支持通过USB接口导出历史数据文件。
(5) 提供RS485通信接口,支持Modbus协议,可接入工业监控系统。
项目硬件模块组成
(1)主控与计算模块:采用STM32F407IGT6单片机,利用其高性能内核和FPU进行浮点及FFT运算。
(2)信号调理与采集模块:采用三相电压/电流互感器(ZMPT101B、ZMCT103C)进行隔离采样,配合AD7606 16位8通道同步采样ADC芯片。
(3)存储模块:采用SD卡模块(SPI接口)进行数据存储。
(4)显示模块:采用5.0寸TFT LCD电容触摸屏(型号:NT35510)。
(5)接口与电源模块:包括SP3485 RS485芯片、CH340 USB转串口芯片以及多路输出开关电源模块(提供±12V, 5V, 3.3V)。
设计意义
三相智能电能质量分析仪的设计具有重要的实际应用价值。在工业电力系统中,电能质量的稳定直接关系到设备的安全运行和生产效率,该分析仪通过同步采集三相电压和电流信号,并实时计算有功功率、无功功率、功率因数、频率及谐波含量,能够有效监测电网状态,帮助用户及时发现潜在问题,从而预防因电能质量问题导致的设备故障或生产中断,提升整体系统的可靠性。
设计采用STM32F407IGT6单片机的高性能内核和浮点运算单元,结合AD7606同步采样ADC芯片,实现了高效的FFT运算和精确的数据采集,这确保了谐波分析和电能质量事件检测的实时性与准确性,为电力系统的精细化管理和优化提供了技术支撑。同时,通过电压暂升、暂降和短时中断等事件的实时判断与记录,该仪器能够辅助故障诊断,减少排查时间,降低维护成本。
数据存储与导出功能的实现,借助SD卡模块和USB接口,支持分钟级数据的长期保存和便捷导出,这使得历史数据的回溯与分析成为可能,有助于用户进行趋势研究和制定改进措施,为电力质量评估和能效管理提供可靠依据。此外,LCD触摸屏显示实时波形、参数表格及矢量图,提供了直观的人机交互界面,简化了现场操作,增强了仪器的实用性和易用性。
RS485通信接口与Modbus协议的支持,使得该分析仪能够无缝接入工业监控系统,实现远程数据传输和集中监控,促进了自动化管理水平的提升,满足了现代智能电网和工业物联网的需求。信号调理模块采用互感器进行隔离采样,确保了高压环境下的操作安全,增强了仪器的耐用性和适应性,为在各种复杂工况下的稳定运行奠定了基础。
设计思路
整个设计思路以高性能微控制器STM32F407IGT6为核心,充分利用其ARM Cortex-M4内核和浮点运算单元,实现高效的三相电压电流信号同步采集与实时计算。系统通过三相电压互感器ZMPT101B和电流互感器ZMCT103C进行隔离采样,确保电气安全并减少噪声干扰,再配合AD7606 16位8通道同步采样ADC芯片,实现高精度数据转换,为后续分析提供可靠的原始信号。
在数据处理方面,主控模块执行快速傅里叶变换运算,分析谐波含量并计算有功功率、无功功率、功率因数及频率。通过实时算法监测电压暂升、暂降和短时中断等电能质量事件,一旦检测到异常便立即记录时间戳和相关参数,确保事件分析的准确性和及时性。
显示功能通过5.0寸TFT LCD电容触摸屏实现,驱动芯片NT35510支持图形渲染,可实时展示三相电压电流波形、参数表格及矢量图。用户可以通过触摸屏交互切换显示模式,直观查看电能质量数据,提升操作便捷性。
数据存储与导出依赖于SD卡模块,采用SPI接口定期存储分钟级计算数据,形成历史记录文件。同时,CH340 USB转串口芯片支持通过USB接口快速导出数据文件,便于离线分析。RS485通信接口基于SP3485芯片,集成Modbus协议,使得设备能够轻松接入工业监控系统,实现远程数据读取和控制。
电源模块提供多路输出,包括±12V、5V和3.3V,为各个硬件组件稳定供电,确保系统长期可靠运行。整体设计注重硬件模块的协同与集成,以实际需求为导向,实现从信号采集到数据显示、存储和通信的全流程功能。
框架图
+---------------------------------------+
| 三相电压/电流输入 |
| (三相系统信号源) |
+------------------+--------------------+
|
v
+---------------------------------------+
| 信号调理与采集模块 |
| - 电压互感器 ZMPT101B |
| - 电流互感器 ZMCT103C |
| - ADC芯片 AD7606 (16位8通道同步采样)|
+------------------+--------------------+
|
v (数字信号)
+---------------------------------------+
| 主控与计算模块 |
| STM32F407IGT6单片机 |
| (浮点运算、FFT、电能质量分析) |
+------------------+--------------------+
|
+-----------------------------------+-----------------------------------+
| | |
v v v
+------------------+ +------------------+ +------------------+
| 显示模块 | | 存储模块 | | 接口模块 |
| TFT LCD | | SD卡模块 | | - RS485 (SP3485)|
| (NT35510) | | (SPI接口) | | - USB (CH340) |
+------------------+ +------------------+ +------------------+
| | |
v v v
+------------------+ +------------------+ +------------------+
| 实时显示 | | 数据存储 | | 通信输出 |
| (波形、表格、 | | (分钟级数据) | | (Modbus协议) |
| 矢量图) | | | | |
+------------------+ +------------------+ +------------------+
+---------------------------------------+
| 电源模块 |
| 多路输出开关电源模块 |
| (±12V, 5V, 3.3V输出) |
+---------------------------------------+
|
v
为所有模块提供稳定电源
系统总体设计
系统总体设计基于三相智能电能质量分析仪的功能需求和硬件模块组成,旨在实现对三相电力系统的实时监测与综合分析。该系统以STM32F407IGT6单片机为核心主控与计算模块,利用其高性能ARM Cortex-M4内核和浮点单元(FPU)执行高速数据处理,包括有功功率、无功功率、功率因数、频率的计算,以及通过快速傅里叶变换(FFT)进行谐波含量(THD)分析。同时,该模块实时判断电压暂升、暂降和短时中断等电能质量事件,并触发记录机制,确保数据的准确性和及时性。
信号调理与采集模块负责三相电压和电流信号的同步采集,采用ZMPT101B电压互感器和ZMCT103C电流互感器进行隔离采样,以保障系统安全并减少干扰。采集到的模拟信号由AD7606 16位8通道同步采样ADC芯片转换为数字信号,实现高精度同步采样,为后续计算提供可靠数据基础。处理后的数据通过内部总线传输至主控模块进行计算和分析,形成实时参数和事件记录。
显示模块采用5.0寸TFT LCD电容触摸屏(型号NT35510),用于直观展示实时波形、参数表格和矢量图,用户可通过触摸屏交互查看电能质量状态,增强操作便捷性。存储模块通过SPI接口连接的SD卡模块,将分钟级计算数据和事件记录存入SD卡,实现长期数据存储;同时,系统集成CH340 USB转串口芯片,支持通过USB接口导出历史数据文件,便于离线分析和备份。
接口与通信模块包括SP3485 RS485芯片,提供RS485通信接口并支持Modbus协议,使分析仪能够无缝接入工业监控系统,实现远程数据读取和控制。电源模块采用多路输出开关电源,提供±12V、5V和3.3V电压,为各个硬件模块稳定供电,确保整个系统在复杂工业环境中可靠运行。通过硬件模块的协同工作和软件算法的优化,系统实现了从信号采集、处理、显示到存储和通信的全流程自动化,满足对三相电能质量的全面监控需求。
系统功能总结
| 功能类别 | 描述 |
|---|---|
| 信号采集与计算 | 同步采集三相电压、电流信号,计算有功功率、无功功率、功率因数、频率及谐波含量(THD)。 |
| 事件检测与记录 | 实时判断并记录电压暂升、暂降、短时中断等电能质量事件。 |
| 数据显示 | 通过LCD屏显示实时波形、参数表格及矢量图。 |
| 数据存储与导出 | 将分钟级数据存入SD卡,并支持通过USB接口导出历史数据文件。 |
| 通信接口 | 提供RS485通信接口,支持Modbus协议,可接入工业监控系统。 |
设计的各个功能模块描述
主控与计算模块采用STM32F407IGT6单片机作为核心控制器,利用其高性能ARM Cortex-M4内核和浮点单元(FPU)进行实时浮点运算和快速傅里叶变换(FFT),以计算有功功率、无功功率、功率因数、频率和谐波含量(THD),并控制整个系统的同步采集、事件判断和数据处理流程。
信号调理与采集模块通过三相电压互感器ZMPT101B和电流互感器ZMCT103C对电压和电流信号进行隔离采样,确保安全性和准确性,配合AD7606 16位8通道同步采样ADC芯片实现三相电压和电流信号的高精度同步采集,为后续计算提供原始数据基础。
存储模块采用SD卡模块通过SPI接口进行数据存储,将系统生成的分钟级电能质量数据(如功率参数和事件记录)存入SD卡,并支持通过USB接口导出历史数据文件,便于离线分析和归档。
显示模块使用5.0寸TFT LCD电容触摸屏(型号NT35510)实时显示三相电压电流波形、参数表格及矢量图,提供直观的用户界面,方便操作人员监控电能质量状态和事件信息。
接口与电源模块集成SP3485 RS485芯片实现RS485通信接口,支持Modbus协议以便接入工业监控系统,同时配备CH340 USB转串口芯片用于数据传输和调试,电源部分采用多路输出开关电源模块提供±12V、5V和3.3V稳定电压,确保各硬件模块的正常运行。
上位机代码设计
// main.cpp
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <chrono>
#include <thread>
#include <mutex>
#include <queue>
#include <atomic>
#include <winsock2.h>
#include <windows.h>
#include <modbus.h>
#pragma comment(lib, "ws2_32.lib")
// 数据结构定义
struct PowerData {
double timestamp;
double voltage[3]; // 三相电压
double current[3]; // 三相电流
double active_power[3]; // 有功功率
double reactive_power[3];// 无功功率
double power_factor[3]; // 功率因数
double frequency;
double thd_v[3]; // 电压谐波畸变率
double thd_i[3]; // 电流谐波畸变率
int event_flag; // 电能质量事件标志
};
struct EventRecord {
double timestamp;
std::string event_type; // "SAG", "SWELL", "INTERRUPTION"
double duration;
double magnitude;
int phase;
};
// Modbus配置
struct ModbusConfig {
std::string port;
int baudrate;
int slave_id;
int timeout_ms;
};
// 全局变量
std::mutex data_mutex;
std::queue<PowerData> data_queue;
std::vector<EventRecord> event_records;
std::atomic<bool> running{true};
modbus_t *mb_ctx = nullptr;
HANDLE hSerial = INVALID_HANDLE_VALUE;
// 配置文件路径
const std::string CONFIG_FILE = "config.ini";
const std::string DATA_DIR = "data/";
const std::string LOG_FILE = "power_quality.log";
// 串口通信类
class SerialPort {
private:
HANDLE hPort;
DCB dcbSerialParams;
COMMTIMEOUTS timeouts;
public:
SerialPort() : hPort(INVALID_HANDLE_VALUE) {}
bool open(const std::string& port_name, int baudrate) {
std::string port_path = "\\\\.\\" + port_name;
hPort = CreateFile(port_path.c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hPort == INVALID_HANDLE_VALUE) {
std::cerr << "Error opening serial port" << std::endl;
return false;
}
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
if (!GetCommState(hPort, &dcbSerialParams)) {
CloseHandle(hPort);
return false;
}
dcbSerialParams.BaudRate = baudrate;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;
dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE;
if (!SetCommState(hPort, &dcbSerialParams)) {
CloseHandle(hPort);
return false;
}
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
SetCommTimeouts(hPort, &timeouts);
PurgeComm(hPort, PURGE_RXCLEAR | PURGE_TXCLEAR);
return true;
}
int read(unsigned char* buffer, int size) {
DWORD bytes_read;
if (!ReadFile(hPort, buffer, size, &bytes_read, NULL)) {
return -1;
}
return bytes_read;
}
int write(const unsigned char* buffer, int size) {
DWORD bytes_written;
if (!WriteFile(hPort, buffer, size, &bytes_written, NULL)) {
return -1;
}
return bytes_written;
}
void close() {
if (hPort != INVALID_HANDLE_VALUE) {
CloseHandle(hPort);
hPort = INVALID_HANDLE_VALUE;
}
}
bool isOpen() {
return hPort != INVALID_HANDLE_VALUE;
}
};
// 数据处理类
class DataProcessor {
private:
std::vector<double> voltage_history[3];
std::vector<double> current_history[3];
const size_t HISTORY_SIZE = 1000;
public:
DataProcessor() {
for (int i = 0; i < 3; i++) {
voltage_history[i].reserve(HISTORY_SIZE);
current_history[i].reserve(HISTORY_SIZE);
}
}
void addData(const PowerData& data) {
for (int i = 0; i < 3; i++) {
voltage_history[i].push_back(data.voltage[i]);
current_history[i].push_back(data.current[i]);
if (voltage_history[i].size() > HISTORY_SIZE) {
voltage_history[i].erase(voltage_history[i].begin());
}
if (current_history[i].size() > HISTORY_SIZE) {
current_history[i].erase(current_history[i].begin());
}
}
}
EventRecord analyzeEvents(const PowerData& data) {
EventRecord event;
event.timestamp = data.timestamp;
// 电压暂降检测
for (int i = 0; i < 3; i++) {
if (data.voltage[i] < 0.9 * 220.0) { // 低于90%标称值
event.event_type = "SAG";
event.phase = i + 1;
event.magnitude = data.voltage[i];
return event;
}
}
// 电压暂升检测
for (int i = 0; i < 3; i++) {
if (data.voltage[i] > 1.1 * 220.0) { // 高于110%标称值
event.event_type = "SWELL";
event.phase = i + 1;
event.magnitude = data.voltage[i];
return event;
}
}
// 短时中断检测
bool all_interrupted = true;
for (int i = 0; i < 3; i++) {
if (data.voltage[i] > 0.1 * 220.0) { // 高于10%标称值
all_interrupted = false;
break;
}
}
if (all_interrupted) {
event.event_type = "INTERRUPTION";
event.phase = 0; // 所有相
event.magnitude = 0.0;
return event;
}
event.event_type = "NORMAL";
return event;
}
void calculateStatistics(double& avg_voltage, double& avg_current,
double& max_voltage, double& min_voltage) {
avg_voltage = avg_current = max_voltage = min_voltage = 0.0;
if (voltage_history[0].empty()) return;
// 计算A相统计
double sum_v = 0.0, sum_i = 0.0;
max_voltage = voltage_history[0][0];
min_voltage = voltage_history[0][0];
for (size_t i = 0; i < voltage_history[0].size(); i++) {
sum_v += voltage_history[0][i];
sum_i += current_history[0][i];
if (voltage_history[0][i] > max_voltage) max_voltage = voltage_history[0][i];
if (voltage_history[0][i] < min_voltage) min_voltage = voltage_history[0][i];
}
avg_voltage = sum_v / voltage_history[0].size();
avg_current = sum_i / current_history[0].size();
}
};
// 数据存储类
class DataStorage {
private:
std::ofstream data_file;
std::ofstream log_file;
std::string current_data_file;
public:
DataStorage() {
// 创建数据目录
CreateDirectory(DATA_DIR.c_str(), NULL);
// 打开日志文件
log_file.open(LOG_FILE, std::ios::app);
if (!log_file.is_open()) {
std::cerr << "Cannot open log file" << std::endl;
}
}
~DataStorage() {
closeCurrentFile();
if (log_file.is_open()) {
log_file.close();
}
}
void createNewDataFile() {
closeCurrentFile();
auto now = std::chrono::system_clock::now();
auto time_t = std::chrono::system_clock::to_time_t(now);
std::tm tm;
localtime_s(&tm, &time_t);
std::ostringstream filename;
filename << DATA_DIR << "power_data_"
<< (tm.tm_year + 1900) << "_"
<< std::setw(2) << std::setfill('0') << (tm.tm_mon + 1) << "_"
<< std::setw(2) << std::setfill('0') << tm.tm_mday << "_"
<< std::setw(2) << std::setfill('0') << tm.tm_hour << "_"
<< std::setw(2) << std::setfill('0') << tm.tm_min << ".csv";
current_data_file = filename.str();
data_file.open(current_data_file);
if (data_file.is_open()) {
data_file << "Timestamp,VA,VB,VC,IA,IB,IC,PA,PB,PC,QA,QB,QC,PFA,PFB,PFC,Freq,THD_VA,THD_VB,THD_VC,THD_IA,THD_IB,THD_IC,Event\n";
log("Created new data file: " + current_data_file);
}
}
void saveData(const PowerData& data) {
if (!data_file.is_open()) {
createNewDataFile();
}
std::lock_guard<std::mutex> lock(data_mutex);
data_file << std::fixed << std::setprecision(3)
<< data.timestamp << ","
<< data.voltage[0] << "," << data.voltage[1] << "," << data.voltage[2] << ","
<< data.current[0] << "," << data.current[1] << "," << data.current[2] << ","
<< data.active_power[0] << "," << data.active_power[1] << "," << data.active_power[2] << ","
<< data.reactive_power[0] << "," << data.reactive_power[1] << "," << data.reactive_power[2] << ","
<< data.power_factor[0] << "," << data.power_factor[1] << "," << data.power_factor[2] << ","
<< data.frequency << ","
<< data.thd_v[0] << "," << data.thd_v[1] << "," << data.thd_v[2] << ","
<< data.thd_i[0] << "," << data.thd_i[1] << "," << data.thd_i[2] << ","
<< data.event_flag << "\n";
}
void saveEvent(const EventRecord& event) {
if (log_file.is_open()) {
auto now = std::chrono::system_clock::now();
auto time_t = std::chrono::system_clock::to_time_t(now);
std::tm tm;
localtime_s(&tm, &time_t);
log_file << std::put_time(&tm, "%Y-%m-%d %H:%M:%S") << " - "
<< "Event: " << event.event_type
<< ", Phase: " << event.phase
<< ", Magnitude: " << event.magnitude << "V"
<< ", Timestamp: " << event.timestamp << std::endl;
}
}
void log(const std::string& message) {
if (log_file.is_open()) {
auto now = std::chrono::system_clock::now();
auto time_t = std::chrono::system_clock::to_time_t(now);
std::tm tm;
localtime_s(&tm, &time_t);
log_file << std::put_time(&tm, "%Y-%m-%d %H:%M:%S") << " - " << message << std::endl;
}
std::cout << message << std::endl;
}
private:
void closeCurrentFile() {
if (data_file.is_open()) {
data_file.close();
}
}
};
// Modbus通信类
class ModbusCommunicator {
private:
modbus_t* ctx;
ModbusConfig config;
public:
ModbusCommunicator() : ctx(nullptr) {}
bool connect(const ModbusConfig& cfg) {
config = cfg;
ctx = modbus_new_rtu(config.port.c_str(),
config.baudrate,
'N',
8,
1);
if (ctx == nullptr) {
std::cerr << "Unable to create modbus context" << std::endl;
return false;
}
modbus_set_slave(ctx, config.slave_id);
modbus_set_response_timeout(ctx, 0, config.timeout_ms * 1000);
if (modbus_connect(ctx) == -1) {
std::cerr << "Modbus connection failed: " << modbus_strerror(errno) << std::endl;
modbus_free(ctx);
ctx = nullptr;
return false;
}
return true;
}
PowerData readPowerData() {
PowerData data;
if (ctx == nullptr) {
return data;
}
// 读取保持寄存器
// 假设数据从寄存器0开始,每个数据占2个寄存器(32位浮点数)
const int START_ADDR = 0;
const int NUM_REGISTERS = 50; // 足够读取所有数据
uint16_t tab_reg[NUM_REGISTERS];
int rc = modbus_read_registers(ctx, START_ADDR, NUM_REGISTERS, tab_reg);
if (rc == -1) {
std::cerr << "Modbus read failed: " << modbus_strerror(errno) << std::endl;
return data;
}
// 解析数据(根据实际设备寄存器映射调整)
// 这里假设数据排列顺序:电压A/B/C,电流A/B/C,有功功率A/B/C,...
int idx = 0;
// 时间戳
uint32_t timestamp_raw = (tab_reg[idx] << 16) | tab_reg[idx + 1];
idx += 2;
data.timestamp = *reinterpret_cast<float*>(×tamp_raw);
// 三相电压
for (int i = 0; i < 3; i++) {
uint32_t val = (tab_reg[idx] << 16) | tab_reg[idx + 1];
idx += 2;
data.voltage[i] = *reinterpret_cast<float*>(&val);
}
// 三相电流
for (int i = 0; i < 3; i++) {
uint32_t val = (tab_reg[idx] << 16) | tab_reg[idx + 1];
idx += 2;
data.current[i] = *reinterpret_cast<float*>(&val);
}
// 有功功率
for (int i = 0; i < 3; i++) {
uint32_t val = (tab_reg[idx] << 16) | tab_reg[idx + 1];
idx += 2;
data.active_power[i] = *reinterpret_cast<float*>(&val);
}
// 无功功率
for (int i = 0; i < 3; i++) {
uint32_t val = (tab_reg[idx] << 16) | tab_reg[idx + 1];
idx += 2;
data.reactive_power[i] = *reinterpret_cast<float*>(&val);
}
// 功率因数
for (int i = 0; i < 3; i++) {
uint32_t val = (tab_reg[idx] << 16) | tab_reg[idx + 1];
idx += 2;
data.power_factor[i] = *reinterpret_cast<float*>(&val);
}
// 频率
uint32_t freq_raw = (tab_reg[idx] << 16) | tab_reg[idx + 1];
idx += 2;
data.frequency = *reinterpret_cast<float*>(&freq_raw);
// 电压THD
for (int i = 0; i < 3; i++) {
uint32_t val = (tab_reg[idx] << 16) | tab_reg[idx + 1];
idx += 2;
data.thd_v[i] = *reinterpret_cast<float*>(&val);
}
// 电流THD
for (int i = 0; i < 3; i++) {
uint32_t val = (tab_reg[idx] << 16) | tab_reg[idx + 1];
idx += 2;
data.thd_i[i] = *reinterpret_cast<float*>(&val);
}
// 事件标志
data.event_flag = tab_reg[idx];
return data;
}
void disconnect() {
if (ctx != nullptr) {
modbus_close(ctx);
modbus_free(ctx);
ctx = nullptr;
}
}
bool isConnected() {
return ctx != nullptr;
}
};
// 配置管理类
class ConfigManager {
private:
std::string modbus_port;
int modbus_baudrate;
int modbus_slave_id;
int modbus_timeout;
int data_save_interval;
bool auto_start;
public:
ConfigManager() : modbus_port("COM3"), modbus_baudrate(9600),
modbus_slave_id(1), modbus_timeout(1000),
data_save_interval(60), auto_start(false) {}
bool loadConfig() {
std::ifstream file(CONFIG_FILE);
if (!file.is_open()) {
saveDefaultConfig();
return false;
}
std::string line;
while (std::getline(file, line)) {
std::istringstream iss(line);
std::string key, value;
if (std::getline(iss, key, '=') && std::getline(iss, value)) {
if (key == "modbus_port") modbus_port = value;
else if (key == "modbus_baudrate") modbus_baudrate = std::stoi(value);
else if (key == "modbus_slave_id") modbus_slave_id = std::stoi(value);
else if (key == "modbus_timeout") modbus_timeout = std::stoi(value);
else if (key == "data_save_interval") data_save_interval = std::stoi(value);
else if (key == "auto_start") auto_start = (value == "true");
}
}
file.close();
return true;
}
void saveDefaultConfig() {
std::ofstream file(CONFIG_FILE);
if (file.is_open()) {
file << "modbus_port=COM3\n";
file << "modbus_baudrate=9600\n";
file << "modbus_slave_id=1\n";
file << "modbus_timeout=1000\n";
file << "data_save_interval=60\n";
file << "auto_start=false\n";
file.close();
}
}
ModbusConfig getModbusConfig() {
ModbusConfig cfg;
cfg.port = modbus_port;
cfg.baudrate = modbus_baudrate;
cfg.slave_id = modbus_slave_id;
cfg.timeout_ms = modbus_timeout;
return cfg;
}
int getDataSaveInterval() { return data_save_interval; }
bool getAutoStart() { return auto_start; }
};
// 主应用程序类
class PowerQualityAnalyzer {
private:
ConfigManager config_manager;
ModbusCommunicator modbus;
DataProcessor processor;
DataStorage storage;
SerialPort serial_port;
std::thread data_thread;
std::thread display_thread;
std::thread event_thread;
std::atomic<bool> data_collection_active{false};
std::atomic<bool> display_active{false};
public:
PowerQualityAnalyzer() {
// 初始化配置
config_manager.loadConfig();
}
~PowerQualityAnalyzer() {
stop();
}
bool initialize() {
storage.log("Initializing Power Quality Analyzer...");
// 连接Modbus设备
ModbusConfig mb_cfg = config_manager.getModbusConfig();
storage.log("Connecting to Modbus device on " + mb_cfg.port + "...");
if (!modbus.connect(mb_cfg)) {
storage.log("Failed to connect to Modbus device");
return false;
}
storage.log("Modbus connection established");
return true;
}
void start() {
if (!modbus.isConnected()) {
if (!initialize()) {
return;
}
}
storage.log("Starting data collection...");
data_collection_active = true;
display_active = true;
// 启动数据采集线程
data_thread = std::thread(&PowerQualityAnalyzer::dataCollectionThread, this);
// 启动显示线程
display_thread = std::thread(&PowerQualityAnalyzer::displayThread, this);
// 启动事件处理线程
event_thread = std::thread(&PowerQualityAnalyzer::eventProcessingThread, this);
storage.log("All threads started");
}
void stop() {
storage.log("Stopping Power Quality Analyzer...");
data_collection_active = false;
display_active = false;
if (data_thread.joinable()) data_thread.join();
if (display_thread.joinable()) display_thread.join();
if (event_thread.joinable()) event_thread.join();
modbus.disconnect();
storage.log("Power Quality Analyzer stopped");
}
private:
void dataCollectionThread() {
auto last_save_time = std::chrono::steady_clock::now();
int save_interval = config_manager.getDataSaveInterval();
while (data_collection_active) {
// 读取Modbus数据
PowerData data = modbus.readPowerData();
if (modbus.isConnected()) {
// 添加时间戳
auto now = std::chrono::system_clock::now();
auto duration = now.time_since_epoch();
data.timestamp = std::chrono::duration<double>(duration).count();
// 添加到队列
{
std::lock_guard<std::mutex> lock(data_mutex);
data_queue.push(data);
}
// 每分钟保存一次数据
auto current_time = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(
current_time - last_save_time).count();
if (elapsed >= save_interval) {
storage.saveData(data);
last_save_time = current_time;
}
}
// 控制采样率
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 10Hz
}
}
void displayThread() {
PowerData last_data;
double avg_voltage = 0.0, avg_current = 0.0;
double max_voltage = 0.0, min_voltage = 0.0;
while (display_active) {
// 从队列获取最新数据
PowerData current_data;
bool has_new_data = false;
{
std::lock_guard<std::mutex> lock(data_mutex);
if (!data_queue.empty()) {
current_data = data_queue.back();
while (!data_queue.empty()) {
data_queue.pop(); // 清除旧数据
}
has_new_data = true;
}
}
if (has_new_data) {
last_data = current_data;
processor.addData(current_data);
processor.calculateStatistics(avg_voltage, avg_current, max_voltage, min_voltage);
}
// 清屏
system("cls");
// 显示实时数据
std::cout << "========== 三相电能质量分析仪 ==========\n";
std::cout << "时间: " << std::fixed << std::setprecision(3) << last_data.timestamp << "s\n";
std::cout << "----------------------------------------\n";
std::cout << "相别\t电压(V)\t电流(A)\t有功功率(W)\t无功功率(var)\t功率因数\tTHD_V(%)\tTHD_I(%)\n";
std::cout << "----------------------------------------------------------------------------------------\n";
for (int i = 0; i < 3; i++) {
char phase = 'A' + i;
std::cout << phase << "\t"
<< std::fixed << std::setprecision(2) << last_data.voltage[i] << "\t"
<< std::fixed << std::setprecision(3) << last_data.current[i] << "\t"
<< std::fixed << std::setprecision(1) << last_data.active_power[i] << "\t\t"
<< std::fixed << std::setprecision(1) << last_data.reactive_power[i] << "\t\t"
<< std::fixed << std::setprecision(3) << last_data.power_factor[i] << "\t\t"
<< std::fixed << std::setprecision(2) << last_data.thd_v[i] << "\t\t"
<< std::fixed << std::setprecision(2) << last_data.thd_i[i] << "\n";
}
std::cout << "----------------------------------------\n";
std::cout << "系统频率: " << std::fixed << std::setprecision(2)
<< last_data.frequency << " Hz\n";
std::cout << "----------------------------------------\n";
std::cout << "统计信息 (A相):\n";
std::cout << "平均电压: " << avg_voltage << " V\n";
std::cout << "平均电流: " << avg_current << " A\n";
std::cout << "最大电压: " << max_voltage << " V\n";
std::cout << "最小电压: " << min_voltage << " V\n";
std::cout << "----------------------------------------\n";
std::cout << "事件标志: ";
switch (last_data.event_flag) {
case 0: std::cout << "正常"; break;
case 1: std::cout << "电压暂降"; break;
case 2: std::cout << "电压暂升"; break;
case 3: std::cout << "短时中断"; break;
default: std::cout << "未知";
}
std::cout << "\n";
std::cout << "----------------------------------------\n";
std::cout << "命令: [S]tart/[P]ause/[Q]uit\n";
// 控制显示刷新率
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
void eventProcessingThread() {
while (data_collection_active) {
PowerData current_data;
{
std::lock_guard<std::mutex> lock(data_mutex);
if (!data_queue.empty()) {
current_data = data_queue.back();
}
}
// 分析事件
EventRecord event = processor.analyzeEvents(current_data);
if (event.event_type != "NORMAL") {
storage.saveEvent(event);
// 发出警报
std::cout << "\x07"; // 终端响铃
std::cout << "!!! 电能质量事件: " << event.event_type
<< " Phase " << event.phase
<< " Magnitude: " << event.magnitude << "V !!!\n";
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
};
// USB数据导出函数
void exportDataToUSB(const std::string& date_str) {
std::string source_dir = DATA_DIR;
std::string dest_dir = "E:/power_data/"; // 假设U盘盘符为E:
// 创建目标目录
CreateDirectory(dest_dir.c_str(), NULL);
// 复制文件
std::string source_file = source_dir + "power_data_" + date_str + "*.csv";
std::string dest_file = dest_dir;
// 使用系统命令复制文件(简化实现)
std::string command = "copy " + source_file + " " + dest_file;
system(command.c_str());
std::cout << "数据已导出到U盘: " << dest_dir << std::endl;
}
// 主函数
int main() {
std::cout << "三相智能电能质量分析仪上位机软件 v1.0\n";
std::cout << "========================================\n";
PowerQualityAnalyzer analyzer;
if (!analyzer.initialize()) {
std::cerr << "初始化失败,按任意键退出..." << std::endl;
std::cin.get();
return 1;
}
char command = ' ';
bool running = true;
while (running) {
std::cout << "\n主菜单:\n";
std::cout << "1. 启动数据采集\n";
std::cout << "2. 停止数据采集\n";
std::cout << "3. 导出数据到USB\n";
std::cout << "4. 查看事件记录\n";
std::cout << "5. 退出程序\n";
std::cout << "请选择: ";
std::cin >> command;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
switch (command) {
case '1':
analyzer.start();
std::cout << "数据采集已启动,按任意键返回主菜单...";
std::cin.get();
analyzer.stop();
break;
case '2':
analyzer.stop();
std::cout << "数据采集已停止\n";
break;
case '3': {
std::cout << "请输入导出日期 (格式: YYYY_MM_DD): ";
std::string date_str;
std::cin >> date_str;
exportDataToUSB(date_str);
break;
}
case '4': {
std::ifstream log_file(LOG_FILE);
if (log_file.is_open()) {
std::string line;
std::cout << "\n事件记录:\n";
std::cout << "========================================\n";
while (std::getline(log_file, line)) {
std::cout << line << std::endl;
}
log_file.close();
} else {
std::cout << "无法打开日志文件\n";
}
std::cout << "按任意键继续...";
std::cin.get();
break;
}
case '5':
running = false;
analyzer.stop();
std::cout << "程序退出\n";
break;
default:
std::cout << "无效选择,请重试\n";
break;
}
}
return 0;
}
模块代码设计
由于代码长度限制,我将提供关键模块的代码设计,重点关注STM32F407寄存器方式开发,特别是传感器(AD7606、ZMPT101B、ZMCT103C)的驱动代码。ZMPT101B和ZMCT103C是模拟传感器,代码主要通过AD7606读取其输出,因此重点在AD7606的初始化、数据采集和处理。
1. 头文件和宏定义
// 定义STM32F407寄存器地址(示例,基于STM32F4参考手册)
#define RCC_BASE 0x40023800
#define GPIOA_BASE 0x40020000
#define GPIOB_BASE 0x40020400
#define GPIOC_BASE 0x40020800
#define SPI1_BASE 0x40013000
#define TIM2_BASE 0x40000000
// RCC寄存器偏移
#define RCC_AHB1ENR (*((volatile unsigned int*)(RCC_BASE + 0x30)))
#define RCC_APB2ENR (*((volatile unsigned int*)(RCC_BASE + 0x44)))
#define RCC_APB1ENR (*((volatile unsigned int*)(RCC_BASE + 0x40)))
// GPIO寄存器偏移
#define GPIO_MODER_OFFSET 0x00
#define GPIO_OTYPER_OFFSET 0x04
#define GPIO_OSPEEDR_OFFSET 0x08
#define GPIO_PUPDR_OFFSET 0x0C
#define GPIO_ODR_OFFSET 0x14
#define GPIO_BSRR_OFFSET 0x18
#define GPIO_IDR_OFFSET 0x10
// SPI寄存器偏移
#define SPI_CR1_OFFSET 0x00
#define SPI_CR2_OFFSET 0x04
#define SPI_SR_OFFSET 0x08
#define SPI_DR_OFFSET 0x0C
// 定时器寄存器偏移
#define TIM_CR1_OFFSET 0x00
#define TIM_PSC_OFFSET 0x28
#define TIM_ARR_OFFSET 0x2C
#define TIM_CNT_OFFSET 0x24
#define TIM_SR_OFFSET 0x10
// 硬件引脚定义(假设连接,根据实际PCB调整)
// AD7606引脚
#define AD7606_CS_PIN 7 // PC7作为片选
#define AD7606_RESET_PIN 6 // PC6作为复位
#define AD7606_CONVST_PIN 5 // PC5作为转换开始
#define AD7606_BUSY_PIN 4 // PC4作为忙信号
// SPI引脚:PA5(SCK), PA6(MISO), PA7(MOSI)
#define SPI_SCK_PIN 5
#define SPI_MISO_PIN 6
#define SPI_MOSI_PIN 7
// 三相通道定义(AD7606的8通道,假设通道0-2为电压,3-5为电流)
#define VOLTAGE_CHANNELS 3
#define CURRENT_CHANNELS 3
#define SAMPLES_PER_CYCLE 256 // 每个周期采样点数,用于FFT
// 全局变量
volatile uint16_t adc_raw_data[8]; // 存储AD7606的8通道原始数据
volatile float voltage[3], current[3]; // 三相电压和电流有效值
volatile float power_active, power_reactive, power_factor, frequency, thd;
2. GPIO初始化函数(寄存器方式)
void GPIO_Init(void) {
// 使能GPIO时钟
RCC_AHB1ENR |= (1 << 0); // 使能GPIOA时钟
RCC_AHB1ENR |= (1 << 1); // 使能GPIOB时钟
RCC_AHB1ENR |= (1 << 2); // 使能GPIOC时钟
// 配置GPIOC引脚:PC4(BUSY输入), PC5(CONVST输出), PC6(RESET输出), PC7(CS输出)
volatile unsigned int* GPIOC_MODER = (volatile unsigned int*)(GPIOC_BASE + GPIO_MODER_OFFSET);
volatile unsigned int* GPIOC_PUPDR = (volatile unsigned int*)(GPIOC_BASE + GPIO_PUPDR_OFFSET);
*GPIOC_MODER &= ~(0xFF << (AD7606_BUSY_PIN * 2)); // 清除模式
*GPIOC_MODER |= (0x00 << (AD7606_BUSY_PIN * 2)); // PC4输入模式
*GPIOC_MODER |= (0x01 << (AD7606_CONVST_PIN * 2)); // PC5输出模式
*GPIOC_MODER |= (0x01 << (AD7606_RESET_PIN * 2)); // PC6输出模式
*GPIOC_MODER |= (0x01 << (AD7606_CS_PIN * 2)); // PC7输出模式
*GPIOC_PUPDR |= (0x01 << (AD7606_BUSY_PIN * 2)); // PC4上拉
// 配置GPIOA引脚:PA5(SCK), PA6(MISO), PA7(MOSI)为复用功能
volatile unsigned int* GPIOA_MODER = (volatile unsigned int*)(GPIOA_BASE + GPIO_MODER_OFFSET);
*GPIOA_MODER &= ~((0x03 << (SPI_SCK_PIN * 2)) | (0x03 << (SPI_MISO_PIN * 2)) | (0x03 << (SPI_MOSI_PIN * 2)));
*GPIOA_MODER |= ((0x02 << (SPI_SCK_PIN * 2)) | (0x02 << (SPI_MISO_PIN * 2)) | (0x02 << (SPI_MOSI_PIN * 2))); // 复用模式
// 设置GPIO速度为高速
volatile unsigned int* GPIOA_OSPEEDR = (volatile unsigned int*)(GPIOA_BASE + GPIO_OSPEEDR_OFFSET);
*GPIOA_OSPEEDR |= (0x03 << (SPI_SCK_PIN * 2)) | (0x03 << (SPI_MISO_PIN * 2)) | (0x03 << (SPI_MOSI_PIN * 2));
// 初始状态:拉高CS,拉低RESET和CONVST
volatile unsigned int* GPIOC_ODR = (volatile unsigned int*)(GPIOC_BASE + GPIO_ODR_OFFSET);
*GPIOC_ODR |= (1 << AD7606_CS_PIN); // CS高
*GPIOC_ODR &= ~(1 << AD7606_RESET_PIN); // RESET低
*GPIOC_ODR &= ~(1 << AD7606_CONVST_PIN); // CONVST低
}
3. SPI初始化函数(用于AD7606通信)
void SPI1_Init(void) {
// 使能SPI1时钟
RCC_APB2ENR |= (1 << 12); // 使能SPI1时钟
volatile unsigned int* SPI1_CR1 = (volatile unsigned int*)(SPI1_BASE + SPI_CR1_OFFSET);
volatile unsigned int* SPI1_CR2 = (volatile unsigned int*)(SPI1_BASE + SPI_CR2_OFFSET);
// 配置SPI1:主模式,时钟极性低,相位第1边沿,8位数据,MSB先,预分频256(约84MHz/256=328kHz,AD7606支持最高200kHz SPI)
*SPI1_CR1 = (0 << 15) | // 双向模式
(0 << 14) | // 输出使能
(1 << 2) | // 主模式
(0 << 11) | // 8位数据
(0 << 7) | // MSB先
(1 << 5) | // 时钟极性CPOL=0(低电平空闲)
(0 << 4) | // 时钟相位CPHA=0(第1边沿采样)
(0x07 << 3); // 预分频256,fPCLK2=84MHz,SCK=84MHz/256=328kHz
*SPI1_CR2 = (0 << 12) | // 8位数据
(1 << 2); // 使能SS输出模式(硬件NSS管理,但这里用软件CS)
// 使能SPI1
*SPI1_CR1 |= (1 << 6); // 使能SPI
}
4. AD7606初始化函数
void AD7606_Init(void) {
// 复位AD7606
volatile unsigned int* GPIOC_BSRR = (volatile unsigned int*)(GPIOC_BASE + GPIO_BSRR_OFFSET);
*GPIOC_BSRR = (1 << (AD7606_RESET_PIN + 16)); // 拉低RESET
delay_ms(1); // 延时1ms(需实现delay_ms函数,例如用SysTick)
*GPIOC_BSRR = (1 << AD7606_RESET_PIN); // 拉高RESET
delay_ms(1);
// 初始配置:通过SPI写入配置寄存器(AD7606默认模式通常无需配置,但可设置范围等)
// AD7606在并行模式下无需SPI配置,但串行模式下需配置。假设使用串行模式。
// 这里假设AD7606设置为±10V范围,同步采样模式。
// 实际中,AD7606的配置通过引脚(如RANGE)完成,代码中通过GPIO控制。
// 为简化,我们假设硬件连接已设置范围引脚。
}
5. AD7606数据读取函数(通过SPI)
uint16_t AD7606_ReadChannel(uint8_t channel) {
// 选择通道(在AD7606中,串行模式下连续读取所有通道,这里简化分通道读)
// 实际中,AD7606转换后,通过SPI连续读出8通道数据。
volatile unsigned int* GPIOC_BSRR = (volatile unsigned int*)(GPIOC_BASE + GPIO_BSRR_OFFSET);
volatile unsigned int* SPI1_DR = (volatile unsigned int*)(SPI1_BASE + SPI_DR_OFFSET);
volatile unsigned int* SPI1_SR = (volatile unsigned int*)(SPI1_BASE + SPI_SR_OFFSET);
// 启动转换
*GPIOC_BSRR = (1 << (AD7606_CONVST_PIN + 16)); // 拉低CONVST
delay_us(1); // 短延时
*GPIOC_BSRR = (1 << AD7606_CONVST_PIN); // 拉高CONVST,启动转换
// 等待转换完成(检查BUSY引脚)
volatile unsigned int* GPIOC_IDR = (volatile unsigned int*)(GPIOC_BASE + GPIO_IDR_OFFSET);
while ((*GPIOC_IDR & (1 << AD7606_BUSY_PIN)) != 0); // 等待BUSY变低
// 拉低CS,开始SPI读取
*GPIOC_BSRR = (1 << (AD7606_CS_PIN + 16)); // CS低
// 通过SPI读取8通道数据(每个通道16位)
uint16_t data[8];
for (int i = 0; i < 8; i++) {
// 发送哑元数据以读取
*SPI1_DR = 0x0000;
while (!(*SPI1_SR & (1 << 1))); // 等待RXNE
data[i] = *SPI1_DR & 0xFFFF;
}
*GPIOC_BSRR = (1 << AD7606_CS_PIN); // CS高
return data[channel]; // 返回指定通道数据
}
void AD7606_ReadAllChannels(uint16_t *buffer) {
// 类似上述,但读取所有通道到buffer
volatile unsigned int* GPIOC_BSRR = (volatile unsigned int*)(GPIOC_BASE + GPIO_BSRR_OFFSET);
volatile unsigned int* SPI1_DR = (volatile unsigned int*)(SPI1_BASE + SPI_DR_OFFSET);
volatile unsigned int* SPI1_SR = (volatile unsigned int*)(SPI1_BASE + SPI_SR_OFFSET);
// 启动转换
*GPIOC_BSRR = (1 << (AD7606_CONVST_PIN + 16));
delay_us(1);
*GPIOC_BSRR = (1 << AD7606_CONVST_PIN);
// 等待BUSY
volatile unsigned int* GPIOC_IDR = (volatile unsigned int*)(GPIOC_BASE + GPIO_IDR_OFFSET);
while ((*GPIOC_IDR & (1 << AD7606_BUSY_PIN)) != 0);
// 读取数据
*GPIOC_BSRR = (1 << (AD7606_CS_PIN + 16));
for (int i = 0; i < 8; i++) {
*SPI1_DR = 0x0000;
while (!(*SPI1_SR & (1 << 1)));
buffer[i] = *SPI1_DR & 0xFFFF;
}
*GPIOC_BSRR = (1 << AD7606_CS_PIN);
}
6. 定时器初始化(用于定期采样)
void TIM2_Init(uint32_t sampling_freq) {
// 使能TIM2时钟
RCC_APB1ENR |= (1 << 0);
volatile unsigned int* TIM2_CR1 = (volatile unsigned int*)(TIM2_BASE + TIM_CR1_OFFSET);
volatile unsigned int* TIM2_PSC = (volatile unsigned int*)(TIM2_BASE + TIM_PSC_OFFSET);
volatile unsigned int* TIM2_ARR = (volatile unsigned int*)(TIM2_BASE + TIM_ARR_OFFSET);
// 配置TIM2:向上计数,预分频和重载值设置采样频率
uint32_t timer_clock = 84000000; // APB1时钟84MHz
uint32_t prescaler = timer_clock / 1000000 - 1; // 分频到1MHz
*TIM2_PSC = prescaler;
*TIM2_ARR = 1000000 / sampling_freq - 1; // 例如,sampling_freq=25600Hz for 50Hz系统,每周期512点
// 使能更新中断
volatile unsigned int* TIM2_DIER = (volatile unsigned int*)(TIM2_BASE + 0x0C);
*TIM2_DIER |= (1 << 0);
// 配置NVIC(嵌套向量中断控制器)使能TIM2中断
volatile unsigned int* NVIC_ISER0 = (volatile unsigned int*)0xE000E100;
*NVIC_ISER0 |= (1 << 28); // TIM2中断号28
// 使能定时器
*TIM2_CR1 |= (1 << 0);
}
7. 数据处理函数(计算有效值、功率、FFT等)
// 使用CMSIS DSP库进行FFT(需包含arm_math.h,并在项目中链接CMSIS DSP库)
#include "arm_math.h"
void ProcessData(void) {
// 假设adc_raw_data已更新(在定时器中断中读取)
// 将原始ADC值转换为电压和电流(基于传感器变比和ADC范围)
// 例如,AD7606 ±10V范围,16位输出,转换公式:V = (raw / 32768) * 10.0
for (int i = 0; i < 3; i++) {
voltage[i] = ((float)adc_raw_data[i] / 32768.0) * 10.0; // 电压通道
current[i] = ((float)adc_raw_data[i+3] / 32768.0) * 5.0; // 电流通道,假设ZMCT103C输出±5A
}
// 计算有效值
float voltage_rms[3] = {0}, current_rms[3] = {0};
for (int i = 0; i < 3; i++) {
voltage_rms[i] = voltage[i] / sqrt(2.0); // 假设采集的是瞬时值,需多周期平均
current_rms[i] = current[i] / sqrt(2.0);
}
// 计算有功功率、无功功率、功率因数(基于瞬时值积分)
// 这里简化,假设已采集一个周期的瞬时值数组
static float voltage_inst[SAMPLES_PER_CYCLE][3], current_inst[SAMPLES_PER_CYCLE][3];
// 在定时器中断中填充瞬时值数组
// 计算有功功率 P = (1/N) * sum(v[i] * i[i]) 对时间积分
power_active = 0;
for (int j = 0; j < SAMPLES_PER_CYCLE; j++) {
for (int i = 0; i < 3; i++) {
power_active += voltage_inst[j][i] * current_inst[j][i];
}
}
power_active /= SAMPLES_PER_CYCLE;
// 使用FFT计算谐波(以A相电压为例)
arm_rfft_fast_instance_f32 fft_instance;
arm_rfft_fast_init_f32(&fft_instance, SAMPLES_PER_CYCLE);
float input[SAMPLES_PER_CYCLE], fft_output[SAMPLES_PER_CYCLE];
for (int j = 0; j < SAMPLES_PER_CYCLE; j++) {
input[j] = voltage_inst[j][0]; // A相电压瞬时值
}
arm_rfft_fast_f32(&fft_instance, input, fft_output, 0);
// fft_output包含实部和虚部,计算幅值
float magnitude[SAMPLES_PER_CYCLE/2];
for (int k = 0; k < SAMPLES_PER_CYCLE/2; k++) {
magnitude[k] = sqrt(fft_output[2*k]*fft_output[2*k] + fft_output[2*k+1]*fft_output[2*k+1]);
}
// 计算THD(总谐波畸变率)
float fundamental = magnitude[1]; // 基波幅值(假设50Hz,索引1对应基波)
float harmonic_sum = 0;
for (int k = 2; k < SAMPLES_PER_CYCLE/2; k++) {
harmonic_sum += magnitude[k] * magnitude[k];
}
thd = sqrt(harmonic_sum) / fundamental * 100.0; // 百分比
// 频率计算:通过过零检测或FFT基波频率
frequency = 50.0; // 简化,实际从FFT或定时器计算
}
8. 主函数和中断服务例程框架
int main(void) {
// 系统初始化
SystemInit(); // 假设有系统时钟配置函数(例如设置到168MHz)
GPIO_Init();
SPI1_Init();
AD7606_Init();
TIM2_Init(25600); // 采样频率25600Hz,每周期512点 for 50Hz
// 启用全局中断
__enable_irq();
while (1) {
// 主循环:处理数据、更新显示、存储等
ProcessData();
// 显示到LCD(需实现LCD驱动,这里省略)
// 存储数据到SD卡(需实现SD卡SPI驱动,这里省略)
}
}
// TIM2中断服务程序,用于定期采样
void TIM2_IRQHandler(void) {
volatile unsigned int* TIM2_SR = (volatile unsigned int*)(TIM2_BASE + TIM_SR_OFFSET);
if (*TIM2_SR & (1 << 0)) { // 更新中断
*TIM2_SR &= ~(1 << 0); // 清除中断标志
// 读取AD7606所有通道数据
AD7606_ReadAllChannels((uint16_t*)adc_raw_data);
// 存储瞬时值用于处理
static int sample_index = 0;
for (int i = 0; i < 3; i++) {
voltage_inst[sample_index][i] = voltage[i]; // 需从adc_raw_data转换
current_inst[sample_index][i] = current[i];
}
sample_index = (sample_index + 1) % SAMPLES_PER_CYCLE;
}
}
注意事项
- 以上代码为简化示例,实际开发中需根据硬件连接调整引脚定义和时序。
- 延时函数(如
delay_ms、delay_us)需实现,例如使用SysTick定时器。 - FFT计算使用了CMSIS DSP库,需在项目中包含
arm_math.h并链接库文件。 - 电能质量事件检测、LCD显示、SD卡存储、RS485通信等模块代码未详细展开,需根据具体硬件实现。
- 代码采用寄存器方式开发,直接操作STM32寄存器,避免了库函数调用。
项目核心代码
/**
******************************************************************************
* @file main.c
* @brief 三相智能电能质量分析仪主程序
* @author STM32 Development Team
* @date 2024-06-15
******************************************************************************
* @attention
* 硬件平台: STM32F407IGT6
* 开发方式: 寄存器直接操作
* 功能说明:
* 1. 三相电压电流同步采样与电能质量分析
* 2. LCD实时显示与触摸控制
* 3. SD卡数据存储
* 4. RS485 Modbus通信
******************************************************************************
*/
#include "stm32f4xx.h"
#include "sys_config.h"
#include "ad7606_driver.h"
#include "lcd_nt35510.h"
#include "sd_card.h"
#include "modbus_rtu.h"
#include "power_analysis.h"
#include "event_detect.h"
/* 全局变量定义 */
volatile uint32_t sys_tick = 0; // 系统时基
PQ_Data pq_data; // 电能质量数据结构
Event_Record event_buf[EVENT_BUF_SIZE]; // 事件记录缓冲区
/* 函数声明 */
static void System_Clock_Config(void);
static void GPIO_Config(void);
static void NVIC_Config(void);
static void Timer_Config(void);
static void DMA_Config(void);
static void Peripherals_Init(void);
static void Data_Processing_Task(void);
static void Display_Update_Task(void);
static void Storage_Task(void);
static void Communication_Task(void);
/**
* @brief 主函数
* @param None
* @retval int
*/
int main(void)
{
/* 系统初始化 */
System_Clock_Config(); // 配置系统时钟为168MHz
GPIO_Config(); // GPIO初始化
NVIC_Config(); // 中断配置
Timer_Config(); // 定时器配置
DMA_Config(); // DMA配置
/* 外设初始化 */
Peripherals_Init();
/* 初始化电能质量数据结构 */
PQ_Data_Init(&pq_data);
/* 使能全局中断 */
__enable_irq();
/* 主循环 */
while (1)
{
/* 数据采集与处理任务 */
if (TIM2->SR & TIM_SR_UIF) // 定时器2更新中断标志
{
TIM2->SR &= ~TIM_SR_UIF;
Data_Processing_Task();
}
/* 显示更新任务 (100ms) */
if (sys_tick % 100 == 0)
{
Display_Update_Task();
}
/* 数据存储任务 (1分钟) */
if (sys_tick % 60000 == 0)
{
Storage_Task();
}
/* 通信任务 */
Communication_Task();
}
}
/**
* @brief 系统时钟配置
* @param None
* @retval None
*/
static void System_Clock_Config(void)
{
/* 使能外部时钟 */
RCC->CR |= RCC_CR_HSEON;
while (!(RCC->CR & RCC_CR_HSERDY));
/* 配置PLL */
RCC->PLLCFGR = (8 << RCC_PLLCFGR_PLLM_Pos) | // PLLM = 8
(336 << RCC_PLLCFGR_PLLN_Pos) | // PLLN = 336
(0 << RCC_PLLCFGR_PLLP_Pos) | // PLLP = 2
(7 << RCC_PLLCFGR_PLLQ_Pos); // PLLQ = 7
/* 选择HSE作为PLL源 */
RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE;
/* 使能PLL */
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY));
/* 设置AHB、APB1、APB2预分频 */
RCC->CFGR |= RCC_CFGR_HPRE_DIV1; // AHB = 168MHz
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; // APB1 = 42MHz
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; // APB2 = 84MHz
/* 选择PLL作为系统时钟 */
RCC->CFGR &= ~RCC_CFGR_SW;
RCC->CFGR |= RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
}
/**
* @brief GPIO配置
* @param None
* @retval None
*/
static void GPIO_Config(void)
{
/* 使能GPIO时钟 */
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN |
RCC_AHB1ENR_GPIOCEN | RCC_AHB1ENR_GPIODEN;
/* LCD控制引脚配置 */
GPIOA->MODER |= (1 << 10) | (1 << 12) | (1 << 14); // PA5,6,7 推挽输出
GPIOA->OSPEEDR |= (3 << 10) | (3 << 12) | (3 << 14); // 高速
/* RS485控制引脚 */
GPIOC->MODER |= (1 << 12); // PC6 推挽输出
GPIOC->BSRR = GPIO_BSRR_BS6; // 默认置高
/* ADC芯片控制引脚 */
GPIOB->MODER |= (1 << 0) | (1 << 2) | (1 << 4); // PB0,1,2 推挽输出
}
/**
* @brief 中断配置
* @param None
* @retval None
*/
static void NVIC_Config(void)
{
/* 定时器2中断 (数据采集) */
NVIC_SetPriority(TIM2_IRQn, 0);
NVIC_EnableIRQ(TIM2_IRQn);
/* USART3中断 (RS485通信) */
NVIC_SetPriority(USART3_IRQn, 1);
NVIC_EnableIRQ(USART3_IRQn);
/* SDIO中断 (SD卡) */
NVIC_SetPriority(SDIO_IRQn, 2);
NVIC_EnableIRQ(SDIO_IRQn);
/* 外部中断 (触摸屏) */
NVIC_SetPriority(EXTI0_IRQn, 3);
NVIC_EnableIRQ(EXTI0_IRQn);
}
/**
* @brief 定时器配置
* @param None
* @retval None
*/
static void Timer_Config(void)
{
/* 使能TIM2时钟 */
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
/* 配置TIM2为1kHz采样频率 */
TIM2->PSC = 84 - 1; // 84MHz/84 = 1MHz
TIM2->ARR = 1000 - 1; // 1MHz/1000 = 1kHz
/* 使能更新中断 */
TIM2->DIER |= TIM_DIER_UIE;
/* 启动定时器 */
TIM2->CR1 |= TIM_CR1_CEN;
}
/**
* @brief DMA配置
* @param None
* @retval None
*/
static void DMA_Config(void)
{
/* 使能DMA2时钟 */
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
/* ADC数据DMA通道配置 */
DMA2_Stream0->CR = DMA_SxCR_CHSEL_0 | // 通道0
DMA_SxCR_MINC | // 存储器地址递增
DMA_SxCR_CIRC | // 循环模式
DMA_SxCR_TCIE; // 传输完成中断
/* 设置外设地址 */
DMA2_Stream0->PAR = (uint32_t)&ADC1->DR;
}
/**
* @brief 外设初始化
* @param None
* @retval None
*/
static void Peripherals_Init(void)
{
/* 初始化AD7606同步采样ADC */
AD7606_Init();
/* 初始化LCD显示屏 */
LCD_Init();
LCD_Clear(0x0000);
LCD_ShowString(10, 10, "PQ Analyzer V1.0", 0xFFFF, 0x0000);
/* 初始化SD卡 */
SD_Init();
/* 初始化Modbus RTU */
Modbus_Init(9600, PARITY_NONE);
/* 初始化电能质量分析模块 */
Power_Analysis_Init();
}
/**
* @brief 数据处理任务
* @param None
* @retval None
*/
static void Data_Processing_Task(void)
{
static uint16_t sample_count = 0;
float voltage[3], current[3];
/* 读取AD7606采样数据 */
AD7606_ReadAllChannels(voltage, current);
/* 更新电能质量数据 */
PQ_UpdateData(&pq_data, voltage, current);
/* 电能质量事件检测 */
Event_Detection(&pq_data, event_buf, EVENT_BUF_SIZE);
/* 每1秒计算一次谐波 */
if (++sample_count >= 1000)
{
sample_count = 0;
PQ_CalculateHarmonics(&pq_data);
}
}
/**
* @brief 显示更新任务
* @param None
* @retval None
*/
static void Display_Update_Task(void)
{
static uint8_t display_mode = 0;
/* 检查触摸屏输入 */
if (TOUCH_GetState() == TOUCH_PRESSED)
{
Touch_Point tp = TOUCH_GetPoint();
display_mode = (display_mode + 1) % 3; // 切换显示模式
}
/* 根据显示模式更新界面 */
switch (display_mode)
{
case 0: // 实时波形
LCD_ShowWaveform(&pq_data);
break;
case 1: // 参数表格
LCD_ShowParamTable(&pq_data);
break;
case 2: // 矢量图
LCD_ShowPhasorDiagram(&pq_data);
break;
}
}
/**
* @brief 数据存储任务
* @param None
* @retval None
*/
static void Storage_Task(void)
{
static uint32_t file_index = 0;
char filename[32];
/* 生成文件名 */
sprintf(filename, "PQ_DATA_%04d.csv", file_index++);
/* 保存分钟级数据到SD卡 */
SD_SaveData(filename, &pq_data);
/* 保存电能质量事件 */
if (Event_GetCount() > 0)
{
SD_SaveEvents("EVENTS.CSV", event_buf, Event_GetCount());
Event_ClearBuffer();
}
}
/**
* @brief 通信任务
* @param None
* @retval None
*/
static void Communication_Task(void)
{
/* 处理Modbus RTU请求 */
if (Modbus_CheckRequest())
{
Modbus_ProcessRequest(&pq_data);
}
/* USB数据导出检查 */
if (USB_CheckExportRequest())
{
USB_ExportData();
}
}
/**
* @brief 系统滴答定时器中断服务函数
* @param None
* @retval None
*/
void SysTick_Handler(void)
{
sys_tick++;
}
/**
* @brief TIM2中断服务函数
* @param None
* @retval None
*/
void TIM2_IRQHandler(void)
{
if (TIM2->SR & TIM_SR_UIF)
{
TIM2->SR &= ~TIM_SR_UIF;
// 数据处理在main循环中执行
}
}
/**
* @brief USART3中断服务函数 (RS485)
* @param None
* @retval None
*/
void USART3_IRQHandler(void)
{
if (USART3->SR & USART_SR_RXNE)
{
uint8_t data = USART3->DR;
Modbus_RxCallback(data);
}
}
/******************************** END OF FILE *********************************/
总结
本设计的三相智能电能质量分析仪旨在实现对电能质量的全面监测与分析。它能够同步采集三相电压和电流信号,实时计算有功功率、无功功率、功率因数、频率及谐波含量(THD),并能准确判断和记录电压暂升、暂降及短时中断等电能质量事件。通过LCD屏直观显示实时波形、参数表格及矢量图,同时将分钟级数据存储至SD卡,并支持USB接口导出历史数据,便于后续分析和存档。
该分析仪的硬件系统基于STM32F407IGT6高性能单片机,利用其浮点运算单元进行高效FFT计算,确保数据处理的速度和精度。信号调理与采集模块采用三相电压/电流互感器配合AD7606同步采样ADC芯片,实现隔离采样和高分辨率数据转换。存储模块通过SPI接口的SD卡实现大容量数据记录,而显示模块采用5.0寸TFT LCD电容触摸屏,提供友好的交互界面。此外,接口与电源模块集成RS485通信支持Modbus协议,方便接入工业监控系统,并配备多路输出开关电源,保障系统稳定供电。
整体设计集成了信号采集、处理、显示、存储和通信等多功能模块,具备高精度、实时性和可扩展性。该分析仪不仅满足工业现场对电能质量监测的严格要求,还为用户提供便捷的数据管理和系统集成方案,适用于电力系统、工业自动化等领域的电能质量优化与故障诊断。
- 点赞
- 收藏
- 关注作者
评论(0)