有效值(RMS)的计算原理
有效值的数学定义是:对于连续信号,有效值是信号瞬时值的平方在一个周期内的平均值的平方根;
X
r
m
s
=
1
T
∫
0
T
[
x
(
t
)
]
2
d
t
X_{rms} = \sqrt{\frac{1}{T} \int_{0}^{T} [x(t)]^2 dt}
Xrms=T1∫0T[x(t)]2dt
对于离散采样信号,计算公式为:
X
r
m
s
=
1
N
∑
i
=
0
N
−
1
x
i
2
X_{rms} = \sqrt{\frac{1}{N} \sum_{i=0}^{N-1} x_i^2}
Xrms=N1i=0∑N−1xi2
其中:
- N N N 是一个周期内的采样点数
- x i x_i xi 是第 i i i 个采样点的数值
在FPGA/Verilog实现中,通常分三步:
- 对采样值进行平方运算
- 对平方结果进行累加并求平均
- 对平均值进行开方运算(牛顿迭代法)
Verilog实现代码
以下是一个可综合的有效值计算模块,包含平方、累加平均、开方核心逻辑,适合FPGA实现:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2025/08/04 10:21:00
// Design Name:
// Module Name: current_rms_calculator
// Project Name:
// Target Devices:
// Tool Versions:
// Description: 电流有效值计算器,支持任意大小的采样窗口
//
// Dependencies: advanced_divider.v, sqrt_calculator.v
//
// Revision:
// Revision 0.01 - File Created
// Revision 0.02 - 增加窗口大小可调功能
// Revision 0.03 - 支持任意窗口大小,不再局限于2的幂次
// Revision 0.04 - 补充完整开方计算模块
// Revision 0.05 - 替换为高级除法器模块,增加错误处理
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module current_rms_calculator (
input wire clk_100MHZ, // 系统时钟
input wire rst_n, // 异步复位(高有效)
input [15:0] data, // 电流采样值(有符号16位)
input sample_valid, // 采样有效信号
input [15:0] sin_cycle, // 采样窗口大小(可调,任意正整数)
output [31:0] rms_value, // 计算得到的RMS值(无符号16位)
output reg rms_valid // RMS值有效信号
);
// 参数定义
// parameter MAX_WINDOW_SIZE = 1024; // 最大窗口大小
// parameter SQUARE_WIDTH = 32; // 平方结果的位宽
// parameter RMS_WIDTH = 16; // RMS结果位宽
// 步骤1:对输入数据进行平方运算(有符号数处理)
reg signed [31:0] din_square;
reg signed [15:0] din_signed;
always @(posedge clk_100MHZ or negedge rst_n) begin
if(!rst_n) begin
din_signed <= 16'd0;
din_square <= 32'd0;
end else if(sample_valid) begin
din_signed <= data;
// 有符号数平方:先取绝对值再相乘,避免负数平方溢出
din_square <= (din_signed >= 0) ? (din_signed * din_signed) : ((-din_signed) * (-din_signed));
end
end
wire [31:0] avg_out;
wire avg_valid;
// // 步骤2:累加平方值并求平均(N为2的幂次,用移位代替除法)
sliding_window_avg_variable #(
.DATA_WIDTH (32), // 重定义数据位宽为16
.MAX_WINDOW (200), // 重定义最大窗口为2048
.SUM_WIDTH (40) // 总和位宽:16 + log2(2048)=16+11=27,这里设41留余量
) u_sliding_window_avg_variable (
.clk (clk_100MHZ), // 时钟信号
.rst_n (rst_n), // 复位信号
.din (din_square), // 输入数据
.din_valid (sample_valid), // 输入有效
.window_size (200), // 窗口大小配置
.avg_out (avg_out), // 平均值输出
.avg_valid (avg_valid) // 输出有效
);
// // 步骤3:开方运算(牛顿迭代法,适合硬件实现) 10个时钟周期才能完成开方运算
//核心公式:x_{n+1} = 1/2*(x_n + S/x_n)
reg [31:0] sqrt_result;
reg [31:0] sqrt_temp;
reg [4:0] sqrt_state; // 迭代状态机
reg [31:0] rms_out;
always @(posedge clk_100MHZ or negedge rst_n) begin
if(!rst_n) begin
sqrt_result <= 'd5000;
sqrt_temp <= 'd5000;
sqrt_state <= 'd0;
rms_out <= 'd0;
end
else begin//一直进行开方运算 10个时钟周期输出一次有效值
case(sqrt_state)
0: begin
sqrt_temp <= avg_out;
sqrt_result <= (avg_out/2);
sqrt_state <= sqrt_state + 1'b1;
end
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30: begin // 8次迭代(足够收敛)
sqrt_result <= (sqrt_result + sqrt_temp / sqrt_result) >> 1;
sqrt_state <= sqrt_state + 1'b1;
end
31: begin
rms_out <= sqrt_result; // 迭代完成,输出有效值
sqrt_state <= 'd0;
end
endcase
end
end
endmodule
滑动窗口求取开方后的累加平均值
module sliding_window_avg_variable #(
parameter DATA_WIDTH = 32, // 输入数据位宽
parameter MAX_WINDOW = 1024, // 支持的最大窗口大小(可根据需求调整)
parameter SUM_WIDTH = 40 // 总和位宽(32 + log2(MAX_WINDOW) ≈ 40)
)(
input wire clk, // 时钟信号
input wire rst_n, // 低电平复位
input wire [DATA_WIDTH-1:0] din, // 输入32位数据
input wire din_valid, // 输入数据有效信号
input wire [9:0] window_size, // 运行时配置的窗口大小(0~1023,需≥1)
output reg [DATA_WIDTH-1:0] avg_out, // 输出平均值(32位)
output reg avg_valid // 输出平均值有效信号
);
// 1. 定义滑动窗口寄存器(最大深度MAX_WINDOW)
reg [DATA_WIDTH-1:0] window_reg [0:MAX_WINDOW-1];
// 2. 辅助寄存器:记录已存入的数据个数、移出的旧数据、当前有效总和
reg [9:0] data_cnt; // 已存入数据计数(0~MAX_WINDOW)
reg [DATA_WIDTH-1:0] old_data;
reg [SUM_WIDTH-1:0] sum_reg;
// 3. 滑动窗口移位+数据计数逻辑
integer i;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
// 复位清零
for(i = 0; i < MAX_WINDOW; i = i + 1) begin
window_reg[i] <= {DATA_WIDTH{1'b0}};
end
old_data <= {DATA_WIDTH{1'b0}};
data_cnt <= 10'd0;
end
else if(din_valid) begin
// 步骤1:更新数据计数(不超过当前窗口大小)
if(data_cnt < window_size) begin
data_cnt <= data_cnt + 10'd1;
end
// 步骤2:获取即将移出的旧数据(仅当窗口填满时)
if(data_cnt >= window_size && window_size > 10'd0) begin
old_data <= window_reg[window_size-1]; // 移出当前窗口的最旧数据
end else begin
old_data <= {DATA_WIDTH{1'b0}}; // 窗口未填满时,旧数据为0(仅累加)
end
// 步骤3:窗口数据移位(仅移位当前窗口大小内的数据)
if(window_size > 10'd1) begin
// 窗口填满后,才移位
for(i = window_size-1; i > 0; i = i - 1) begin
window_reg[i] <= window_reg[i-1];
end
end
// 新数据始终存入第一个位置
window_reg[0] <= din;
end
end
// 4. 动态增量求和逻辑(适配窗口大小变化)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
sum_reg <= {SUM_WIDTH{1'b0}};
end else if(din_valid) begin
if(data_cnt < window_size) begin
// 窗口未填满:仅累加新数据
sum_reg <= sum_reg + din;
end else begin
// 窗口已填满:增量更新(加新数据-减旧数据)
sum_reg <= sum_reg + din - old_data;
end
end
end
// 5. 动态求平均值(通用除法逻辑,适配任意窗口大小)
// 方案:使用移位近似法(兼顾资源和精度),也可替换为除法器IP
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
avg_out <= {DATA_WIDTH{1'b0}};
avg_valid <= 1'b0;
end else if(din_valid && data_cnt >= window_size && window_size > 10'd0) begin
// 仅当窗口填满且配置有效时,输出平均值
// 通用除法实现:sum_reg / window_size
// 方法1:直接用除法(综合器会自动优化,适合FPGA/ASIC)
avg_out <= sum_reg / window_size;
// 方法2:移位近似(可选,资源更少,精度略低)
// avg_out <= (sum_reg << 16) / (window_size << 16); // 扩展位宽后除法,减少截断误差
avg_valid <= 1'b1;
end else begin
avg_valid <= 1'b0;
end
end
endmodule
testbench
`timescale 1ns / 1ps
module rms_sine_100_000points_fixed_v2();
// 信号定义
reg clk_100MHZ;
reg rst_n;
reg [15:0] sin_cycle;
reg [15:0] sin_source;
wire [31:0] rms_value;
wire rms_valid;
wire div_error;
// 计数器、状态变量和计算用变量(全部在块外声明)
reg [31:0] count; //
reg [15:0] count_5us;
reg sample_valid;
/*
real是 Verilog 中的实数数据类型,用于存储带小数的数值(类似 C 语言的double),取值范围远大于整数类型,精度约为双精度浮点数。
Verilog 中real类型主要用于仿真阶段的数值计算(比如三角函数、浮点运算),不可综合(无法映射到硬件电路)
*/
real angle; // 角度变量(移到块外声明)
real sin_value; // 正弦值变量(移到块外声明)
real temp_paramter;
// 正弦波参数
parameter PI = 3.1415926535;
parameter AMPLITUDE = 100;//32767; // 16位有符号数最大值,正弦峰值,理论有效值23169
parameter POINTS = 100_000; // 一个周期的点数 100MHZ的时钟最大数据点数就是100K 最小步长
// 生成100MHz时钟
initial begin
clk_100MHZ = 0;
forever #5 clk_100MHZ = ~clk_100MHZ;
end
// 主测试流程
initial begin
// 初始化
rst_n = 0;
temp_paramter = 4;
// 复位操作
#100;
rst_n = 1;
#1000_000;
temp_paramter = 2;
#1000_000;
temp_paramter = 3;
#1000_000;
temp_paramter = 4;
#1000_000;
temp_paramter = 5;
#1000_000;
temp_paramter = 9;
#1000_000;
temp_paramter = 12;
#1000_000;
temp_paramter = 7;
#1000_000;
temp_paramter = 6;
end
//正弦波信号源
// 使用always块和计数器生成采样点
always @(posedge clk_100MHZ or negedge rst_n) begin
if (~rst_n) begin
count <= 32'd0;
sin_source <= 16'd0;
angle <= 0.0;
sin_value <= 0.0;
end
else begin
if (count < POINTS) begin
// 计算当前角度(弧度)和正弦值
angle <= 2 * PI * count / POINTS;
sin_value <= temp_paramter * AMPLITUDE * $sin(angle);
sin_source <= $rtoi(sin_value); // 量化为整数
count <= count + 32'd1;
end
else begin
count <= 32'd0;
end
end
end
//实例化采样
//假设有AD采样
always @(posedge clk_100MHZ or negedge rst_n) begin
if (~rst_n) begin
count_5us <= 10'd0;
sample_valid <= 1'b0;
end
else begin
if (count_5us < 499) begin
count_5us <= count_5us + 32'd1;
sample_valid <= 1'b0;
end
else begin
count_5us <= 32'd0;
sample_valid <= 1'b1;
end
end
end
// 实例化有效值计算模块
current_rms_calculator uut (
.clk_100MHZ(clk_100MHZ),
.rst_n(rst_n),
.data(sin_source),
.sample_valid(sample_valid),
.sin_cycle(sin_cycle),//周期
.rms_value(rms_value)
);
endmodule
代码关键部分解释
-
有符号数平方处理:
- 先将输入的有符号采样值取绝对值,再进行平方运算,避免负数平方导致的溢出问题。
- 平方后的位宽扩展为
2*DATA_WIDTH,保证数据不丢失。
-
累加与平均:
- 累加器位宽设计为
2*DATA_WIDTH + $clog2(N),避免累加N个平方值时溢出。 - 利用“移位代替除法”(仅当N为2的幂次时适用),提升硬件运算效率。
- 累加器位宽设计为
-
开方运算:
- 采用牛顿迭代法实现开方,这是硬件中最常用的开方算法,收敛速度快、资源占用少。
- 通过8次迭代即可达到足够的精度(可根据实际需求调整迭代次数)。
模块使用说明
-
参数配置:
DATA_WIDTH:根据你的采样精度设置(如12/16位)。N:设置为一个周期内的采样点数,建议为2的幂次(如256、512、1024),简化除法运算。
-
输入输出:
- 输入:
clk(系统时钟)、rst_n(复位)、din(采样数据)、din_valid(数据有效标志)。 - 输出:
rms_out(计算得到的有效值)、rms_valid(输出有效标志)。
- 输入:
-
前置条件:
- 确保采样时钟与系统时钟同步,采样率满足奈奎斯特采样定理。
- 采样点数N需覆盖完整的信号周期,否则计算结果会有误差。
总结
- Verilog实现有效值计算的核心步骤是:平方运算 → 累加平均 → 开方运算,其中累加平均优先用移位代替除法以提升硬件效率。
- 有符号采样值需先取绝对值再平方,避免溢出;开方采用牛顿迭代法,通过多次迭代保证精度。
- 采样点数N建议设为2的幂次,且需覆盖完整信号周期,才能得到准确的有效值结果。
的计算&spm=1001.2101.3001.5002&articleId=157185490&d=1&t=3&u=ca0f9489be6e467aafb4b02d281a4e84)
516

被折叠的 条评论
为什么被折叠?



