## 脉冲宽度调制(PWM)实验
### 1. 实验内容
本实验的目的希望通过一个简单的实验来熟悉脉宽调制的原理,了解脉宽调制在LED显示上的应用。具体内容是采用脉宽调制来控制核心板上的LED的亮度,使板子上的8个LED灯依次渐亮和渐灭。
\\
\\
### 2. 实验原理
脉宽调制即Pulse width Modulation,简称PWM。脉宽调制是利用脉冲信号来实现模拟输出的一种方法,其在电力电子等方面有着广泛的应用。下面以脉宽调制在LED显示控制中的应用来介绍脉宽调制的原理。
如今在城市的各个繁华街区都可以看到各种尺寸的大屏幕,播放着商家的广告、新闻甚至体育直播,这些大屏幕就是LED屏。LED屏由成千上万个LED灯组成,一般情况下每个像素点由红绿蓝三个LED灯组成。这些红绿蓝三原色的LED灯发出不同亮度的光,由于每个像素点上这三个LED灯离得非常近,从远处看这三原色的光就自然在视网膜上进行了合成,组合成了几十万种颜色。成千上万个这样的像素点放在一起就组合成了大的LED屏。
LED屏的技术原理并不十分复杂,但是对实现工艺的要求非常高。由于一个LED屏上的LED灯的数量非常大,要具有非常好的显示效果,就要求这么多的LED灯的亮度要有非常好的一致性。如果亮度不均匀的化,整体的显示效果就非常差。另外就是控制系统,即要实现对大量LED灯的控制,又要保证控制效果,这对控制系统提出了很高的要求。那么LED的亮度是采用何种方式来控制呢?
简单而言,LED灯的亮度是采用下图所示的原理来控制,一般控制芯片采用的是FPGA、MCU或其它类型的芯片。当Dout的电平为低时,LED灯点亮;当Dout的电平为高时,LED熄灭。由此可见,Dout只能点亮或熄灭,不能调整亮度。但是如果将Dout变成一个脉冲信号,例如信号频率是1kHz,占空比是1/2。那么,LED就以很高的频率被点亮然后熄灭,而后又点亮又熄灭,由于人眼的视觉特性此时的LED就会看其来比点亮时按了一些。不断地调整LED的占空比,就会使得LED发出亮度不同的光,这就是脉宽调制。脉宽调制就是通过调整输出脉冲信号的占空比来实现对外围器件的控制。
{{ :图_8_1.png |图 8 1 PWM控制LED亮度示意图}}
图 8 1 PWM控制LED亮度示意图
本实验其实是LED大屏幕控制的一个简化实验。核心板上有8个LED灯,利用脉宽调制来控制LED0到LED7的亮度逐渐变化,同时每个LED灯的亮度也随时间的变化而逐渐变亮或变暗。
\\
\\
### 3. 程序设计
#### 3.2 总体架构(PWM.v)
{{ :图8_2.png |图8 2 脉宽调制程序的总体架构}}
图8 2 脉宽调制程序的总体架构
整个程序的架构如图8 2所示,主要由两个模块PWM_Controller和PWM_basic1~PWM_basic8组成:\\
1. PWM_controller实现对8个LED灯亮度的控制,即决定每个LED灯的亮度应该是多少;\\
2. PWM_basic1~PWM_basic8每一个模块用于控制一个LED灯亮度的实现,也就是使得LED灯的亮度满足PWM_controller提出的要求;\\
3. Counter是一个从0到PERIOD_WIDTH的一个计数器,PERIOD_WIDTH是脉宽调制信号的周期。 \\
\\
#### 3.1 PWM_Basic模块(PWM_Basic.v)
PWM_Basic控制LED达到所要求的亮度,PWM_Basic1~PWM_Basic8是模块PWM_Basic的实例化。
PWM_Basic的输入输出接口如下:
input clk, reset; //时钟和复位信号。
input [15:0] pulse_width; //脉宽调制信号脉冲宽度,以时钟周期为单位。
input [15:0] clk_tick; //这是counter计数器的计数值。
output reg pwm_out; //控制LED的输出信号。
对LED灯的控制逻辑如下:
always @(posedge clk or negedge reset ) begin
if(!reset) begin
pwm_out <= 1;
end
else begin
if( clk_tick == 16'd0 )
pwm_out <= 1'b0; // 注释1。
if( clk_tick == pulse_width )
pwm_out <= 1'b1; //注释2。
end
end
在注释1处,当计数器clk_tick为0时点亮LED灯,Pwm_out为低时点亮LED灯。在注释2处,当计数器clk_tick等于pulse_width,也就是脉冲宽度达到所需要的脉冲宽度时,熄灭LED灯。如此,就完成对脉宽调制。
\\
#### 3.3 PWM_controller模块(PWM_controller.v)
PWM_controller的主要功能如下:\\
1)控制每个LED的初始亮度,也就是初始脉宽;\\
2)更新每个LED的亮度,让每个LED灯逐渐变亮或变暗。\\
always @(posedge clk or negedge reset ) begin : PWM_controller
integer loop;
if( !reset ) begin
state_counter <= 7'd0;
add <= 8'b1111_1111;
//add的每一位用来控制一个LED是变亮还是变暗,初始时都要求变亮。
trigger_pointer[0] <= 16'd0;
trigger_pointer[1] <= `RESET_PHASE;
trigger_pointer[2] <= 2*`RESET_PHASE;
trigger_pointer[3] <= 3*`RESET_PHASE;
trigger_pointer[4] <= 4*`RESET_PHASE;
trigger_pointer[5] <= 5*`RESET_PHASE;
trigger_pointer[6] <= 6*`RESET_PHASE;
trigger_pointer[7] <= 7*`RESET_PHASE;
// reg[15:0] trigger_pointer[7:0];
//trigger_pointer是一个二维数组,用于控制LED灯的脉宽,上列8行对每个LED灯进行了初始化,从上面可以看出,LED7的初始值最大,也就是初始时最亮。RESET_PHASE是宏定义。
end
else begin
if( clk_tick == 16'd0 ) begin
if( state_counter == `STAT_TRANSFER_NUM ) begin
//clk_tick和State_counter共同决定了亮度变化的间隔,为STAT_TRANSFER_NUM*PERIOD_WIDTH个时钟周期。如果设STAT_TRANSFER_NUM为20,PERIOD_WIDTH为25000,时钟为50MHz,则亮度约10ms变化依次。
state_counter <= 7'd0;
for( loop = 0; loop < 8; loop = loop + 1 ) begin
//对每个LED灯,更新其亮度,也就是脉宽。
if( add[loop] == 1 ) begin
if( trigger_pointer[loop] >= `PERIOD_WIDTH - `STEP )
add[loop] <= 0;
trigger_pointer[loop] <= trigger_pointer[loop] + `STEP;
end
//如果是add为1,则要求变亮,即使脉宽加上一个步长STEP;但如果亮度为最亮,则将add置为0,使其开始变暗。
else begin
if( trigger_pointer[loop] <= 2*`STEP )
add[loop] <= 1;
trigger_pointer[loop] <= trigger_pointer[loop] - `STEP;
end
//如果add不为1,也就是0,则要求变暗,即使脉宽减去一个步长STEP;但如果亮度为最暗,则将add置为1,使其开始变亮。
end
end
else
state_counter <= state_counter + 7'd1;
end
end
end
上述涉及的宏包括脉宽增加步长STEP、脉宽调制的周期PERIOD_WIDTH、亮度变化控制STAT_TRANSFER_NUM,这些宏可以设置成不同的值以观察控制效果。
\\
\\
### 4. 程序仿真
Ledtest.v是仿真模块,用于产生需要的时钟和复位信号,同时实例化PWM模块。
{{ :图_8_3.png |图 8 3 PWM程序仿真结果}}
图 8 3 PWM程序仿真结果
图 8 3为仿真的结果,由于仿真时间不能太长,因此三个宏的设置如下:
`define STEP 16'd1
`define PERIOD_WIDTH 16'd10
`define RESET_PHASE 16'd1
`define STAT_TRANSFER_NUM 7'd2
上面三个宏都设的很小,便于仿真。从图中可以看出,每个LED灯的脉宽调制信号的脉宽都不一样的;同时由于STAT_TRANSFER_NUM为 2,每个脉宽调制信号是每三个周期变一次,也就是每三个周期亮度变化一次。
\\
\\
### 5. 演示程序文件说明
|文件名|功能|
|PWM.v|顶层模块,并包含PWM_Controller模块|
|Counter.v|Counter定时器模块|
|PWM_Basic.v|PWM_Basic模块|
|Pwmtest.mpf|ModelSim仿真工程|
|Ledtest.v|仿真顶层模块|
|PWM.qpf|Quartus II项目文件|
\\
\\
### 6. 演示程序使用
演示设备:核心板。
演示方法:把程序下载到开发系统中后,可以观察到核心板上8个LED灯不断地变亮或变暗,reset为复位键。
### 7. 实验中注意的问题
由于核心板上的LED灯数量有限,而且观察距离近,亮灭的变化不是很明显。在本实验中采用的是线性脉宽调制,即脉冲宽度的变化是个常数。事实上,LED的亮度和脉冲宽度不是线性的关系,是一个指数曲线,因此要使得LED的亮度线性地变化,脉冲宽度的变化就不应该是线性的,这就是伽玛校正。有兴趣的同学可以自己尝试对脉冲宽度进行非线性调制使得LED亮度按线性进行变化。