Tiny ZYNQ板 工程十二  基于ZYNQ FPGA资源的PWM演示 v1.0

PWM也即脉冲宽度调试技术,是电子领域的重要技术之一(广泛应用于开关电源,电机速度控制,LED灯亮度控制等领域),类似DSP,单片机,STM32等微处理器中都带有硬件PWM,本文将介绍如何使用VERILOG语言来写一个PWM,并通过呼吸灯的方式来进行效果的演示。

本文在 vivado2018.3版本上 演示, 其他版本请自行研究

PWM调整LED亮度的简单原理分析:

我们先来看下PWM的标准波形,如下图所示, 其中上中下三个波形的周期T和频率F是相同的,第一个波形高电平占整个周期的T的时间是25%,第二个波形占50%,第三个占75%。

因为三个波形的周期T和频率F是相同的,比方说 三个PWM的波形都是1KHZ,那么1秒钟内,三个PWM波形都会出现1000个周期为T的方波。如果此时拿这3个波形分别去点亮3个LED灯,那3个LED都会闪烁1000次(只是因为人眼的视觉残留因素,所以感觉不到这个闪烁),但是因为3者的占空比不同(高电平占整个周期的百分比不同)导致最上面25%占空比的波形单位时间内亮的时间最少,75%占空比的波形亮的时间最久。反应到视觉上的结果就是 第三个波形点亮的LED灯最亮,第一个波形对应的LED灯最暗,这也就是PWM调光的简单原理。同样的借助这个方法同样可以分析电机等场景。

同样当占空比为0%时,LED灯完全熄灭,当占空比为100%时LED灯完全点亮,借助这个原理,我们只要将占空比从0%逐渐增加到100%,再从100%逐渐减少到0%不断变化,就可以实现呼吸灯的效果了。

代码编写

首先我们要设计一个周期性的计数器来来进行波形周期的计数,我们定义占空比的频率为1KHZ,那波形的一个周期就是1ms,在50mhz时钟下,1ms相当于振荡了50000次(对应二进制1100001101010000,即16位),所以这里定义一个16位的寄存器作计数用

reg [15:0] period_cnt;

计数的代码如下

 reg  [15:0]  time_count;
 
//从0-50000的计数器,即1ms计数器
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        time_count<=16'd0;
    end
    else if(time_count == 16'd50_000)
        time_count <= 16'd0;
    else 
        time_count <= time_count + 1'b1;
end

其中 if(!rst_n)begin time_count <=16’d0; end 这句是负责复位用,外部rst_n信号接入到按键上

仅仅有计数器不能实现波形的输出功能,接下来我们简单写个 输出50%方波的程序,仅仅一句话就可以了

wire led=(time_count <16’d25_000)?1:0;

这句话相当于,在time_count由0计数到50_000的过程中,当小于25000时,输出1,当大于25000时候输出0,又因为整个周期是50000,而25000刚好是一半,那样就产生了一半的高电平,一半的低电平,即标准的50%方波。

我们的目标是设计呼吸灯,那只要把25000变成一个由小到大又从大变到小的寄存器pwm_perid就可以了。

代码如下

reg  [15:0]  pwm_perid=16'd0;
reg  mode=0;//mode为0则自增,mode为1为自减
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        pwm_perid<=16'd0;
    end
    else if(time_count== 16'd50_000)begin
        if(mode==1'b0)begin
            if(pwm_perid<16'd50_000) pwm_perid<=pwm_perid+50;
            else mode<=1'b1;
         end
        else if(mode==1'b1)begin
            if(pwm_perid>16'd0) pwm_perid<=pwm_perid-50;
            else mode<=1'b0;
         end
    end
end

增减程序的改变周期为1ms,即1秒钟改变1000次,这里偷个懒,直接用上面的time_count计数器来作这边的改变周期用if(time_count== 16’d50_000),当然也可以自己重新写一个,都一样的

程序里有一个reg mode,这个是用来记录工作模式的,mode为0则pwm_perid自增,mode为1pwm_perid为自减

而mode切换的条件就是 占空比达到最大,或者占空比被减少到0%

代码很简单,直接编译综合并且下载进FPGA观测结果

增加约束文件如下

set_property IOSTANDARD LVCMOS33 [get_ports led]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property PACKAGE_PIN K18 [get_ports clk]
set_property PACKAGE_PIN D18 [get_ports led]
set_property PACKAGE_PIN G15 [get_ports rst_n]

set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets {clk_IBUF}

下载完后就能看到 LED1在不停的变量变暗变量变暗了

完整工程如下:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注