ZYNQ——Pulse width modulation breathing lamp implementation


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.
Insert image description here
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.
Insert image description here
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. .
Insert image description here
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.
Insert image description here
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.
Insert image description here
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.
Please add image description


The above is all the content implemented by ZYNQ-Pulse Width Modulation Breathing Light!
Reference:
ZYNQ Development Platform FPGA Tutorial AX7020

Guess you like

Origin blog.csdn.net/weixin_42570192/article/details/131258728