ZYBO (Zynq) 初心者ガイド (3) PSのGPIOでLチカ

- [1回目: 開発環境の準備](https://qiita.com/take-iwiw/items/966f252f6ca954aff08b)
- [2回目: Hello Worldプロジェクト](https://qiita.com/take-iwiw/items/24a8a94741fdbb80f62a)
- __[3回目: PSのGPIOでLチカ](https://qiita.com/take-iwiw/items/39d2ed00fa88a1204fce) <--- 今回の内容__
- [4回目: PLのAXI GPIOでPSからLチカ](https://qiita.com/take-iwiw/items/a3b935a4e4dbd41593e1)
- [5回目: PLだけでLチカ](https://qiita.com/take-iwiw/items/efcdfca1866b9941fd2a)
- [6回目: 自作IPでLチカ](https://qiita.com/take-iwiw/items/b0f2ce7b4653bc6f32b9)
- [7回目: ブートイメージを作る](https://qiita.com/take-iwiw/items/ece0cdf83e6e1908fad8)
- [8回目: Linux起動する](https://qiita.com/take-iwiw/items/6e6915f7318689818368)
- [9回目: Linuxカーネルを少しカスタマイズする](https://qiita.com/take-iwiw/items/6f2644d16cbfdb021d07)
- [10回目: LinuxのRootFSをカスタマイズする / PythonでHello World](https://qiita.com/take-iwiw/items/94a90047978664159681)
- [11回目: LinuxユーザアプリケーションでLチカ](https://qiita.com/take-iwiw/items/5e4ab9a106acfda2c866)
- [12回目: LinuxカーネルモジュールでLチカ](https://qiita.com/take-iwiw/items/e7efc48d92d7b979efdc)
- [13回目: LAN(Ethernet 0)を使う](https://qiita.com/take-iwiw/items/ac489acaca1ab54d7ce8)
- [14回目: Linuxユーザアプリをデバッグする / RootFSに取り込む](https://qiita.com/take-iwiw/items/8134ca7098622219b177)
- [15回目: Linux起動時にアプリケーションを自動実行させる](https://qiita.com/take-iwiw/items/50065df6cdd1ba8bcbd6)
- [16回目: Linuxから自作IPをUIOで制御する](https://qiita.com/take-iwiw/items/da91ce4dc2a8a8df3c0a)
- [17回目: Linuxで自作IPのデバイスドライバを作る](https://qiita.com/take-iwiw/items/05eb1204cb51ce0a5c03)
- [18回目: IoT化してスマホからLチカ](https://qiita.com/take-iwiw/items/e9311292c5078d785699)

この記事の内容を4分で見る ( https://www.youtube.com/watch?v=VJCL2Z81DLQ )

# 環境
- 開発用PC: Windows 10 64-bit 
	- Vivado 2017.4 WebPACKライセンス
	- Xilinx SDK 2017.4
- ターゲットボード: ZYBO (Z7-20) 

Windows環境は[1回目](https://qiita.com/take-iwiw/items/966f252f6ca954aff08b)を参照。

# PSのGPIOでLチカ
前回、PSのみを搭載したハードウェアを作成して、PSのUARTとCPUを使ってHello World出力をしました。今回は、PSのGPIOを使ってLEDをチカチカさせます。これは通常のマイコンとほぼ同じなので、ソフト屋さんにもとっつきやすいです。


# プロジェクトの作成
[前回](https://qiita.com/take-iwiw/items/24a8a94741fdbb80f62a)作成したプロジェクトをそのまま使います。これは、PSだけを搭載したハードウェア(hdf)から作成したSDKプロジェクトです。

# 事前知識
## MIOについて
ZynqはPSとPLから構成されます。PSにはCPUのほかにUART、I2C、GPIO等のペリフェラルがハードマクロIPとして搭載されています。これら専用のピン(IO)が用意されています。それがMIO(Multiplexed IO)です。ピン毎にどの機能に割り当てるかを選ぶことが出来るので「Multiplexed」となっています。STM32などのマイコンと同じです。

このMIOはPS専用です。一方、MIOの数が足りなくなった場合には、PLにつながっている普通のIOをPSのペリフェラル用IOとして使うことが出来ます。これをEMIO(Extended MIO)というらしいです。

## PSのGPIOについて
PS上のペリフェラルは通常のマイコンのペリフェラルと同じように制御できます。GPIOも通常のマイコン通り、入出力の方向を設定して、出力する、という流れです。設定するレジスタのアドレスも決まっています。

機能仕様やレジスタ詳細は、Zynq-7000のテクニカルリファレンスマニュアルに載っています。

![01.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/c715ade1-424b-8686-ce55-0599b18f8e65.jpeg)

## ZYBOボード上での接続
ZYBO(Z7-20)ボードでは、LED4がMIO7に接続されています。このLEDを光らせることにします。

念のため、VivadoでPSのコンフィグを確認します。MIO7はどの機能ペリにも割り当てられてなく、GPIO MIOとなっていました。また、MIO7はBank0に属していることが分かります。
![02.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/d11941e0-dac5-70cf-4efe-50008c5345e8.jpeg)

# レジスタ直叩きでPSのGPIO制御
まずは、レジスタ直叩きでGPIO制御してみます。Zynq-7000のテクニカルリファレンスマニュアルを見ると、関連するレジスタは以下の通りでした。

- GPIOPSのベースアドレス: 0xE000A000
- DATA_0 (Output Data (GPIO Bank0, MIO))のオフセット: 0x00000040
- DIRM_0 (Direction mode (GPIO Bank0, MIO))のオフセット: 0x00000204
- OEN_0 (Output enable (GPIO Bank0, MIO))のオフセット: 0x00000208

## コード
Vivado SDK上で、以下のようなコードを実装します。(前回のHello Worldの続きなので、ファイル名もそのままですが気にしない) 
MIO7を出力設定して、その後1秒間隔でHigh/Lowを出力させることでLEDを点滅させます。

```c:helloworld.c
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "sleep.h"

#define REG(address) *(volatile unsigned int*)(address)

#define GPIOPS_BASE   (0xE000A000)
#define GPIOPS_DATA_0 (GPIOPS_BASE + 0x0040)
#define GPIOPS_DIRM_0 (GPIOPS_BASE + 0x0204)
#define GPIOPS_OEN_0  (GPIOPS_BASE + 0x0208)

int main()
{
    init_platform();

    print("Hello World\n\r");

    /* Set MIO7 as output */
    REG(GPIOPS_DIRM_0) |= 1 << 7;
    REG(GPIOPS_OEN_0)  |= 1 << 7;
    while(1) {
    	/* Set MIO7 as High */
    	REG(GPIOPS_DATA_0) |= 1 << 7;
    	sleep(1);
    	/* Set MIO7 as Low */
    	REG(GPIOPS_DATA_0) &= ~(1 << 7);
    	sleep(1);
    }

    cleanup_platform();
    return 0;
}
```

# Xilinx SDK Driver APIでPSのGPIO制御
レジスタ直叩きでの制御は、中身を理解するためには良いですが、実際にはやりたくありません。実はドライバAPI(関数)が用意されています。Xilinx SDK上でアプリケーションプロジェクトを作ったときに、BSPプロジェクトも一緒に作られたと思います。そのBSPプロジェクト内に、Vivadoで有効にしたペリフェラル制御用の関数が用意されています。さらに、使い方もドキュメントに書かれています。

![03.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/766317ef-3f75-0f62-3981-6c6fa82f982a.jpeg)

## コード
ドライバAPIを使用した場合のコードは、以下のようになります。基本的に、Xilinx提供の関数は`X`から始まります。そして、PS用の場合には最後に`Ps`とつくようです。

```c:helloworld.c
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "sleep.h"
#include "xgpiops.h"

int main()
{
    init_platform();

    print("Hello World\n\r");

    XGpioPs instXGpioPs;
    XGpioPs_Config *configXGpioPs;

    configXGpioPs = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
    XGpioPs_CfgInitialize(&instXGpioPs, configXGpioPs,configXGpioPs->BaseAddr);
    /* Set MIO7 as output */
    XGpioPs_SetDirectionPin(&instXGpioPs, 7, 1);
    XGpioPs_SetOutputEnablePin(&instXGpioPs, 7, 1);
    while(1) {
    	/* Set MIO7 as High */
    	XGpioPs_WritePin(&instXGpioPs, 7, 1);
    	sleep(1);
    	/* Set MIO7 as Low */
    	XGpioPs_WritePin(&instXGpioPs, 7, 0);
    	sleep(1);
    }

    cleanup_platform();
    return 0;
}
```

# おわりに
今回はGPIOを使ってのLED制御でしたが、PSの他のペリフェラル(UARTやI2C等)も同じ感じで制御できると思います。

# 参考資料
https://www.xilinx.com/support/documentation/sw_manuals_j/xilinx2013_4/ug898-vivado-embedded-design.pdf
https://japan.xilinx.com/support/documentation/sw_manuals_j/xilinx2015_2/ug1165-zynq-embedded-design-tutorial.pdf
https://japan.xilinx.com/support/documentation/user_guides/j_ug585-Zynq-7000-TRM.pdf