---
# System prepended metadata

title: PYNQ Z2 雜記
tags: [notes, ' linux', ' FPGA', ' PYNQ']

---

---
tags: notes, PYNQ, linux, FPGA
---
# PYNQ Z2 雜記
有關PYNQ Z2的各種小筆記，雜亂無綱。
[官方參考鏈結](https://pynq.readthedocs.io/en/latest/getting_started.html)


## Setup
### Power
SW1旁邊的jumper選擇供電來源：REG為12伏特DC jack輸入，USB為Micro USB輸入供電。
電源開關在Mircro USB母孔旁邊。
### Communication
- **網路線**
  1. 板子上電(廢話)
  2. 並透過網路線(RJ45)直接跟筆電(or PC)的網路孔對接
  3. 指定筆電乙太網路卡靜態IPv4:192.168.2.X，X可以是除了99以外0~254的數字
  4. 稍待一兩分鐘後，在瀏覽器輸入`pynq/`及可連到jupyter的web介面
  5. 如果輸入`pynq/`不行，試著輸入`192.168.2.99`看看(也可以ping看看有沒有回應)

- **USB COM port**
  1. 用Micro USB連接板子跟筆電
  2. 到裝置管理員查看com port號碼，不同筆電、不同USB孔，號碼都會不一樣
  4. 用Terminal軟體打開([推薦Mobaxterm]，設定如下(https://mobaxterm.mobatek.net/))
    ![com port setting](https://i.imgur.com/2BqrgAO.png)
  4. 剛連進去可能甚麼都沒有，鍵盤隨便敲幾個英文字後enter應該就會看到linux的文字介面了
  note: 一次只能有一個軟體去開同個com port
  
- **接上網際網路**
  1. 先將板子與筆電用網路線對接，但是不要指定筆電乙太網路卡的IP，在IPv4中讓他自動取得IP位址
  2. 到筆電上的網路介面卡選項，選擇網際網路來源(ex: wifi)
     右鍵->內容->共用->允許...打勾->下拉選單選"乙太網路"
     按下確定後會跳出一個視窗"當網際網路連線共用後blablabla"，按是
  ![](https://i.imgur.com/CSFWcCB.png)
  ![](https://i.imgur.com/akgffWT.png)
  3. 稍待一兩分鐘後，用上述的**USB COM port**連接板子，檢查網際網路連線:
    `ping google.com -c 5`
    ![](https://i.imgur.com/zfa4lrc.png)
  4. 如果要用jupyter開發的話，要在**USB COM port**查板子的IP位址
    `ifconfig`
    找到後在瀏覽器輸入IP位址即可進入jupyter介面
    (也可以用[這個](https://blog.gtwang.org/linux/ping-and-arp-scan-ip-mac-address-script/)去掃區網的有哪些裝置)

    ![](https://i.imgur.com/PitZ1tR.png)
- **Wifi**
  Ralink RT5370 recommended, it works without installing any extra driver.
  ```python=
  from pynq.lib import Wifi

  port = Wifi()
  ssid = input("Type in the SSID:")
  pwd = input("Type in the password:")
  port.connect(ssid=ssid, password=pwd, auto=True)
  ```
  For `auto=True`, it sets up an service that connect to the wifi everytime reboot.
  To disconnect the wifi:
  ```python=7
  port.reset()
  ```


## Arduino IO
The official [library](https://pynq.readthedocs.io/en/latest/pynq_package/pynq.lib/pynq.lib.arduino.html#pynq.lib.arduino.arduino_io.Arduino_IO) for accesing arduino IOs on the board is sucks. You can't use 1 pin for output and 1 pin for input at the same time. Here are some modified functions from the source code.
### Preset

```python=
from pynq.lib.arduino import Arduino_DevMode
from pynq.lib.arduino import ARDUINO_SWCFG_DIOALL
from pynq.lib.arduino import ARDUINO_DIO_BASEADDR
from pynq.lib.arduino import ARDUINO_DIO_TRI_OFFSET
from pynq.lib.arduino import ARDUINO_DIO_DATA_OFFSET
from pynq.lib.arduino import ARDUINO_CFG_DIO_ALLINPUT
from pynq.lib.arduino import ARDUINO_CFG_DIO_ALLOUTPUT
from pynq.lib.arduino import ARDUINO_NUM_ANALOG_PINS
from pynq.lib.arduino import ARDUINO_NUM_DIGITAL_PINS
from pynq.overlays.base import BaseOverlay
from pynq.lib.arduino.arduino_io import Arduino_IO
from pynq.lib.arduino.arduino_analog import Arduino_Analog
import time

base=BaseOverlay('base.bit')
PT=Arduino_IO(base.ARDUINO,0,'in')

# being careful, set all digital IO as input
Arduino_IO.write_cmd(PT,ARDUINO_DIO_BASEADDR + ARDUINO_DIO_TRI_OFFSET, ARDUINO_CFG_DIO_ALLINPUT)

"""
parameters:
  pin:   int, 0~13 corresponding pin AR0~AR13 on the board
  value: int/boolean, the output state
return:
  None
"""
def io_write(pin, value):
  if pin in range(ARDUINO_NUM_ANALOG_PINS + ARDUINO_NUM_DIGITAL_PINS):
    if value:
      cur_val = Arduino_IO.read_cmd(PT, ARDUINO_DIO_BASEADDR + ARDUINO_DIO_DATA_OFFSET)
      new_val = cur_val | (0x1 << pin)
      Arduino_IO.write_cmd(PT, ARDUINO_DIO_BASEADDR + ARDUINO_DIO_DATA_OFFSET, new_val)
    else:
      cur_val = Arduino_IO.read_cmd(PT, ARDUINO_DIO_BASEADDR + ARDUINO_DIO_DATA_OFFSET)
      new_val = cur_val & (0xffffffff ^ (0x1 << pin))
      Arduino_IO.write_cmd(PT, ARDUINO_DIO_BASEADDR + ARDUINO_DIO_DATA_OFFSET, new_val)
      


"""
parameters:
  pin:   int, 0~13 corresponding pin AR0~AR13 on the board
return:
  int/boolean, the state read from the pin
"""
def io_read(pin):
  if pin in range(ARDUINO_NUM_ANALOG_PINS + ARDUINO_NUM_DIGITAL_PINS):
    raw_value = Arduino_IO.read_cmd(PT, ARDUINO_DIO_BASEADDR + ARDUINO_DIO_DATA_OFFSET)
  return (raw_value >> pin) & 0x1
```

### Input/Output

```python=0
Arduino_IO.write_cmd(PT,
                     ARDUINO_DIO_BASEADDR + ARDUINO_DIO_TRI_OFFSET, 
                     IO_GROUP_STATE)
```
The first 2 parameters are fixed. The third one `IO_GROUP_STATE` is 32bit unsigned int. If the n-th bit is **1** then then the n-th pin is configured as **input**. If the n-th bit is **0** then then the n-th pin is configured as **output**. Here are some examples.

```python=
# set pin0(labeled AR0 on the board) as output and the rest as input
Arduino_IO.write_cmd(PT,ARDUINO_DIO_BASEADDR + ARDUINO_DIO_TRI_OFFSET, 
                     0b1111_1111_1111_1111_1111_1111_1111_1110)
                     
# set pin3(AR3) and pin6(AR6) as input and the rest as output
Arduino_IO.write_cmd(PT,ARDUINO_DIO_BASEADDR + ARDUINO_DIO_TRI_OFFSET,
                     0b0000_0000_0000_0000_0000_0000_0100_1000)
```

### Write value
The snippet in **Preset** already define two functions `io_write()` and `io_read()`. You can write a simple program that blinks the AR1 onece AR0 is 0:
```python=
Arduino_IO.write_cmd(PT,ARDUINO_DIO_BASEADDR + ARDUINO_DIO_TRI_OFFSET, 
                     0b1111_1111_1111_1111_1111_1111_1111_1101)
while io_read(0)==1: 
  time.sleep(1)
  print("waiting...")
print("Now blinking!")    
for i in range(5): # toggle five times
  time.sleep(1)
  io_write(1,1)
  time.sleep(1)
  io_write(1,0)
```
Since the library interacts with the entire IO set(from AR0~AR13) with `.write_cmd()`, so in a latency sensitive case you can write value into IOs with:
```python=0
Arduino_IO.write_cmd(PT, ARDUINO_DIO_BASEADDR + ARDUINO_DIO_DATA_OFFSET, IO_VAL)
```
where the first two parameters are fixed, and the third one `IO_VAL` is 32bit unsigned int. If the value of n-th bit is the the output state of n-th pin.



## On board LED, Button, Switch
Import the library and assign the object.
```python=
from pynq.overlays.base import BaseOverlay
base = BaseOverlay("base.bit")
```
### LED
```python=
# you can access the API directly
base.leds[0].on()
base.leds[1].off()
base.leds[2].toggle()

# you can also assign it to a var
led3=base.leds[3]
led3.on()

# since base.leds is iterable, you can also do this
for led in base.leds:
    led.on()
    time.sleep(1)
```

### Button and Switch
```python=
print(base.buttons[0].read())
print(base.switched[0].read())
```


## HDMI out
[Official github Ref](https://github.com/Xilinx/PYNQ/blob/master/boards/Pynq-Z1/base/notebooks/video/opencv_filters_webcam.ipynb)
Displaying a cv2 image object.

HDMI setupt:
```python=
from pynq.overlays.base import BaseOverlay
from pynq.lib.video import *
base = BaseOverlay("base.bit")
# monitor configuration: 1920*1080 @ (8 bits)*(BGR)=24, 60Hz
Mode = VideoMode(width=1920,height=1080,bits_per_pixel=24,fps=60)
hdmi_out = base.video.hdmi_out
hdmi_out.stop()
hdmi_out.close()
hdmi_out.configure(Mode,PIXEL_BGR)  # OpenCV default color space is BGR
try:
  hdmi_out.start()
except:
  print("[Error] HDMI Out failed to initialize.")
  hdmi_out.stop()
  hdmi_out.close()
  del hdmi_out
else:
  print("[INFO] HDMI out initialized.")
```

Simple function to producce cv2 image object:
```python=
import numpy as np
import cv2

def mk_img(text):
    img = np.zeros((1080, 1920, 3), np.uint8)
    img.fill(170) # fill with gray
    cv2.line(img, (0, 0), (255, 255), (0, 0, 255), 5) # draw red line
    cv2.putText(img, text, (700, 700), cv2.FONT_HERSHEY_SIMPLEX, 10, (0, 255, 255), 1, cv2.LINE_AA)
    return img
```

Get a image object and output to HDMI out:
```python=
img=mk_img('bla bla bla')
print("[INFO] Image size:", end='')
print(img.shape)
outframe = hdmi_out.newframe()
outframe[:] = img
hdmi_out.writeframe(outframe)
```

It is **neccesarry** to clean and release the hardware HDMI out, otherwise bad things happen when using HDMI out next time. If, unfortunately, bad things happen when using HDMI out, then you might like to reboot the board.
```python=
hdmi_out.stop()
hdmi_out.close()
del hdmi_out
```