ZYNQ+Vivado2015.2系列(八)ARM+FPGA的优势,PS控制PL产生需要的PWM波(基于AXI总线)

上一节我们观察了AXI总线的信号,了解了基于AXI总线读写的时序,这一节我们继续探索基于AXI总线的设计,来看一看ZYNQ系列开发板的独特优势,PS可以控制PL产生定制化的行为,而不需要去动硬件代码。

这次实验是产生频率和占空比可调的PWM(Pulse Width Modulation)信号,调用8次,产生8路PWM波,并用这些信号去控制8路LED灯,观察实验效果。后面会做一个比较。

用的板子是zc702。

新建一个工程,命名为PWM_AXI_Lite

创建基于AXI总线的PWM波IP

IP设计为一个寄存器负责控制频率,一个寄存器负责控制占空比。

创建一个IP核,tools-->Create and Package ,这里需要16个寄存器,创建方法见系列(六)、系列(七),这里命名为PWM_AXI_Lite。

在IP核工程里,新建一个PWM模块文件,这里占空比设计的比较糙,直接就用一个计数值代替功能,后面的软件设计要注意:

module PWM(
input clk,
input rst_n,
input cnt_set,
input fre_set,
output pwm_o
);

wire[31:0] cnt_set;
wire[31:0] fre_set;
reg [31:0] fre_cnt;

always @(posedge clk) begin
if(!rst_n) begin
fre_cnt end
else begin
if(fre_cnt fre_cnt else
fre_cnt end
end

assign pwm_o=(cnt_set>fre_cnt);
endmodule

在自动产生的实例文件里,添加端口信号和自定义功能,后面要约束到LED上:


自定义的功能就是一个寄存器控制频率(fre_set),一个寄存器控制占空比(cnt_set):
// Add user logic here
PWM PWM0(
.clk(S_AXI_ACLK),
.rst_n(S_AXI_ARESETN),
.cnt_set(slv_reg1),
.fre_set(slv_reg0),
.pwm_o(PWM_o[0])
);

PWM PWM1(
.clk(S_AXI_ACLK),
.rst_n(S_AXI_ARESETN),
.cnt_set(slv_reg3),
.fre_set(slv_reg2),
.pwm_o(PWM_o[1])
);

PWM PWM2(
.clk(S_AXI_ACLK),
.rst_n(S_AXI_ARESETN),
.cnt_set(slv_reg5),
.fre_set(slv_reg4),
.pwm_o(PWM_o[2])
);

PWM PWM3(
.clk(S_AXI_ACLK),
.rst_n(S_AXI_ARESETN),
.cnt_set(slv_reg7),
.fre_set(slv_reg6),
.pwm_o(PWM_o[3])
);
PWM PWM4(
.clk(S_AXI_ACLK),
.rst_n(S_AXI_ARESETN),
.cnt_set(slv_reg9),
.fre_set(slv_reg8),
.pwm_o(PWM_o[4])
);
PWM PWM5(
.clk(S_AXI_ACLK),
.rst_n(S_AXI_ARESETN),
.cnt_set(slv_reg11),
.fre_set(slv_reg10),
.pwm_o(PWM_o[5])
);
PWM PWM6(
.clk(S_AXI_ACLK),
.rst_n(S_AXI_ARESETN),
.cnt_set(slv_reg13),
.fre_set(slv_reg12),
.pwm_o(PWM_o[6])
);

PWM PWM7(
.clk(S_AXI_ACLK),
.rst_n(S_AXI_ARESETN),
.cnt_set(slv_reg15),
.fre_set(slv_reg14),
.pwm_o(PWM_o[7])
);
// User logic ends

在顶层模块添加好用户信号,一个是端口里的,一个是调用里的:


修改完后重新打包好。

回到原先建的工程,将这个IP添加到IP库里,然后Create Block Design,添加ZYNQ核和PWM_AXI_Lite,为了观察PWM波信号,这里又添加了一个ILA(为了简化,可以去掉),配置如下:


连接CLK 和 FCLK_CLK0 ,连接 Probe0 和 PWM_o,最后创建的系统如下:

添加约束文件,将8路PWM波绑定到8个LED上:
#GPIO PMOD1
set_property PACKAGE_PIN E15 [get_ports {PWM_o[7]}]
set_property IOSTANDARD LVCMOS25 [get_ports {PWM_o[7]}]
set_property PACKAGE_PIN D15 [get_ports {PWM_o[6]}]
set_property IOSTANDARD LVCMOS25 [get_ports {PWM_o[6]}]
set_property PACKAGE_PIN W17 [get_ports {PWM_o[5]}]
set_property IOSTANDARD LVCMOS25 [get_ports {PWM_o[5]}]
set_property PACKAGE_PIN W5 [get_ports {PWM_o[4]}]
set_property IOSTANDARD LVCMOS25 [get_ports {PWM_o[4]}]
#GPIO PMOD2
set_property PACKAGE_PIN V7 [get_ports {PWM_o[3]}]
set_property IOSTANDARD LVCMOS25 [get_ports {PWM_o[3]}]
set_property PACKAGE_PIN W10 [get_ports {PWM_o[2]}]
set_property IOSTANDARD LVCMOS25 [get_ports {PWM_o[2]}]
set_property PACKAGE_PIN P18 [get_ports {PWM_o[1]}]
set_property IOSTANDARD LVCMOS25 [get_ports {PWM_o[1]}]
set_property PACKAGE_PIN P17 [get_ports {PWM_o[0]}]
set_property IOSTANDARD LVCMOS25 [get_ports {PWM_o[0]}]

一系列常规操作,生成比特流文件后,Lanch到SDK。

SDK部分设计
在BSP包里找到xparameter.h文件:

在xparameters.h文件里找到系统为我们的PWM IP配置的地址,待会我们要操作它的寄存器:

打开xparameters.h文件,Ctrl+F:

这个基地址就是我们的寄存器0的地址,然后我们将各路PWM波的频率和占空比写入:
#include
#include "xparameters.h"
#include "xil_io.h"
#include "sleep.h"
#include "xil_types.h"

int main(){

Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR,40000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+4,30000000);

Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+8,40000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+12,20000000);

Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+16,40000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+20,10000000);

Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+24,40000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+28,8000000);

Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+32,40000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+36,6000000);

Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+40,40000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+44,4000000);

Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+48,40000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+52,2000000);

Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+56,40000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+60,100000);

return 0;
}

板子上电,然后Program FPGA,debug as后,在vivado里会自动打开调试界面,触发后能看到8路波形,在板子上我们可以看到LED灯依次闪烁!

总结:

这里我们达到了led依次闪烁的效果,如同系列(六)达到的效果,但是这里有本质的区别,系列(六)需要CPU一直发送指令控制LED灯,而这次试验CPU写入频率和占空比后,现在8路PWM波自己工作,是PL完成的,不需要CPU发命令,CPU可以去干其他事情。这就是SOPC的优势!
---------------------
作者:ChuanjieZhu
来源:CSDN
原文:https://blog.csdn.net/u014485485/article/details/78847436

推荐阅读