Article directory
Principle introduction
The realization process of the breathing light is to add pulse output with different duty cycles to the LED, and the LED light will display different brightness. By constantly adjusting the duty cycle of the square wave, the brightness of the LED light will also change accordingly. See It feels like "breathing".
To obtain pulses with different duty cycles, the method of pulse width modulation (PWM) must be used. Pulse width modulation is a commonly used module. In practical applications, such as controlling the speed of motors and adjusting the brightness of lights, etc. The schematic diagram of pulse width modulation is as follows.
Using an N-bit counter, the maximum value can be expressed as 2 to the Nth power and the minimum value is 0. The counter accumulates with a given value as a step value. After adding to the maximum value, it will overflow and then enter the next accumulation. cycle. When the value of the counter is greater than a certain threshold (note that the threshold value here is not fixed, but changes), the pulse output is high, otherwise the output is low, so that the pulse shown by the red line in the figure above can be completed. Pulse output with adjustable duty cycle.
The step value of the counter can be calculated based on the output frequency: step value = output frequency×2 n system clock frequency\frac{output frequency×2^n}{system clock frequency}System clock frequencyOutput frequency × 2n。
Experimental code
The code used in this experiment has two modules. First, the first module is the pulse width modulation module, and its code is as follows.
`timescale 1ns / 1ps
module pwm
# (parameter N = 16) //端口使用的参数在这里定义
(
input clk,
input rst,
input[N-1:0] period, //步进值,根据想要的输出频率计算得到
input[N-1:0] threshold, //与count进行比较的门限值(可变的)
output pwm_out
);
reg[N-1:0] period_reg; //接收输入值对应的寄存器
reg[N-1:0] threshold_reg;
reg[N-1:0] count; //以period为步进值的计数器
reg pwm_reg;
assign pwm_out = pwm_reg;
always@(posedge clk or negedge rst)
begin
if(rst == 0)
begin
period_reg <= {
N{
1'b0}}; //将N位二进制的0拼接
threshold_reg <= {
N{
1'b0}};
end
else
begin
period_reg <= period; //将输入值存放在各自的寄存器中
threshold_reg <= threshold;
end
end
always@(posedge clk or negedge rst)
begin
if(rst == 0)
count <= {
N{
1'b0}};
else
count <= count + period_reg; //计数器累加
end
always@(posedge clk or negedge rst)
begin
if(rst == 0)
pwm_reg <= 1'b0;
else
begin
if(count >= threshold_reg) //将count值与输入的门限值比较,大于门限则将脉冲输出置为1,小于则置为0
pwm_reg <= 1'b1;
else
pwm_reg <= 1'b0;
end
end
The second module is used to set input parameters, and its code is as follows.
`timescale 1ns / 1ps
module led_breathing(
input clk,
input rst,
output led
);
localparam STEP = 32'd100000; //threshold对应的步进值
localparam MIN_VALUE = 32'h6fffffff; //threshold最小值,减到比此值更小时就进行加操作
localparam MAX_VALUE = 32'hffffffff; //threshold最大值,加到比此值更大时就进行减操作
localparam IDLE = 0; //空闲
localparam PWM_PLUS = 1; //加操作
localparam PWM_MINUS = 2; //减操作
localparam PWM_GAP = 3; //间隔,判断下次是加操作是减操作
wire pwm_out; //脉冲输出
reg pwm_flag; //加减操作的标志位,加操作设置为0,减操作设置为1
reg [1:0] state; //四个状态
reg [31:0] period; //count的步进值
reg [31:0] threshold;
reg [31:0] timer; //PWM_GAP状态下使用
assign led = ~pwm_out; //pwm_out输出高电平时,led亮,led是低电平有效的
always@(posedge clk or negedge rst)
begin
if(rst == 1'b0)
begin
pwm_flag <= 1'b0;
period <= 32'd0;
threshold <= 32'd0;
timer <= 32'd0;
state <= IDLE;
end
else
case(state)
IDLE:
begin
period <= 32'd17179; //period = 200*2^32/50000000,pwm=200Hz
state <= PWM_PLUS; //状态设置为加
threshold <= MIN_VALUE; //threshold的初始值设置为最小值
end
PWM_PLUS:
begin
if(threshold > MAX_VALUE - STEP) //判断threshold的值是否超过了最大值,超过了就将加减操作状态标志设为减操作状态
begin
pwm_flag <= 1'b1;
threshold <= threshold - STEP;
end
else
begin
pwm_flag <= 1'b0;
threshold <= threshold + STEP;
end
state <= PWM_GAP; //完成一次加操作后进入PWM_GAP状态
end
PWM_MINUS:
begin
if(threshold < MAX_VALUE + STEP) //判断threshold的值是否低于了最小值,小于了就将加减操作状态标志设为加操作状态
begin
pwm_flag <= 1'b0;
threshold <= threshold + STEP;
end
else
begin
pwm_flag <= 1'b1;
threshold <= threshold - STEP;
end
state <= PWM_GAP; //完成一次减操作后进入PWM_GAP状态
end
PWM_GAP:
begin
if(timer >= 5000) //100us的计数次数,做仿真时,这里设置的小一点
begin
if(pwm_flag) //根据加减操作标志决定下一次执行的操作
state <= PWM_MINUS;
else
state <= PWM_PLUS;
timer <= 32'd0;
end
else
timer <= timer + 32'd1; //计数到达规定的时长后再执行下次加或减操作
end
default: state <= IDLE;
endcase
end
pwm //将第一个模块在这里进行例化
# (.N(32))
pwm_inst(
.clk(clk),
.rst(rst),
.period(period),
.threshold(threshold),
.pwm_out(pwm_out)
);
endmodule
Software simulation
The simulation test code used in this experiment is as follows.
`timescale 1ns / 1ps
module led_breathing_sim();
reg clk;
reg rst;
wire led;
initial
begin
clk = 0;
rst = 0;
#100
rst = 1;
end
always #10 clk = ~clk;
led_breathing uut_led_breathing(
.clk(clk),
.rst(rst),
.led(led)
);
endmodule
The results of the simulation are shown in the figure below.
In the figure above, pwm_out is the output waveform after pulse width modulation, and led is the inversion of the pwm_out signal. It can be seen that the duty cycle of the waveform is different.
The step value period of the counter set in the code is the value corresponding to 200Hz, and the period corresponding to 200Hz is 5ms. If you zoom in on the above simulation diagram, you can see that although the duty cycle of each square wave is different, its period is 5ms. .
I was quite dizzy when I was reading the code, and I didn’t quite understand why the duty cycles of the output square waveforms were different. I converted several hexadecimal numbers in the code to decimal numbers. First, the minimum value corresponding to threshold is 1879048191 (6fffffff in hexadecimal), and the maximum is 4294967295 (ffffffff in hexadecimal). Its step value is 100000, that is to say, from 1879048191 to 4294967295, adding 100000 each time requires 24159 times; the minimum value corresponding to count is 0, and the maximum value is after automatic overflow, which is 4294967295. The step value of count is period=17179, it takes 250013 times from 0 to overflow. The relationship between the two is almost ten times, and one thing is different is that after the threshold is added to the maximum value, it does not drop directly to the minimum value, but decreases step by step, while the count is overflowed, which is equivalent to starting from 0 A new round of counting.
Based on the above analysis, I simply drew a schematic diagram, as shown below.
It stands to reason that the count from 0 to overflow should correspond to about 10 such waveforms, but in order to draw clearly, a simple schematic diagram is drawn here to illustrate the principle. And here I replaced the ascending and descending steps with straight lines, because these small steps can be abstracted into a point from a macro perspective. In the figure above, the part where the blue line (count) is higher than the black line (threshold), the output pulse is high; the part where the blue line is lower than the black line, the output pulse is low. By drawing a schematic diagram like this, you can clearly reflect how the code generates waveforms with different duty cycles.
On-board verification
The pin assignments in this experiment are shown in the figure below.
Through the above simulation, we only look at the waveform of the output signal LED. The duty cycle of the waveform is constantly changing, which is reflected in the LED on and off characteristics of the development board like "breathing". The animation of the breathing light is shown in the figure below. Show.
The above is all the content implemented by ZYNQ-Pulse Width Modulation Breathing Light!
Reference:
ZYNQ Development Platform FPGA Tutorial AX7020