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上:

20171219214749453

20171219214845967

自定义的功能

自定义的功能就是一个寄存器控制频率(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

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

20171219214934994

20171219215011605

添加ILA

修改完后重新打包好。

回到原先建的工程,将这个IP添加到IP库里,然后Create Block Design,

添加ZYNQ核和PWM_AXI_Lite,为了观察PWM波信号。

这里又添加了一个ILA(为了简化,可以去掉),配置如下:

20171219220324893

20171219220521756

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

20171219220020291

添加约束文件

添加约束文件,将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文件:

20171219220954972

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

20171219221219289

这个基地址就是我们的寄存器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的优势!

posted @ 2024-03-27 18:25  L707  阅读(34)  评论(0编辑  收藏  举报