一段PWM输出的FPGA实现

  记得很早之前做过一个项目,有个需求是要写一个IP核生成特殊的一段PWM波形,当时看着波形挺简单的,就是递减的方波,实现起来非常简单,信号从2.0ms的脉宽按步进0.1ms减到1.0ms。这个实现起来确实很容易,不过后面笔者想到,做项目不能只顾眼前,后期可能需求会变更,可能需要产生的一段PWM更加的没规律,所以笔者就为了让代码更加通用,就通过时间计数方式产生一段PWM,而PWM的时间段则由FIFO进行存储。

  其实代码也是非常的简单,笔者特意在此讲这个例子的目的是希望做项目的时候能考虑后面修改,把代码写得更通用,对于后面需求更改了,也无需修改代码了。这里只列出实现PWM输出的部分,其他就是通过axi4_lite控制模块加FIFO缓存来实现,前面UART的TX发送数据类似。这里只需上位机网FIFO写好产生PWM的时间序列,然后使能输出即可。

  核心代码pwm_gen.v如下:

 1 //**************************************************************************
 2 // *** file name      : pwm_gen.v
 3 // *** version        : 1.0
 4 // *** Description    : generate pwm
 5 // *** Blogs          :
 6 // *** Author         : Galois_V
 7 // *** Date           : 2021.11.20
 8 // *** Changes        : Initial
 9 //**************************************************************************
10 `timescale 1ns/1ps
11 module pwm_gen
12 (
13     input                                    i_sys_clk      ,
14     input                                    i_sys_rstn     ,
15     input            [31:0]                  i_pwm_cnt      ,  //产生0或者1持续的时间长度,该值*8ns即为电平持续的时间
16     input                                    i_fifo_end     ,  //FIFO空信号,FIFO空的时候结束产生PWM
17     input                                    i_generate_en  ,  //开始产生PWM信号
18     output                                   o_fifo_read_en ,  //fifo读使能信号
19     output    reg                            o_pwm
20 );
21     reg        [31:0]    r_pwm_cnt;
22     reg        [31:0]    r_clk_cnt;
23     wire                 w_pwm_cnt_end;
24     reg                  r_fifo_end;
25     reg                  r_output_valid;
26     
27     always@(posedge i_sys_clk)
28     begin
29         if(~i_sys_rstn | r_fifo_end)
30         begin
31             r_output_valid <= 'd0;
32         end
33         else if(i_generate_en)
34         begin
35             r_output_valid <= 1'b1;
36         end
37     end
38     
39     always@(posedge i_sys_clk)
40     begin
41         if(~i_sys_rstn)
42         begin
43             r_fifo_end <= 'd0;
44         end
45         else if(w_pwm_cnt_end)
46         begin
47             r_fifo_end <= i_fifo_end;
48         end
49     end
50     
51     always@(posedge i_sys_clk)
52     begin
53         if(~i_sys_rstn)
54         begin
55             r_pwm_cnt <= 'd0;
56         end
57         else if(w_pwm_cnt_end | i_generate_en)
58         begin
59             r_pwm_cnt <= i_pwm_cnt;
60         end
61     end
62     
63     always@(posedge i_sys_clk)
64     begin
65         if(~i_sys_rstn | w_pwm_cnt_end | r_fifo_end)
66         begin
67             r_clk_cnt <= 'd1;
68         end
69         else if(r_output_valid)
70         begin
71             r_clk_cnt <= r_clk_cnt + 1'b1;
72         end
73     end
74     
75     assign w_pwm_cnt_end = (r_clk_cnt == r_pwm_cnt);
76     assign o_fifo_read_en = w_pwm_cnt_end;
77     
78     
79     always@(posedge i_sys_clk)
80     begin
81         if(~i_sys_rstn)
82         begin
83             o_pwm <= 1'b0;
84         end
85         else if(w_pwm_cnt_end)
86         begin
87             o_pwm <= ~o_pwm;
88         end
89     end
90 endmodule

  时间序列:由于系统时钟是125MHz,这里最小单位为8ns,每个时间序列表示该电平持续的时间为t*8ns。结束之后发生电平反转,继续下一个时间计数。IP核默认0电平输出,列表中的第一个数据是统计0电平的时间后翻转成高电平。第二个数据是高电平持续时间,依次类推。

  这里就不进行仿真了,代码过于简单。

posted on 2023-05-13 20:36  Galois_V  阅读(514)  评论(0编辑  收藏  举报