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

STM32 SPI详解

[复制链接]
STMCU-管管 发布时间:2020-11-6 13:12
STM32 SPI详解
0 `0 Q" `+ R. d. M
7 S' J+ s% Y$ E! b3 f8 O1 c01、SPI简介
3 m1 A% x+ f7 [' M' t) k5 oSPI 规定了两个SPI 设备之间通信必须由主设备(Master) 来控制次设备(Slave). 一个Master 设备可以通过提供Clock 以及对Slave 设备进行片选(Slave Select) 来控制多个Slave 设备,SPI 协议还规定Slave 设备的Clock 由Master 设备通过SCK 管脚提供给Slave 设备,Slave 设备本身不能产生或控制Clock, 没有Clock 则Slave 设备不能正常工作。
" r; z( F: M7 i4 O4 c/ C$ \; o02、SPI特点
3 X+ z' L2 A( H# R, Z2.1、SPI控制方式6 A! d5 Y0 l7 c7 g; L
采用主-从模式(Master-Slave)的控制方式。
2 u, R2 x& g  {
SPI 规定了两个SPI 设备之间通信必须由主设备(Master) 来控制次设备(Slave). 一个Master 设备可以通过提供Clock 以及对Slave 设备进行片选(Slave Select) 来控制多个Slave 设备,SPI 协议还规定Slave 设备的Clock 由Master 设备通过SCK 管脚提供给Slave 设备,Slave 设备本身不能产生或控制Clock, 没有Clock 则Slave 设备不能正常工作。4 ^5 i1 G6 l# {
2.2、SPI传输方式/ P4 n" X# o& ]% q) R" h$ S5 A  d, j/ V
采用同步方式(Synchronous)传输数据。7 q# _0 H4 s. N8 B$ {5 x) Z
Master 设备会根据将要交换的数据来产生相应的时钟脉冲(ClockPulse), 时钟脉冲组成了时钟信号(ClockSignal) , 时钟信号通过时钟极性(CPOL) 和时钟相位 (CPHA) 控制着两个SPI 设备间何时数据交换以及何时对接收到的数据进行采样,来保证数据在两个设备之间是同步传输的。* r, ?  V- S/ L7 V) k, D0 L5 S
1.png
7 `2 x4 H, l, A; o! x2.3、SPI数据交换" E- h" U/ w! i
SPI数据交换框图
9 x! P0 ?% H- R2 c 2.png 8 ~4 ]( s& y6 r( ]/ C5 }& a
上图只是对SPI 设备间通信的一个简单的描述,下面就来解释一下图中所示的几个组件(Module):
& p+ I  D3 V9 U; M5 L  d( w8 @- fSSPBUF,Synchronous Serial Port Buffer, 泛指SPI 设备里面的内部缓冲区,一般在物理上是以FIFO 的形式,保存传输过程中的临时数据;0 }0 k& q; {- L8 e; ^
SSPSR, Synchronous Serial Port Register, 泛指SPI 设备里面的移位寄存器(ShiftRegitser), 它的作用是根据设置好的数据位宽(bit-width)把数据移入或者移出SSPBUF;  t; I* }) `' s6 t& ?
Controller, 泛指SPI 设备里面的控制寄存器,可以通过配置它们来设置SPI 总线的传输模式。
' U8 v5 ?% o6 A0 |* ISPI 设备间的数据传输之所以又被称为数据交换,是因为SPI 协议规定一个SPI 设备不能在数据通信过程中仅仅只充当一个"发送者(Transmitter)"或者"接收者(Receiver)".在每个Clock 周期内,SPI 设备都会发送并接收一个bit 大小的数据,相当于该设备有一个bit 大小的数据被交换了.一个Slave 设备要想能够接收到Master 发过来的控制信号,必须在此之前能够被Master 设备进行访问(Access). 所以,Master 设备必须首先通过SS/CS pin 对Slave 设备进行片选,把想要访问的Slave 设备选上.在数据传输的过程中,每次接收到的数据必须在下一次数据传输之前被采样.如果之前接收到的数据没有被读取,那么这些已经接收完成的数据将有可能会被丢弃,导致SPI 物理模块最终失效.因此,在程序中一般都会在SPI 传输完数据后,去读取SPI 设备里的数据,即使这些数据(DummyData)在我们的程序里是无用的。
3 A# m& `% a" n9 `: d# u% Q# [上面的过程转为动画0 q0 P8 s# M: ]/ J# @
初始状态
5 v+ i& I+ L/ Z- M" j* b% ~! P! I 3.png
6 ^" s/ E4 \. x9 p" F主机读取一个bit过程* v: W, }* r7 E& i7 f: Y) U
4.png : C* E1 j2 R/ R5 d# J
当读取7次后,也就是读取7位后+ {8 W. }2 F7 g8 U6 `9 ?
5.png # ]) a) v( C/ L" L9 E
总结:: q/ r$ z' }% m2 \& O7 V5 r
没有读和写的说法,因为实质上每次SPI是主从设备在交换数据。也就是说,你发一个数据必然会收到一个数据;你要收一个数据必须也要先发一个数据。
5 _8 |4 P  E) Q# L4 q% U2.4、SPI传输模式
) _- |9 E; p# O1 ~2 s# @上升沿、下降沿、前沿、后沿触发。当然也有MSB和LSB传输方式.
0 J7 Y4 N  n* ]  E8 B4 E 7.png % n% Z' ~. ~& ~- s0 j; S5 N% u. P2 }
03、工作机制
6 ^' g; d' i0 k' I$ r3.1、相关缩写, w$ q% _4 w7 d
SPI的极性Polarity和相位Phase,最常见的写法是CPOL和CPHA,不过也有一些其他写法,简单总结如下:

7 x$ A- S0 X3 O' j(1) CKPOL (Clock Polarity) = CPOL = POL = Polarity = (时钟)极性
5 Y% y4 K! ?5 r% @& h(2) CKPHA (Clock Phase) = CPHA = PHA = Phase = (时钟)相位% [2 @0 x. K, ]$ q7 a
(3) SCK=SCLK=SPI的时钟
, D  F* x2 F( T/ a) {! H: t& ](4) Edge=边沿,即时钟电平变化的时刻,即上升沿(risingedge)或者下降沿(fallingedge)* R6 e+ s) ^- d' I+ S
对于一个时钟周期内,有两个edge,分别称为:
7 K6 g  V5 O/ h# B2 G& vLeading edge=前一个边沿=第一个边沿,对于开始电压是1,那么就是1变成0的时候,对于开始电压是0,那么就是0变成1的时候;
' R8 Q* w% i* n! F2 u! DTrailingedge=后一个边沿=第二个边沿,对于开始电压是1,那么就是0变成1的时候(即在第一次1变成0之后,才可能有后面的0变成1),对于开始电压是0,那么就是1变成0的时候;
( L4 m, o, b" B& x) U, k  r3.2、CPOL极性6 [: q1 f% ?+ K3 X
先说什么是SCLK时钟的空闲时刻,其就是当SCLK在数发送8个bit比特数据之前和之后的状态,于此对应的,SCLK在发送数据的时候,就是正常的工作的时候,有效active的时刻了。  E* f- W% d6 d  V2 R
先说英文,其精简解释为:ClockPolarity = IDLE state of SCK。. U0 o8 k5 y2 a, Y- W/ K+ p
再用中文详解:
7 c. a. W8 N3 P& n2 i, ySPI的CPOL,表示当SCLK空闲idle的时候,其电平的值是低电平0还是高电平1:9 v4 ^! i0 O+ I2 c# G9 z, G) ~: M
CPOL=0,时钟空闲idle时候的电平是低电平,所以当SCLK有效的时候,就是高电平,就是所谓的active-high;& C$ V0 ~9 r* N& ]! s
CPOL=1,时钟空闲idle时候的电平是高电平,所以当SCLK有效的时候,就是低电平,就是所谓的active-low;& F4 a2 _  t" p: d' ]
3.3、CPHA相位
, D% q/ c( r. o( r% ^/ H. o2 A3 n首先说明一点,capturestrobe = latch = read =sample,都是表示数据采样,数据有效的时刻。相位,对应着数据采样是在第几个边沿(edge),是第一个边沿还是第二个边沿,0对应着第一个边沿,1对应着第二个边沿。
  ~! x/ y! h4 [对于:
/ T. v4 H. J: I, N; G3 r$ Y( pCPHA=0,表示第一个边沿:
# S0 D2 t0 C) f$ q; R9 K0 Y对于CPOL=0,idle时候的是低电平,第一个边沿就是从低变到高,所以是上升沿;. l# ?2 p$ Z' a! u1 s1 r
对于CPOL=1,idle时候的是高电平,第一个边沿就是从高变到低,所以是下降沿;( V, u4 v% k, M* Z
CPHA=1,表示第二个边沿:3 M* Z* l" b0 g0 j  ^! ?6 ~
对于CPOL=0,idle时候的是低电平,第二个边沿就是从高变到低,所以是下降沿;6 m% @) z$ u! x
3.4、极性和相位图示
( T! z+ N$ R$ s8 r图例1# J1 j+ U4 U7 U2 I! x5 U
8.png 5 g9 ^6 E( g/ N$ s3 c: ~
图例2
' O: c/ C" n* j5 @0 d" J9 G 9.png
4 e6 G: j' S  `% R3.5、软件设置极性和相位
* H% I" @; z% H# {8 O# [6 g, ]$ BSPI分主设备和从设备,两者通过SPI协议通讯。7 q. T* T8 T. |; v. q
而设置SPI的模式,是从设备的模式,决定了主设备的模式。
, s+ x8 ^3 B3 u0 B% Z) R所以要先去搞懂从设备的SPI是何种模式,然后再将主设备的SPI的模式,设置和从设备相同的模式,即可正常通讯。1 w. w' A' K2 R
对于从设备的SPI是什么模式,有两种:! _2 z8 k5 P; i! v4 I$ U! v1 S
1.固定的,有SPI从设备硬件决定的
) N& {1 p$ ?8 i/ q+ Z% m6 ZSPI从设备,具体是什么模式,相关的datasheet中会有描述,需要自己去datasheet中找到相关的描述,即:  T8 {+ W0 E+ m' S7 Y
关于SPI从设备,在空闲的时候,是高电平还是低电平,即决定了CPOL是0还是1;' b( X! `6 e$ r& |
然后再找到关于设备是在上升沿还是下降沿去采样数据,这样就是,在定了CPOL的值的前提下,对应着可以推算出CPHA是0还是1了。
. h+ _% r0 p) l% a# l2.可配置的,由软件自己设定# u: n$ C! X& n- a- W4 z
从设备也是一个SPI控制器,4种模式都支持,此时只要自己设置为某种模式即可。" ?$ J! n6 ~- }- c# V7 j  a- Z3 J
然后知道了从设备的模式后,再去将SPI主设备的模式,设置为和从设备模式一样,即可。! |) b+ `6 R! n" |# `$ c0 g' A  c
对于如何配置SPI的CPOL和CPHA的话,不多细说,多数都是直接去写对应的SPI控制器中对应寄存器中的CPOL和CPHA那两位,写0或写1即可。
2 n. N5 g8 D9 i5 X3 s" I04、STM32的SPI控制模块

6 x8 }# C6 j% I* lSPI是英语SerialPeripheralinterface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,STM32也有SPI接口。, ^$ p6 m3 K1 d; h" I! x
SPI接口一般使用4条线通信:
) {8 l/ v+ y0 y! L% \1 y! e2 Y 10.png 6 M; S. W- f* d, `6 L, w
SPI主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。) \$ U( c" S3 f: t6 h
STM32的SPI功能很强大,SPI时钟最多可以到18Mhz,支持DMA,可以配置为SPI协议或者I2S协议
; U5 }! Y3 {8 U# Z关于SPI,从数据手册查到
) V& L9 _4 ~! J; j 11.png 6 D4 B6 O8 ]9 j
STM32F207VCT6共有3个SPI。8 c3 z$ D# [( Q- M! N
从下表查出每个SPI对应的管脚
! u4 H7 q, B1 m. g5 A, W+ O
" X: R/ Y0 q! s" |* Q# O
4 k1 {. q; j3 X/ J% m7 p! b
STM32标准外设库SPI_InitTypeDef的定义:3 A! N; T) e4 t4 ], U' ^; N, X

- l, c5 W& Z1 p+ T, Y# _6 W! I
' r7 m+ a& T& E; p* Q6 C+ d
  1. 3 y- r2 n7 `' c
  2. typedef struct{    4 a' h; h+ g+ f$ X; z
  3. uint16_t SPI_Direction; // 设置SPI 的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式   
    5 s' Z) q$ r* E! C
  4. uint16_t SPI_Mode; // 设置SPI 的主从模式   
    ) P# l4 w! J9 E  y! X& |
  5. uint16_t SPI_DataSize; // 为8 位还是16 位帧格式选择项   
    ( E. s* B: o8 s# z# s8 J
  6. uint16_t SPI_CPOL; // 设置时钟极性   
    1 n5 j7 S$ y$ u) n
  7. uint16_t SPI_CPHA; // 设置时钟相位   
    2 l/ t, E' u+ N6 p5 k) q
  8. uint16_t SPI_NSS;   //设置NSS 信号由硬件(NSS管脚)还是软件控制   
    ) P. a: C: W* H' S! S& [
  9. uint16_t SPI_BaudRatePrescaler;  //设置SPI 波特率预分频值   ) b7 ]4 z0 I0 [( C9 [
  10. uint16_t SPI_FirstBit;    //设置数据传输顺序是MSB 位在前还是LSB 位在前   
    0 b5 a- S/ J/ a5 X! }) r
  11. uint16_t SPI_CRCPolynomial; //设置CRC 校验多项式,提高通信可靠性,大于1 即可
    % z. X: z$ k, `) z8 J/ Y+ V; O
  12. }SPI_InitTypeDef;) [1 P' Z( ]0 E
复制代码
+ ]4 B3 B' g: K' h

( s0 K: l9 x, T) w; _3 ^7 `( M参数
+ V" }2 Q. [4 u$ [. p# wSPI_Direction
5 a. e2 c5 D7 r用来设置SPI的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式,这里我们选择全双工模式SPI_Direction_2Lines_FullDuplex。) O" d( f- [9 R
SPI_Mode3 j( [6 _. a2 m- i" E# {3 t( C
来设置SPI的主从模式,这里我们设置为主机模式 SPI_Mode_Master,当然有需要你也可以选择为从机模式 SPI_Mode_Slave。
) K% w# ]% c; W. P! I/ L0 Y
SPI_CPOL
) I2 f# _( H3 T8 o9 I6 c用来设置时钟极性,我们设置串行同步时钟的空闲状态为高电平所以我们选择SPI_CPOL_High。
2 w/ x7 q# w0 A$ `
SPI_CPHA% O- r& D/ y3 F$ J. ]8 X9 |! r
来设置时钟相位,也就是选择在串行同步时钟的第几个跳变沿(上升或下降)数据被采样,可以为第一个或者第二个条边沿采集,这里我们选择第二个跳变沿,所以选择 SPI_CPHA_2Edge。
/ w4 ~' _4 ~5 K1 ^) ~& m2 _, p
SPI_NSS4 J8 P7 S: }, s5 W8 O3 ~
设置NSS信号由硬件(NSS管脚)还是软件控制,这里我们通过软件控制NSS关键,而不是硬件自动控制,所以选择 SPI_NSS_Soft。( ^  |% l) V$ z8 c
SPI_BaudRatePrescaler
4 |5 e/ D/ }; J8 h, x: ~很关键,就是设置SPI波特率预分频值也就是决定 SPI 的时钟的参数,从不分频道256分频8个可选值,初始化的时候我们选择256分频值SPI_BaudRatePrescaler_256,传输速度为36M/256=140.625KHz。7 N4 a5 m' m3 b/ w: [
SPI_FirstBit
% Q- i( g& J3 N& r0 e  _& R设置数据传输顺序是MSB位在前还是LSB位在前,这里我们选择SPI_FirstBit_MSB高位在前。
! x2 z& }  v3 h8 K! dSPI_CRCPolynomial
( \+ t6 S- ^7 ]2 u- P' y用来设置CRC校验多项式,提高通信可靠性,大于1即可。' q5 M) F) i( w  \4 T& G
示例代码:
0 Y+ p9 M" F- I7 ]7 b4 `% C; T! y
$ J/ b; O& T4 K( k/ s
  1. void  SPIInit( void )! l) ]8 m& A& `. V$ Y
  2. {    9 ^1 K$ V6 q" e7 W4 t# Z
  3. SPI_InitTypeDef SPI_InitStructure;   
    . V/ D" Q9 o; g( a% M
  4. FLASH_GPIO_Init();    /*!< Deselect the FLASH: Chip Select high */   
    7 m8 ^( D( z) Y# e! s1 f6 D
  5. GPIO_SetBits( GPIOA, GPIO_Pin_4 );    + E+ `9 M) T) B
  6. /*!< SPI configuration */   
    8 j; ?1 c) [* {8 D
  7. SPI_InitStructure.SPI_Direction    = SPI_Direction_2Lines_FullDuplex;      /* 双线双向全双工 */   
    ; [, E: @0 n$ l+ ?' v: \1 t1 h' n
  8. SPI_InitStructure.SPI_Mode     = SPI_Mode_Master;                      /* 主 SPI */   
      h! n- w) A) n+ j8 B4 K$ C
  9. SPI_InitStructure.SPI_DataSize     = SPI_DataSize_8b;                      /* SPI 发送接收 8 位帧结构 */   
    . Q) U0 _" P" v) @% c
  10. SPI_InitStructure.SPI_CPOL     = SPI_CPOL_High;                        /* 串行同步时钟的空闲状态为高电平 */    . |" d4 R) f; d2 f; G5 k; h
  11. SPI_InitStructure.SPI_CPHA     = SPI_CPHA_2Edge;                       /* 第二个跳变沿数据被采样 */    + W( ]: J' Z4 y4 r) b
  12. SPI_InitStructure.SPI_NSS      = SPI_NSS_Soft;                         /* NSS 信号由软件控制 */   
    * s; \/ Q$ \9 s- X2 V
  13. SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;             /* 预分频 16 */     
    ; T+ {3 C6 z) Q: m" z8 [- Y
  14. SPI_InitStructure.SPI_FirstBit     = SPI_FirstBit_MSB;                     /* 数据传输从 MSB 位开始 */    ! k: t8 o! A( m8 h. l4 t; I
  15. SPI_InitStructure.SPI_CRCPolynomial= 7;                                    /* CRC 值计算的多项式 */    ) X% a. Q. i( F1 [  R, H0 Y! S+ Z+ L1 x
  16. SPI_Init( SPI1, &SPI_InitStructure );    . T  \, ~+ A/ T& `
  17. /*!< Enable the sFLASH_SPI  */   
    # M. m3 T9 f1 u8 k4 i8 R
  18. SPI_Cmd( SPI1, ENABLE );
    0 B& L7 F1 h2 G# Q7 T
  19. }
复制代码
& ]2 i8 W' e  i) v7 F

5 u# _1 L* O4 B; n看到这里,可能觉的前面讲原理并没有太大的用处,因为STM32集成了SPI控制器,配置一下即可。
+ Z" E. _7 d5 ]$ R6 b/ M4 j. |; d. |一方面我们学习原理是为了更好的理解SPI,用于对接不同的SPI设备,像norflash的spi驱动网上有大量的例子,不容易出错。但并不是特别常见的,spi驱动SD卡,SPI驱动网络PHY,SPI驱动ESP8266,甚至在设计两个IC通信时,由于没有过多GPIO,又觉的IIC通信速度慢的话,可以设计两个IC之间使用SPI通信,显然这些场景就需要了解SPI的原理% E& X0 E  ?, X4 ?" q9 m& {- i
另外一方面,实际应用中,有可能因为芯片其他管脚用于特殊功能,留下的管脚没有硬件SPI功能,只能模拟实现,这个时候学习SPI原理就很有必要了。
) a  S1 ~7 C" w# M  h05、SPI的应用6 f- U9 K* r3 F
SPI的常用应用NorFlash0 m* h6 g! w% [3 {) t9 \" x8 ^& D
从数据手册上看到,SPI传输:CKPOL=1,CKPHA=1
; e, i$ Q; o% I 12.png
; ^* m" O7 P0 A  T, R所以STM32的SPI读取NorFlash的配置如下1 `4 f( C3 Y% S& }0 W
13.png 8 ?1 w: x# m/ X- b
抓取波形1 b9 N4 M, u' n5 ]9 p4 m1 P
14.png
9 q6 \, K, o; o) o* U1 X- V2 K波形如下:  V0 O/ s' D. i
15.png
* c* ]6 ~* F: j& e/ M0100 1011  就是0X4B。# H9 L  r" p/ A  U  |2 H2 P
其中看到:
$ c! D  `* X4 @& v起始电平是高电平,也就是CKPOL=1& A2 ^# [+ T; |) k3 P( i' q
第二个边沿采样,也就是CKPHA=13 G# z3 C* e0 E& d$ S
其实说成类似IIC的高电平有效也是没有问题的& r3 a; H5 G  K- |/ i% Z
下面这句话是写模拟SPI的核心8 g! g4 o7 Q/ v" X
自己的理解:在下降沿转换数据,在上升沿采样数据1 r/ e' C; S3 @/ w- O* k0 @
除了抓取波形,在华邦Flash也看到了时序图
0 y$ h# d3 ^5 S1 N% C/ Y$ s4 l 16.png 6 H  c; Z" x8 Y- G6 N1 v) t$ i
06、code
& ]$ B8 i5 U1 \读取norflash两种驱动实现方式
! w. I; a9 ?, k7 q# K! T使用STM32F207硬件SPI模块:2 G4 t9 W$ m( k: Y
  1. /**  
    + f4 U8 }3 h* V9 G- C9 i( m# x/ Z
  2. * @brief  Initializes the peripherals used by the SPIFLASH driver.  
    8 q& E8 K  k9 |5 ]
  3. * @param  None  / V7 P' D2 E4 I" i7 k5 U
  4. * @retval None 6 p$ K3 W6 M# _
  5. */1 x! Y* U" \% R# ?" m9 b) K8 u" h
  6. void FLASH_GPIO_Init(void)
    7 a* t1 t- S: J
  7. {  $ B* C' B% Y: R: y$ `' r- @
  8. GPIO_InitTypeDefGPIO_InitStructure;    /*!< Enable the SPI clock */ ' Q7 D" J, q2 C2 d: ~. e8 `3 ]
  9. RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);   /*!< Enable GPIO clocks */ + n4 \" j7 D) T2 h, k& Y7 B
  10. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + D( D3 Q3 o" `$ D4 _
  11. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);   
    & R: l6 H& t7 ?% |7 w6 R
  12. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);  
    # Z+ U% B+ O0 l, I! m7 w0 f
  13. /*!<SPI pins configuration*************************************************/   
    . b  O% X' O* n! R
  14. /*!< Connect SPI pins to AF5 */    % ], y/ G7 k3 A
  15. GPIO_PinAFConfig(GPIOA,5, GPIO_AF_SPI1);  ; R/ C- ^; B3 E( _, o/ M
  16. GPIO_PinAFConfig(GPIOA, 6, GPIO_AF_SPI1);
    + d2 V' R' _$ U- R# L
  17. GPIO_PinAFConfig(GPIOB, 5, GPIO_AF_SPI1);   ; R3 J( p' N% i! B. _" g
  18. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; ; b& E; @/ j$ {3 l% _8 E
  19. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    ( n( j0 B7 j' L% K7 R) l, S7 |, `% Z
  20. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; 3 c& w- J' m3 w$ x
  21. GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;//GPIO_PuPd_DOWN;   % s2 Z: {! h- ^# `, |5 s
  22. /*!< SPI SCK pin configuration */ 3 [; j1 L3 L6 z- t! w4 W- x) V1 X
  23. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;  
    ) H; [2 q- ]# L1 V$ I' r" b
  24. GPIO_Init(GPIOA,&GPIO_InitStructure);    - C2 }) X- D5 B' C/ F% V. n: z0 E
  25. /*!< SPI MISO pinconfiguration */  
    , Z2 x! L4 ]0 l1 }
  26. GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6; , i* q/ S% m8 U0 E' \
  27. GPIO_Init(GPIOA, &GPIO_InitStructure);    7 F5 L' O7 s) Y
  28. /*!<SPI MOSI pin configuration */  
    % ]* ?& A2 Y) k! H2 i
  29. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;  
    ' M0 C" F2 \0 M  _3 M" x
  30. GPIO_Init(GPIOB, &GPIO_InitStructure);   & A0 Y3 y$ l; o/ E/ j5 C7 \1 h
  31. /*!< Configure sFLASH Card CS pin in output pushpull mode*/  
    & Y) g% u% T3 A6 i* U
  32. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; . \7 ^" p( I7 H; `/ h( B* {" u
  33. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; ; K( w$ D! d5 ~$ v( i+ k: j" w
  34. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; % Y( N' T& u* b+ W5 A* j# `
  35. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    $ F( W1 Q5 d  |! S' |" H# `
  36. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  ; e& h+ u5 t7 |. ]
  37. GPIO_Init(GPIOE,&GPIO_InitStructure);) o" ]1 ^$ ]6 s. ~3 R) O' P5 k
  38. }% R) z5 C, m1 S1 J% e
  39. /**  
      y- [0 y& W3 a% G* T
  40. * @brief Initializes the peripherals used by the SPI FLASH driver.  ) B. p; n1 E) u
  41. *@param  None  
    7 l% ^% J( X/ v7 O. r9 v+ N+ L8 Y9 v
  42. * @retval None  
      s. }. p5 {& p* r# f5 ]8 J
  43. */
    - M% x, e' P- J; p
  44. void FLASH_SPIInit(void)
    - r. \( c1 r1 o% F" D
  45. {      
    1 s8 V1 J, V. N# E! X
  46. SPI_InitTypeDef SPI_InitStructure;   
    2 G6 k/ w1 ?+ E; h% \
  47. FLASH_GPIO_Init();   /*!< Deselect the FLASH: Chip Select high */
    % ^2 p! \  v/ a/ D$ b5 V3 ]/ `
  48. GPIO_SetBits(GPIOE,GPIO_Pin_12);    /*!< SPIconfiguration */  0 R) ]. D* ~0 {
  49. SPI_InitStructure.SPI_Direction =SPI_Direction_2Lines_FullDuplex;//双线双向全双工 + q4 r" ~: O) w1 z
  50. SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//主SPI  
    : q, }2 u) s" P# e/ q
  51. SPI_InitStructure.SPI_DataSize =SPI_DataSize_8b;// SPI 发送接收8 位帧结构
    0 g) j( |. @# n) S, @7 J  R4 Q: @0 I! `6 C
  52. SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串行同步时钟的空闲状态为高电平  
    7 a8 K- _$ E5 l- l8 v
  53. SPI_InitStructure.SPI_CPHA =SPI_CPHA_2Edge;//第二个跳变沿数据被采样
    # u! a6 z9 G$ B
  54. SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//NSS 信号由软件控制 ( i# o& c6 N1 e3 ^. r4 M2 m& h0 N
  55. SPI_InitStructure.SPI_BaudRatePrescaler =SPI_BaudRatePrescaler_16;//预分频16   
    # @. A- R1 g& {* k8 }5 Q' t" K) |
  56. SPI_InitStructure.SPI_FirstBit= SPI_FirstBit_MSB;//数据传输从MSB 位开始 ' @+ J, p0 G2 F4 U1 F
  57. SPI_InitStructure.SPI_CRCPolynomial = 7;//CRC 值计算的多项式
    6 w, J0 S! w2 e' q" R
  58. SPI_Init(SPI1, &SPI_InitStructure);  /*!< Enable thesFLASH_SPI  */    C/ Q$ D& [6 \& L2 ^; \
  59. SPI_Cmd(SPI1, ENABLE);& b& M) r3 g. E' o( G3 J2 C5 B
  60. }
复制代码

  h6 s! O3 j9 R+ s) ~6 B2 s% q$ k, B
) M+ m$ L& |+ q* f0 {
软件模拟SPI协议:
6 c5 s9 f1 b; ]4 e* {+ V0 ~
  1. ( \9 `2 a4 O1 _5 p/ M! t
  2. /**  
    ; N; [0 c5 `( y7 v) r7 b
  3. * @brief  Sends a byte through the SPI interface andreturn the byte received  
    2 h/ m1 ?) e5 `0 ]
  4. *         from the SPI bus.  * ~/ ?, L1 k0 u0 @0 ~9 U
  5. *@param  byte: byte to send.  0 g8 C8 B( x. `) |( u' e$ o
  6. * @retval The value of thereceived byte.  
    : o( B; d1 c5 Q* `
  7. */
    & m; I1 O. _8 C  O* g
  8. uint8_t SPI_ReadWriteByte(uint8_tdata)
    ( [. R8 T* \9 F9 L1 z
  9. {  ( t8 }7 u5 u7 N
  10. uint8_t i,data_read = 0;   
    , @/ R" X0 c$ j9 g) n/ j
  11. if(data!=0xA5)$ d  \( h# n" N+ E
  12. {    : }1 m: s+ x% [. d& T6 c
  13. for(i=0;i<8;i++)
    : V& P; o# [- s
  14. {     7 R) m. ]& ?" ]  ^+ }# s$ Y$ |
  15. MSPI_CLK_L();                                                        
    : Y3 Z& T" {/ G+ P
  16. if(data&0x80){                                                    * L- n# E+ u  W7 J( s+ ^
  17. MSPI_MOSI_H();      * h1 z( p. ~9 K9 c
  18. }
    + N8 L; O1 e9 B" h3 J& V
  19. else
    ; ^4 `2 d6 {  L% D8 t* W
  20. {  9 S6 @  j- Z6 l9 a' t  {
  21. MSPI_MOSI_L();     
    9 P# ]' I# H; l2 r
  22. }      
    5 D) |3 N; E( }4 J8 R: G& O: |
  23. MSPI_DELAY();      
    , b! z' [  N' ?: c4 i9 ]' x
  24. data = data<<1;                                                        
    6 Z2 n% K7 Y8 j8 }
  25. MSPI_CLK_H();                                                         * [0 L( N+ N  p& `- [
  26. MSPI_DELAY();                                                            
    8 t+ w. ?/ y; F& K$ N* b, O
  27. }   
    6 `3 b1 ?4 r# \, z; `3 j
  28. return data_read;  
    ) R, C1 {3 W2 L$ ?+ y
  29. }. t/ F% N. j: s: X& A
  30. else. X/ E  r( T$ t/ u" {
  31. {   
    ) M3 Q7 p6 k; H
  32. for(i=0;i<8;i++)2 m  ~1 L5 D& H9 h+ Z
  33. {      
    ' R  m: J/ S9 S' R0 E  m
  34. MSPI_CLK_L();                                            - w4 m; S+ s) f; m2 S  u
  35. MSPI_DELAY();                                            9 }8 p7 [$ A, l3 h, H, A' {- p
  36. data_read = data_read<<1;                                          : r0 W! g8 ]4 @! |' R8 b$ h
  37. MSPI_CLK_H();                                          
    ' {, X4 x' [$ y/ ~1 [
  38. if(MSPI_READ_IN())5 J5 K" d0 H+ U0 W
  39. {                                                  : k5 d9 J0 {$ F4 N% R
  40. data_read |= 0x01;                                       " M" l$ [9 N6 _, y' T8 {( a1 o
  41. }      : }; O3 H" _* }8 Q& g4 k! R, X
  42. MSPI_DELAY();   
    ! h$ q6 w0 t7 r( {
  43. }   
    + L' \6 f6 |: ]% k# G+ W3 j# w
  44. returndata_read;  
    / G, W; ~  I- N7 T
  45. }                                            
    4 ?9 y! M. \# O! E
  46. }
    , ?4 y! y, [7 H" L, _
  47. /**  
    2 Y* M; F7 h/ h
  48. * @brief  Initializes the peripheralsused by the SPI FLASH driver.  
    . m. s; c5 G7 v! W. Y  g
  49. * @param  None  
    / a" H! ]1 R  N5 s0 ]
  50. * @retvalNone  ) q! {: W' n) a# \5 l  w
  51. */
    ' Y5 C7 I' f7 b4 E# I
  52. void FLASH_GPIO_Init(void)
    ) w) T. h2 a( z4 r
  53. { + E8 j0 Y! R7 F  y: }) a0 e3 t
  54. GPIO_InitTypeDef GPIO_InitStructure;  /*!< Enable GPIOclocks */  + s% v8 s+ M6 Z  s0 D% k3 U  I7 p
  55. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);  ! M  k' w" v% D
  56. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);  % ^2 C. t( a! f6 U
  57. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);   
    2 M3 l% E6 C( K" w
  58. /*!< Configure sFLASH Card CS pin in output pushpull mode*/  
    : ~  G/ L2 y* @0 Y* S8 B  K
  59. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
    ) ?4 H& L+ |9 D+ q3 d0 T1 t, x
  60. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; / r7 ^; {9 K0 V1 Q
  61. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; 6 A2 n5 ~9 I- w3 H4 g8 z7 D3 e
  62. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    ! w" p. C: |! R' J: x+ `% K$ Z6 v& Y
  63. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  ( C# j/ C" Q$ |' [- ?4 K2 H/ I
  64. GPIO_Init(GPIOE,&GPIO_InitStructure);    3 {" P' ]3 `! w/ p& j
  65. /*!< SPI SCK pinconfiguration */  
    9 N/ S  N! ]" f' t3 E) x0 F
  66. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    , U! f& L) p! Z* D2 r
  67. GPIO_Init(GPIOA, &GPIO_InitStructure);  9 E, w2 `/ ~1 {0 S& p
  68. MSPI_CLK_H();   
    2 v7 s3 ?) z/ M1 }) \, i% z
  69. /*!< SPI MOSI pin configuration */
    * [5 S' y+ e7 y/ e
  70. GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_5;  
    8 }/ O6 U0 c( P4 g7 d! R# p% F
  71. GPIO_Init(GPIOB,&GPIO_InitStructure);  , V6 B. d' d% `5 b8 N
  72. MSPI_MOSI_H();    & _2 g- c  f, U0 v) ^+ v, s
  73. /*!<SPI MISO pin configuration */  
    ; \6 ?. ~( @( W7 u! e
  74. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;  , a. g3 {: E9 Z; ]
  75. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    ) _! |) G4 Y8 u' {+ G8 F
  76. GPIO_Init(GPIOA, &GPIO_InitStructure);
    ; q$ [  x# r% O; C8 I  _4 `
  77. }. X  c/ O- O" R( n# F- Y
  78. /**
    3 c; G5 a8 |7 H% E1 H
  79. * @brief  Initializes the peripherals used by the SPI FLASH driver. / A! v0 y6 X* I1 s: a! n$ ?( }- H3 `
  80. * @param  None  $ Y5 v- E: X  r/ w
  81. * @retval None  ! q! W! U$ X/ l" K6 E2 b( J
  82. */
    ( s0 H; t5 K: Y) @7 W6 {
  83. void FLASH_SPIInit(void)
    + w; |' p1 Z2 r% ?6 o+ ~% z8 y
  84. {      
    5 ?1 D& @. i  V! `
  85. FLASH_GPIO_Init();   /*!< Deselect the FLASH: Chip Select high */ - b4 t; g1 {5 h# C
  86. GPIO_SetBits(GPIOE,GPIO_Pin_12);+ o$ F0 r$ x9 V9 L+ ]
  87. }
复制代码

2 \+ w! @9 j1 z/ \) n
3 I* P! B) S- I8 V9 Y" |
* E6 a  m0 Q+ X0 L3 b* W

, k4 \# R/ |8 B" w5 j6 d1 g2 ~1 O3 R
4 s8 ~, q" v2 A. V8 H
8 q; {5 E6 ~- a/ l; [
赞 收藏 评论0 发布时间:2020-11-6 13:12

举报

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