1. 程式人生 > >PYNQ上手筆記 | ③PS端+PL端點燈

PYNQ上手筆記 | ③PS端+PL端點燈

上一節中分別獨立實驗了Zynq的PS端和PL端,並初步實驗了PS端先硬體再軟體的開發流程和IP核設計的設計方法。第一節中提及到:Zynq是以PS端的ARM處理器系統為核心的,PS端和PL端是通過AXI匯流排,並且Xilinx已經提供了各種AXI通訊的IP核,接下來的實驗中將會更加明確的體驗到利用IP核設計的設計方法。

1.實驗目標

板載的LED和RGBLED都是接在PL端的,這個實驗將利用PS端程式通過AXI匯流排控制連線在PL端的LED。

2.實驗步驟

整個實驗流程參考《The Zynq Book Tutorials》的chpt1:First Designs on Zynq(這本實驗指導手冊是英文的,但我個人認為英文原版要比中文看著清楚很多)。
需要注意的一點是,這本實驗指導手冊是針對Zybo和Zedboard的,將其中關於板子的操作換為Pynq中的即可。關於軟體驅動程式的部分,書中是採用空白工程+匯入隨書程式的方法,這裡因為板子不同,不採用這種方法,直接利用SDK匯入例程

2.1.建立基於Pynq-Z2開發板的Vivado工程

2.2.基於ip核構建BlockDesign

點選左側導航欄“IP INTEGRATOR -> Create Bolock Design”,在IP整合環境下建立一個塊上設計:

2.2.1.ZYNQ7 Processing System ip 核

注:搜尋“zynq”即可新增。

從圖中可以看出,這只是一個空的IP核,並沒有任何連線,雙擊開啟該ip核配置,也全都是預設配置,接下來需要將這個IP核與實際PS處理器系統對應,點選Run Block Automation,Vivado就會自動根據板子的PS配置資訊進行對應,這就是為什麼要使用board file的原因,如果沒有這個官方提供的board file,所有的PS端配置資訊都需要手動配置:

自動連線完成後,可以看到ip核的DDR埠和FIXED_IO埠已經匯出,與實際開發板配置對應:

這個時候雙擊ip核開啟設定可以看到都已經自動完成了,其中“Peripheral I/O Pins”是指定PS端I/O外設引腳,此處我們暫且不作修改:

2.2.2.AXI GPIO ip 核

注:搜尋“gpio”即可新增。

同樣的,AXI GPIO ip核也是一個空核,並沒有進行任何連線,要注意的是,這是AXI GPIO軟核,在PL端實現,S_AXI端連線在AXI總線上與PS端處理器進行通訊,GPIO端連線實際引腳(可任意分配),驅動板載外設
首先對AXI GPIO ip核雙擊進行設定:

然後同樣的,點選“Run Connection Automation”,自動完成ZynqPS核和AXI GPIO核之間的連線:

連線完成後可以看到,整個硬體設計如下:

點選右側Address Editor即可看到硬體相關資訊,在PL端實現的axi_gpio_0 IP核中所包含的暫存器首地址和結束地址,這個資訊待會就要匯出到硬體設計檔案中,通過軟體操作相關暫存器。

2.2.3.驗證設計

整體設計完成後,首先按F6或者選擇Tools -> Validate Design驗證設計:

2.3.匯出硬體設計到SDK

2.3.1.由設計檔案建立HDL檔案

在原始檔頁面右擊設計檔案,選擇建立HDL檔案

2.3.2.生成Bitstream

在右側導航欄選擇"PROGRAM AND DEBUG -> Generate Bitstream",生成位元流檔案,用來配置PL端的設計(AXI GPIO 軟ip核), 此過程需要花10-20min不等。

2.3.3.匯出硬體設計檔案

接下來匯出整個設計的硬體設計檔案(hdf)供SDK進行軟體設計使用,因為設計中包含PL端設計,所以要勾選包含Bitstream檔案:

2.3.3.過渡到SDK

匯出完成之後硬體設計工作完成,開啟SDK,進入軟體設計工作:左上角進入“File -> Launch SDK”:

2.4.SDK中軟體設計

2.4.1.工程目錄

其中:

  • 橙色部分是需要自己編寫的應用程式程式碼
  • 帶有bsp的綠色部分是SDK自動生成的板級驅動支援包
  • 黃色部分是關於ARM核心的一些硬體設計檔案

2.4.1.1.硬體設計檔案(hdf)

紅色檔案system.hdf就是在vivado中設計完硬體之後匯出的硬體設計檔案,其中包含了一些設計資訊和暫存器地址對映,可以看到其中axi_gpio_0 ip核所包含暫存器的地址資訊和之前在vivado中看到的完全一樣:
在這裡插入圖片描述

2.4.1.2.板級驅動支援包(BSP)

板級驅動包中只需要關注綠色的檔案system.mss,這個檔案說明了該工程中板級驅動支援包的所有資訊,包括:

  • 目標硬體資訊

  • 採用作業系統資訊

  • 外設驅動資訊

  • 庫資訊

2.4.1.3.應用程式程式碼(APP)

應用程式程式碼可以自己編寫或者從例程匯入,這裡直接在system.mss檔案中匯入:

一共包括4個例程,匯入第一個基本例程:

2.4.2.xgpio_example

最好的參考資料是官方文件,包含XGpio操作所有的API說明

2.4.2.1.Xilinx GPIO 控制器介紹

Xilinx GPIO控制器是專為Xilinx FPGA設計的軟IP核,有以下功能:

  • 每個通道可設定32個I/O
  • 每個I/O可設定為輸入或輸出
  • 可配置支援雙通道和外部中斷

XGpio的API宣告在xgpio.h檔案中,API實現在xgpio.c檔案中

2.4.2.2. 初始化XGpio

API:int Xgpio_Initialize(InstancePtr,DeviceID)
功能:根據給定的DeviceID初始化呼叫者提供的XGpio例項

引數 型別 說明
InstancePtr sturct* XGpio GPIO例項指標(xgpio.h)
DeviceID u16 匯出hdl時自動生成(xparameters.h)
return int 成功-XST_SUCCESS 失敗-XST_DEVICE_NOT_FOUND

注:
xparameters.h是在匯出硬體設計檔案時由Vivado自動生成的,包含了所有系統硬體引數的巨集定義。
開啟xparameters.h找到AXI_GPIO_0的有關巨集定義,即可看到和之前在vivado中,在hdf檔案中看到的,都是對應的:

2.4.2.3. 設定XGpio輸入/輸出方向

API:void XGpio_SetDataDirection(XGpio *InstancePtr, unsigned Channel, u32 DirectionMask)
功能:根據給定的DeviceID初始化呼叫者提供的XGpio例項

引數 型別 說明
InstancePtr sturct* XGpio GPIO例項指標(xgpio.h)
Channel unsigned 包含要操作的GPIO的通道
DirectionMask u32 指定32個I/O的方向,0-出,1-入

注意:如果此功能與除1之外的任何通道一起使用,則必須為雙通道構建硬體。如果不是,則此功能將斷言。

2.4.2.4. XGpio電平邏輯操作

API:XGpio_DiscreteWrite(XGpio * InstancePtr,unsigned Channel,u32 Data)
功能:寫入指定GPIO通道的暫存器

引數 型別 說明
InstancePtr sturct* XGpio GPIO例項指標(xgpio.h)
Channel unsigned 包含要操作的GPIO的通道
Data u32 32個I/O的電平,0-低,1-高
2.4.2.4.1.置高某個I/O的電平

API:XGpio_DiscreteSet(XGpio * InstancePtr,unsigned Channel,u32 Mask)
功能:將輸出設定為指定GPIO通道的邏輯1

引數 型別 說明
InstancePtr sturct* XGpio GPIO例項指標(xgpio.h)
Channel unsigned 包含要操作的GPIO的通道
DirectionMask u32 將被設定為1的位組,0-不選中,1-選中(其它位不受影響)
2.4.2.4.1.拉低某個I/O的電平

API:XGpio_DiscreteClear(XGpio * InstancePtr,unsigned Channel,u32 Mask)
功能:將輸出設定為指定GPIO通道的邏輯0

引數 型別 說明
InstancePtr sturct* XGpio GPIO例項指標(xgpio.h)
Channel unsigned 包含要操作的GPIO的通道
DirectionMask u32 將被設定為0的位組,0-不選中,1-選中(其它位不受影響)

2.4.3.修改main程式

匯入的例程中如果未發生異常,串列埠沒有任何輸出,所以為了測試,進行以下小修改,將串列埠輸出程式碼提前:

2.4.4.下載並執行程式

因為該設計中包含PL端設計位元流,所以在run程式前需要設定,右鍵->Run As -> Run Configurations:

3.實驗結果

注:要在執行之前開啟串列埠終端。

4.實驗總結

完成了整個實驗後再來看這張圖,首先在PL端通過新增AXI GPIO IP核實現一個GPIO控制器,GPIO控制器由於在PL端,所以輸出直接與4個板載LED相連,然後vivado自動佈局佈線,將GPIO控制器的受控端與PS端處理器核進行對應連線,然後生成匯出硬體設計檔案供SDK使用。

SDK根據硬體設計檔案生成C工程,通過運行於PS端ARM處理器的程式控制PL端實現的GPIO控制器,完成PS通過AXI匯流排控制PL端LED(GPIO)的實驗。

5.實驗擴充套件

5.1.自己動手實現led驅動

5.1.1.新建空工程

在SDK中左上角“File -> New -> Application Project”,建立一個空工程:

5.1.2.標頭檔案

/**
 *   @filename		led_bsp_mculover666.h
 *   @brief				實現板載4顆LED驅動
 *   @data				 2018/10/18
 *   @author			Mculover666
 */

#ifndef LED_BSP_MCULOVER666_H_
#define LED_BSP_MCULOVER666_H_

#include "xgpio.h"
#include "xparameters.h"

#define ON								 1
#define OFF								 0
#define LED_XGpio_Device_ID  			 XPAR_GPIO_0_DEVICE_ID
#define LED_XGpio_Channel	 		     1
#define LED								 0x0000000F
#define LED1							 0x00000001
#define LED2							 0x00000002
#define LED3							 0x00000004
#define LED4							 0x00000008

/* 初始化LED所在io */
void led_init();
void LED1_Statue(int statue);
void LED2_Statue(int statue);
void LED3_Statue(int statue);
void LED4_Statue(int statue);
void LED_Statue(int statue);

#endif /* LED_BSP_MCULOVER666_H_ */

5.1.3.原始檔

/**
 *   @filename		led_bsp_mculover666.c
 *   @brief				實現板載4顆LED驅動
 *   @data				 2018/10/18
 *   @author			Mculover666
 */
#include "led_bsp_mculover666.h"

XGpio gpio;		//例項化一個Xgpio型別結構體

/**
 * @brief		LED IO初始化
 * @param   none
 * @retval		none
 * */
void led_init()
{
	/* 初始化XGpio */
	XGpio_Initialize(&gpio, LED_XGpio_Device_ID);

	/* 設定XGpio中LED所在引腳方向為輸出 */
	/* !!!注意:0-output 1-input !!! */
	XGpio_SetDataDirection(&gpio, LED_XGpio_Channel, ~LED);

	/* 預設輸出低電平 */
	XGpio_DiscreteClear(&gpio, LED_XGpio_Channel, LED);
}


/* 定義LED單個操作函式 */
void LED1_Statue(int statue)
{
	if(statue)
		XGpio_DiscreteSet(&gpio, LED_XGpio_Channel, LED1);	//低電平點亮
	else
		XGpio_DiscreteClear(&gpio, LED_XGpio_Channel, LED1);		//高電平點亮
}
void LED2_Statue(int statue)
{
	if(statue)
		XGpio_DiscreteSet(&gpio, LED_XGpio_Channel, LED2);	//低電平點亮
	else
		XGpio_DiscreteClear(&gpio, LED_XGpio_Channel, LED2);		//高電平點亮
}
void LED3_Statue(int statue)
{
	if(statue)
		XGpio_DiscreteSet(&gpio, LED_XGpio_Channel, LED3);	//低電平點亮
	else
		XGpio_DiscreteClear(&gpio, LED_XGpio_Channel, LED3);		//高電平點亮
}
void LED4_Statue(int statue)
{
	if(statue)
		XGpio_DiscreteSet(&gpio, LED_XGpio_Channel, LED4);	//低電平點亮
	else
		XGpio_DiscreteClear(&gpio, LED_XGpio_Channel, LED4);		//高電平點亮
}

/* 定義LED整體操作函式 */
void LED_Statue(int statue)
{
	if(statue)
		XGpio_DiscreteSet(&gpio, LED_XGpio_Channel, LED);	//低電平點亮
	else
		XGpio_DiscreteClear(&gpio, LED_XGpio_Channel, LED);		//高電平點亮
}

5.1.4.主檔案

/**
 *   @filename		main.c
 *   @brief				測試LED驅動
 *   @data				 2018/10/18
 *   @author			Mculover666
 */
#include "led_bsp_mculover666.h"
#include "xil_printf.h"

int main()
{
	/* 初始化LED所在io */
	led_init();
	print("LED init ok\r\n");

	/* 開啟所有io */
	LED_Statue(ON);
	print("LED is all on\r\n");

	while(1);

}

5.1.5.實驗結果

5.2.加入RGBLED驅動

加入RGBLED驅動有兩種方法:

  • 掛載在已實現的axi_gpio_0的通道1上:axi_gpio_0支援32個io,led只使用了前4個,所以可以將兩顆rgbled的6個引腳掛載在axi_gpio_0上

  • 掛載在已實現的axi_gpio_0的通道2

因為硬體設計檔案更改,所以需要重新自動佈局佈線,生成位元流,匯出硬體檔案

然後將程式中通道值改為2,LED的值改為0x3f(使用了6個io)即可看到現象:

#define LED 0x3f   /* Assumes bit 0 of GPIO is connected to an LED  */
#define LED_CHANNEL 2