Xilinx ZYNQ 7000+Vivado2015.2系列(八)ARM+FPGA的優勢,PS控制PL產生需要的PWM波(基於AXI匯流排)

L707發表於2024-03-27

上一節我們觀察了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的優勢!

相關文章