FPGA之呼吸灯(pwm)设计

FPGA(Field-Programmable Gate Array),即现场可编程门阵列,它是在PAL、GAL、CPLD等可编程器件的基础上进一步发展的产物。它是作为专用集成电路(ASIC)领域中的一种半定制电路而出现的,既解决了定制电路的不足,又克服了原有可编程器件门电路数有限的缺点。

 
设计背景:
 
呼吸灯广泛应用于手机之上,并成为各大品牌新款手机的卖点之一。如果手机里面有未处理的通知,比如说未接来电,未查收的短信等等,呼吸灯就会在控制之下完成由亮到暗的逐渐变化,感觉好像是人在呼吸,起到一个通知提醒的作用。
 
设计原理: 
 
关于呼吸灯设计实现的理论主要是PWM有关知识。PWM(Pluse Width Modulation)脉冲宽度调制,是一种对模拟信号电平进行数字编码的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。并广泛应用在从测量、通信、功率控制与变换及LED照明等许多领域中。顾名思义,就是占空比可调的信号,那么什么是占空比呢?
 
占空比(Duty Cycle or Duty Ratio),可以解释为,在一脉冲序列中(方波),正脉冲序列的持续时间与脉冲总周期的比值。也可理解为,电路释放能量的有效时间与总释放时间的比值。
 
PWM是怎样实现调光呢?想要调节LED的亮度变化,实则是调节控制流经LED的电流。电流增大则LED亮度增强,反之减弱。但由于电流为模拟信号,所以这时就用到了PWM。正如下图所示:
FPGA之呼吸灯(pwm)设计
使用一系列等幅不等宽的脉冲来代替一个正弦波,脉冲的宽度根据正弦波a的幅度变化,幅度高,则脉冲宽,反之。    
 
多数负载需要的PWM调制频率都高于10Hz,要想实现呼吸灯的效果,必须提高调制频率,通常调制频率为1Khz~200Khz之间。在LED控制中PWM作用于电源部分,脉宽调制的脉冲频率通常大于100Hz,人眼就不会感到闪烁。这里我们取PWM调制频率为1KHz,PWM周期为1ms。
 
脉冲频率一定时,输出脉冲的占空比越大,相当于输出的有效电平越大,随着占空比的不同,LED的亮度也将不同。如占空比为0时,则LED不亮,为100%时,则LED最量,我们让占空比从0~100%变化,再从100%~0不断变化,则就可实现呼吸灯效果。
 
本设计呼吸灯的一个周期为2s,分为占空比增“吸”和占空比减“呼”两种模式,每个为1s,一个PWM周期为2ms,所以每个模式包含1000个PWM周期,将每个PWM周期分为1000份,即每个时间段2us。
 
设计架构图:
 
时钟50M
FPGA之呼吸灯(pwm)设计
设计代码:
 
设计模块
 
0  module huxi_led_state(clk,led,rst_n);
 
1   input clk;
 
2   input rst_n;
 
3  
 
4   output reg led;
 
5  
 
6   parameter T = 100_000;
 
7  
 
8   localparam s0 = 1'b0;
 
9   localparam s1 = 1'b1;
 
10 
 
11  reg [25:0] lw;
 
12  reg [25:0] hw;
 
13 
 
14  reg [16:0] count;
 
15 
 
16  // 产生2MS的脉冲
 
17  always @(posedge clk or negedge rst_n)
 
18   if(!rst_n)
 
19    begin
 
20     count <= 1'b0;
 
21    end
 
22   else
 
23    begin
 
24     if(count == T - 1)
 
25      begin
 
26       count <= 1'b0;
 
27      end
 
28     else
 
29      begin
 
30       count <= count + 1'b1;
 
31      end
 
32    end
 
33 
 
34  wire flag;
 
35  assign flag =(count == T - 1) ? 1'b1:1'b0;
 
36 
 
37  reg state;
 
38 
 
39  // 通过在一个周期中加减高低电平的时间来产生PWM波
 
40  always @(posedge clk or negedge rst_n)
 
41   if(!rst_n)
 
42    begin
 
43     lw <= T - 100;
 
44     hw <= 100;
 
45     state <= 1'b0;
 
46    end
 
47   else
 
48    begin
 
49     case (state)
 
50      s0:begin
 
51        if(flag && (lw > 100))   //判断低电平的时间
 
52         begin
 
53          lw <= lw - 100;
 
54          hw <= hw + 100;
 
55          state <= s0;
 
56         end
 
57        else if(flag && (lw == 100))
 
58         begin
 
59          hw <= hw - 100;
 
60          lw <= lw + 100;
 
61          state <= s1;
 
62         end
 
63        else
 
64         begin
 
65          hw <= hw;
 
66          lw <= lw;
 
67          state <= s0;
 
68         end
 
69       end
 
70      s1:begin
 
71        if(flag && (hw > 100))   //判断高电平的时间
 
72         begin
 
73          hw <= hw - 100;
 
74          lw <= lw + 100;
 
75          state <= s1;
 
76         end
 
77        else if(flag && (hw ==100))   
 
78         begin
 
79          hw <= hw + 100;
 
80          lw <= lw - 100;
 
81          state <= s0;
 
82         end
 
83        else
 
84         begin
 
85          hw <= hw;
 
86          lw <= lw;
 
87          state <= s1;
 
88         end
 
89       end
 
90     default : state <= s0;
 
91     endcase
 
92    end
 
93 
 
94  reg [25:0] cnt;
 
95  reg sum;
 
96 
 
97  //分频模块,产生不同占空比的LED亮的时间
 
98  always @(posedge clk or negedge rst_n)
 
99   if(!rst_n)
 
100   begin
 
101    sum <= 1'b0;
 
102    led <= 1'b1;
 
103    cnt <= 1'b0; 
 
104   end
 
105  else
 
106    case (sum)
 
107     s0:begin
 
108       if(cnt < hw -1 )   //高电平的时间
 
109        begin
 
110         led <= 1'b0;
 
111         cnt <= cnt + 1'b1;
 
112        end
 
113       else
 
114        begin
 
115         cnt <= 1'b0;
 
116         sum <= s1;
 
117        end
 
118      end
 
119     s1:begin
 
120       if(cnt < lw -1) //低电平的时间
 
121        begin
 
122         led <= 1'b1;
 
123         cnt <= cnt + 1'b1;
 
124        end
 
125       else
 
126        begin
 
127         cnt <= 1'b0;
 
128         sum <= s0;
 
129        end
 
130      end
 
131     default:sum <= s0;
 
132    endcase
 
133
 
134 endmodule 
 
测试模块
 
0 `timescale 1ns/1ps 
 
1  module huxi_led_state_tb();
 
2  reg clk;
 
3  reg rst_n;
 
 
5  wire led;
 
 
7  parameter T = 100_000;
 
 
9  initial begin 
 
10   clk = 1'b1;
 
11   rst_n = 1'b0;
 
12
 
13   #200.1 rst_n = 1'b1;
 
14
 
15
 
16  end
 
17
 
18  always #10 clk = ~ clk; 
 
19
 
20
 
21
 
22 huxi_led_state  huxi_led_state_date(  //例化设计
 
23     .clk(clk),
 
24     .led(led),
 
25     .rst_n(rst_n)
 
26     );
 
27 endmodule 
 
仿真图:
FPGA之呼吸灯(pwm)设计