Linux

【Zynq7】実践5. ZynqPL-GPIOをLinuxで使う【液晶編2】

1.記事一覧

記事は複数回に分けて投稿します。XilinxのARM SoC (Zynq7020SoC)で動作する、YoctoベースのPetaLinuxを使わない素のLinuxをソースコードから構築する記事の続編です。

ZynqMP(arm64版)はこちら

エッジArm Linux構築編(Armv7)

エッジArm Linux実践編(Armv7)

2.【概要】PythonでGPIOを使いたい

240×320ピクセルのSPI液晶(ILI9328)を使う編の第2回になります。前回はこちら。この液晶はSPIインターフェースの他にGPIOの制御が必要なので、今回はZynq7000のPL(=FPGA)上にAXI-GPIOのIPを構築し、それをLinux上から使えるようにしてPythonで使えるようにします。

手順は以下のようになります。

  1. AXI-GPIOのIPを構築し、配線情報を追加したBitstreamを生成する
  2. Linuxデバイスツリーを更新し、GPIOをシステムから使えるようにする

前回のSPIを使う記事とほぼ同じ手順なので、前回まとめてやったほうが手間がかからないです。

液晶制御に必要なGPIOはRS,Resetの2線になります。

【追記】RS信号は不要でした。SPIのスタートバイトにRSのビットフィールドがあります。ただし、次回の液晶編でSPIのCS信号がうまく使えないことが判明したので、GPIOでCS信号をソフト制御します。よって2線必要です。

今回使用する環境です。

開発PCUbuntu20.04
開発ツールXilinx社 Vivado・Vitis v2022.2
ターゲットボードDigilent社製 Zybo z7-20
SoC:Xilinx社 Zynq7020 armv7hf (Cortex-A9×2、666MHz)
SDRAM:DDR3 1GB
u-boot:v2022.01
Linux Kernel: v5.15.0
RootFs:Ubuntu22.04 jammy armv7l
SPIデバイス2.2インチ320×240フルカラー液晶
液晶コントローラ:ILI9328
aitendoモジュールキット
ロジックアナライザZeroplus社 LAP-C 16064 USB接続タイプ

3.AXI-GPIOを有効化

VivadoでAXI-GPIOを設定していきます。プロジェクトは以前のLinux構築で作成したものを改変します。GPIOにはPSのGPIOもありますが、今回はPL(≒FPGA)に新たにGPIOのIPを構築したいと思います。

Xilinxから無償で使用可能なAXI-GPIOをIPに選んで使用します。

3-1. VivadoでAXI-GPIOのIPを追加

IP INTEGRATOR>Open Block Designでブロック図を開きます。何もないところを右クリックし、”Add IP”をクリックします。Serchに”gpio”と入力し、AXI GPIOをダブルクリックします。

Run Connection Automationをクリックし自動配線していきます。ウィンドウが出るので、以下のようにチェックを入れ、OKを押します。

以下のようになります。AXI Interconnectブロックが自動追加されていますが、正常です。ZynqのPSブロックのM_AXI_GP0ポート->AXI Interconnect->AXI GPIOとバスが接続されていますが、ZynqのGPポートがAXI3バスに対し、AXI GPIOがAXI4(Lite)なので、プロトコル変換が必要とのことらしいです。

続いてAXI-GPIOの設定をするので、ブロックをダブルクリックします。BoardタブのGPIOとGPIO2のうち、GPIO側のBoard InterfaceをCustomにします。

次にIP Configurationタブを開き、次のように設定します。

  • All Inputs チェックなし
  • All Outputs チェックなし
  • GPIO Width =2
  • Default Output Value すべて0
  • Default Tri State Value すべてF
  • Enable Dual Channel チェックなし

最後に端子名をわかりやすいように変えます。今回はdisp_ctrlに変えておきます。

最後にAXIGPIOのレジスタアドレス割当を確認します。Addres Editorタブを開きます。

先頭アドレス0x41200000から64KBが割り当てられたようですね。

3-2. 端子の紐づけ

毎度やっているので詳細手順は省略します。xdcファイルで端子制約条件を追記します。

まずは信号名の確認ですね。[1:0]disp_ctrl_tri_ioになっています。

xdcを更新します。各ビットは以下のように記述します。

  • 0bit目:disp_ctrl_tri_io[0]
  • 1bit目:disp_ctrl_tri_io[1]

今回はJC端子の7,8番に接続します。

回路図引用<https://digilent.com/reference/programmable-logic/zybo-z7/start>

あとはboot.binを作成し直して終了です。

4.デバイスツリー生成

デバイスツリー構築記事を参考にVivadoプラットフォームファイルから再生成します。

新たにpl.dtsiファイルが作られています。PL側にamba_plバスが作られ、

あとはDTCコンパイルして完了です。

5.動作確認

LEDを接続して確認をしていきます。

5-1. 端子定格の確認とLEDの配線

GPIOを紐付けしたPmod JC端子にLEDを接続していきます。
LEDを接続する前に端子の電流定格を確認します。

ドキュメントDS187[DC 特性および AC スイッチ特性] 表10 PL の I/O レベル
https://docs.xilinx.com/v/u/ja-JP/ds187-XC7Z010-XC7Z020-Data-Sheet

xdcファイル(端子制約条件)で該当端子は”LVCMOS33″つまり3.3VのCOMS入出力です。この場合のIOL、IOHは以下の記述とあります。

HR I/O バンクでは、4、8、12、または 16mA の駆動電流をサポート しています。

HR I/OバンクとはHigh Range I/Oバンクのことで3.3Vまでの広い電圧範囲に対応できる端子群です。今回使用しているGPIO端子はBank34の端子でXCZ7020の場合、HR I/Oバンクになります。

xdcファイルで駆動電流をDRIVEプロパティを設定できます。今回は記述していないのでデフォルトは12mA(おそらく)になるはずです。

LED1個程度は駆動できなくはないのですが、基本的に信号I/O用の端子で必要電流が大きいデバイスを駆動すべきではないので、電流増幅用にバッファICを外付けします。

今回は秋月で売っていた74HC245を使用します(今は非売でした)。

5-2. SysFsインターフェースによるLED点滅確認

GPIO2本でLED点滅確認をしてみます。

やり方

SysFsとは/sys/class/gpio以下のファイルを読書きすることでGPIOを制御するインターフェースです。今回追加したGPIOを有効にしてLinuxを起動すると以下のファイルが現れます。

有効前

Bash
zynq@zyboz7:~$ ls /sys/class/gpio/
.. export gpiochip904 unexport

有効後

Bash
zynq@zyboz7:~$ ls /sys/class/gpio/
.. export gpiochip1022 gpiochip904 unexport

gpiochip1022が現れました。番号は環境によって変わります。904の方はPS側のGPIOです。1022がAXI-GPIOです。

GPIOを初期化します。GPIO端子ごとに制御するためのファイル群を作成します。GPIO端子の関係は以下のようになります。

  • disp_ctrl_tri_io[0] -> gpio1022
  • disp_ctrl_tri_io[1] -> gpio1023

まずrootユーザに切り替えます。

Bash
$ su

次にファイル群を作成します。

Bash
root$ echo 1022 > /sys/class/gpio/export

/sys/class/gpio/gpio1022フォルダが生成します。次に入出力方向を設定します。

Bash
root$ echo out > /sys/class/gpio/gpio1022/direction

今の手順をgpio1023でも行います。次に端子出力をHigh,Lowに切り替えて手動でLED点滅をやってみます。
High出力

Bash
root$ echo 1 > /sys/class/gpio/gpio1022/value

Low出力

Bash
root$ echo 0 > /sys/class/gpio/gpio1023/value

5-3. PythonでLED点滅

PythonでLED点滅をやってみました。gif変換のやり方が悪くて0.5倍速位になってますが。

実行する際はあらかじめGPIOの初期化と入出力方向をやっておく必要があります。また、sudo実行が必要です。

Pythonではファイルの書き込みバッファなし(buffering=0)でopenするか、write()のあとにflush()メソッドを呼ぶことをしないと、GPIOがうまく切り替わりません。また、バッファなしにする場合はバイナリモードのみでテキストモードは使用不可です。

6.【補足】DockerコンテナからGPIOを使う

GPIOのインターフェースはSysfsなので、docker run時に -vオプションで共有フォルダを作り/sys以下の任意の場所をコンテナと共有します。

イメージ=debian/sampleの場合、以下のようになります。

Bash
$ docker run -v "/sys:/sys"  --name "py-gpio-app" -it "debian/sample" /bin/bash
ABOUT ME
sh-goto
組込エンジニア. 最近の遊びはLinuxの低レイヤいじりとXilinxのZynqとFPGAを使った電子工作
関連記事