原文来自http://www.eefocus.com/Kevin/blog/13-03/292519_c38f2.html
及
讲述一个led灯,包含整个芯片架构的设计
当你可以修改点亮LED灯那个实验led_test.sh, 改变LED点灯的速度,以及顺序时, 一定想知道究竟发生了什么, 凭什么一个简单的脚本, 就在Linux的用户层控制板子上的某一个硬件,比如说,灯。
对于习惯了在单片机环境下无操作系统裸奔C/汇编的兄弟来说, 上面的问题似乎有些多余. 直接对某个地址进行读写操作, 这个地址经过译码,对应的管脚,连到板子上LED,不就可以控制LED了吗。
这就说来话长了.简言之,Linux 为了给每个进程一个独立的地址空间,特意划分了用户空间和内核空间。内核空间可以对物理地址操作,用户空间只是虚地址。 而内核与用户空间程序接口,采用特殊文件系统。也就是看上去像文件,其实连接到系统和设备的系统。比较老的有devfs文件系统位于设备驱动/dev,或者procfs位于/proc。当然,还有比较新的sysfs文件系统。
Led_test.sh, 采用的就是sysfs文件系统。 这个文件系统的特点吧,主要是可以一层一层垒硬件的驱动,比如先总线驱动,再某个USB驱动,在USB挂载的设备的驱动。这比/dev一个设备驱动包括所有相关设备层次清晰很多。在linux内核2.6以后才有的。
对sysfs先来点感性认识,在ZedBoard任何一个终端窗口输入
sudo echo 61 > /sys/class/gpio/export
sudo echo out > /sys/class/gpio/gpio61/direction
sudo echo 1 >/sys/class/gpio/gpio61/value
sudo echo 0 >/sys/class/gpio/gpio61/value
你可以手工点亮或灭掉LD0那盏灯
输入命令的时候, 到底发生了什么, 信号是怎么传递的呢 ? 先大致了解一下,具体细节在书的后续章节还会详细说明
第一步,设备驱动控制GPIO的寄存器. 这就需要交给Linux 设备驱动来完成了。相关的介绍请参考书本在13.2节找到 .
第二步,那么GPIO的寄存器地址是多少,有如何控制呢? 我们可以在Xilinx ug585http://www.xilinx.com/support/documentation/user_guides/ug585-Zynq-7000-TRM.pdf 找到
第三步,GPIO是如何在PL那边,也就是FPGA连接到芯片的管脚上的呢? 需要参考这个设计的硬件部分http://www.digilentinc.com/Data/Products/ZEDBOARD/ZedBoard_Linux_Design.zip
zedboard上的linaro-ubuntu demo所使用的硬件工程。
其中对于zedboard的system.ucf中我们可以找到如下约束.比如,LD0那盏灯,是PS侧的processing_system7_0_GPIO<7> 通过PL测,连到管脚T22上的。
############################
# #
# On-board LED's #
# #
############################
net processing_system7_0_GPIO<7> LOC = T22 | IOSTANDARD = LVCMOS33; # LD0
net processing_system7_0_GPIO<8> LOC = T21 | IOSTANDARD = LVCMOS33; # LD1
net processing_system7_0_GPIO<9> LOC = U22 | IOSTANDARD = LVCMOS33; # LD2
net processing_system7_0_GPIO<10> LOC = U21 | IOSTANDARD = LVCMOS33; # LD3
net processing_system7_0_GPIO<11> LOC = V22 | IOSTANDARD = LVCMOS33; # LD4
net processing_system7_0_GPIO<12> LOC = W22 | IOSTANDARD = LVCMOS33; # LD5
net processing_system7_0_GPIO<13> LOC = U19 | IOSTANDARD = LVCMOS33; # LD6
net processing_system7_0_GPIO<14> LOC = U14 | IOSTANDARD = LVCMOS33; # LD
第四步,T22那个管脚是如何连到真正的灯上的,需要参考ZED板的文档:
http://zedboard.org/sites/default/files/documentations/ZedBoard_HW_UG_v1_9.pdf
2.7.3 User LEDs
The ZedBoard has eight user LEDs, LD0 – LD7. A logic high from the Zynq-7000 AP SoC I/O causes the LED to turn on. LED’s are sourced from 3.3V banks through 390Ω resistors.
Signal Name Subsection Zynq pin
LD0 PL T22
LD1 PL T21
LD2 PL U22
LD3 PL U21
LD4 PL V22
LD5 PL W22
LD6 PL U19
LD7 PL U14
LD9 PS D5 (MIO7)
我们可以看到LD0-LD7 都是PL部分的,也就是说这些都是EMIO,从PL的管脚连出。
那么,EMIO是如何在设计中定义了processing_system7_0_GPIO这个端口呢?需要看xps
2.我们可以在ports tag中找到这个标签。
3.同时system.mhs中就会自动出现这个定义
这样,从操作系统的脚本一直到点亮LED,我们顺藤摸瓜,找到了软件侧,PS侧,可扩展IO侧,PL侧所有相关文档和信号,完成了最简单的一个穿越软件,硬件,I/O, 板卡的All Programmable 之旅。
------------------第二部分-------------------------------------------
ZYNQ中的PS部分与PL部分是独立的,可以独立运行,那么我们对于ZYNQ的学习研究就可以结合嵌入式学习的方法来进行。
首先是GPIO的使用,基于ZYNQ的特殊性,其GPIO分为MIO(multiuse I/O)和EMIO(extendable multiuse I/O)两种,GPIO的相关信号被分为了4个bank,bank0与bank1为MIO信号,bank2与bank3为EMIO信号,MIO具有54根三态GPIO引脚,EMIO可以达到192根信号,64根输入,128根输出。由XILINX的官方文件还可以看出,EMIO是与PL部分直接相连的。
MIO与EMIO的控制寄存器主要有以下部分
控制寄存器的位宽及每个寄存器的作用均给出了详细的解释。
以最基本的使用GPIO点灯为例,直接使用XPS+SDK进行设计。
首先打开XPS,进行PS部分的设计,这部分设计主要是针对PS中IO,uart,interrupt(均为IP核的形式)等进行定义,相当于在一个平台上搭积木,需要哪一部分的功能就将那部分的功能加入平台中。
我们使用BSB(Base System Builder) Wizard板级开发向导新建一个项目,输入文件名地址。
选择ZC702这块板卡,选项均为默认。
删除SW与LED外设。
在system assembly view中可以看到外设,XILINX公司为我们提供了ZEDboard的初始化文件zedboard_Revc_v1或v2,可以使用这个文件对zedboard的外设进行初始化。单击import,导入zedboard_Revc_v1即完成了ZEDboard的外设初始化。
添加GPIO的IP核AXI_General Purpose I/O,使用channel1,位宽为1,使用process_system7.0连接。在Bus Interface中可以看到添加的GPIO挂载到了processing_system7下。
在port中可以看到GPIO时钟,外接端口的名称等等,我们定义GPIO的名字为LED。
XPS设计中主要包含MHS(Microprocessor Hardware Specification),MSS(Microprocessor Software Specification),UCF约束文件,ELF文件等等。对UCF进行编辑,添加LED约束:
NET LED LOC = T22 | IOSTANDARD=LVCOMS33;
在project里生成Block Diagram Image即可看到所设计的PS部分。紫色部分代表主设备,通过互联AXI总线控制从设备axi_gpio。
这样就完成了PS部分的设计,XPS部分设计完成后需要生成比特流文件导入SDK中,在SDK中进行编程软件设计。在SDK的MSS文件中提供了设计中主要部分的C例程,在例程中可以查找自己所需要的东西。
建立一个新的project,最好使用memory test模板,设计较为齐全。之后就可以对GPIO进行操作了。GPIO分为MIO与EMIO,SDK为我们提供了各种各样的头文件以简化设计,MIO的头文件为xgpio.h,MEIO的头文件为xgpiops.h。
MIO的初始化过称为首先对GPIO进行初始化(XGpio_Initialize),之后设置数据传输方向(XGpio_SetDataDirection),最后对GPIO进行数据的写(XGpio_DiscreteWrite)或读(XGpio_DiscreteRead).
EMIO的初始化过称比MIO的略复杂,需要对EMIO的标号进行定义。例如板子上有LD9,标号为7.
具体代码为:
MIO:
static XGpio Instance_ptr
int xstatus
xstatus=XGpio_Initialize(&Instance_ptr,DeviceId) MIO初始化
If (Xstatus != XST_status)
print(“error”)
XGpio_SetDataDirection(&Instance_ptr,1,1) 通道1,1输入,0输出
XGpio_DiscreteWrite(&Instance_ptr,1,1) 通道1,写1
EMIO:
static XGpioPs Instance_ptr
XGpio_Config *Instance_Config_ptr
int xstatus
int iPinNumber = 7 7为LD9
u32 iPinDirection = 0x0 0为输出
Instance_Config_ptr = XGpioPs_LookupConfig(DeviceId)
if (Instance_Config_ptr == NULL)
return XST_FAILURE
xstatus =XGpioPs_CfgInitialize(&Instance_ptr,Instance_Config_ptr,Instance_Config_ptr->BaseAddr)
if (xstatus != XST_SUCCESS)
print(“EMIO error”)
XGpioPs_SetDirectionPin(&Instance_ptr,iPinNumber,iPinDirection) iPinNumber为7,是LD9,iPinDirection为0,0为输出,1为输入
XGpioPs_SetOutputEnablePin(&Instance_ptr,iPinNumber,1) 1为enable,0为dis
XGpioPs_WirtePin(&Instance_ptr,iPinNumber,1) 1为高电平0为低电平