《基于FPGA的便携式PWM方波信号发生器》论文分析(二)——方波信号产生
一、论文概述
基于FPGA的便携式PWM方波信号发生器是一篇由任青颖、庹忠曜、黄洵桢、李智禺和张贤宇 等人发表的一篇期刊论文。该论文主要研究了一种新型的信号发生器,旨在解决传统PWM信号发生器在移动设备信号调控中存在的精准度低和便携性差的问题 。其基于现场可编程门阵列(FPGA)技术,设计了一款便携式PWM信号发生器。该设备能够实现占空比以1%的步长可调,频率通过四个按键分别控制实现100 Hz、1 kHz、10 kHz、100 kHz的四个频率脉宽可调的方波信号发生器。系统能够以10 μs的最小分辨率在数码管上进行显示 。对于电子工程和信号处理领域的专业人士提供了一种新的解决方案,有助于提高信号发生器的性能和便携性 。
二、可控时钟信号输入
假定FPGA的输入周期信号的周期为,频率为,则其之间满足关系为:测量脉宽的最小分辨率为,得到测量信号的最大周期为,由公式(1)可求得其最小频率为。根据所设计的电气指标,输出频率范围应在至之间,而FPGA芯片所携带的晶振通常,为实现目标频率的输出信号,需对输入信号进行分频处理,假定分频前脉冲信号的频率为,分频后脉冲信号的频率为,则其相互之间满足以下关系:为实现的测量信号,需对晶振信号进行120分频处理,按照题目要求,后续还需对于的信号进行10分频,100分频和1000分频处理,并通过频率开关对这四个信号进行选择输出,其时钟信号选择电路设计图如图2所示:
设定按键开关按下时真值为1,反之即为0。本实例以四个按键key1、key2、key3、key4的高低位顺序将这四个按键视为一个按键组,分别控制信号发生器分别输出时钟信号,对应的按键组真值分别为1000、0100、0010、0001时,其他情况下不输出任何信号,即当且仅当接通其中一个频率开关时,信号发生器输出其对应的频率信号。按照此设计思路,理论上可以通过组合逻辑电路实现2n个(n为按键数)分频状态,但由于篇幅所限,本文暂不做详细讨论。
三、模100计数
如2.1所述,需要对信号进行模100计数,为实现该功能,特采用两片CB4CLED进行异步级联,其中一个作为个位计数器,当其数据由9重新置为0时,另一片CB4CLED作为十位计数器,计数加一,其电路设计图如图3所示:
其中左侧的CB4CLED即为个位计数器,自0000起始逐渐递增,至1001时,L端置1,CB4CLED数据置1,此时右侧的十位计数器开始计数一次,以此类推整个模100计数模块的数据可一直加至99。当数据到达99后,遇到下一个CP信号的上升沿,则其由1001变为0000,并向前进位一位,十位的数据也因此从1001变为0000,至此整个计数模块的数据重新置为0000,实现模100的计数循环。
四、模100可逆置数
模100可逆置数的功能分为可逆计数与按键置数两部分。其中可逆计数的原理与模100计数的原理大致相同,相较于模100计数的循环递增而言,增添了可控递减功能,为区别2.2的模100计数的异步级联,此处对两片芯片采用同步级联,则有可逆加减逻辑功能表如表1所示:
表1.可逆加减逻辑功能表
Input | Output | ||||
CLR | RESET | INC | DEC | CP2 | |
1 | X | X | X | X | 00000000 |
0 | 1 | X | X | 00110010 | |
0 | 0 | 0 | 0 | No Change | |
0 | 0 | 0 | 1 | DEC | |
0 | 0 | 1 | 0 | INC | |
0 | 0 | 1 | 1 | No Change |
其中CLR为清零信号,RESET为置数50开关,即实现一键控制得到50%占空比波形。INC与DEC分别为加开关与减开关控制按键,CP2为此模块的输入脉冲信号,根据上述逻辑功能表关系可对图2进行设计,得到模100可逆置数电路图如图4所示:
五、脉宽测量与BCD比较
在之前的设计中输出信号的周期长度约为测量信号的100倍至100000倍之间,假定输出信号的上升沿与测量信号的某一上升沿位于同一时刻,则输出信号的高电平持续时间t与测量信号的个数、周期,应当满足以下关系:由公式(1)可推得T为,确保测量信号的频率一定,令数码管的的显示为,联立公式(1)与公式(6)即可求得所需的脉宽测量值。
以两片COMPM4进行级联输入扩展为8位2进制的比较器,将模一百计数模块的数据A与模一百可逆置数模块的数据B按照自高位至低位的顺序进行相互比较,若高位A不等于B,则直接输出指定占空比的PWM波,反之则进行下一位的比较,直至所有数据比较完毕。
由之前所述,输出信号的周期长度最大为测量信号的100000倍,故为实现信号的精准测量,需设计一个模100000的计数器,该计数器原理与2.2的模100计数原理相同,此处不予赘述,其脉宽测量与BCD比较电路如图5所示:
六、代码示例分析
此处提供一个基于FPGA的便携式PWM方波信号发生器的示例代码的大致框架和分析。这个示例代码将使用Verilog HDL编写,这是FPGA设计中常用的硬件描述语言。
module PWM_Generator(
input clk, // 时钟信号
input rst_n, // 复位信号,低电平有效
input [15:0] freq, // 频率控制输入
input [15:0] duty, // 占空比控制输入
output reg pwm_out // PWM输出
);
// 定义参数
parameter CLOCK_FREQ = 50_000_000; // FPGA时钟频率
parameter MAX_COUNT = CLOCK_FREQ / 100; // 最大计数器值
// 计数器变量
reg [31:0] counter = 0;
reg [31:0] period = 0;
reg [31:0] high_time = 0;
// 计算周期和高电平时间
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
counter <= 0;
pwm_out <= 0;
end else begin
counter <= counter + 1;
if (counter >= period) begin
counter <= 0;
pwm_out <= ~pwm_out; // 切换输出状态
end
end
end
// 根据频率和占空比更新周期和高电平时间
always @(posedge clk) begin
if (counter == 0) begin
period <= (CLOCK_FREQ / (freq + 1)) - 1; // 计算周期
high_time <= (period * duty) / 100; // 计算高电平时间
end
end
endmodule
为了使基于FPGA的PWM方波信号发生器更加复杂和功能丰富,我们可以考虑添加以下功能:
- 可配置的频率和占空比:允许通过外部输入动态调整频率和占空比。
- 多位频率和占空比控制:使用更多的位数来控制频率和占空比,以提高分辨率。
- 多位输出:生成多个PWM通道,每个通道可以独立控制。
- 同步和异步复位:提供同步和异步复位选项,以提高系统的灵活性和可靠性。
- 死区时间控制:在PWM波形中添加死区时间,以防止短路和电磁干扰。
- 中断和事件标志:在特定的PWM事件(如周期结束)时生成中断或事件标志。
- 可编程输出极性:允许用户选择PWM输出的高电平或低电平为活动电平。
- 动态调整和实时更新:在运行时动态调整频率和占空比,而不需要复位。
以下是一个扩展的示例代码,实现了上述部分功能:
module PWM_Generator_Advanced(
input clk, // 时钟信号
input rst_n, // 复位信号,低电平有效
input sync_rst_n, // 同步复位信号,低电平有效
input [15:0] freq, // 频率控制输入
input [15:0] duty, // 占空比控制输入
input [7:0] channel_enable, // 通道使能控制
output reg [7:0] pwm_out // 多位PWM输出
);
// 定义参数
parameter CLOCK_FREQ = 50_000_000; // FPGA时钟频率
parameter MAX_COUNT = CLOCK_FREQ / 100; // 最大计数器值
// 计数器变量
reg [31:0] counter = 0;
reg [31:0] period = 0;
reg [31:0] high_time = 0;
reg [7:0] high_time_array = 0;
// 动态调整频率和占空比
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
counter <= 0;
pwm_out <= 0;
end else if (!sync_rst_n) begin
counter <= 0;
pwm_out <= 0;
end else begin
counter <= counter + 1;
if (counter >= period) begin
counter <= 0;
pwm_out <= pwm_out << 1; // 左移一位,为下一个通道腾出空间
if (channel_enable & (1 << 0)) pwm_out[0] <= ~pwm_out[0]; // 通道0
if (channel_enable & (1 << 1)) pwm_out[1] <= ~pwm_out[1]; // 通道1
// 继续为其他通道添加逻辑
end
end
end
// 根据频率和占空比更新周期和高电平时间
always @(posedge clk) begin
if (counter == 0) begin
period <= (CLOCK_FREQ / (freq + 1)) - 1; // 计算周期
high_time <= (period * duty) / 100; // 计算高电平时间
high_time_array <= high_time; // 更新高电平时间数组
end
end
// 动态调整频率和占空比
always @(posedge clk) begin
if (counter == 0) begin
period <= (CLOCK_FREQ / (freq + 1)) - 1;
high_time <= (period * duty) / 100;
for (int i = 0; i < 8; i++) begin
if (channel_enable & (1 << i)) begin
high_time_array[i] <= (period * duty) / 100;
end
end
end
end
endmodule
这个扩展的示例代码提供了一个更复杂的PWM信号发生器,支持多位输出和动态调整频率和占空比。在实际应用中,可以根据具体需求进一步扩展和优化。
module Advanced_PWM_Generator(
input wire clk, // 主时钟
input wire rst_n, // 异步复位
input wire sync_rst_n, // 同步复位
input wire [15:0] freq, // 频率控制输入
input wire [15:0] duty, // 占空比控制输入
input wire [15:0] phase, // 相位控制输入
input wire [15:0] dead_time, // 死区时间控制输入
input wire [7:0] channel_enable, // 通道使能控制
output reg [7:0] pwm_out // 多位PWM输出
);
// 定义参数
parameter CLOCK_FREQ = 50_000_000; // FPGA时钟频率
parameter MAX_COUNT = CLOCK_FREQ / 100; // 最大计数器值
// 内部变量
reg [31:0] counter = 0;
reg [31:0] period = 0;
reg [31:0] high_time = 0;
reg [31:0] low_time = 0;
reg [31:0] dead_count = 0;
reg [31:0] ramp_up = 0;
reg [31:0] ramp_down = 0;
reg [7:0] pwm_state = 0;
// 计算周期、高电平时间和低电平时间
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
counter <= 0;
pwm_out <= 0;
pwm_state <= 0;
end else if (!sync_rst_n) begin
counter <= 0;
pwm_state <= 0;
end else begin
counter <= counter + 1;
case (pwm_state)
0: begin // 斜坡上升
if (counter < ramp_up) pwm_out <= pwm_out | (1 << pwm_state);
else begin
pwm_state <= pwm_state + 1;
counter <= 0;
end
end
1: begin // 高电平
if (counter < high_time) ;
else begin
pwm_state <= pwm_state + 1;
counter <= 0;
end
end
2: begin // 死区时间
if (counter < dead_time) ;
else begin
pwm_state <= pwm_state + 1;
counter <= 0;
end
end
3: begin // 低电平
if (counter < low_time) ;
else begin
pwm_state <= pwm_state + 1;
counter <= 0;
end
end
4: begin // 斜坡下降
if (counter < ramp_down) pwm_out <= pwm_out & ~(1 << (pwm_state - 1));
else begin
pwm_state <= 0;
counter <= 0;
end
end
default: pwm_state <= 0;
endcase
end
end
// 根据频率、占空比、相位和死区时间更新周期、高电平时间和低电平时间
always @(posedge clk) begin
if (counter == 0) begin
period <= (CLOCK_FREQ / (freq + 1)) - 1;
high_time <= (period * duty) / 100;
low_time <= period - high_time - dead_time;
ramp_up <= (high_time * phase) / 100;
ramp_down <= (low_time * phase) / 100;
dead_count <= dead_time;
end
end
endmodule
这个示例代码提供了一个高度复杂的PWM信号发生器,支持多位输出、相位控制、死区时间和斜坡控制。