你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

介绍AXI quad SPI 使用方法

[复制链接]
gaosmile 发布时间:2020-11-16 20:09
初识AXI quad SPI
0 W4 Q, P7 z7 h# R! x: m/ ^
微信图片_20201116195853.png

自《PG153 AXI Quad SPI v3.2》

支持:

  • Legacy Mode
  • standard mode: 准SPI通常就称SPI,它是一种串行外设接口规范,有4根通信脚:SCK (时钟), CS(片选), MOSI(主出从入), MISO(主入从出)。
  • Dual/Quad SPI Mode:/ A. y% k! b) Y. h* H
微信图片_20201116195857.png
AXI Quad SPI 模式

在标准模式下,支持高达32个从站,这是非常灵活的指标。本文对于手册中的详细技术细节不做过多阐述,有兴趣的自行深入阅读研究。

该SPI IP能干神马呢?

完成如下这样一个应用场景:

微信图片_20201116195900.png
SPI IP访问多从SPI芯片

所需要实现的需求用例为:

微信图片_20201116195904.png
本文实现用例描述
  • 利用AXI quad SPI 实现SPI外设控制器
  • 实现SPI外设控制器驱动
  • 实现多SPI从设备挂载在SPI总线
  • 实现用户空间访问多从SPI物理从设备# D! O4 U, X, |8 }- T

从软件分层的视角来看,上述的需求需要实现下面的访问层级:

微信图片_20201116195908.png
PS/PL软硬件层次架构图

为什么要研究这个呢?实际用ZYNQ芯片做产品时,很有可能外部有多个SPI从设备芯片需要利用Linux访问,你或许会说ZYNQ的PS端不是自带了两个SPI控制器吗?但有时候项目中这两个SPI对应的引脚可能用做其他用途了,而一个复杂的项目中又不得不使用多个SPI从设备芯片时,本文所讨论的话题就能很好的解决这样的需求场景了。通过本文,你会发现,原来ZYNQ的SPI IP是如此灵活好用!

本文目的实战描述,如何一步一步从PL端设计:

  • block design
  • 约束
  • 综合
  • 导出
    : \1 J* }: t8 n# w; N1 D

乃至PS端:

  • SPI驱动配置
  • 设备树修改
  • 系统编译部署
  • 设备驱动测试5 z- a1 u4 d# B# h- t

按照这个流程,那么第一步需要设计PL端与PS端的配置,且看:

AXI Quad SPI 之配置

从IP catalog中按下图从ip库中添加如下IP:

  • ZYNQ7 processing System
  • AXI interconnect
  • AXI Quad SPI(可根据需要添加多个)
  • Processing System Reset(添加ZYNQ7 processing System 点自动连线会自动添加,当然也可以手动添加)
  • Concat6 Q; A, _8 g4 p. K. u
微信图片_20201116195911.png
Block设计图# [% O% O3 Q/ p0 z

使能ZYNQ7 processing System的时钟PL Fabric clocks,用以驱动PL端的IP:

微信图片_20201116195914.png
PL Fabric clocks设置

使能M AXI GP0接口如下:

微信图片_20201116195916.png
M AXI GP0
设置

双击AXI interconnect,设置2主1从:

微信图片_20201116195920.png
AXI interconnect
设置

双击axi_quad_spi_0设置如下,设置4个从设备(最多可支持32个从设备,PS端内置的SPI控制器1个最多支持3个从设备,从这一点可看出该IP的灵活性)

微信图片_20201116195924.png
axi_quad_spi
设置

同样将axi_quad_spi_1设置为2个从设备接口。

然后按照前面的连线图,将各块连接好,做过硬件的盆友会比较适应,这就像画原理图一样,就将各IP建立了逻辑连接关系了。除此之外,对于一个ZYNQ的板子而言,你还需要做如下的PS端设置:

  • DDR RAM设置,根据自身的板子的内存芯片以及内存大小进行设置
  • Peripheral IO外设设置,比如SD卡,UART,QUAD SPI Flash,erthernet等
  • clock时钟系统设置,根据板子的情况进行设置CPU、DDR时钟频率、IO时钟等
  • ......
      R: H* s4 H9 q; ^  v7 n

至于这些怎么配置,比较常见这里就不赘述了。

对于AXI quad SPI外设还有一个很重要的配置,就是其地址范围:

微信图片_20201116195927.png
AXI quad SPI
地址设置

该地址最终将导出到设备树描述文件,用于SPI控制器驱动访问,从而让SPI控制器驱动得以与该IP通过AXI总线进行通信。

导出硬件文件

点击open elaborated design ,然后打开io ports进行管脚分配,这需要根据各自的硬件实际情况进行设置,比如我是这样设置的:

微信图片_20201116195940.png
管脚约束设置
  • 电平标准
  • 是否上拉
  • 驱动能力
  • .....) q1 a5 O3 r: ^; \- h9 ]5 u

然后点击Run synthesis进行综合,成功之后点击生成bit stream。再点击export hardware,得到.hdf文件,这个文件用于构建内核。

微信图片_20201116195944.png
导出硬件描述文件

将得到的硬件描述hdf文件以及bitstream文件拷贝至内核编译文件夹下:

微信图片_20201116195948.png
硬件描述及bit文件配置编译内核

运行命令读取硬件描述文件:

petalinux-config --get-hw-description ../base.sdk
, d* h: R7 Y1 S' t' J( k" e+ d

注:这里将hdf文件以及.bit文件放置在petalinux编译路径的上级目录的base.sdk,根据习惯可自行设置,只有上述命令传入的路径正确即可。

等待一段时间后,可得到一个配置界面,用于配置内核源、u-boot源、Image 等配置。

微信图片_20201116195952.png
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 }( X* x3 u; e2 Y1 S};
0 k1 g8 [& i, _0 |5 u0 `- p3 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";
8 ~/ i/ @, ]5 ^0 e# d    clocks = <&clkc 15>, <&clkc 15>, <&clkc 15>; + Y' b! g" m1 {& V) }  \2 ~. E& E
    spi0_dev_0@0 {
% J) L6 v9 s# o$ ~. I3 c        compatible = "spidev";
' x$ g7 s- z/ ?9 u0 [: n        reg = <0>;$ m9 l8 a* H" V; K, W
        spi-max-frequency = <500000>;
/ G( K# ^% U7 y" ~1 u3 O% ~. Y: {        #address-cells = <1>;
3 P. k: Q. ^+ |5 \        #size-cells = <1>;
7 j# a1 n; O( c8 }    };+ u* ^& w6 Y' u+ V: ~& P% @$ Z% W

4 e* [0 Y4 L2 n2 m& [    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>;
) _* e; {3 W" X7 C8 v        spi-max-frequency = <500000>;, n; k( J1 A+ X* ^: [
        #address-cells = <1>;* L. d. {: {! C3 H4 Z
        #size-cells = <1>;
( j. x+ K2 h5 U    };3 d' o! u4 k2 x' v6 ?2 R( }  {
    spi0_dev_2@2 {
9 y+ K  R0 H4 e  {. t6 V& V        compatible = "spidev";' o/ F1 @0 r6 x& M7 C4 n
        reg = <2>;
1 e1 q1 P1 g+ W        spi-max-frequency = <500000>;
9 c4 ?" h1 F9 R. C& ]        #address-cells = <1>;. J# B6 ~5 i+ \6 |" n! [5 W
        #size-cells = <1>;
4 \/ s; R+ A" u5 c7 F    };- B# `, Q/ u" A2 [9 y9 p: {9 U
    spi0_dev_3@3 {
2 s2 L* k0 t( V9 E, Y        compatible = "spidev";
* q9 g. t6 d2 t8 ?' C6 E& c- E        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>;
+ D2 k5 {( ?7 d1 Y& `# H, |, ^, p    };        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";
5 B7 J6 t/ \; n2 Y    clock-names = "axi_clk", "axi4_clk", "spi_clk";) \1 O) ~$ Q$ W# O( f/ V: T6 u
    clocks = <&clkc 15>, <&clkc 15>, <&clkc 15>;
9 ^1 w2 o' a8 x7 v0 m; U    spi1_dev_0@0 {
& F- i- T* Q( o( A4 w        compatible = "spidev";
; T/ m4 |4 b9 E2 ]- [7 E        reg = <0>;
6 S  t, ]" l# }1 t( c" L; W        spi-max-frequency = <500000>;
6 B  o; B# p$ M" N; E( N1 X) D        #address-cells = <1>;4 }# M5 u/ m* K  ]
        #size-cells = <1>;
9 u. K  Q* X8 H7 ~4 q6 i8 }    };+ o0 X1 U: X& S, p! X
   
- W) N, b+ N$ N# D    spi1_dev_1@1 {
' L5 g; l4 ?! I( }+ i5 I* |" U6 }* u        compatible = "spidev";
+ N7 F* h# ?1 o+ {+ P% N' H4 |! v        reg = <1>;
+ n% y, s% D+ R0 V        spi-max-frequency = <500000>;
! Q/ }( J5 B0 N+ ^8 \8 [        #address-cells = <1>;
0 r0 t1 B2 M* l* z5 z2 V# G        #size-cells = <1>;  I2 p# f8 X0 `, Z. H! A5 q3 a0 P: e
    }; 9 g  W- E' B0 G; }  k
};
1 a" O. \* N+ L. m# O  j, w

这里直接使用内置spidev兼容从设备驱动,当然如果需要自己定义一个SPI设备驱动也是非常容易的,但是对于大部分普通的SPI从芯片而言直接使用spidev设备驱动即可,只需要在读写时按照芯片手册协议进行访问即可。

配置内核

运行下面命令进行内核配置:

petalinux-config -c kernel  
/ w6 M$ p- j; W# y  `% W2 b8 c0 \
* h) ~! v$ k! s6 N
微信图片_20201116195955.png
内核配置1 W9 V- \) [5 V1 h; f

对于本应用而言,需要配置SPI驱动:

Device Drivers  --->" l! U+ D, I  ^: |- E2 o% ^* s
     +-SPI support--->     
' n, _0 S5 O; _3 v. u8 W

配置如下:

微信图片_20201116195958.png
SPI控制器及设备驱动配置

这里调试中遇到一个奇怪的问题,CONFIG_SUSPEND需要禁止,否则控制器驱动加载不成功,目前还没有深入研究为什么不成功,猜想可能是主控制器驱动关于SUSPEND功能还不支持或者有bug,如果有哪位大神知道怎么解决请求留言指点。

Power management options  --->- J* T7 D7 M. ?/ {7 k$ f# B; O+ f
     Suspend to RAM and standby
微信图片_20201116200001.png
功能管理配置
+ ~) W" _3 K# @& N9 [8 Z

退出并保存配置,然后运行下面命令编译系统:

petalinux-build
  a7 R( [# K" h- W+ N. |) n0 S  B

等待编译成功后,运行下面命令将bitstream文件包进BOOT.bin中。

petalipackage --boot --fsbl ./images/linux/zynq_fsbl.elf --fpga ../base.sdk/design_1_wrapper.bit --u-boot    --force
* A6 h: V% H4 j7 N" \; y. O1 P" B

将得到下面的输出信息,表示操作成功:

INFO: File in BOOT BIN: "/home/zynq/ALINX/spi_ip/ax_peta/images/linux/zynq_fsbl.elf") H7 h7 Z7 q5 s0 G
INFO: 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"
# B! d5 s' T' p; _3 sINFO: 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>
3 L+ Q- ^/ Q: g; d2 [) U#include <string.h>
- V# t7 C5 E! H* A9 T5 ?) ?' B) v#include <unistd.h>/ K+ z) S+ q& w' U  A
#include <fcntl.h>' w+ R& g/ @5 s* U7 G% z/ e' p

6 W9 m0 v) S8 o5 p' Kint main(int argc, char **argv)
$ A" b2 K4 x! n. Y- Q{
+ p2 F* Q+ Q, u; U    int fd;
* b8 @8 d+ v% A# r2 d    int len;) z0 a8 i4 M4 ^7 {- C- s
    unsigned char buf[10];
4 k0 w. ?* y1 t6 z    unsigned char tmp;; Z) W8 k/ |( X; d) q

* ?4 w( D2 c$ D4 m* F( Y    /* 验证输入参数个数 */" 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");
3 z6 y# s) J3 r0 x1 L9 n1 P( ^3 [        return -1;5 a' n7 t* D4 v3 x+ M3 q
    }7 w, L; o0 t7 \/ x; F
  ]7 L& \  |2 K: z( I
    /* 打开输入的设备文件, 获取文件句柄 */
0 S8 L% M1 o3 T( ~! F    fd = open(argv[1], O_RDWR);
7 K& g: C, K# m+ B9 M    if(fd < 0)
0 v4 \1 h7 _7 [% {3 m    {% 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;
7 g% R( u8 P0 u! Q! h. s    }
$ h4 S& Z- g' @7 c) 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++)
; _  c& x$ C1 x3 G( P' k    {3 n  g! c- b% e% \* T
        if(argv[2]>='0' && argv[2]<='9'), W1 x( I2 s, v8 l' J
        {
( h2 V4 }& Q, B& I            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')
* I3 `1 ?+ R( u( U8 C3 a        {- N: j, m$ }1 \, s5 \4 H
            tmp = argv[2] - 'a'+10;  e+ c/ L. {3 }8 h6 M
        }
) d' P: M9 z* u" m3 Z! \        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
        }
1 c  C' ~0 K' Y- k' c        else* A. [- M( h1 Z  L0 g- v/ Q' H. }
        {
1 b0 z! Z1 c0 r! q4 p$ i6 H            printf("Invalid input parameters \r\n");
0 ^3 P; {5 ~# {) i            return -1;
5 p' D. p  ^/ H2 D        }
5 ^) w8 u) B- A4 L6 d6 |        if(i%2==0)
" ^( ]/ v* v' u- g$ R7 I6 V           buf[j] = tmp<<4;
0 H# k2 p" |7 v: z7 x        else
9 [" Q9 m8 h- m8 ^) ^        {# w# k( Q  \4 G3 k. P$ y
            buf[j] += tmp;; S2 `* H4 X. C  O4 e" K' p
            j++;
  [" \7 |" V, G) X# L  n5 @        }! D. p2 \& ?( O
    }: H! v! P- t: }3 [4 R- h
    len = j;
+ I3 ^; e6 K9 Y& l& k    printf("Test wr:");) ~" X8 d% u% T$ ?* {2 p2 M
    for(i=0;i<len;i++)
8 c9 O* n1 P. w0 t        printf(" %x",buf);: \8 _) @3 q: M* R: N
   
; C* m8 S' Q% P3 R( q0 r& _1 O, A( D    write(fd, &buf[0], len);
! l* ^* E1 X+ e! o    printf("\n");
& J) M9 n! q8 B7 B- ?2 x    /* 操作结束后关闭文件 */
0 q* s, d1 O& d& w2 f  \    close(fd);
0 i8 `% a9 h8 I3 }( x    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
- b- @% j( q% `

将编译所得的BOOT.BIN以及image.ub文件拷贝至制作好的SD的BOOT区,test文件拷贝至/home下。然后插上SD卡上电运行电路板:

登录控制台后,运行ls /dev查看spidev设备是否加载成功:

微信图片_20201116200005.png
spidev设备挂载情况

可见spedev1.0、spidev1.1以及spidev2.0--spidev2.3加载成功,与预期一样。

然后运行测试程序:

root@ax_peta:/run/media/mmcblk0p2/home#./test /dev/spidev1.0 78aa
4 V: {8 {  [( G. n( a  iTest wr: 78 aa
# I5 U5 e, L( K1 {2 G

用示波器或者逻辑分析仪观察对应引脚,将出现正确的SPI通信波形。

总结一下

至此,就基本实现了从PS端Linux用户空间访问PL端的SPI从设备了。当然实际项目中还有很多细节需要进一步研究:

  • CPOL/CPHA 组合四种模式设置
  • SPI通信速率设置
  • 从设备应用协议程序编写
  • AXI Quad SPI FIFO特性的深入应用
  • AXI Quad SPI 其他模式及细节研究等8 Z( {! F3 ]8 |  P+ q+ ]" N9 W

对于这些更细节的内容,相信在将基本框架搭建成功后,只要深入细致研究都不会有太大的难度。从本文可看出,ZYNQ之所以如此灵活好用,是其厂家或者第三方提供了大量成熟可供使用的IP以及配套的驱动程序。如有兴趣尝试用来开发项目,相信你会很快喜欢上这个体系的芯片,真的可以做到片上即可实现系统这一目标!

" I( R  |$ Y. z9 t0 e/ x  s
赞 收藏 评论0 发布时间:2020-11-16 20:09

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版