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

STM32 SPI详解

[复制链接]
STMCU-管管 发布时间:2020-9-8 13:47
01 SPI简介
SPI 规定了两个SPI 设备之间通信必须由主设备(Master) 来控制次设备(Slave). 一个Master 设备可以通过提供Clock 以及对Slave 设备进行片选(Slave Select) 来控制多个Slave 设备,SPI 协议还规定Slave 设备的Clock 由Master 设备通过SCK 管脚提供给Slave 设备,Slave 设备本身不能产生或控制Clock, 没有Clock 则Slave 设备不能正常工作。
8 a" D5 \# o* i1 I& g
02 SPI特点

+ R/ J2 _0 ?  @- `& g1 E
2.1、SPI控制方式
采用主-从模式(Master-Slave)的控制方式。
, y) ~3 Q+ i+ I8 x( ~9 w" c/ C: O
SPI 规定了两个SPI 设备之间通信必须由主设备(Master) 来控制次设备(Slave). 一个Master 设备可以通过提供Clock 以及对Slave 设备进行片选(Slave Select) 来控制多个Slave 设备,SPI 协议还规定Slave 设备的Clock 由Master 设备通过SCK 管脚提供给Slave 设备,Slave 设备本身不能产生或控制Clock, 没有Clock 则Slave 设备不能正常工作。
; @" q$ [; w+ ^; I& b! Q3 Z+ P9 }
2.2、SPI传输方式
采用同步方式(Synchronous)传输数据。

  C: Z, P" q3 J$ A: f* |$ X
Master 设备会根据将要交换的数据来产生相应的时钟脉冲(ClockPulse), 时钟脉冲组成了时钟信号(ClockSignal) , 时钟信号通过时钟极性(CPOL) 和时钟相位 (CPHA) 控制着两个SPI 设备间何时数据交换以及何时对接收到的数据进行采样,来保证数据在两个设备之间是同步传输的。

( M5 @, _  A& ~2 {6 \4 x% o
1.png
2.3、SPI数据交换
SPI数据交换框图

# }' l7 r8 k; p2 o
2.png
+ T3 X& |1 }( W# p
上图只是对SPI 设备间通信的一个简单的描述,下面就来解释一下图中所示的几个组件(Module):

2 N, T; m8 b9 n+ x$ @1 W! c, }
SSPBUF,Synchronous Serial Port Buffer, 泛指SPI 设备里面的内部缓冲区,一般在物理上是以FIFO 的形式,保存传输过程中的临时数据;
9 Q9 l. ?6 {4 Z
SSPSR, Synchronous Serial Port Register, 泛指SPI 设备里面的移位寄存器(ShiftRegitser), 它的作用是根据设置好的数据位宽(bit-width)把数据移入或者移出SSPBUF;
7 L2 w( r- o8 a+ }; B/ h
Controller, 泛指SPI 设备里面的控制寄存器,可以通过配置它们来设置SPI 总线的传输模式。
3 F4 b* m& G5 y! ?; a' ~+ h
SPI 设备间的数据传输之所以又被称为数据交换,是因为SPI 协议规定一个SPI 设备不能在数据通信过程中仅仅只充当一个"发送者(Transmitter)"或者"接收者(Receiver)".在每个Clock 周期内,SPI 设备都会发送并接收一个bit 大小的数据,相当于该设备有一个bit 大小的数据被交换了.一个Slave 设备要想能够接收到Master 发过来的控制信号,必须在此之前能够被Master 设备进行访问(Access). 所以,Master 设备必须首先通过SS/CS pin 对Slave 设备进行片选,把想要访问的Slave 设备选上.在数据传输的过程中,每次接收到的数据必须在下一次数据传输之前被采样.如果之前接收到的数据没有被读取,那么这些已经接收完成的数据将有可能会被丢弃,导致SPI 物理模块最终失效.因此,在程序中一般都会在SPI 传输完数据后,去读取SPI 设备里的数据,即使这些数据(DummyData)在我们的程序里是无用的。

; D5 S7 C( ~- K1 C3 i4 h2 @1 e
上面的过程转为动画
, p+ n* _6 a- E
初始状态
* W' ^- B0 g* C/ }- o: `. r
3.png
主机读取一个bit过程
, E5 v. ~* l/ w. r/ k
640.gif

) K+ H4 W- `: V2 ~, n
当读取7次后,也就是读取7位后
3 S) \( H3 i: i( \6 i* E
1 u; I) d$ R. W( b+ d% p6 L% ^
5.png
5 v2 f+ A$ V) L4 b7 O
  t2 D% e' M" s+ ?/ Q1 v* q4 j
总结:
没有读和写的说法,因为实质上每次SPI是主从设备在交换数据。也就是说,你发一个数据必然会收到一个数据;你要收一个数据必须也要先发一个数据。
0 r! Y  h/ T; z: u8 m
2.4、SPI传输模式
上升沿、下降沿、前沿、后沿触发。当然也有MSB和LSB传输方式.

4 |5 I4 U+ @& k# J" T% `" M% d# @# Q
6.png

( f( ?2 P  m9 S8 ~: i- A- j
03 工作机制
3.1、相关缩写
  j2 H* T' p  r* B: N" ]
SPI的极性Polarity和相位Phase,最常见的写法是CPOL和CPHA,不过也有一些其他写法,简单总结如下:

  N# C/ d- d, Q, C3 f+ P  w0 W" W
(1) CKPOL (Clock Polarity) = CPOL = POL = Polarity = (时钟)极性
$ q  A5 R2 z! ~
(2) CKPHA (Clock Phase) = CPHA = PHA = Phase = (时钟)相位

" \6 i% e. G7 M" p5 K0 l$ t
(3) SCK=SCLK=SPI的时钟

$ r( ^/ s* \" q% l1 A3 q8 K
(4) Edge=边沿,即时钟电平变化的时刻,即上升沿(risingedge)或者下降沿(fallingedge)

  q( _& Q% V  h- O- T( T5 I8 x
对于一个时钟周期内,有两个edge,分别称为:

7 |' c8 q( {9 A5 ~  \
Leading edge=前一个边沿=第一个边沿,对于开始电压是1,那么就是1变成0的时候,对于开始电压是0,那么就是0变成1的时候;
' ~* _5 F6 B% R- _& A
Trailingedge=后一个边沿=第二个边沿,对于开始电压是1,那么就是0变成1的时候(即在第一次1变成0之后,才可能有后面的0变成1),对于开始电压是0,那么就是1变成0的时候;

6 H" w$ ^( J/ y! z
3.2、CPOL极性
先说什么是SCLK时钟的空闲时刻,其就是当SCLK在数发送8个bit比特数据之前和之后的状态,于此对应的,SCLK在发送数据的时候,就是正常的工作的时候,有效active的时刻了。
4 y* |% V8 O/ W7 y6 E3 N
先说英文,其精简解释为:ClockPolarity = IDLE state of SCK。

' D; T; ^: r9 I1 j3 R: f  F+ F
再用中文详解:

3 u# _) [! t, B$ U# D  I6 a, ~
SPI的CPOL,表示当SCLK空闲idle的时候,其电平的值是低电平0还是高电平1:

2 y) }& V/ t) t. z' b1 x' C: m6 u
CPOL=0,时钟空闲idle时候的电平是低电平,所以当SCLK有效的时候,就是高电平,就是所谓的active-high;
  E5 M' J3 O- E- c% O  c
CPOL=1,时钟空闲idle时候的电平是高电平,所以当SCLK有效的时候,就是低电平,就是所谓的active-low;
2 u4 [9 W4 m) c& m- M
3.3、CPHA相位
首先说明一点,capturestrobe = latch = read =sample,都是表示数据采样,数据有效的时刻。相位,对应着数据采样是在第几个边沿(edge),是第一个边沿还是第二个边沿,0对应着第一个边沿,1对应着第二个边沿。

0 U% \) l/ k( |: K
对于:

# o0 B4 d0 f! {' \
CPHA=0,表示第一个边沿:

0 Z. D* P( L4 Y1 K, [
对于CPOL=0,idle时候的是低电平,第一个边沿就是从低变到高,所以是上升沿;

% ]$ R& ?  t7 y1 t% f1 f
对于CPOL=1,idle时候的是高电平,第一个边沿就是从高变到低,所以是下降沿;

5 N- e. m- L3 y7 l8 _
CPHA=1,表示第二个边沿:

8 w; |8 g& s. n" d+ d' q6 S2 x# `
对于CPOL=0,idle时候的是低电平,第二个边沿就是从高变到低,所以是下降沿;

" G9 X' a" f1 u+ C8 z/ J* C6 \! ?
3.4、极性和相位图示
图例1
7.png
图例2
8.png
7 ~1 ?2 \  w# W* G
3.5、软件设置极性和相位
SPI分主设备和从设备,两者通过SPI协议通讯。
& `2 o" j; T  i, G
而设置SPI的模式,是从设备的模式,决定了主设备的模式。

% o  K: u) B" Y* s( \; K: {
所以要先去搞懂从设备的SPI是何种模式,然后再将主设备的SPI的模式,设置和从设备相同的模式,即可正常通讯。
9 q0 F1 F1 ~8 G. c+ @" w6 `
对于从设备的SPI是什么模式,有两种:
$ [/ r' C; [5 A- q! c4 \
1.固定的,有SPI从设备硬件决定的

  Q8 u$ G1 a7 {) a+ y/ h- V
SPI从设备,具体是什么模式,相关的datasheet中会有描述,需要自己去datasheet中找到相关的描述,即:

" y- k* I* b9 j0 [
关于SPI从设备,在空闲的时候,是高电平还是低电平,即决定了CPOL是0还是1;

- i# {7 C" {5 R! I% h  W  R
然后再找到关于设备是在上升沿还是下降沿去采样数据,这样就是,在定了CPOL的值的前提下,对应着可以推算出CPHA是0还是1了。
3 i4 z4 L' R% V, O9 x; Z: C0 `% ^/ u
2.可配置的,由软件自己设定

7 c& J& b9 _# y6 c* }8 c: C7 k+ l# ]) B
从设备也是一个SPI控制器,4种模式都支持,此时只要自己设置为某种模式即可。

+ m( o1 s( O, ?2 @0 h
然后知道了从设备的模式后,再去将SPI主设备的模式,设置为和从设备模式一样,即可。

. x4 s, Y% u/ W; m- }
对于如何配置SPI的CPOL和CPHA的话,不多细说,多数都是直接去写对应的SPI控制器中对应寄存器中的CPOL和CPHA那两位,写0或写1即可。* H+ i0 D% m4 u1 i& l7 Z, P  K
; ?1 Q$ m1 m" k  J3 O! V
04 STM32的SPI控制模块
SPI是英语SerialPeripheralinterface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。

# I$ [: U% k; V
SPI接口主要应用在EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,STM32也有SPI接口。- S5 \0 Y1 x) b/ P! e
6 z+ `/ `, ^+ |4 K1 n0 ?
SPI接口一般使用4条线通信:
' ?+ i( K2 L9 @3 E: Y$ s  W; f; ~
1 MISO
& b$ o5 s$ W4 o% O, T2 p& R
主设备数据输入,从设备数据输出。
# C# g* I3 Q8 B4 i
2 MOSI
" C+ o6 C( O6 q2 z0 h% h4 z) S7 r) C
主设备数据输出,从设备数据输入。
3 {! k4 E. [5 c
3 SCLK
1 O# ?2 s' |1 y, j+ t2 N
时钟信号,由主设备产生
" i' g/ |) x7 w* s. ?, W
4 CS
6 Q) v& i7 F" @
从设备片选信号,由主设备控制。
4 R' v8 H' H/ ?
SPI主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。

* V5 _) D$ m% x4 O3 b
STM32的SPI功能很强大,SPI时钟最多可以到18Mhz,支持DMA,可以配置为SPI协议或者I2S协议

/ O- W2 z( q) B/ {' u8 U, p
关于SPI,从数据手册查到
9.png
- P' }7 y0 q5 i/ q
STM32F207VCT6共有3个SPI。

+ o$ @5 ?$ o) I; }+ @
从下表查出每个SPI对应的管脚
10.png
! L% H" k0 j3 i* e* |& d  G
STM32标准外设库SPI_InitTypeDef的定义

: S: W7 v, A  Q' Q0 t, r1 t
  1. 3 @: Z0 n5 W$ s+ d4 `
  2. typedef struct
    % c: I$ T% z$ }% B
  3. {
    $ r5 g! `' x) e' f) g& h, t
  4.     uint16_t SPI_Direction; // 设置SPI 的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式
    / d/ |3 i( f. N2 b: _1 x+ X
  5.    uint16_t SPI_Mode; // 设置SPI 的主从模式. I: L. ~' l- @& g  p
  6.    uint16_t SPI_DataSize; // 为8 位还是16 位帧格式选择项% L0 C. P$ i) {* W5 z% H# E8 p# h! H
  7.    uint16_t SPI_CPOL; // 设置时钟极性
    4 W2 m5 G$ i! p) L; K  M  v( f2 P
  8.    uint16_t SPI_CPHA; // 设置时钟相位- N9 Z# r* H) J
  9.    uint16_t SPI_NSS;   //设置NSS 信号由硬件(NSS管脚)还是软件控制
    0 D! [1 E1 h4 Z8 e
  10.    uint16_t SPI_BaudRatePrescaler;  //设置SPI 波特率预分频值# i2 `5 o! y- O+ _8 l
  11.    uint16_t SPI_FirstBit;    //设置数据传输顺序是MSB 位在前还是LSB 位在前8 h: `8 r( y% f# F. T. e' B
  12.    uint16_t SPI_CRCPolynomial; //设置CRC 校验多项式,提高通信可靠性,大于1 即可
    5 y: u% P8 P! Q2 P: v
  13. }SPI_InitTypeDef;3 c7 \: |/ g% v# z

  14. 0 \  g. Z' J) I  e3 L" R
复制代码
3 l6 `% {* m; `8 j5 H* _" @

+ Q0 ]" V  h- D8 G# }8 A+ `
参数

9 x$ q& \  C8 F) i% |
  • SPI_Direction
    & i9 k6 o% x7 p
用来设置SPI的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式,这里我们选择全双工模式SPI_Direction_2Lines_FullDuplex。

2 G, W! F' ?' y
  • SPI_Mode
    " a2 h/ T, |1 d
来设置SPI的主从模式,这里我们设置为主机模式 SPI_Mode_Master,当然有需要你也可以选择为从机模式 SPI_Mode_Slave。
" A) q7 H2 w, B: U% B% T
  • SPI_CPOL, q; o5 z# h3 h) H$ O8 {  d6 B
用来设置时钟极性,我们设置串行同步时钟的空闲状态为高电平所以我们选择SPI_CPOL_High。
4 y1 F2 T* A  R+ G
  • SPI_CPHA2 l, O* L8 R. `) Y. J& c& M
来设置时钟相位,也就是选择在串行同步时钟的第几个跳变沿(上升或下降)数据被采样,可以为第一个或者第二个条边沿采集,这里我们选择第二个跳变沿,所以选择 SPI_CPHA_2Edge

: n4 E! h' \9 j- ?( i8 P$ Z) G9 t  ^
  • SPI_NSS6 v; `9 P" J- Q3 A; T+ H) h
设置NSS信号由硬件(NSS管脚)还是软件控制,这里我们通过软件控制NSS关键,而不是硬件自动控制,所以选择 SPI_NSS_Soft。
7 y3 N& J, G3 w% t
  • SPI_BaudRatePrescaler
    2 F5 j8 W2 n: K' P% x
很关键,就是设置SPI波特率预分频值也就是决定 SPI 的时钟的参数,从不分频道256分频8个可选值,初始化的时候我们选择256分频值SPI_BaudRatePrescaler_256,传输速度为36M/256=140.625KHz。
/ P) l3 W$ T0 c( Y' B# c
  • SPI_FirstBit
    8 W# ^! L( w* [. C( C/ [! \0 T( r9 z
设置数据传输顺序是MSB位在前还是LSB位在前,这里我们选择SPI_FirstBit_MSB高位在前。

/ f8 L0 p* V" H; z6 V
  • SPI_CRCPolynomial( A" K( @+ B+ ~
用来设置CRC校验多项式,提高通信可靠性,大于1即可。

8 o- X4 [3 @( t) w, E. b
示例代码:
4 c- y3 c) W1 Y: n* n
  1. void  SPIInit( void )
    ' Y8 t8 S$ h8 ~
  2. {4 [2 C2 Y8 `2 h$ s! c9 z
  3.     SPI_InitTypeDef SPI_InitStructure;5 k$ E- a3 D1 L0 }3 a
  4.     FLASH_GPIO_Init();
    5 O! O3 a8 R' X2 E$ u7 ?1 h
  5.     /*!< Deselect the FLASH: Chip Select high */, u) L$ e" x: Q/ L; A3 I+ j
  6.     GPIO_SetBits( GPIOA, GPIO_Pin_4 );
    " _$ v/ t1 N3 T8 h( o
  7.     /*!< SPI configuration */
    8 X7 s0 `( K% X0 B8 V, y% m
  8.     SPI_InitStructure.SPI_Direction    = SPI_Direction_2Lines_FullDuplex;      /* 双线双向全双工 */
    " L( B6 c  E4 N2 N
  9.     SPI_InitStructure.SPI_Mode     = SPI_Mode_Master;                      /* 主 SPI */! c* a; H: V; E
  10.     SPI_InitStructure.SPI_DataSize     = SPI_DataSize_8b;                      /* SPI 发送接收 8 位帧结构 */& B" v' h. ~, y
  11.     SPI_InitStructure.SPI_CPOL     = SPI_CPOL_High;                        /* 串行同步时钟的空闲状态为高电平 */+ \9 m- I: F* ], y/ Z2 V$ [. z' `
  12.     SPI_InitStructure.SPI_CPHA     = SPI_CPHA_2Edge;                       /* 第二个跳变沿数据被采样 */. _; k  ]" X8 Y. m+ |, [
  13.     SPI_InitStructure.SPI_NSS      = SPI_NSS_Soft;                         /* NSS 信号由软件控制 */$ K$ z$ j" ?0 q! q) n7 u
  14.     SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;             /* 预分频 16 */
    - v: s; z; {; h6 Z5 ^( l& F

  15. . e5 G1 \4 e  I' l
  16.     SPI_InitStructure.SPI_FirstBit     = SPI_FirstBit_MSB;                     /* 数据传输从 MSB 位开始 */; `" U7 Q7 u- I$ \9 }7 k
  17.     SPI_InitStructure.SPI_CRCPolynomial= 7;                                    /* CRC 值计算的多项式 */5 F0 `. C# T$ \3 D- \4 \
  18.     SPI_Init( SPI1, &SPI_InitStructure );
    $ I4 }& O" D. y2 `; v# \8 x  X
  19.     /*!< Enable the sFLASH_SPI  */
    : b& W. s+ \! \' Q2 O; T  ~
  20.     SPI_Cmd( SPI1, ENABLE );0 L: A5 N; g, N1 B  L8 Z
  21. }
复制代码

( y! g( s( q& j0 P) V2 k7 L, q
看到这里,可能觉的前面讲原理并没有太大的用处,因为STM32集成了SPI控制器,配置一下即可。

: r: O& ^/ T* C$ A; g
一方面我们学习原理是为了更好的理解SPI,用于对接不同的SPI设备,像norflash的spi驱动网上有大量的例子,不容易出错。但并不是特别常见的,spi驱动SD卡,SPI驱动网络PHY,SPI驱动ESP8266,甚至在设计两个IC通信时,由于没有过多GPIO,又觉的IIC通信速度慢的话,可以设计两个IC之间使用SPI通信,显然这些场景就需要了解SPI的原理

# J6 ]# u7 @. C; h# F- \0 r$ {: g
另外一方面,实际应用中,有可能因为芯片其他管脚用于特殊功能,留下的管脚没有硬件SPI功能,只能模拟实现,这个时候学习SPI原理就很有必要了。
4 f. h' e$ e2 A2 r! l. |
$ ~/ R5 [8 @# E
05 SPI的应用
5 I6 c3 N: G$ x+ Z3 C
SPI的常用应用NorFlash+ h8 c1 V: B4 h3 |4 P. |* w
从数据手册上看到,SPI传输:CKPOL=1,CKPHA=1

& T# L) k' b+ U! \$ F
11.png
, `6 Y% C, L3 T' k. v* t  V, Q: m
所以STM32的SPI读取NorFlash的配置如下
12.png
9 C: Y' P( `( Z- t' i
抓取波形
13.png
, p% a3 Q5 ?/ s
波形如下:
14.png
3 \, K- k: {! k3 Q: S- ^  {
0100 1011  就是0X4B6 B+ k5 u) P0 x3 t& |, X6 T7 t) l
0 \$ a* G9 e0 h" _4 G
其中看到:

( e( `7 W, `4 ]
起始电平是高电平,也就是CKPOL=1

$ W/ |- ?  ^0 a
第二个边沿采样,也就是CKPHA=1
1 }% q0 N, J! _* v
其实说成类似IIC的高电平有效也是没有问题的

. v9 H) D8 e0 y! s) g: h# v) w
下面这句话是写模拟SPI的核心

. t( B/ C; f  \) Q
自己的理解:在下降沿转换数据,在上升沿采样数据

# x  C: O, e; B+ O1 F
除了抓取波形,在华邦Flash也看到了时序图
15.png

7 n( q5 J- k0 c0 w4 y
06 code
8 {! x- q% l5 w0 w
读取norflash两种驱动实现方式:
" Q4 r1 J: ^6 o4 v3 s& _
使用STM32F207硬件SPI模块
6 M  j5 I8 [; h4 X2 B; z

: u5 t: g( D( F; z

* _& r# [, M+ w' d5 v; M
" h! A8 V/ E6 d5 r! b

  1. - T# l& X* @% j- s% h" U
  2. /**$ |2 R7 {6 K3 L7 [' h
  3. 9 h( E0 A. s1 s! B- u7 q" N% l
  4.   * @brief  Initializes the peripherals used by the SPIFLASH driver.
    7 l9 a3 S! a% Q
  5.   * @param  None" w! d) V* ]* H- \- E
  6.   * @retval None
    ! L$ e  N  O0 |0 ?' w* T( X
  7. */
    6 ^* w! m8 F6 B8 t) W
  8. void FLASH_GPIO_Init(void)7 h7 W( ~7 D6 B8 n8 b0 @: m& U
  9. {
    3 s: b. @3 G7 B
  10.   GPIO_InitTypeDefGPIO_InitStructure;" q/ r% ?1 W  F! Z
  11.   
    * b) P: S& T: M, j' j
  12.   /*!< Enable the SPI clock */1 y9 y9 ~2 z7 b' |+ S4 N
  13. RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    ! h) B1 r5 n: u) B8 E4 D
  14.   3 d* e6 I! A7 `
  15. /*!< Enable GPIO clocks */
    4 F) J) j- u7 o/ @
  16. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    8 k( i. a0 e7 b% P
  17. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);  
    5 W" A# L& }- Y
  18. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);+ e! C# d, A* _. K
  19.   /*!<SPI pins configuration*************************************************/; M/ N7 h  q* ~, ^
  20.   
    9 @5 f; h! D1 ?9 B" B
  21. /*!< Connect SPI pins to AF5 */  
    * r0 C- P- e( p* n& u, z4 o, z! }6 N2 {
  22.   GPIO_PinAFConfig(GPIOA,5, GPIO_AF_SPI1);
    2 x: W- B# H! G/ a; e! h
  23.   GPIO_PinAFConfig(GPIOA, 6, GPIO_AF_SPI1);
    - B! c/ r( l) S: T" p% I
  24. GPIO_PinAFConfig(GPIOB, 5, GPIO_AF_SPI1);
    4 D9 `6 \! l' R2 e
  25.   
    9 @+ z3 Z( W* E- i" r, k
  26. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;. b, n6 W4 G/ }8 N: ^
  27. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    ; u) K0 e5 L: O* b1 u* {
  28. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    # M0 H* [  q7 _$ o: {) s" e+ i
  29. GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;//GPIO_PuPd_DOWN;* h( w! r% {* B) z) f( J0 T6 H
  30. 0 J# F: O- g# A2 D5 m1 O0 `# m
  31.   /*!< SPI SCK pin configuration */8 f* d6 j/ U* K0 k5 ^# y9 m
  32. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    2 e+ k" J. C7 H: K  h: x- M, B
  33.   GPIO_Init(GPIOA,&GPIO_InitStructure);
    0 s/ m& n9 ~# v1 ^$ z& G
  34.   
    " ]1 G4 z7 ~4 G3 s" J
  35.   /*!< SPI MISO pinconfiguration */
    6 o5 A* S/ h( S/ z) e) }
  36.   GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6;" b9 l8 R$ S; c) U' G5 Z) V* W
  37. GPIO_Init(GPIOA, &GPIO_InitStructure);
    $ D' \, k" h: t2 Q8 c
  38.   " l( O/ s) H" S! B' ^5 Y! q  M! G+ J, f
  39.   /*!<SPI MOSI pin configuration */
    4 R5 R. U% R7 {/ \
  40.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;5 ~  B# Q4 V: P$ S, j1 d
  41.   GPIO_Init(GPIOB, &GPIO_InitStructure);
    9 ?# X9 J" R8 @, q( O! `- ]

  42. ! g9 v6 B1 {( v) ^6 q# @
  43.   /*!< Configure sFLASH Card CS pin in output pushpull mode*/; r: M' T  l, V: E, M
  44.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
    7 _9 M* H4 b# }- M9 E
  45. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;& _3 @/ T7 L! F5 c8 P3 K
  46. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;5 ?2 ]; z3 ^$ A0 }6 R( \( w
  47. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;: m# S( L* X, @7 u* p) s
  48. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;5 d" \! c/ i3 }0 E) |0 u  y; E
  49.   GPIO_Init(GPIOE,&GPIO_InitStructure);( i% H6 ]* U7 c) k+ @
  50. }8 ?3 ?: l3 o  G
  51. 6 G. {: r9 Y5 ~' ^# s) h4 \* I
  52. /**
    7 u8 c( Y. i! k4 z
  53.   * @brief Initializes the peripherals used by the SPI FLASH driver.
    # t- Q" R" c+ F/ w8 e
  54.   *@param  None2 ?/ J7 w" y' L' R
  55.   * @retval None
      `( E( F6 G' A9 J/ v( c
  56.   */# R5 y7 A8 H% g: Y2 s
  57. void FLASH_SPIInit(void)3 s4 ]! o) V+ }* R" ^8 A
  58. {  
    ( t0 l# \$ U0 w) ^
  59.   
    7 q7 j* c# f3 \6 {" a
  60.   SPI_InitTypeDef SPI_InitStructure;$ S  P6 I9 e5 j- `
  61.   
    ) o8 O. G% \- z9 S
  62.   FLASH_GPIO_Init();
    4 y  m8 Y% ?* z- M! r! y1 Z
  63.   7 h$ |$ w! `) w7 ?4 t
  64. /*!< Deselect the FLASH: Chip Select high */% x2 h2 n" o! {9 N6 n5 |4 k6 y, C
  65. GPIO_SetBits(GPIOE,GPIO_Pin_12);+ B" T$ |! b' i: l0 ?; b9 k: o# ~
  66.   3 `* C! H1 |( M+ Q* @
  67.   /*!< SPIconfiguration */
    ) @& Q. i/ N* {5 [, }9 K. z
  68.   SPI_InitStructure.SPI_Direction =SPI_Direction_2Lines_FullDuplex;//双线双向全双工
    + N6 ?. o8 t0 n7 }) Q9 P
  69. SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//主SPI
    8 g' H, a* [7 V
  70.   SPI_InitStructure.SPI_DataSize =SPI_DataSize_8b;// SPI 发送接收8 位帧结构
    - _4 E) G" T- r7 m* n
  71. SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串行同步时钟的空闲状态为高电平2 C& {+ O# a4 A6 A: u
  72.   SPI_InitStructure.SPI_CPHA =SPI_CPHA_2Edge;//第二个跳变沿数据被采样
      ^" \% i/ o+ j. h' D, \" C
  73. SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//NSS 信号由软件控制6 E' B( O( T. M  j) x& y  t3 h9 j
  74. SPI_InitStructure.SPI_BaudRatePrescaler =SPI_BaudRatePrescaler_16;//预分频16
    / m" Z$ h+ A( o7 Z- v
  75.   
    ; f9 k7 D! ]8 L$ J4 k1 I- j" _# X8 ]$ x
  76.   SPI_InitStructure.SPI_FirstBit= SPI_FirstBit_MSB;//数据传输从MSB 位开始# t% H+ s% r4 l
  77. SPI_InitStructure.SPI_CRCPolynomial = 7;//CRC 值计算的多项式. v3 T1 A, K- v5 e8 s
  78. SPI_Init(SPI1, &SPI_InitStructure);
    / w6 h" q1 G+ n% g. J( D
  79.   /*!< Enable thesFLASH_SPI  */
    # z$ T! Y2 ^0 o( F7 ~# B
  80.   SPI_Cmd(SPI1, ENABLE);7 Q3 F+ p( j1 K$ U  `
复制代码
( ?9 ~' v: Q! _4 |+ _
$ h! ~) ^( z. m
软件模拟SPI协议
7 _  Y6 N! [" C7 E; H+ B; S
9 I6 z1 f  @' e% h$ r+ l& d

  1. ) w5 M% @. E3 T
  2. /**0 M: V; o7 ]8 d* d

  3. 4 _, j5 ?$ B, @  Z3 n0 Z3 C
  4.   * @brief  Sends a byte through the SPI interface andreturn the byte received
    1 H$ Q' x6 j, W% T8 ~1 A
  5.   *         from the SPI bus.. m( a/ E* f- D' G* A) z
  6.   *@param  byte: byte to send.
      h6 i! ?: E7 _( g1 t2 E' b
  7.   * @retval The value of thereceived byte.
      a/ Y; B: O7 U) n* {0 h
  8.   */$ D* Z& v7 c$ [% n% f6 H" q
  9. uint8_t SPI_ReadWriteByte(uint8_tdata)
    8 u% p% a6 V5 V( r+ q
  10. {
    + s3 i) x, o/ L& @  q
  11.   uint8_t i,data_read = 0;  
    ; c$ h/ V& w1 L' z! M
  12. if(data!=0xA5){
    ; F+ Q/ a& m" W, p# }# D' E( P# q
  13.     for(i=0;i<8;i++){4 B( @0 s& v$ z8 A% g% V! Q: w3 O
  14.      MSPI_CLK_L();                                                   ; U2 W: I6 h0 C0 }1 m
  15.      if(data&0x80){                                                  1 I, B4 L$ a7 X
  16.   MSPI_MOSI_H();4 y6 q, E2 I- s6 o) n( m! Q0 k
  17.       }else{
    ( O* X" {2 O# n5 J
  18.   MSPI_MOSI_L();
    7 U1 w7 w' Q1 M
  19.      }
    " [: U/ P  I4 w9 `
  20.       MSPI_DELAY();2 t! S2 o/ C, l  o  N- [
  21.       data = data<<1;                                                  8 C1 P# x, \4 g6 g' H
  22.       MSPI_CLK_H();                                                   
    2 k( F6 n( f, v5 l7 M; C! ?: I
  23.      MSPI_DELAY();                                                        : y" h8 }& g  Q
  24.     }
    ) k; t7 b& ~) Q9 b
  25.     return data_read;
    " H; ~/ F* i$ r+ k* |
  26.   }else{9 W  Q! B7 r' F3 {; X6 k8 I( T5 t
  27.    for(i=0;i<8;i++){. R6 p" C' n9 {( M
  28.       MSPI_CLK_L();                                      
    4 ~! h' f, ?$ f- Q4 j
  29.       MSPI_DELAY();                                      
    4 N8 Z) f. w% I" F9 ^
  30.       data_read = data_read<<1;                                    
    ; C6 d( Y' U3 c% {8 H! w
  31.       MSPI_CLK_H();                                    " K+ U3 k0 f7 r; l* e4 O4 b
  32.       if(MSPI_READ_IN()){                                                
    9 a6 X8 n1 J- R
  33.   data_read |= 0x01;                                  ( d  f# S$ M' S% k" A
  34.      }! a! h! ]" B& e( Y) \  d  u& e7 w
  35.       MSPI_DELAY();
    & c: i1 r5 ^0 h6 M" v/ Y9 y7 f) Q% z
  36.     }
    . ?$ j( w' e1 w% N8 l' {4 ?. u
  37.     returndata_read;
    ( }  V+ W9 o( |% l
  38.   }                                            
    - K+ b8 I; I8 @  X+ q
  39. }
    2 L: q2 E" G( E* S& R" S: d
  40. % y, B* F3 U9 M  e, I1 t# ~
  41. /**2 |- q8 K( h0 Z$ _; T' a7 _& P
  42.   * @brief  Initializes the peripheralsused by the SPI FLASH driver.
    : Q5 Y2 _/ k/ _- l! j
  43.   * @param  None& m# w) A1 L: |. Q: O) R3 o
  44.   * @retvalNone0 |+ K0 U7 L' }; s1 B
  45.   */
    8 W8 E% K. N6 {2 L" ]# L( L
  46. void FLASH_GPIO_Init(void)
    & c4 F$ q/ E1 \* S( }
  47. {
    : T% C, F4 C) S4 F, \' }& L
  48. GPIO_InitTypeDef GPIO_InitStructure;
    3 O8 A! G, O0 s1 q% Q

  49. 6 y2 w. T" S' L! U3 z
  50.   /*!< Enable GPIOclocks */& w+ k2 ~  V5 f- R% O# T
  51.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
    ; }' H* ^0 v/ H8 e& p/ o
  52.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    + \# k/ B2 i8 \+ j% H) f
  53.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
    ' H! \" l6 U3 A/ C* \% L8 q: C

  54. 9 n- n, `, V3 G; I
  55.   /*!< Configure sFLASH Card CS pin in output pushpull mode*/
    # y3 w  t+ K2 u8 s8 W- ~
  56.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;, d! y5 O" l9 g& j: Q3 u/ H
  57. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;+ S& N1 D: y# Z9 b* ^; d; k
  58. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
      B4 ^3 B$ ]; u% L# @; Q
  59. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;: y( l/ z$ _* e% h( Z6 J8 o
  60. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;# K3 R# L* }$ |2 k6 m  D
  61.   GPIO_Init(GPIOE,&GPIO_InitStructure);
    3 q0 t+ R  V4 E2 \
  62.   9 z' y8 @  }( ?( a& l1 j
  63.   /*!< SPI SCK pinconfiguration */6 J* Y2 m, G6 z- v2 o
  64.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;( N. F( \  ^2 z  Q/ A
  65. GPIO_Init(GPIOA, &GPIO_InitStructure);  S6 R; g4 @4 R( K- h4 x' b
  66.   MSPI_CLK_H();3 ?5 n2 K1 A6 T
  67. 7 c. d( T3 U4 d4 f
  68.   /*!< SPI MOSI pin configuration */; Q7 ~: \. H# m1 X
  69. GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_5;5 m2 B  Q5 G' }) V& P4 l
  70.   GPIO_Init(GPIOB,&GPIO_InitStructure);* {6 F0 \; K7 e- x6 h
  71.   MSPI_MOSI_H();2 n0 `5 i: U9 D6 {8 f6 I
  72.   
    ( W. L. P4 f( W& y4 X! d$ X1 c
  73.   /*!<SPI MISO pin configuration */) s1 N" D# m. @/ r# ^! X8 \* \  l1 }
  74.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;$ Y7 c8 B2 [) Y9 B8 U& }$ y, \
  75.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    ' ?- J  g2 [/ ^: X3 |! ~4 Q4 _- S
  76. GPIO_Init(GPIOA, &GPIO_InitStructure);
    , f9 R; o- }+ z+ h8 D
  77. }1 A& T8 e& H/ [, j7 u5 C

  78. / ]0 V0 Y* w* u5 v/ O2 e2 Z
  79. /**) ^' M& g4 r6 J& L
  80. * @brief  Initializes the peripherals used by the SPI FLASH driver.4 D0 g  _: Y% {# Z
  81. * @param  None7 @3 F) i5 I( M  T* p4 ~$ K; y
  82.   * @retval None
    $ ^8 m! F) J6 W1 b
  83.   */
    , ^# L& A. j. ~2 d4 v) t* `
  84. void FLASH_SPIInit(void)
    9 ]9 M! ~% y) ?8 u
  85. {  7 e4 ~3 c1 G' f- r
  86.   
    / _9 Y# `* H3 w- l6 r; ^* d& N' p
  87.   FLASH_GPIO_Init();; o) _4 w/ ^3 x* a, W# }, E% a
  88. ( }; L1 h0 d: \* Z! F; w2 {2 ]
  89.   /*!< Deselect the FLASH: Chip Select high */
    : t6 M9 X- E" a0 B( J
  90. GPIO_SetBits(GPIOE,GPIO_Pin_12);
    6 g4 V6 ^# {. T  R
  91. }
复制代码

1 C) x' I0 e  l5 k/ W1 X* s4 A+ B9 m6 ?! B5 v

( w6 D; v8 d% C! ?5 E( _( C- t' a
4.png
16.png
赞 2 收藏 7 评论1 发布时间:2020-9-8 13:47

举报

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