一段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电平的时间后翻转成高电平。第二个数据是高电平持续时间,依次类推。
这里就不进行仿真了,代码过于简单。