ZYBO (Zynq) 初心者ガイド (11) Linuxユーザアプリケーションで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)


# 環境
- 開発用PC: Windows 10 64-bit 
	- Vivado 2017.4 WebPACKライセンス
	- Xilinx SDK 2017.4  <- 今回は使わない
- 開発用PC (Linux): Ubuntu 16.04 本家 (日本語版じゃない) (on VirtualBox 5.2.4)
	- PetaLinux 2017.4
- ターゲットボード: ZYBO (Z7-20) 

Windows環境は[1回目](https://qiita.com/take-iwiw/items/966f252f6ca954aff08b)、Linux環境は[8回目](https://qiita.com/take-iwiw/items/6e6915f7318689818368)を参照。
使用するハードウェア(hdf)は[8回目](https://qiita.com/take-iwiw/items/6e6915f7318689818368)を参照。

# LinuxユーザアプリケーションでLチカ
PetaLinuxツールで作られるLinux内に、ユーザアプリケーションを追加します。そのアプリケーションでLED(LD4)をチカチカさせます。内容的には、[3回目: PSのGPIOでLチカ](https://qiita.com/take-iwiw/items/39d2ed00fa88a1204fce)と同じことを、Linuxユーザアプリからやります。

Linuxユーザアプリケーションの作り方は、基本的には、ug1144-petalinux-tools-reference-guide.pdfのガイド通りです。ただし、このガイドにも書いてあるのですが、アプリケーション自体は他の環境(XilinxSDK等)で作成して、完成したら、ここに示す手順でプロジェクトに取り込む、というのが推奨フローのようです。実際、ここで示す手順だと、非常に効率が悪いです。が、現状僕の環境だとLANが使えず、まともなデバッグもできない状況なので、ひとまずこの記事の手順でやろうと思います。


# プロジェクトの準備
新規PetaLinuxプロジェクトの作成からやります。とりあえず、下記操作でBOOT.binとimage.ubを作成して、ちゃんと起動できることを確認します。これはいつも通りの手順で、[8回目](https://qiita.com/take-iwiw/items/6e6915f7318689818368)と同じです。

```sh:開発PCターミナル
cd ~/work/peta
petalinux-create --type project --template zynq --name SimplePS
cd SimplePS/
petalinux-config --get-hw-description=../project_1.sdk
petalinux-build
petalinux-package --boot --force --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/design_1_wrapper.bit --u-boot
```

## シェルからLEDチカチカ
この状態で既に、GPIOにアクセスするためのsysfsインターフェースが用意されています。今回は、PSのGPIOに接続されているLED(LD4)を制御します。これはMIO7に接続されています。もともと、`/sys/class/gpio/gpiochip906`が用意されているので、この906を基準に、906+7=913がMIO7用のGPIO番号になるようです。(https://stackoverflow.com/questions/40131771/sysfs-interface-i-cant-export-gpio-pins-in-a-zybo-board)

```sh:Zyboターミナル
echo 913 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio913/direction
echo 1 > /sys/class/gpio/gpio913/value
echo 0 > /sys/class/gpio/gpio913/value
```

または、レジスタ(メモリ)に直書きでも制御できます。理由は後述しますが、GPIOへのクロック供給をONにするために、`echo 913 > /sys/class/gpio/export`は必要です。レジスタと設定値については[3回目](https://qiita.com/take-iwiw/items/39d2ed00fa88a1204fce)をご参考ください。

```sh:Zyboターミナル
echo 913 > /sys/class/gpio/export
devmem 0xE000A204 32 0x0080
devmem 0xE000A208 32 0x0080
devmem 0xE000A040 32 0x0080
devmem 0xE000A040 32 0x0000
```

# ユーザアプリケーションを作成する
先ほどシェルからやった、LEDチカチカ制御を行うアプリケーション(Cプログラム)を作ります。下記コマンドで、myappというLinuxのユーザ空間で動くアプリケーションを作成します。コードは、`project-spec/meta-user/recipes-apps/myapp/`に作られます。後で編集しますが、ひとまずはこのままにします。`petalinux-build -x package`でこのアプリケーションを含む`image.ub`が再作成されます。

```sh:開発PCターミナル
petalinux-create -t apps --template c --name myapp --enable
petalinux-build -x package
```

作成された`image/linux/image.ub`をSDカードに上書きコピーしてZyboを起動します。(BOOT.binはそのまま)
すると、/usr/bin/myappがインストールされており、実行するとHello Worldが出力されていることが分かります。

```sh:Zyboターミナル
root@SimplePS:~# ls /usr/bin/myapp
/usr/bin/myapp
root@SimplePS:~# myapp
Hello World!
```

## コードを編集して、再ビルドする
コードを編集して、再度ビルドするには、以下のようにします(エディタは適当に置き換えてください)。どういうコードにするかは後述。

```sh:開発PCターミナル
code project-spec/meta-user/recipes-apps/myapp/files/myapp.c &
petalinux-build -c myapp -x do_clean 
petalinux-build -c myapp -x do_install
petalinux-build -x package
```
ug1144-petalinux-tools-reference-guide.pdfには、最初に一度だけ、`petalinux-build -x do_populate_sysroot`と`petalinux-build -c rootfs`をやるように書いてありましたが、無くても問題ありませんでした。(影響は不明)


### メモ
再ビルドする前に`petalinux-build -c myapp -x do_clean`をしないと、ビルドエラーが発生しました(原因不明)。
なお、`petalinux-build -c myapp -x do_install`を省略して、`petalinux-build -x package`だけでも自動的にmyappをビルドしてくれるようです。

### メモ
今回は、毎回、パッケージ化された`image/linux/image.ub`を置き換えて動作確認します。後々、myappのバイナリだけを差し替えて開発できるようにしたいと思っています(TCFやNFSなど)。その時は、`petalinux-build -c myapp -x do_install`だけやればmyappのバイナリ作成が完了します。(`petalinux-build -x package`もそれなりに時間がかかるので、だいぶ時間を節約できます。)
myappのバイナリ自体は、下記の場所に作られていました。ファイルが異なるものが2つ出来ていたのですが、おそらくstripされているかどうかだと思います。[9回目](https://qiita.com/take-iwiw/items/6f2644d16cbfdb021d07)にやったように、RootFSをSDカードにしている場合は、このバイナリをSDカードに書き込むことでも動作確認ができると思います。

- build/tmp/work/cortexa9hf-neon-xilinx-linux-gnueabi/myapp/1.0-r0/myapp
- build/tmp/work/cortexa9hf-neon-xilinx-linux-gnueabi/myapp/1.0-r0/package/usr/bin/myapp

# レジスタ直叩きでLチカ
[3回目](https://qiita.com/take-iwiw/items/39d2ed00fa88a1204fce)でやったように、PS GPIO(MIO7)のレジスタを直叩きして制御します。コードはほとんど同じですが、Linuxユーザ空間からのアクセスになるので、mmapが必要になります。

```c:project-spec/meta-user/recipes-apps/myapp/files/myapp.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>

#define REG(address) *(volatile unsigned int*)(address)
#define GPIOPS_BASE   (0xE000A000)
#define GPIOPS_DATA_0 (0x0040)
#define GPIOPS_DIRM_0 (0x0204)
#define GPIOPS_OEN_0  (0x0208)

int main(int argc, char **argv)
{
    printf("Hello World!\n");

    int address;    /* GPIOレジスタへの仮想アドレス(ユーザ空間) */
    int fd;

    /* メモリアクセス用のデバイスファイルを開く */
    if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
        perror("open");
        return -1;
    }

    /* ARM(CPU)から見た物理アドレス → 仮想アドレスへのマッピング */
    address = (int)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIOPS_BASE);
    if (address == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return -1;
    }

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

    /* 使い終わったリソースを解放する */
    munmap((void*)address, 0x1000);
    close(fd);

    return 0;
}
```

image.ubを再作成して、SDカードに入れて起動します。そして、MIO7に相当するGPIOを有効にしてから、`myapp`を実行します。すると、LED(LD4)が1秒間隔でチカチカするはずです。

```sh:Zyboターミナル
root@SimplePS:~# echo 913 > /sys/class/gpio/export
root@SimplePS:~# myapp
Hello World!
```

1点注意点ですが、`myapp`を実行する前には、`echo 913 > /sys/class/gpio/export`でGPIOを有効化しておく必要があります。これは、デフォルトだと省電力のために、GPIOのクロックがOFFになっているためだそうです。(https://forums.xilinx.com/t5/Embedded-Linux/GPIO-access-via-mmap/td-p/672045)

ここではレジスタ直叩きしてみましたが、実際には、デバイスドライバが用意したデバイスファイル/SysFSインターフェースを使うか(次に紹介)、UIOというものを使うのが一般的なようです。

# GPIOのSysFSインターフェースでLチカ
以前、[他の記事](https://qiita.com/take-iwiw/items/d6645880c5bb91ce8a85#gpio%E5%88%B6%E5%BE%A1)で紹介しましたが、以下のようなコードでGPIOのSysFSにアクセスします。

```c:project-spec/meta-user/recipes-apps/myapp/files/myapp.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>

int main(int argc, char **argv)
{
	printf("Hello World!\n");

	int fd;

	/* 1. GPIO913を使用可能にする */
	fd = open("/sys/class/gpio/export", O_WRONLY);
	write(fd, "913", 3);
	close(fd);

	/* 2. GPIO913を出力設定する */
	fd = open("/sys/class/gpio/gpio913/direction", O_WRONLY);
	write(fd, "out", 3);
	close(fd);

	/* 3. GPIO913に1(High)を出力する */
	fd = open("/sys/class/gpio/gpio913/value", O_WRONLY);
	write(fd, "1", 1);

	while(1) {
		write(fd, "0", 1);
		usleep(1*1000*1000);
		write(fd, "1", 1);
		usleep(1*1000*1000);
	}

	/* 4. 使い終わったので閉じる */
	close(fd);

	return 0;
}
```

image.ubを再作成して、SDカードに入れて起動します。`myapp`を実行すると、LED(LD4)が1秒間隔でチカチカするはずです。このコードの場合は、手動でGPIOを有効にする必要はありません。

```sh:Zyboターミナル
root@SimplePS:~# myapp
Hello World!
```

# アプリケーションを取り除く
このユーザアプリケーションを、作成するLinuxイメージ(image.ub)に含めたくないときは、`petalinux-config -c rootfs`のapps設定で、myappのチェックを外します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/214268/eb4eb44f-0d7b-f0d2-e0d4-6713c33b84b2.png)

# メモ: PL GPIOの場合
PL側のGPIOに接続されたLED(M14、M15、G14、D18)を制御する場合の方法です。
ハードウェアは、[4回目](https://qiita.com/take-iwiw/items/a3b935a4e4dbd41593e1)で作ったものと同じ、AXI GPIOをLED(M14、M15、G14、D18)に接続したものがあるとします。AXI GPIOのBase Addressを`0x41200000`とします。これをベースにPetaLinuxで作成したLinuxを起動します。

まずは、レジスタ直叩きバージョンです。PLのAXI GPIOに関しては、OSで電源管理されていないので、レジスタを叩くだけで使えます。

```sh:レジスタ直叩きの場合
# set GPIO as output
devmem 0x41200004 32 0x0000
# Output High to GPIO (4-bit)
devmem 0x41200000 32 0x000F
# Output Low to GPIO (4-bit)
devmem 0x41200000 32 0x0000
```

SysFS経由の場合です。`/sys/class/gpio/gpiochip1020`が用意されているので、それがAXI GPIOになります。念のため、`more /sys/class/gpio/gpiochip1020/label`で中身を確認してみてください。オフセットをつける必要がないので、1020番をそのまま使えます。

```sh:sysfsを使う場合
echo 1020 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio1020/direction
echo 1 > /sys/class/gpio/gpio1020/value
echo 0 > /sys/class/gpio/gpio1020/value
```

アドレスや番号が違うだけで、プログラムからはPS GPIOと同様に制御できます。


# メモ: ライブラリを作りたい場合
```
petalinux-create -t libs --template c --name mylib --enable
```