1. 软件平台
vivado2019.1
2. 硬件平台
PYNQ_Z2
3. 目的
旨在利用PS端来控制PWM波的频率占空比以及启动和关闭。
在这里不做太复杂的功能。
4. 自定义IP核
如果是刚接触vivado,可以参考vivado-PYNQ_Z2-SDK入门教程
如果没有自定义IP核的经验,可以事先查看博客自定义IP核详细流程
两路pwm波,这里设置用两个16位数据来控制频率和占空比,一位来做使能,所以只需要3个寄存器,这里最低4个,选择4个寄存器。
由于数据量极低,接口类型选择Lite就可以满足了。
这里AXI接口数量,自行探讨。
这里新建一个源文件,pwm_gen.v
代码如下
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2019/10/05 15:33:27
// Design Name:
// Module Name: pwm_gen
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2019/10/05 10:41:44
// Design Name:
// Module Name: pwm_gen
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2019/10/03 18:09:27
// Design Name:
// Module Name: pwm_gen
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
/*时钟信号 clk
复位信号 rst
使能信号 en
周期 period = clk/f
占空比 h_time = period * D
输出 pwm
*/
module pwm_gen(
input clk,
input rst,
input en,
input [15:0]period,
input [15:0]h_time,
output reg pwm
);
reg [31:0]CNT;
always @(posedge clk or negedge rst)
begin
if(!rst)
CNT <= 0;
else if(CNT >= period-1 || en ==0)
CNT <= 0;
else
CNT <= CNT + 1'b1;
end
always @(posedge clk or negedge rst)
begin
if(!rst)
pwm <= 0;
else
begin
if(en == 0)
pwm <= 0;
else
begin
if(CNT <= h_time-1)
pwm <=1;
else
pwm <= 0;
end
end
end
endmodule
在该文件下添加寄存器输出的数据“端口”
点击保存
转到top level,主要是调用模块
添加输出端口,
“输出寄存器的值”
调用pwm发生器模块
// Add user logic here
//pwm0
pwm_gen pwm0(
.clk(s00_axi_aclk),
.rst(s00_axi_aresetn),
.en(en),
.period(ctrl_pwm0[15:0]),
.h_time(ctrl_pwm0[31:16]),
.pwm(pwm_out0)
);
//pwm1
pwm_gen pwm1(
.clk(s00_axi_aclk),
.rst(s00_axi_aresetn),
.en(en),
.period(ctrl_pwm1[15:0]),
.h_time(ctrl_pwm1[31:16]),
.pwm(pwm_out1)
);
// User logic ends
然后保存,进行综合,这里只需要run synthesis,不需要implementation (作用是防止错漏,但不能完全检查出来)
无误后,re_pakage-IP,,这样一个pwm发生器的IP核就建立好了
下图是block design
然后generate output product
然后选择create HDL wrapper
再添加时序约束
##Raspberry Digital I/O
set_property -dict { PACKAGE_PIN W18 IOSTANDARD LVCMOS33 } [get_ports { pwm_out0 }]; #IO_L22P_T3_34 Sch=rpio_02_r
set_property -dict { PACKAGE_PIN W19 IOSTANDARD LVCMOS33 } [get_ports { pwm_out1 }]; #IO_L22N_T3_34 Sch=rpio_03_r
最后点击generate bitstream
之后的步骤参考自定义IP核详细流程
SDK工程代码如下
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
/* Include Files */
#include "xparameters.h"
#include "xil_io.h"
#include "xstatus.h"
/* Definitions */
#define printf xil_printf /* smaller, optimised printf */
/*
* pwm_gen 16λ Ϊperiod 15λΪ ռ¿ձȪperiod 1λΪenable
*/
u32 pwm_gen0= 0x0BB81388; /* h_time & period*/
u32 pwm_gen1= 0x094c1388; /* h_time & period*/
u32 pwm_en = 0x01; /*enable flag*/
#define pwm0 0x43c00000 /* address of pwm0 configure register*/
#define pwm1 0x43c00004 /* address of pwm0 configure register*/
#define en 0x43c00008 /* address of enable flag configure register*/
int main()
{
init_platform();
print("Hello World\n\r");
cleanup_platform();
/* Execute the pwm output. */
Xil_Out32(pwm0,pwm_gen0); //configure pwm0 period and D
Xil_Out32(pwm1,pwm_gen1); //configure pwm1 period and D
Xil_Out32(en,pwm_en); // enable pwm0 pwm1 at the same time
return 0;
}
注意:寄存器的地址间隔为4
运行结果如下
pwm0输出20khz,占空比为50%
pwm0输出20khz,占空比为47.6%
pwm0和pwm1没有相位偏移(verilog部分没操作好可能会造成一定的相位偏移,这是由于pwm发生器计数不是同时开始而造成的)