Xilinx ZYNQ 7000+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模块文件,这里占空比设计的比较糙,直接就用一个计数值代替功能,后面的软件设计要注意:
新建一个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 <= 32'd0;
end
else begin
if(fre_cnt < fre_set)
fre_cnt <= fre_cnt+1'b1;
else
fre_cnt <= 32'd0;
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
在顶层模块添加好用户信号,一个是端口里的,一个是调用里的:
添加ILA
修改完后重新打包好。
回到原先建的工程,将这个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 <stdio.h>
#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的优势!