源代码链接:Ultra96-PYNQ_A-simple-summary
注:这部分是进行简单分析相应的重要函数,如嫌啰嗦,可以直接转到2小节。
本小节与Ultra96-V2板卡的PL部分无关,直接使用PS控制GPIO作为示例
进行下面之前,需要导入GPIO模块
from pynq import GPIO
获取gpio号的基地址,PYNQ的源代码是使用Linux’s GPIO Sysfs API来获取对应的基地址的。
gpio_base = GPIO.get_gpio_base()
print("Ultra96-V2 gpio_base is {}".format(gpio_base))
下面是对应的Linux Sysfs部分
xilinx@pynq:~$ cd /sys/class/gpio/
xilinx@pynq:/sys/class/gpio$ tree
.
├── export
├── gpiochip338 -> ../../devices/platform/amba/ff0a0000.gpio/gpio/gpiochip338
└── unexport
获取gpio设备在Linux Sysfs的路径
gpio_base_path = GPIO.get_gpio_base_path()
print("Ultra96-V2 gpio_base_path is {}".format(gpio_base_path))
获取这个gpio控制器,最多可控制多少个引脚
gpio_npins = GPIO.get_gpio_npins()
print("Ultra96-V2 gpio_npins is {}".format(gpio_npins))
Ultra96-V2 gpio_npins is 174
从数据手册可以获取这个数值的含义,相信你只要上过小学,就可以知道这个174是MIO+EMIO的数量
这个可以获取Linux Sysfs GPIO的引脚号,可用于后续的初始化。
def get_gpio_pin(gpio_user_index, target_label=None):
if target_label is not None:
GPIO_OFFSET = 0
else:
GPIO_OFFSET = GPIO._GPIO_MIN_USER_PIN
return (GPIO.get_gpio_base(target_label) + GPIO_OFFSET +
gpio_user_index)
通过源码可知如使用MIO部分的GPIO,则不建议使用此API。
初始化部分必须慎重对待。区分开MIO与EMIO,EMIO在MIO之外加上相应的偏移。
def __init__(self, gpio_index, direction):
"""Return a new GPIO object.
Parameters
----------
gpio_index : int
The index of the GPIO using Linux's GPIO Sysfs API.
direction : 'str'
Input/output direction of the GPIO.
"""
gpio_index = GPIO.get_gpio_base() + MIO编号
gpio_index = GPIO.get_gpio_base() + GPIO.get_gpio_pin(gpio_user_index = EMIO编号)
这部分API不用多说,很简单明了。
read(self)
write(self, value)
release(self)
经过上面的讲解,假设你看中了Ultra96-V2的一个PS MIO连接的LED,想要试验一下,控制这个LED,找点成就感,在此我遗憾的告诉你,你将碰一鼻子灰…。
led1 = GPIO(gpio_base + 19, 'out')
则会出现设备资源繁忙,无法访问的错误,如下所示
OSError Traceback (most recent call last)
OSError: [Errno 16] Device or resource busy
During handling of the above exception, another exception occurred:
OSError Traceback (most recent call last)
-input-5-22b01faa7ec5> in ()
----> 1 led1 = GPIO(gpio_base + 19, 'out')
/usr/local/lib/python3.6/dist-packages/pynq/gpio.py in __init__(self, gpio_index, direction)
192
193 if not self._impl or not self._impl.is_exported():
--> 194 self._impl = _GPIO(gpio_index, direction)
195 _gpio_map[gpio_index] = self._impl
196
/usr/local/lib/python3.6/dist-packages/pynq/gpio.py in __init__(self, gpio_index, direction)
77 if not os.path.exists(self.path):
78 with open('/sys/class/gpio/export', 'w') as f:
---> 79 f.write(str(self.index))
80
81 with open(self.path + 'direction', 'w') as f:
OSError: [Errno 16] Device or resource busy
根本原因是,Ultra96-V2的PYNQ镜像,已经在设备树中将该GPIO资源分配给LED了,如果再通过GPIO模块对该资源进行访问,则肯定会出现设备资源繁忙错误报告。
Ultra96-V2的设备树中的LED如下所示
xilinx@pynq:/sys/class/gpio$ cd /sys/class/leds/
xilinx@pynq:/sys/class/leds$ tree
.
├── ds2 -> ../../devices/platform/leds/leds/ds2
├── ds3 -> ../../devices/platform/leds/leds/ds3
├── ds4 -> ../../devices/platform/leds/leds/ds4
├── ds5 -> ../../devices/platform/leds/leds/ds5
├── mmc0:: -> ../../devices/platform/amba/ff160000.mmc/leds/mmc0::
├── mmc1:: -> ../../devices/platform/amba/ff170000.mmc/leds/mmc1::
└── vbus_det -> ../../devices/platform/leds/leds/vbus_det
7 directories, 0 files
进入相应目录,可以使用root写入brightness来控制相应LED的亮灭。具体如何通过Python来控制这些,会在文章最后进行简单提及的。
xilinx@pynq:/sys/class/leds/ds3$ cat brightness
0
xilinx@pynq:/sys/class/leds/ds3$ su
Password:
root@pynq:/sys/class/leds/ds3# tree
.
├── brightness
├── device -> ../../../leds
├── max_brightness
├── power
│ ├── autosuspend_delay_ms
│ ├── control
│ ├── runtime_active_time
│ ├── runtime_status
│ └── runtime_suspended_time
├── subsystem -> ../../../../../class/leds
├── trigger
└── uevent
3 directories, 9 files
root@pynq:/sys/class/leds/ds3# echo 1 > brightness
root@pynq:/sys/class/leds/ds3# cat brightness
1
root@pynq:/sys/class/leds/ds3#
既然LED设备资源无法访问,访问那些没有在设备树分配相应功能的GPIO资源了。
这里仅使用MIO36_PS_GPIO1_0输出示例使用方法。
如果尝试输入实验,一定要注意电平标准(MIO 1.8V!!!),不要一不小心,把2K多的板子给烧了…。
如果尝试输入实验,一定要注意电平标准(MIO 1.8V!!!),不要一不小心,把2K多的板子给烧了…。
如果尝试输入实验,一定要注意电平标准(MIO 1.8V!!!),不要一不小心,把2K多的板子给烧了…。
一般的口袋仪器的数字部分电平是LVCMOS 3.3V的,这时候可以考虑使用口袋仪器的信号发生器来构造1.8V的脉冲信号,来进行测试。输入部分没事,1.8V勉强可以触发3.3V,至少烧不坏。
完整测试代码
from pynq import GPIO
import time
gpio_base = GPIO.get_gpio_base()
print("Ultra96-V2 gpio_base is {}".format(gpio_base))
gpio0 = GPIO(gpio_base + 36, 'out')
for i in range(100):
gpio0.write(1)
time.sleep(0.1)
gpio0.write(0)
time.sleep(0.1)
逻辑分析仪的测量结果如下,符合软件设置的间隔。
输入实验,与输出实验类似,唯一要注意的是不要使用不兼容的电压标准。
上面使用gpio0 = GPIO(gpio_base + 36, 'out')
对一个MIO部分的GPIO资源进行实例化,这时候可以在Linux Sysfs部分惊奇的发现
root@pynq:/sys/class/leds/ds3# cd /sys/class/gpio/
root@pynq:/sys/class/gpio# tree
.
├── export
├── gpio374 -> ../../devices/platform/amba/ff0a0000.gpio/gpiochip0/gpio/gpio374
├── gpiochip338 -> ../../devices/platform/amba/ff0a0000.gpio/gpio/gpiochip338
└── unexport
2 directories, 2 files
进入相应的目录,可以与LED类似,直接写入相应的值进行控制。 PYNQ的GPIO模块原理就是使用Linux’s GPIO Sysfs API来进行控制GPIO的,使用Python进行进一步封装,减少开发难度。
这部分需要加载相应的bit流文件,进行使用。
这里我拿这6个小家伙作为EMIO示例,约束为可以输入可以输出,具体如何使用,需要软件进一步配置
很简单的一个设计,可以在Vivado中的TCL控制台通过如下命令,进行快速重建
source 1.ps_emio.tcl
如果打开实现后的器件视图,可以发现PS的EMIO正确的连接到相应的引脚上了
使用类似的代码,即可进行验证
from pynq import GPIO
from pynq import Overlay
import time
emio_gpio_design = Overlay("./data/1.PS_EMIO.bit")
hd_emio_gpio0_n = GPIO.get_gpio_pin(gpio_user_index= 0)
print("Ultra96-V2 hd_emio_gpio0 numbers is {}".format(hd_emio_gpio0))
hd_emio_gpio0 = GPIO(hd_emio_gpio0_n, 'out')
for i in range(100):
hd_emio_gpio0.write(1)
time.sleep(0.1)
hd_emio_gpio0.write(0)
time.sleep(0.1)
逻辑分析仪的测量结果如下,符合软件设置的间隔。
EMIO与MIO类似,可以在sysfs中查询得到,同样可以进行控制
root@pynq:/sys/class/gpio# tree
.
├── export
├── gpio416 -> ../../devices/platform/amba/ff0a0000.gpio/gpiochip0/gpio/gpio416
├── gpiochip338 -> ../../devices/platform/amba/ff0a0000.gpio/gpio/gpiochip338
└── unexport
A pure Python 2/3 library for peripheral I/O (GPIO, LED, PWM, SPI, I2C, MMIO, Serial) in Linux.
这里仅放相应的Github链接,并不进行演示,具体可在链接中查询。
注:控制设备树中的常用外设,与FPGA部分无关,这时候Ultra96-V2与树莓派之类的板卡类似。
原创不易,严禁剽窃!
欢迎大家关注我创建的微信公众号——小白仓库
原创经验资料分享:包含但不仅限于FPGA、ARM、RISC-V、Linux、LabVIEW等软硬件开发,另外分享生活中的趣事以及感悟。目的是建立一个平台记录学习过的知识,并分享出来自认为有用的与感兴趣的道友相互交流进步。