|–作者:小黑同学
本文为明德扬原创及录用文章,转载请注明出处!
脉冲宽度调制技术(Pulse Width Modelation,PWM)是利用微处理器/FPGA的数字输出对模拟电路进行控制的一种有效技术,其广泛应用于测量、通信、功率控制与变换等众多领域。PWM数字信号从处理器到被控系统都采用数字形式,无需进行数模转换。航模中的控制信号大多是PWM信号,比如FUTABA、JR等舵机的控制都采用PWM方式,发射机给接收机输送脉冲后接收机就会控制舵机进行旋转。举个例子,假定基础脉宽是100ms,当发射机的脉宽增大(如增加到150ms)时接收机就控制舵机正向旋转;反之发射机的脉宽减小(如减小到50ms)时,接收机就控制舵机逆向旋转。
PWM是一种对模拟信号电平进行数字编码的方法。通过使用高分辨率计数器,对方波的占空比进行调制,从而对一个具体模拟信号的电平进行编码。由于在给定的任何时刻,满幅值的直流供电只存在有(ON)和无(OFF)两种状态,因此PWM信号仍然是数字信号。电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的。直流供电被加到负载上的时候为“通”,负载供电被断开的时候为“断”。只要有足够的带宽,任何模拟值都可以使用PWM进行编码。
通俗来说,PWM是连续的、具有一定比例占空比的脉冲信号,可以通过控制占空比来对其进行改变。简单来说,可以认为PWM就是一种方波,如图所示是一个周期为10ms,高电平为6ms,低电平时间为4ms的PWM,其占空比(高电平时间占整个周期的比例)为60%
PWM波形图
发光二极管简称为LED,是一种常用的发光器件,通过电子与空穴复合释放能量发光,它在照明领域应用广泛。发光二极管可高效的将电能转化为光能,在现代社会具有广泛的用途,如照明、平板显示、医疗器件等。可通过高低电平的变化来控制LED灯的明灭状态,当输出信号为低电平时,LED灯亮,反之,当输出信号为高电平时,LED灯灭。因此可通过改变PWM波形的占空比,来控制LED灯亮灭的时间,当有多个LED灯的时候,通过不同的PWM波形来控制不同的LED灯,就可以产生各种各样不同的效果。
本模块产生8个不同的PWM脉冲,控制8个LED灯点亮不同的时间,从而达到流水灯的效果。每个脉冲周期为10s,占空比从10%80%。上电后,led0点亮1s,熄灭9s;再点亮1s,熄灭9s,……,依次不断循环。led1led7与led0类似,分别点亮2s~8s,其他时候都是熄灭的。
信号名 | 接口方向 | 定义 |
---|---|---|
clk | 输入 | 系统时钟,50Mhz |
rst_n | 输入 | 低电平复位信号 |
led | 输出 | 8位led灯输出信号 |
根据题目功能要求可知,产生的8个不同的PWM脉冲的周期都是10秒(s),由此我们可以提出两个计数器的架构,如下图所示。
该架构由两个计数器组成:时钟计数器cnt_1s和周期计数器cnt_10s。
时钟计数器cnt_1s:用于计算1秒的时钟个数,加一条件为1,表示一直计数;结束条件为50000000,表示数到1秒就清零。
周期计数器cnt_10s:用于对1秒进行计数,加一条件为end_cnt_1s,表示数到1秒就加1;结束条件为10,表示数完10秒就清零。
至此,我们就将这个练习的计数器架构设计出来了。下面是计数器的代码。
always @(posedge clk or negedgerst_n)begin
if(!rst_n)begin
cnt_1s <= 0;
end
else if(add_cnt_1s)begin
if(end_cnt_1s)
cnt_1s <= 0;
else
cnt_1s <= cnt_1s + 1;
end
end
assign add_cnt_1s = 1;
assign end_cnt_1s = add_cnt_1s && cnt_1s==50_000_000 -1 ;
always @(posedge clk or negedgerst_n)begin
if(!rst_n)begin
cnt_10s <= 0;
end
else if(add_cnt_10s)begin
if(end_cnt_10s)
cnt_10s <= 0;
else
cnt_10s <= cnt_10s + 1;
end
end
assign add_cnt_10s = end_cnt_1s;
assign end_cnt_10s = add_cnt_10s && cnt_10s==10-1 ;
下面我们开始设计输出信号led,在设计这些信号的时候,我们只需要关注他的变化点,也就是0变1、1变0的点。
以led0的变化为例,上电后,led[0]点亮1s,熄灭9s,再点亮1s,熄灭9s。也就是数到1秒时,即add_cnt_10s && cnt_10s==1-1时,led0由0变1。当数到10秒时,即end_cnt_10s时,led[0]由1变0。
Led[1]~led[7]也是同样,即:
由此便可以将所有的led信号设计出来。下面是led的代码。
always @(posedge clk or negedgerst_n)begin
if(rst_n==1'b0)begin
led[0] <= 0;
end
else if(end_cnt_10s)begin
led[0] <= 0;
end
else if(add_cnt_10s && cnt_10s==1-1)begin
led[0] <= 1;
end
end
always @(posedge clk or negedgerst_n)begin
if(rst_n==1'b0)begin
led[1] <= 0;
end
else if(end_cnt_10s)begin
led[1] <= 0;
end
else if(add_cnt_10s && cnt_10s==2-1)begin
led[1] <= 1;
end
end
always @(posedge clk or negedgerst_n)begin
if(rst_n==1'b0)begin
led[2] <= 0;
end
else if(end_cnt_10s)begin
led[2] <= 0;
end
else if(add_cnt_10s && cnt_10s==3-1)begin
led[2] <= 1;
end
end
always @(posedge clk or negedgerst_n)begin
if(rst_n==1'b0)begin
led[3] <= 0;
end
else if(end_cnt_10s)begin
led[3] <= 0;
end
else if(add_cnt_10s && cnt_10s==4-1)begin
led[3] <= 1;
end
end
always @(posedge clk or negedgerst_n)begin
if(rst_n==1'b0)begin
led[4] <= 0;
end
else if(end_cnt_10s)begin
led[4] <= 0;
end
else if(add_cnt_10s && cnt_10s==5-1)begin
led[4] <= 1;
end
end
always @(posedge clk or negedgerst_n)begin
if(rst_n==1'b0)begin
led[5] <= 0;
end
else if(end_cnt_10s)begin
led[5] <= 0;
end
else if(add_cnt_10s && cnt_10s==6-1)begin
led[5] <= 1;
end
end
always @(posedge clk or negedgerst_n)begin
if(rst_n==1'b0)begin
led[6] <= 0;
end
else if(end_cnt_10s)begin
led[6] <= 0;
end
else if(add_cnt_10s && cnt_10s==7-1)begin
led[6] <= 1;
end
end
always @(posedge clk or negedgerst_n)begin
if(rst_n==1'b0)begin
led[7] <= 0;
end
else if(end_cnt_10s)begin
led[7] <= 0;
end
else if(add_cnt_10s && cnt_10s==8-1)begin
led[7] <= 1;
end
end
module pwmled(
clk ,
rst_n ,
led
);
input clk ;
input rst_n ;
output [ 7:0] led ;
reg [25:0] cnt_1s ;
wire add_cnt_1s ;
wire end_cnt_1s ;
reg [ 7:0] cnt_10s ;
wire add_cnt_10s;
wire end_cnt_10s;
reg [ 7:0] led ;
always @(posedge clk or negedgerst_n)begin
if(!rst_n)begin
cnt_1s <= 0;
end
else if(add_cnt_1s)begin
if(end_cnt_1s)
cnt_1s <= 0;
else
cnt_1s <= cnt_1s + 1;
end
end
assign add_cnt_1s = 1;
assign end_cnt_1s = add_cnt_1s && cnt_1s==50_000_000 -1 ;
always @(posedge clk or negedgerst_n)begin
if(!rst_n)begin
cnt_10s <= 0;
end
else if(add_cnt_10s)begin
if(end_cnt_10s)
cnt_10s <= 0;
else
cnt_10s <= cnt_10s + 1;
end
end
assign add_cnt_10s = end_cnt_1s;
assign end_cnt_10s = add_cnt_10s && cnt_10s==10-1 ;
always @(posedge clk or negedgerst_n)begin
if(rst_n==1'b0)begin
led[0] <= 0;
end
else if(end_cnt_10s)begin
led[0] <= 0;
end
else if(add_cnt_10s && cnt_10s==1-1)begin
led[0] <= 1;
end
end
always @(posedge clk or negedgerst_n)begin
if(rst_n==1'b0)begin
led[1] <= 0;
end
else if(end_cnt_10s)begin
led[1] <= 0;
end
else if(add_cnt_10s && cnt_10s==2-1)begin
led[1] <= 1;
end
end
always @(posedge clk or negedgerst_n)begin
if(rst_n==1'b0)begin
led[2] <= 0;
end
else if(end_cnt_10s)begin
led[2] <= 0;
end
else if(add_cnt_10s && cnt_10s==3-1)begin
led[2] <= 1;
end
end
always @(posedge clk or negedgerst_n)begin
if(rst_n==1'b0)begin
led[3] <= 0;
end
else if(end_cnt_10s)begin
led[3] <= 0;
end
else if(add_cnt_10s && cnt_10s==4-1)begin
led[3] <= 1;
end
end
always @(posedge clk or negedgerst_n)begin
if(rst_n==1'b0)begin
led[4] <= 0;
end
else if(end_cnt_10s)begin
led[4] <= 0;
end
else if(add_cnt_10s && cnt_10s==5-1)begin
led[4] <= 1;
end
end
always @(posedge clk or negedgerst_n)begin
if(rst_n==1'b0)begin
led[5] <= 0;
end
else if(end_cnt_10s)begin
led[5] <= 0;
end
else if(add_cnt_10s && cnt_10s==6-1)begin
led[5] <= 1;
end
end
always @(posedge clk or negedgerst_n)begin
if(rst_n==1'b0)begin
led[6] <= 0;
end
else if(end_cnt_10s)begin
led[6] <= 0;
end
else if(add_cnt_10s && cnt_10s==7-1)begin
led[6] <= 1;
end
end
always @(posedge clk or negedgerst_n)begin
if(rst_n==1'b0)begin
led[7] <= 0;
end
else if(end_cnt_10s)begin
led[7] <= 0;
end
else if(add_cnt_10s && cnt_10s==8-1)begin
led[7] <= 1;
end
end
endmodule
看上面的现象,可以发现,跟设计目标要求的都是一样的,成功完成设计目标。
我们的至简设计法和计数器模板在以上的设计中发挥了至关重要的作用,使设计能够快速准确完成。本设计中的led信号的代码为了方便理解,把8个led灯分开写了,希望有兴趣的同学可以将led的代码进行简化,合成一个always,想不到也没关系,在讲解视频中也有讲述如何实现。
感兴趣的朋友也可以访问论坛进行FPGA相关工程设计学习,也欢迎大家在评论与我进行讨论!