#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/RS485/rs485.h"
#include "i2c.h"
#include <math.h>
#include <string.h>
#include <stdio.h>
// RS485设备参数
#define DEVICE_ADDR 0x01
#define VOLTAGE_REG_ADDR 0x0020
#define MODBUS_READ_HOLDING_REG 0x03
// ADS1115参数
#define ADS1115_DEV_ADDR 0x90
#define ADS1115_Conversion 0x00
#define ADS1115_Config 0x01
// ZMCT103C模块参数(根据图片精确配置)
#define ZMCT103C_RATED_INPUT_CURRENT 5.0f // 额定输入电流:5A
#define ZMCT103C_RATED_OUTPUT_CURRENT 0.005f // 额定输出电流:5mA
#define ZMCT103C_TRANSFORMER_RATIO 1000.0f // 变比:1000:1
#define ZMCT103C_BURDEN_RESISTOR 100.0f // 负载电阻:100Ω(相位差测试条件)
#define ZMCT103C_LINEAR_RANGE 10.0f // 线性范围:0-10A
#define ZMCT103C_LINEARITY 0.002f // 线性度:0.2%
#define ZMCT103C_ACCURACY_CLASS 0.002f // 精度等级:0.2级
#define ZMCT103C_PHASE_SHIFT 20.0f // 相位差:≤20°
// ADS1115采样参数
#define SAMPLING_RATE 860 // ADS1115采样率:860 SPS
#define SAMPLES_PER_CYCLE 17 // 每个周期采样点数(50Hz时,860/50≈17)
#define NUM_CYCLES 2 // 采样周期数
#define TOTAL_SAMPLES (SAMPLES_PER_CYCLE * NUM_CYCLES) // 总采样点数
uint8_t BYTE_BUF[2];
// 全局变量
float g_zero_offset_voltage = 0.0f; // 零点偏移电压
uint8_t g_offset_calibrated = 0; // 校准标志
uint32_t last_calibration_time = 0; // 上次校准时间
const uint32_t CALIBRATION_INTERVAL = 10 * 60 * 1000; // 10分钟校准间隔
// 显示通道结构体
typedef struct
{
char* label;
float value;
uint16_t color;
uint16_t x;
uint16_t y;
char last_display[30];
} DisplayChannel;
/* 计算Modbus CRC16校验 */
uint16_t modbus_crc16(uint8_t *data, uint8_t length)
{
uint16_t crc = 0xFFFF;
for (uint8_t pos = 0; pos < length; pos++)
{
crc ^= (uint16_t)data[pos];
for (uint8_t i = 8; i != 0; i--)
{
if ((crc & 0x0001) != 0)
{
crc >>= 1;
crc ^= 0xA001;
} else
{
crc >>= 1;
}
}
}
return crc;
}
/* 通过RS485读取电压数据 */
float read_voltage_via_rs485(void)
{
uint8_t tx_buf[8];
uint8_t rx_buf[256];
uint8_t rx_len = 0;
uint16_t voltage_reg;
// 构造Modbus RTU请求帧
tx_buf[0] = DEVICE_ADDR;
tx_buf[1] = MODBUS_READ_HOLDING_REG;
tx_buf[2] = (VOLTAGE_REG_ADDR >> 8) & 0xFF;
tx_buf[3] = VOLTAGE_REG_ADDR & 0xFF;
tx_buf[4] = 0x00;
tx_buf[5] = 0x01;
uint16_t crc = modbus_crc16(tx_buf, 6);
tx_buf[6] = crc & 0xFF;
tx_buf[7] = (crc >> 8) & 0xFF;
rs485_send_data(tx_buf, 8);
delay_ms(100);
rs485_receive_data(rx_buf, &rx_len);
if (rx_len >= 7)
{
if (rx_buf[0] == DEVICE_ADDR && rx_buf[1] == MODBUS_READ_HOLDING_REG)
{
voltage_reg = (rx_buf[3] << 8) | rx_buf[4];
float voltage = voltage_reg * 0.01f;
return voltage * 10.0f; // 10倍缩放修正
}
}
return -1.0f;
}
/* ADS1115读取单次转换 */
float ADS1115_Read_ADC(uint8_t channel)
{
uint8_t ConfigBuff[2];
uint8_t PGA;
int16_t tempData;
float voltage;
// 配置PGA为2(±2.048V量程)
PGA = 2;
switch (channel)
{
case 0: ConfigBuff[0] = (0xC1 & 0xF1) | (PGA << 1); break;
case 1: ConfigBuff[0] = (0xD1 & 0xF1) | (PGA << 1); break;
case 2: ConfigBuff[0] = (0xE1 & 0xF1) | (PGA << 1); break;
case 3: ConfigBuff[0] = (0xF1 & 0xF1) | (PGA << 1); break;
default: return -1.0f;
}
ConfigBuff[1] = 0xE3; // 860 SPS,不使能比较器
HAL_I2C_Mem_Write(&hi2c2, ADS1115_DEV_ADDR, ADS1115_Config, 1, ConfigBuff, 2, 1000);
HAL_Delay(2);
HAL_I2C_Mem_Read(&hi2c2, ADS1115_DEV_ADDR, ADS1115_Conversion, 1, BYTE_BUF, 2, 1000);
tempData = (int16_t)(BYTE_BUF[0] << 8) | (int16_t)BYTE_BUF[1];
switch (PGA)
{
case 0: voltage = tempData * 0.0001875f; break;
case 1: voltage = tempData * 0.000125f; break;
case 2: voltage = tempData * 0.0000625f; break;
case 3: voltage = tempData * 0.00003125f; break;
case 4: voltage = tempData * 0.000015625f; break;
case 5: voltage = tempData * 0.0000078125f; break;
default: voltage = 0; break;
}
return voltage;
}
/* 校准ZMCT103C零点偏移 */
void calibrate_zero_offset(void)
{
lcd_show_string(10, 210, 200, 16, 16, "Calibrating Zero Offset...", BLUE);
// 确保无电流通过互感器
delay_ms(2000);
float sum = 0.0f;
uint16_t sample_count = 100;
for (int i = 0; i < sample_count; i++)
{
sum += ADS1115_Read_ADC(0);
delay_ms(10);
}
g_zero_offset_voltage = sum / sample_count;
g_offset_calibrated = 1;
char debug[40];
snprintf(debug, sizeof(debug), "Offset: %.3fV Calibrated", (double)g_zero_offset_voltage);
lcd_show_string(10, 210, 200, 16, 16, debug, GREEN);
delay_ms(2000);
}
/* 计算交流电压RMS值(关键函数) */
float calculate_rms_voltage(uint8_t channel)
{
float sum_squares = 0.0f;
float sample;
uint32_t start_time = HAL_GetTick();
// 采样多个周期计算RMS
for (int i = 0; i < TOTAL_SAMPLES; i++)
{
sample = ADS1115_Read_ADC(channel);
// 减去直流偏置(如果已校准)
if (g_offset_calibrated)
{
sample -= g_zero_offset_voltage;
}
sum_squares += sample * sample;
// 根据采样率控制采样间隔
uint32_t expected_time = start_time + (i * 1000 / SAMPLING_RATE);
while (HAL_GetTick() < expected_time)
{
delay_ms(1);
}
}
return sqrtf(sum_squares / TOTAL_SAMPLES);
}
/* ZMCT103C交流电压转电流计算(基于技术参数) */
float zmct103c_voltage_to_current(float rms_voltage)
{
/*
* 基于ZMCT103C技术参数计算:
* 额定点:输入5A → 输出5mA → 在100Ω负载上产生0.5V RMS电压
* 计算比例系数:K = 5A / 0.5V = 10 A/V
*
* 物理原理计算:
* 1. 次级电流 I_secondary = V_rms / R_burden
* 2. 初级电流 I_primary = I_secondary × N
*/
// 方法1:直接比例计算
float current_direct = rms_voltage * 10.0f;
// 方法2:基于物理原理计算(更准确)
float secondary_current = rms_voltage / ZMCT103C_BURDEN_RESISTOR;
float current_physical = secondary_current * ZMCT103C_TRANSFORMER_RATIO;
// 使用方法2,并应用精度修正
float calculated_current = current_physical * (1.0f + ZMCT103C_LINEARITY);
return calculated_current;
}
/* 计算电缆阻抗 */
float calculate_impedance(float voltage, float current)
{
if (fabsf(current) < 0.001f || voltage < 0 || current < 0)
{
return -1.0f;
}
return voltage / current;
}
/* LCD显示函数 */
void lcd_display_channel(DisplayChannel* channel, uint8_t size)
{
char buffer[30];
if (strcmp(channel->label, "Impedance") == 0)
{
if (channel->value < 0)
{
snprintf(buffer, sizeof(buffer), "%s: N/A", channel->label);
}
else if (channel->value > 1000)
{
snprintf(buffer, sizeof(buffer), "%s:%.2fkΩ", channel->label, (double)(channel->value/1000));
}
else
{
snprintf(buffer, sizeof(buffer), "%s:%.2fΩ", channel->label, (double)channel->value);
}
}
else if (strcmp(channel->label, "RMS Voltage") == 0)
{
snprintf(buffer, sizeof(buffer), "%s:%.3fV", channel->label, (double)channel->value);
}
else if (strcmp(channel->label, "Offset Voltage") == 0)
{
snprintf(buffer, sizeof(buffer), "%s:%.3fV", channel->label, (double)channel->value);
}
else
{
const char* unit = (strcmp(channel->label, "Voltage") == 0) ? "V" : "A";
if (strcmp(channel->label, "Voltage") == 0)
{
snprintf(buffer, sizeof(buffer), "%s:%.1f%s", channel->label, (double)channel->value, unit);
}
else
{
snprintf(buffer, sizeof(buffer), "%s:%.3f%s", channel->label, (double)channel->value, unit);
}
}
if (strcmp(buffer, channel->last_display) != 0)
{
uint16_t text_width = strlen(buffer) * size * 6;
lcd_fill(channel->x, channel->y, channel->x + text_width, channel->y + size + 2, WHITE);
lcd_show_string(channel->x, channel->y, 240, size, size, buffer, channel->color);
strncpy(channel->last_display, buffer, sizeof(channel->last_display) - 1);
channel->last_display[sizeof(channel->last_display) - 1] = '\0';
}
}
/* 显示状态信息 */
void lcd_show_status(uint16_t x, uint16_t y, char* status, uint16_t color)
{
static char last_status[50] = "";
if (strcmp(status, last_status) != 0)
{
lcd_fill(x, y, 240, y + 16, WHITE);
lcd_show_string(x, y, 200, 16, 16, status, color);
strncpy(last_status, status, sizeof(last_status) - 1);
last_status[sizeof(last_status) - 1] = '\0';
}
}
/* 显示模块参数信息 */
void lcd_show_module_info(uint16_t x, uint16_t y)
{
char info[80];
snprintf(info, sizeof(info), "ZMCT103C %d:1 R=%.0fΩ 0.2级",
(int)ZMCT103C_TRANSFORMER_RATIO, ZMCT103C_BURDEN_RESISTOR);
lcd_show_string(x, y, 200, 16, 16, info, GREEN);
}
int main(void)
{
HAL_Init();
sys_stm32_clock_init(RCC_PLL_MUL9);
delay_init(72);
usart_init(115200);
led_init();
lcd_init();
rs485_init(9600);
MX_I2C2_Init();
// 初始化显示
lcd_clear(WHITE);
lcd_show_string(30, 40, 200, 16, 16, "RMS Measurement System", RED);
// 显示模块参数
lcd_show_module_info(30, 60);
// 初始化显示通道(增加RMS电压显示)
DisplayChannel channels[4] =
{
{"Voltage", 0.0f, RED, 30, 90, ""}, // RS485读取的线路电压
{"RMS Voltage", 0.0f, MAGENTA, 30, 120, ""}, // ZMCT103C输出的RMS电压
{"Current", 0.0f, BLUE, 30, 150, ""}, // 计算得到的电流RMS值
{"Impedance", 0.0f, GREEN, 30, 180, ""} // 计算得到的阻抗
};
// 显示固定标签
for (int i = 0; i < 4; i++)
{
char label[30];
snprintf(label, sizeof(label), "%s:", channels[i].label);
lcd_show_string(channels[i].x, channels[i].y, 100, 16, 16, label, BLACK);
}
lcd_show_status(10, 210, "Waiting for module stabilization...", BLUE);
// 关键修改1:等待模块稳定(解决缓慢变化问题)
delay_ms(5000); // 等待5秒让ZMCT103C模块稳定
// 关键修改2:校准零点偏移(必须在无电流条件下进行)
lcd_show_status(10, 210, "Please ensure NO current flow", RED);
lcd_show_string(10, 230, 200, 16, 16, "Calibrating in 5 seconds...", RED);
delay_ms(5000);
calibrate_zero_offset();
lcd_show_status(10, 230, "Starting RMS measurement...", GREEN);
delay_ms(1000);
uint32_t last_refresh = 0;
uint16_t success_count = 0;
uint16_t error_count = 0;
uint32_t last_calibration_time = HAL_GetTick();
while (1)
{
uint32_t current_time = HAL_GetTick();
// 定期重新校准(每10分钟)
if (current_time - last_calibration_time > CALIBRATION_INTERVAL)
{
calibrate_zero_offset();
last_calibration_time = current_time;
}
if (current_time - last_refresh > 1000) // 1秒刷新一次
{
float line_voltage, rms_voltage, calculated_current;
uint8_t read_success = 1;
// 1. 读取线路电压
line_voltage = read_voltage_via_rs485();
// 2. 关键修改3:计算ZMCT103C输出的RMS电压(交流信号)
if (read_success)
{
rms_voltage = calculate_rms_voltage(0);
// 验证RMS电压在合理范围内
if (rms_voltage >= 0 && rms_voltage <= 2.0f) // ZMCT103C最大输出约2V
{
// 3. 将RMS电压转换为电流
calculated_current = zmct103c_voltage_to_current(rms_voltage);
success_count++;
}
else
{
lcd_show_status(10, 200, "RMS Voltage Out of Range", RED);
read_success = 0;
error_count++;
}
}
if (read_success)
{
// 4. 更新显示数据
channels[0].value = line_voltage;
channels[1].value = rms_voltage;
channels[2].value = calculated_current;
channels[3].value = calculate_impedance(line_voltage, calculated_current);
// 5. 显示所有数据
for (int i = 0; i < 4; i++)
{
lcd_display_channel(&channels[i], 16);
}
// 显示状态信息(包含偏移电压)
char status[60];
snprintf(status, sizeof(status), "OK:%d RMS:%.3fV ",
success_count, (double)rms_voltage);
lcd_show_status(10, 250, status, GREEN);
}
LED0_TOGGLE();
last_refresh = current_time;
}
delay_ms(10);
}
}
你看看我的代码还有哪里可以改进的,目前测得的电流数值太不稳定了,我怀疑通电后测得得这个电流数值不是真实的,然后再帮我改改电阻显示部分,让其直接显示具体的数值,而不是显示2k,2.5k这种方式
最新发布