初识AXI quad SPI
自《PG153 AXI Quad SPI v3.2》 支持:
在标准模式下,支持高达32个从站,这是非常灵活的指标。本文对于手册中的详细技术细节不做过多阐述,有兴趣的自行深入阅读研究。 该SPI IP能干神马呢?完成如下这样一个应用场景:
所需要实现的需求用例为:
本文实现用例描述
从软件分层的视角来看,上述的需求需要实现下面的访问层级:
为什么要研究这个呢?实际用ZYNQ芯片做产品时,很有可能外部有多个SPI从设备芯片需要利用Linux访问,你或许会说ZYNQ的PS端不是自带了两个SPI控制器吗?但有时候项目中这两个SPI对应的引脚可能用做其他用途了,而一个复杂的项目中又不得不使用多个SPI从设备芯片时,本文所讨论的话题就能很好的解决这样的需求场景了。通过本文,你会发现,原来ZYNQ的SPI IP是如此灵活好用! 本文目的实战描述,如何一步一步从PL端设计:
乃至PS端:
按照这个流程,那么第一步需要设计PL端与PS端的配置,且看: AXI Quad SPI 之配置从IP catalog中按下图从ip库中添加如下IP:
Block设计图# [% O% O3 Q/ p0 z 使能ZYNQ7 processing System的时钟PL Fabric clocks,用以驱动PL端的IP:
PL Fabric clocks设置 使能M AXI GP0接口如下:
M AXI GP0 设置双击AXI interconnect,设置2主1从:
AXI interconnect 设置双击axi_quad_spi_0设置如下,设置4个从设备(最多可支持32个从设备,PS端内置的SPI控制器1个最多支持3个从设备,从这一点可看出该IP的灵活性)
axi_quad_spi 设置同样将axi_quad_spi_1设置为2个从设备接口。 然后按照前面的连线图,将各块连接好,做过硬件的盆友会比较适应,这就像画原理图一样,就将各IP建立了逻辑连接关系了。除此之外,对于一个ZYNQ的板子而言,你还需要做如下的PS端设置:
至于这些怎么配置,比较常见这里就不赘述了。 对于AXI quad SPI外设还有一个很重要的配置,就是其地址范围:
AXI quad SPI 地址设置该地址最终将导出到设备树描述文件,用于SPI控制器驱动访问,从而让SPI控制器驱动得以与该IP通过AXI总线进行通信。 导出硬件文件点击open elaborated design ,然后打开io ports进行管脚分配,这需要根据各自的硬件实际情况进行设置,比如我是这样设置的:
然后点击Run synthesis进行综合,成功之后点击生成bit stream。再点击export hardware,得到.hdf文件,这个文件用于构建内核。
将得到的硬件描述hdf文件以及bitstream文件拷贝至内核编译文件夹下:
运行命令读取硬件描述文件: petalinux-config --get-hw-description ../base.sdk注:这里将hdf文件以及.bit文件放置在petalinux编译路径的上级目录的base.sdk,根据习惯可自行设置,只有上述命令传入的路径正确即可。 等待一段时间后,可得到一个配置界面,用于配置内核源、u-boot源、Image 等配置。
petalinux-config 根据实际情况配置好后,退出配置并保存配置。使用过的会比较熟悉,这里不赘述了。 配置设备树编辑用户设备树文件,用户设备树文件在下面路径中: ./project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi1 h& h. f- ^8 V9 H* d: g' h配置设备树如下: /include/ "system-conf.dtsi"6 @( k: j0 O9 g7 R5 ]4 Y! ^/ {) U4 M0 M8 G) l& W' ] }; 3 a# E1 u9 J$ _# z7 A &axi_quad_spi_0 {! \6 M$ e A) ~1 R* H status = "okay";( z7 @8 h. [) S f/ b) q; ` clock-names = "axi_clk", "axi4_clk", "spi_clk"; clocks = <&clkc 15>, <&clkc 15>, <&clkc 15>; + Y' b! g" m1 {& V) } \2 ~. E& E spi0_dev_0@0 { compatible = "spidev"; reg = <0>;$ m9 l8 a* H" V; K, W spi-max-frequency = <500000>; #address-cells = <1>; #size-cells = <1>; };+ u* ^& w6 Y' u+ V: ~& P% @$ Z% W spi0_dev_1@1 {' z) r3 j: C3 X7 g* x% q2 Q; n* e compatible = "spidev"; s# [0 X8 n; l3 Q reg = <1>; spi-max-frequency = <500000>;, n; k( J1 A+ X* ^: [ #address-cells = <1>;* L. d. {: {! C3 H4 Z #size-cells = <1>; };3 d' o! u4 k2 x' v6 ?2 R( } { spi0_dev_2@2 { compatible = "spidev";' o/ F1 @0 r6 x& M7 C4 n reg = <2>; spi-max-frequency = <500000>; #address-cells = <1>;. J# B6 ~5 i+ \6 |" n! [5 W #size-cells = <1>; };- B# `, Q/ u" A2 [9 y9 p: {9 U spi0_dev_3@3 { compatible = "spidev"; reg = <3>;5 ^0 L2 `3 ?+ K+ U( m$ K/ ?) I spi-max-frequency = <500000>;* S- V: @$ w' O, ~* A #address-cells = <1>;# A0 O7 w8 S- V #size-cells = <1>; }; 3 n1 h, \: H7 Q9 p1 K }; 8 k1 x2 x, Q3 t/ K ( n, L$ F1 T. G2 r9 R% x4 R6 }$ k &axi_quad_spi_1 {" k0 v2 s* l6 {0 k status = "okay"; clock-names = "axi_clk", "axi4_clk", "spi_clk";) \1 O) ~$ Q$ W# O( f/ V: T6 u clocks = <&clkc 15>, <&clkc 15>, <&clkc 15>; spi1_dev_0@0 { compatible = "spidev"; reg = <0>; spi-max-frequency = <500000>; #address-cells = <1>;4 }# M5 u/ m* K ] #size-cells = <1>; };+ o0 X1 U: X& S, p! X spi1_dev_1@1 { compatible = "spidev"; reg = <1>; spi-max-frequency = <500000>; #address-cells = <1>; #size-cells = <1>; I2 p# f8 X0 `, Z. H! A5 q3 a0 P: e }; 9 g W- E' B0 G; } k }; 这里直接使用内置spidev兼容从设备驱动,当然如果需要自己定义一个SPI设备驱动也是非常容易的,但是对于大部分普通的SPI从芯片而言直接使用spidev设备驱动即可,只需要在读写时按照芯片手册协议进行访问即可。 配置内核运行下面命令进行内核配置: petalinux-config -c kernel* h) ~! v$ k! s6 N
内核配置1 W9 V- \) [5 V1 h; f 对于本应用而言,需要配置SPI驱动: Device Drivers --->" l! U+ D, I ^: |- E2 o% ^* s+-SPI support---> 配置如下:
SPI控制器及设备驱动配置 这里调试中遇到一个奇怪的问题,CONFIG_SUSPEND需要禁止,否则控制器驱动加载不成功,目前还没有深入研究为什么不成功,猜想可能是主控制器驱动关于SUSPEND功能还不支持或者有bug,如果有哪位大神知道怎么解决请求留言指点。 Power management options --->- J* T7 D7 M. ?/ {7 k$ f# B; O+ fSuspend to RAM and standby
功能管理配置 退出并保存配置,然后运行下面命令编译系统: petalinux-build等待编译成功后,运行下面命令将bitstream文件包进BOOT.bin中。 petalipackage --boot --fsbl ./images/linux/zynq_fsbl.elf --fpga ../base.sdk/design_1_wrapper.bit --u-boot --force将得到下面的输出信息,表示操作成功: INFO: File in BOOT BIN: "/home/zynq/ALINX/spi_ip/ax_peta/images/linux/zynq_fsbl.elf") H7 h7 Z7 q5 s0 GINFO: File in BOOT BIN: "/home/zynq/ALINX/spi_ip/base.sdk/design_1_wrapper.bit", v/ }# u: `7 t INFO: File in BOOT BIN: "/home/zynq/ALINX/spi_ip/ax_peta/images/linux/u-boot.elf" INFO: Generating zynq binary package BOOT.BIN.... w4 b) F3 r# I% M z' Z INFO: Binary is ready.0 n* d/ c* r/ x+ U0 ? WARNING: Unable to access the TFTPBOOT folder /tftpboot!!!% ?0 O& p6 N% X5 ^ WARNING: Skip file copy to TFTPBOOT folder!!!; q1 T7 Z8 E' j# {; ` 注:/home/zynq/ALINX/spi_ip/ax_peta 是本文工程的目录 测试SPI从设备编写驱动测试程序,代码如下: #include <stdio.h>#include <string.h> #include <unistd.h>/ K+ z) S+ q& w' U A #include <fcntl.h>' w+ R& g/ @5 s* U7 G% z/ e' p int main(int argc, char **argv) { int fd; int len;) z0 a8 i4 M4 ^7 {- C- s unsigned char buf[10]; unsigned char tmp;; Z) W8 k/ |( X; d) q /* 验证输入参数个数 */" R: [% ?2 ^" p* W3 }$ x t if(3 != argc)' I& d' E, D+ R4 ?/ j0 J {1 C% w# b7 s+ {& v printf("none para\n"); return -1;5 a' n7 t* D4 v3 x+ M3 q }7 w, L; o0 t7 \/ x; F ]7 L& \ |2 K: z( I /* 打开输入的设备文件, 获取文件句柄 */ fd = open(argv[1], O_RDWR); if(fd < 0) {% X# C0 f$ m& a4 c8 r% X* @" z /* 打开文件失败 */' m% K7 d w6 }9 T- L2 W printf("Can't open file %s\r\n", argv[1]);; E# ^2 N" u3 K! A" V+ H- Y6 E2 o) v return -1; } ) O7 x' u t0 u( M- S, G; [ int i = 0;9 ~. U/ T, n p7 m4 _& I* |' Y int j = 0;. j8 o7 W: e: |8 r1 X$ e& h/ l len =strlen(argv[2]);3 T ?+ j G+ ~0 ] for(i=0;i<len;i++) {3 n g! c- b% e% \* T if(argv[2]>='0' && argv[2]<='9'), W1 x( I2 s, v8 l' J { tmp = argv[2] - '0';+ \% L" T9 A+ \" m! ^2 J }2 g2 o% ^' l. O. M+ D else if(argv[2]>='a' && argv[2]<='f') {- N: j, m$ }1 \, s5 \4 H tmp = argv[2] - 'a'+10; e+ c/ L. {3 }8 h6 M } else if(argv[2]>='A' && argv[2]<='F')( }1 C, r9 U; x1 \: g4 y7 |/ S {+ g1 {* K1 ]) `' l1 w* w8 e6 P2 C3 J tmp = argv[2] - 'A'+10;" K8 c& O6 d0 ?& j } else* A. [- M( h1 Z L0 g- v/ Q' H. } { printf("Invalid input parameters \r\n"); return -1; } if(i%2==0) buf[j] = tmp<<4; else {# w# k( Q \4 G3 k. P$ y buf[j] += tmp;; S2 `* H4 X. C O4 e" K' p j++; }! D. p2 \& ?( O }: H! v! P- t: }3 [4 R- h len = j; printf("Test wr:");) ~" X8 d% u% T$ ?* {2 p2 M for(i=0;i<len;i++) printf(" %x",buf);: \8 _) @3 q: M* R: N write(fd, &buf[0], len); printf("\n"); /* 操作结束后关闭文件 */ close(fd); return 0;3 e5 G8 w0 [- }4 Y4 G }( M4 K1 u. u; B7 X$ m& _5 J8 B 编译: arm-linux-gnueabihf-gcc test.c -o test将编译所得的BOOT.BIN以及image.ub文件拷贝至制作好的SD的BOOT区,test文件拷贝至/home下。然后插上SD卡上电运行电路板: 登录控制台后,运行ls /dev查看spidev设备是否加载成功:
spidev设备挂载情况 可见spedev1.0、spidev1.1以及spidev2.0--spidev2.3加载成功,与预期一样。 然后运行测试程序: root@ax_peta:/run/media/mmcblk0p2/home#./test /dev/spidev1.0 78aaTest wr: 78 aa 用示波器或者逻辑分析仪观察对应引脚,将出现正确的SPI通信波形。 总结一下至此,就基本实现了从PS端Linux用户空间访问PL端的SPI从设备了。当然实际项目中还有很多细节需要进一步研究:
对于这些更细节的内容,相信在将基本框架搭建成功后,只要深入细致研究都不会有太大的难度。从本文可看出,ZYNQ之所以如此灵活好用,是其厂家或者第三方提供了大量成熟可供使用的IP以及配套的驱动程序。如有兴趣尝试用来开发项目,相信你会很快喜欢上这个体系的芯片,真的可以做到片上即可实现系统这一目标! " I( R |$ Y. z9 t0 e/ x s |
微信公众号
手机版