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

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 设备不能正常工作。
1 i3 g  ~$ a' W! q) d5 }6 ^
02 SPI特点

3 I5 T/ x/ ^* E+ F" C% K$ W. B( F
2.1SPI控制方式
采用主-从模式(Master-Slave)的控制方式。

1 q, V5 F$ K9 G6 L+ k: d
SPI 规定了两个SPI 设备之间通信必须由主设备(Master) 来控制次设备(Slave). 一个Master 设备可以通过提供Clock 以及对Slave 设备进行片选(Slave Select) 来控制多个Slave 设备,SPI 协议还规定Slave 设备的Clock 由Master 设备通过SCK 管脚提供给Slave 设备,Slave 设备本身不能产生或控制Clock, 没有Clock 则Slave 设备不能正常工作。
  @9 C. @  f: V6 H) f3 D
2.2SPI传输方式
采用同步方式(Synchronous)传输数据。

7 ?+ f4 y) f- N& q, Z  G
Master 设备会根据将要交换的数据来产生相应的时钟脉冲(ClockPulse), 时钟脉冲组成了时钟信号(ClockSignal) , 时钟信号通过时钟极性(CPOL) 和时钟相位 (CPHA) 控制着两个SPI 设备间何时数据交换以及何时对接收到的数据进行采样,来保证数据在两个设备之间是同步传输的。
* w! D9 C1 h+ P, l7 w: j
1.png
2.3SPI数据交换
SPI数据交换框图

1 E+ j6 N1 [" e; K4 f% A! |
2.png

% y; {/ t; f+ b6 F
上图只是对SPI 设备间通信的一个简单的描述,下面就来解释一下图中所示的几个组件(Module):

' Y9 a+ H7 S" n; i8 Y
SSPBUF,Synchronous Serial Port Buffer, 泛指SPI 设备里面的内部缓冲区,一般在物理上是以FIFO 的形式,保存传输过程中的临时数据;
/ z$ X0 v9 J9 o6 v" a
SSPSR, Synchronous Serial Port Register, 泛指SPI 设备里面的移位寄存器(ShiftRegitser), 它的作用是根据设置好的数据位宽(bit-width)把数据移入或者移出SSPBUF;
5 P& X# L) X; n# U# y8 R) t5 \
Controller, 泛指SPI 设备里面的控制寄存器,可以通过配置它们来设置SPI 总线的传输模式。
! L' o$ W1 @0 D) ~" m) c, x" F" N
SPI 设备间的数据传输之所以又被称为数据交换,是因为SPI 协议规定一个SPI 设备不能在数据通信过程中仅仅只充当一个"发送者(Transmitter)"或者"接收者(Receiver)".在每个Clock 周期内,SPI 设备都会发送并接收一个bit 大小的数据,相当于该设备有一个bit 大小的数据被交换了.一个Slave 设备要想能够接收到Master 发过来的控制信号,必须在此之前能够被Master 设备进行访问(Access). 所以,Master 设备必须首先通过SS/CS pin 对Slave 设备进行片选,把想要访问的Slave 设备选上.在数据传输的过程中,每次接收到的数据必须在下一次数据传输之前被采样.如果之前接收到的数据没有被读取,那么这些已经接收完成的数据将有可能会被丢弃,导致SPI 物理模块最终失效.因此,在程序中一般都会在SPI 传输完数据后,去读取SPI 设备里的数据,即使这些数据(DummyData)在我们的程序里是无用的。

8 S0 Q( ?' X4 V' J6 R& N
上面的过程转为动画
) W4 C3 K- @6 j
初始状态

/ _9 ?3 T8 W! C1 }) _
3.png
主机读取一个bit过程

8 J  l- h6 n/ ?% \* H# \
640.gif
7 A; Z0 ]6 }# ^
当读取7次后,也就是读取7位后: D: i) M; [+ t* b2 q( G

- h0 s- e( T) Q! o
5.png
5 g- L' F  l3 R- L; @6 ^
0 g8 b7 K* H  O/ J+ o6 \5 o
总结:
没有读和写的说法,因为实质上每次SPI是主从设备在交换数据。也就是说,你发一个数据必然会收到一个数据;你要收一个数据必须也要先发一个数据。

8 t" S8 g1 m, `3 a$ N8 @
2.4SPI传输模式
上升沿、下降沿、前沿、后沿触发。当然也有MSB和LSB传输方式.

2 W+ d" j' J+ n+ e
6.png

) c: m8 ^7 D' f) z5 ]; j& H
03 工作机制
3.1、相关缩写9 X/ @; f. D9 T
SPI的极性Polarity和相位Phase,最常见的写法是CPOL和CPHA,不过也有一些其他写法,简单总结如下:

1 h. y5 i5 P) w+ x) J$ n1 ]
(1) CKPOL (Clock Polarity) = CPOL = POL = Polarity = (时钟)极性

0 ?( M. J/ @" Q' }3 R
(2) CKPHA (Clock Phase) = CPHA = PHA = Phase = (时钟)相位
+ G" s4 Y& b& f
(3) SCK=SCLK=SPI的时钟

, L; C  D) J7 O' ?8 c
(4) Edge=边沿,即时钟电平变化的时刻,即上升沿(risingedge)或者下降沿(fallingedge)

  x& |& h+ x, E3 A! Z
对于一个时钟周期内,有两个edge,分别称为:
) O! ~6 H) d7 d$ |( U
Leading edge=前一个边沿=第一个边沿,对于开始电压是1,那么就是1变成0的时候,对于开始电压是0,那么就是0变成1的时候;

- s1 s+ F9 E9 A( r3 A
Trailingedge=后一个边沿=第二个边沿,对于开始电压是1,那么就是0变成1的时候(即在第一次1变成0之后,才可能有后面的0变成1),对于开始电压是0,那么就是1变成0的时候;
1 ^8 p! x, I- k% |, A' J1 `
3.2CPOL极性
先说什么是SCLK时钟的空闲时刻,其就是当SCLK在数发送8个bit比特数据之前和之后的状态,于此对应的,SCLK在发送数据的时候,就是正常的工作的时候,有效active的时刻了。
2 l; j( c( J) Z, n- V* ]
先说英文,其精简解释为:ClockPolarity = IDLE state of SCK。
6 P3 }6 H, l* S/ i
再用中文详解:

/ ~' x1 C6 E% ?
SPI的CPOL,表示当SCLK空闲idle的时候,其电平的值是低电平0还是高电平1:
  s- P. A6 H+ E+ H5 d
CPOL=0,时钟空闲idle时候的电平是低电平,所以当SCLK有效的时候,就是高电平,就是所谓的active-high;

$ d2 E3 b. q/ N* v: c
CPOL=1,时钟空闲idle时候的电平是高电平,所以当SCLK有效的时候,就是低电平,就是所谓的active-low;
# I8 G5 J+ Z8 w9 h" k9 T) ^6 y
3.3CPHA相位
首先说明一点,capturestrobe = latch = read =sample,都是表示数据采样,数据有效的时刻。相位,对应着数据采样是在第几个边沿(edge),是第一个边沿还是第二个边沿,0对应着第一个边沿,1对应着第二个边沿。

( y: X$ b5 c$ I, }" n( M! B. p
对于:
) q+ c  y) y5 K0 J: u) K
CPHA=0,表示第一个边沿:
$ s! M. y) _$ ?7 c: G
对于CPOL=0,idle时候的是低电平,第一个边沿就是从低变到高,所以是上升沿;
+ K) _! l" P. M3 J- H
对于CPOL=1,idle时候的是高电平,第一个边沿就是从高变到低,所以是下降沿;
) i! I* F  N: f# s$ n9 _  C3 }; m& P
CPHA=1,表示第二个边沿:
1 e1 ]& N5 d& `  ^) }( c" n& \
对于CPOL=0,idle时候的是低电平,第二个边沿就是从高变到低,所以是下降沿;

7 j* g5 z/ w; {
3.4、极性和相位图示
图例1
7.png
图例2
8.png

! K3 M4 `0 S! ?( H8 F$ O5 `. i, y  O
3.5、软件设置极性和相位
SPI分主设备和从设备,两者通过SPI协议通讯。

2 m9 t7 a3 k  c2 V& l/ ~
而设置SPI的模式,是从设备的模式,决定了主设备的模式。

* G# v& l6 u5 i' n, s0 R8 l8 _
所以要先去搞懂从设备的SPI是何种模式,然后再将主设备的SPI的模式,设置和从设备相同的模式,即可正常通讯。

% G# ?& e$ ~( c  T9 n- [+ d
对于从设备的SPI是什么模式,有两种:

' ^  X1 u' d4 T# P$ i: M
1.固定的,有SPI从设备硬件决定的
, p. z4 f, w) T0 @% B5 G
SPI从设备,具体是什么模式,相关的datasheet中会有描述,需要自己去datasheet中找到相关的描述,即:

0 X, N% E( s8 c8 _
关于SPI从设备,在空闲的时候,是高电平还是低电平,即决定了CPOL是0还是1;
7 i, @) R8 R4 f% j& c8 D
然后再找到关于设备是在上升沿还是下降沿去采样数据,这样就是,在定了CPOL的值的前提下,对应着可以推算出CPHA是0还是1了。

+ f+ R! v) Z+ w+ U
2.可配置的,由软件自己设定

( y# L) ^5 w) k- D1 a
从设备也是一个SPI控制器,4种模式都支持,此时只要自己设置为某种模式即可。

& l) r" v# e) x) h; {
然后知道了从设备的模式后,再去将SPI主设备的模式,设置为和从设备模式一样,即可。
+ t# N) ?5 x/ F. e; B
对于如何配置SPI的CPOL和CPHA的话,不多细说,多数都是直接去写对应的SPI控制器中对应寄存器中的CPOL和CPHA那两位,写0或写1即可4 g' o  A! T: ]* v6 g( Y! m  \% [  V

. p' N! ?1 z9 @
04 STM32的SPI控制模块
SPI是英语SerialPeripheralinterface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。

! ^+ X/ `  @) K" J! h( A) P+ b
SPI接口主要应用在EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,STM32也有SPI接口。
1 ^! O$ i" ~) g, K" V5 F

& C0 {; X: H$ u3 D. e
SPI接口一般使用4条线通信:
# ^! Y2 r1 \2 ?. k9 u1 t0 v
1 MISO
* s1 z, @# ]1 {
主设备数据输入,从设备数据输出。
/ Y, d9 v* @3 \& L8 J1 \4 `# T1 g: V
2 MOSI

. V! {' G/ T, x* \  P  @
主设备数据输出,从设备数据输入。
1 u' n" X! {: n/ Y
3 SCLK

2 W+ k: W! x6 i3 L% o
时钟信号,由主设备产生

  g  Y3 W2 e0 f0 X, l, N
4 CS

; r1 L! u4 h8 @& b3 h# C8 Q9 Y
从设备片选信号,由主设备控制。
! S+ |0 Q3 s' ]* K7 b0 }
SPI主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。

1 k2 m2 Z9 f1 E, V! l0 o/ b
STM32的SPI功能很强大,SPI时钟最多可以到18Mhz,支持DMA,可以配置为SPI协议或者I2S协议
4 x/ c9 ~! B+ ^9 f6 ~) t
关于SPI,从数据手册查到
9.png

' J" [3 j* Z9 i% E* T
STM32F207VCT6共有3个SPI。
- Q+ q; t% p6 h* L/ F2 e
从下表查出每个SPI对应的管脚
10.png
: j6 x  C+ r/ J- E
STM32标准外设库SPI_InitTypeDef的定义

- u# j. G, T/ H3 N6 J
  1. % b) S, K' V; X* l$ X; g1 {; M# k
  2. typedef struct
    ' I; E' T7 S7 d' Z
  3. {
    3 w+ ~2 b, t$ k! K) r
  4.     uint16_t SPI_Direction; // 设置SPI 的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式
    * t3 l  c4 F; R  k0 k
  5.    uint16_t SPI_Mode; // 设置SPI 的主从模式
    + l1 g4 q: M" J6 k# q3 h( ]' }
  6.    uint16_t SPI_DataSize; // 为8 位还是16 位帧格式选择项  o  t8 T) @' [3 C+ D
  7.    uint16_t SPI_CPOL; // 设置时钟极性
    ; X' t& y, b- V3 m
  8.    uint16_t SPI_CPHA; // 设置时钟相位" r/ ^( A; d, k
  9.    uint16_t SPI_NSS;   //设置NSS 信号由硬件(NSS管脚)还是软件控制
      t& v  R& G' Y, y
  10.    uint16_t SPI_BaudRatePrescaler;  //设置SPI 波特率预分频值8 `  t3 p  m* v2 ~! m1 [
  11.    uint16_t SPI_FirstBit;    //设置数据传输顺序是MSB 位在前还是LSB 位在前  l& L4 ^5 p1 v8 p' h) Z: ^
  12.    uint16_t SPI_CRCPolynomial; //设置CRC 校验多项式,提高通信可靠性,大于1 即可% n% B) H/ d/ R
  13. }SPI_InitTypeDef;
    0 z6 B: O, y5 p6 I9 T3 F, F
  14. ; L, r$ f' Z4 B" `6 H
复制代码
# l6 }/ B' t! ^) a0 O, S) i

; _/ F  J  h5 n" _1 R6 S
参数
4 l( E9 R, k6 t# v# s
  • SPI_Direction
    6 L9 Q3 B% w1 G$ g, Z- {
用来设置SPI的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式,这里我们选择全双工模式SPI_Direction_2Lines_FullDuplex。

7 \( W" ]9 L" G6 I9 ?3 T
  • SPI_Mode
    9 O& i8 X6 m; `7 G6 |* E
来设置SPI的主从模式,这里我们设置为主机模式 SPI_Mode_Master,当然有需要你也可以选择为从机模式 SPI_Mode_Slave。

( S8 a( s0 L6 M( T- ?" M! Q
  • SPI_CPOL
    $ u, G4 g+ J9 h
用来设置时钟极性,我们设置串行同步时钟的空闲状态为高电平所以我们选择SPI_CPOL_High。

2 ^# v/ K% u3 L4 z. h
  • SPI_CPHA* @; H/ T3 T" z
来设置时钟相位,也就是选择在串行同步时钟的第几个跳变沿(上升或下降)数据被采样,可以为第一个或者第二个条边沿采集,这里我们选择第二个跳变沿,所以选择 SPI_CPHA_2Edge

' r1 S/ R3 T1 |& C* K
  • SPI_NSS
    + [" h. n* O7 {( X2 d5 s
设置NSS信号由硬件(NSS管脚)还是软件控制,这里我们通过软件控制NSS关键,而不是硬件自动控制,所以选择 SPI_NSS_Soft。
1 @& B8 C( B% Y! V2 d
  • SPI_BaudRatePrescaler
    * O5 O% b% \+ _& i9 F+ w
很关键,就是设置SPI波特率预分频值也就是决定 SPI 的时钟的参数,从不分频道256分频8个可选值,初始化的时候我们选择256分频值SPI_BaudRatePrescaler_256,传输速度为36M/256=140.625KHz。
, Y5 ^- g6 x# l! d/ a: k" `
  • SPI_FirstBit
    3 Q' @3 p, `% i2 h9 n: \; W% k
设置数据传输顺序是MSB位在前还是LSB位在前,这里我们选择SPI_FirstBit_MSB高位在前。
5 I9 `" v- z  U5 a& q( I( o
  • SPI_CRCPolynomial
    " V: e6 D# c; b$ e) G
用来设置CRC校验多项式,提高通信可靠性,大于1即可。
' o- D9 V3 @: T$ ^8 r7 P
示例代码:
0 m6 b: ~, r, V% P. k* y: k
  1. void  SPIInit( void )
    # v+ c1 T+ X9 @. f+ T0 A8 F
  2. {
    * \5 b8 |8 a# p+ K0 z
  3.     SPI_InitTypeDef SPI_InitStructure;( U3 ]1 B: j0 R( `) {" {8 o
  4.     FLASH_GPIO_Init();4 R  O! n" j- l5 }, E3 W
  5.     /*!< Deselect the FLASH: Chip Select high */2 ^( j$ o7 `) `4 U
  6.     GPIO_SetBits( GPIOA, GPIO_Pin_4 );
    0 I, e8 v. e3 y: x. b
  7.     /*!< SPI configuration */+ K9 u4 z, s1 {4 t* P3 j
  8.     SPI_InitStructure.SPI_Direction    = SPI_Direction_2Lines_FullDuplex;      /* 双线双向全双工 */
      q8 ]3 {: j+ _6 j
  9.     SPI_InitStructure.SPI_Mode     = SPI_Mode_Master;                      /* 主 SPI */2 I% F; T# l- L, b, m* w) ~
  10.     SPI_InitStructure.SPI_DataSize     = SPI_DataSize_8b;                      /* SPI 发送接收 8 位帧结构 */* p( \8 ]# l, ]1 F9 \! E
  11.     SPI_InitStructure.SPI_CPOL     = SPI_CPOL_High;                        /* 串行同步时钟的空闲状态为高电平 */
    * V# N: j) Y* e% x
  12.     SPI_InitStructure.SPI_CPHA     = SPI_CPHA_2Edge;                       /* 第二个跳变沿数据被采样 */, h2 e  P( w. C! r" Q. `
  13.     SPI_InitStructure.SPI_NSS      = SPI_NSS_Soft;                         /* NSS 信号由软件控制 */
    ; g7 p: m9 b7 \) S. ~! d9 O' h1 m
  14.     SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;             /* 预分频 16 */5 L) s9 M0 r* G2 |( I% b3 Q

  15. . b8 f0 I5 c& f/ N- A- E/ D2 \
  16.     SPI_InitStructure.SPI_FirstBit     = SPI_FirstBit_MSB;                     /* 数据传输从 MSB 位开始 */' C( G+ H4 U7 C
  17.     SPI_InitStructure.SPI_CRCPolynomial= 7;                                    /* CRC 值计算的多项式 */0 T6 E: H: t- H# F  f. O
  18.     SPI_Init( SPI1, &SPI_InitStructure );- R( t2 T( L8 s0 O& w
  19.     /*!< Enable the sFLASH_SPI  */
    ! g% ]: ~7 T) Q
  20.     SPI_Cmd( SPI1, ENABLE );
    # g- o8 J9 Y0 M$ N. Y' Q3 r
  21. }
复制代码
, Q4 }6 e- @0 `9 n3 c
看到这里,可能觉的前面讲原理并没有太大的用处,因为STM32集成了SPI控制器,配置一下即可。
% S3 J2 C8 G# U, S# N) |
一方面我们学习原理是为了更好的理解SPI,用于对接不同的SPI设备,像norflash的spi驱动网上有大量的例子,不容易出错。但并不是特别常见的,spi驱动SD卡,SPI驱动网络PHY,SPI驱动ESP8266,甚至在设计两个IC通信时,由于没有过多GPIO,又觉的IIC通信速度慢的话,可以设计两个IC之间使用SPI通信,显然这些场景就需要了解SPI的原理

' E* ]" r! \6 ~* p# ~/ U# [
另外一方面,实际应用中,有可能因为芯片其他管脚用于特殊功能,留下的管脚没有硬件SPI功能,只能模拟实现,这个时候学习SPI原理就很有必要了。

% ?6 k2 L3 d9 v& r* P8 ]8 k7 d; |
8 o7 c; v* ]6 L6 T
05 SPI的应用
* t3 A' t4 ?# w6 {* V+ [7 V$ @" P
SPI的常用应用NorFlash/ Y  Z2 K( q" d
从数据手册上看到,SPI传输:CKPOL=1,CKPHA=1

( C* B! W# f: [, A
11.png
' `4 v. _3 \& e$ G6 `/ `; G# H$ b
所以STM32的SPI读取NorFlash的配置如下
12.png
4 S4 q, L2 |- O& p
抓取波形
13.png
3 ]: i4 L7 Q$ ~* l# j6 o
波形如下:
14.png

/ z% \. P/ Y6 ^4 Y
0100 1011  就是0X4B& Q: l- i8 g+ _" I# q1 M

  t3 X) E& `7 ]% e. C& ~
其中看到:
, l# p; _; Q) Q; z. {) P! B
起始电平是高电平,也就是CKPOL=1

  v# K- c2 `4 ?2 e
第二个边沿采样,也就是CKPHA=1

. n. y1 f" v+ [2 [& O  N
其实说成类似IIC的高电平有效也是没有问题的
3 W1 R$ a5 C8 Q: y+ S
下面这句话是写模拟SPI的核心
! q4 P6 G7 d9 F
自己的理解:在下降沿转换数据,在上升沿采样数据
* s8 _9 ~3 t4 h, l. g* w  w
除了抓取波形,在华邦Flash也看到了时序图
15.png

1 B" i% K. A4 N. I; u* ]
06 code
% c) C; F: z1 \; r% Q* R
读取norflash两种驱动实现方式:

- R( u6 L  w* S- ^/ N2 B( {0 w, C
使用STM32F207硬件SPI模块

3 e! x# Q$ E4 o6 w
5 R0 X  H/ t& D3 X9 _! d3 I. m

" _/ F) H. _; E" A# Q
4 ~  a5 j* a- b$ n( |& W3 e1 Z) W
  1. 7 x+ `, a! M' e9 I  G( p  A
  2. /**+ x5 t+ x. T) ~' w$ t* i
  3. 9 f5 N! ?; _: L+ Y
  4.   * @brief  Initializes the peripherals used by the SPIFLASH driver.
    & E, M; D3 s* v: z' x% l+ l
  5.   * @param  None2 v, o+ P  |# j1 N
  6.   * @retval None  S8 _2 h1 b! e" I, E' C
  7. */
    " _' R# j# r4 u2 o
  8. void FLASH_GPIO_Init(void)
    9 f) o5 G0 ^1 w, l# g
  9. {
    + E7 |' U% u0 j( ?# D! s8 h. |
  10.   GPIO_InitTypeDefGPIO_InitStructure;' Y/ J5 q' W) X% ~' L1 z0 y
  11.   
    , L9 K& Q8 f, {
  12.   /*!< Enable the SPI clock */' b  y7 L9 J9 Q
  13. RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    3 j6 S+ H) L0 ?4 h# k
  14.   ' j4 U  ]$ u( m( L
  15. /*!< Enable GPIO clocks */
    , @- s& l1 F( y6 Y' |6 r5 N2 N8 }
  16. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    " x* X- b5 r0 _" n- D
  17. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);  $ g( Q9 Y4 J2 ?
  18. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
    + P0 Z1 D  u' E2 K' h* [+ a+ ~
  19.   /*!<SPI pins configuration*************************************************/" V- N- R! L. j0 t
  20.   
    ) |) {6 j& m! H; c
  21. /*!< Connect SPI pins to AF5 */  4 V9 y. N9 P+ U& S7 B2 E
  22.   GPIO_PinAFConfig(GPIOA,5, GPIO_AF_SPI1);! t. |+ A7 \+ W2 f: y9 X
  23.   GPIO_PinAFConfig(GPIOA, 6, GPIO_AF_SPI1);
    % r" Z) j9 V$ A9 K( F/ x
  24. GPIO_PinAFConfig(GPIOB, 5, GPIO_AF_SPI1);/ h' K  S& g* f0 Q; y9 V
  25.   7 Y) }$ \, q9 p& A4 z
  26. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    5 f  e  q: ~3 ^
  27. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;, s1 J  n0 w* B1 U" E
  28. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    & n  `) l" c! s" P: u, r
  29. GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;//GPIO_PuPd_DOWN;
    0 y; K' G7 O5 I/ F7 u* R
  30. 9 l5 `$ {- ^' J% L
  31.   /*!< SPI SCK pin configuration */* z4 G0 X5 N5 k, d! m
  32. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    ; `3 N' a  q5 U& s
  33.   GPIO_Init(GPIOA,&GPIO_InitStructure);2 T5 k; {" \" b
  34.   % s  ^" r! X) A4 v, T
  35.   /*!< SPI MISO pinconfiguration */
    $ L6 D/ O; l6 B7 j
  36.   GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6;( p8 m% A: p9 G4 b% L  e
  37. GPIO_Init(GPIOA, &GPIO_InitStructure);
    1 z) d$ Q, U2 ?3 Z
  38.   
    - \7 k  L. D+ E' i) V! a/ _$ i
  39.   /*!<SPI MOSI pin configuration */
    * w/ @" m/ {$ c( p
  40.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    5 v5 U, i: G& Z! g) K  ^4 e
  41.   GPIO_Init(GPIOB, &GPIO_InitStructure);: R) ^' w) Q2 B& e7 T& Q

  42. 9 W4 i4 Q  i/ z# ?: A9 m
  43.   /*!< Configure sFLASH Card CS pin in output pushpull mode*/
    $ N  z; z9 q$ N/ T, |9 [
  44.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
    * m6 ]( E3 Z! I( k$ V
  45. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;3 L- h7 k$ a' D7 G# ~
  46. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;1 M+ C5 i" w! D/ _' p' T
  47. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;' p8 j( h* o' X7 `
  48. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;$ q( E1 c: S: T( X! t
  49.   GPIO_Init(GPIOE,&GPIO_InitStructure);( s. v) \* S5 o$ i( p( E' {5 o
  50. }
    : l% M. Z# d2 M! L* P. P

  51. / E" g8 Z  v2 ]7 P* a
  52. /**, R0 Q6 }1 t" ]: S
  53.   * @brief Initializes the peripherals used by the SPI FLASH driver.
    # [; f  r1 c& N
  54.   *@param  None. s7 `% |. Z4 k* j
  55.   * @retval None
    1 C4 b# O& `, c# k  i* [
  56.   */
    ( n5 l& k- i9 Q/ H6 c
  57. void FLASH_SPIInit(void)
    , \4 t0 x' F; o7 M0 y+ S6 t
  58. {    T: S0 J5 l- B# ?% R8 }/ w  ?
  59.   ' Y0 @. d4 d# \# J! a
  60.   SPI_InitTypeDef SPI_InitStructure;' @, s# K$ E! q2 Z9 L* W$ Y
  61.   5 e! ~8 c0 \: f+ N
  62.   FLASH_GPIO_Init();
    0 {4 }- q; m2 C$ ~% Z9 e: C( B& g2 A! q
  63.   3 R/ ]* P8 k) p( c* D) G
  64. /*!< Deselect the FLASH: Chip Select high */
    2 |7 a" Z* c2 k; l
  65. GPIO_SetBits(GPIOE,GPIO_Pin_12);
    2 e: p* V1 u$ o0 e3 i2 u
  66.   
      z  y2 i' i2 p
  67.   /*!< SPIconfiguration */" ]  e" }8 m& F+ ?( L, C
  68.   SPI_InitStructure.SPI_Direction =SPI_Direction_2Lines_FullDuplex;//双线双向全双工
    # f. D2 A% @$ Q
  69. SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//主SPI
    1 ~5 c, y+ c- @2 l& V6 A& c
  70.   SPI_InitStructure.SPI_DataSize =SPI_DataSize_8b;// SPI 发送接收8 位帧结构
    & t9 H7 R  P% {% [- w# J1 [4 L% [
  71. SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串行同步时钟的空闲状态为高电平
    " x. B; L1 W) ?% c7 O. V* t( G0 w* y
  72.   SPI_InitStructure.SPI_CPHA =SPI_CPHA_2Edge;//第二个跳变沿数据被采样
    & y! T/ t. Q' r& o* r7 _0 @
  73. SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//NSS 信号由软件控制
    3 X0 P( [* k$ |* g
  74. SPI_InitStructure.SPI_BaudRatePrescaler =SPI_BaudRatePrescaler_16;//预分频16: R( c, h& h+ N; d& y! M2 h0 d
  75.   
    : R; M" `6 y' S0 G
  76.   SPI_InitStructure.SPI_FirstBit= SPI_FirstBit_MSB;//数据传输从MSB 位开始8 ~% B1 I$ |7 W: K! J5 H
  77. SPI_InitStructure.SPI_CRCPolynomial = 7;//CRC 值计算的多项式
    3 v5 `- M% o8 Z$ \
  78. SPI_Init(SPI1, &SPI_InitStructure);
    - C+ O; f7 y4 i1 R# x
  79.   /*!< Enable thesFLASH_SPI  */$ H* }& L4 [$ K+ ~5 A/ g
  80.   SPI_Cmd(SPI1, ENABLE);
    + k, I2 U2 E) ~  \- `
复制代码
) I; Y$ f5 ^) N! B6 w" j/ o
) e9 B' P' i, J- D
软件模拟SPI协议
* ~6 M! Z0 R& i  u( v7 D% y6 b
2 y4 D4 @" m7 [+ t

  1. 2 t4 N4 u& i$ q9 P$ M, r) H
  2. /**2 n  o8 k9 p5 r; B6 E% ]! E/ }

  3. . H) j) e; u) ?  x5 ]+ q3 a( q
  4.   * @brief  Sends a byte through the SPI interface andreturn the byte received3 Z% p$ `  b' w5 P& z4 k
  5.   *         from the SPI bus.+ Y% E7 w7 p) u5 ~9 t) k# l
  6.   *@param  byte: byte to send.) q4 W9 U0 G7 S6 o
  7.   * @retval The value of thereceived byte.% \% f% _4 l5 A; @
  8.   */
    4 i- S9 J! B; B, J% h, q! Q
  9. uint8_t SPI_ReadWriteByte(uint8_tdata)4 i7 {0 J: {4 h/ ?* D
  10. {$ y" a; Q. J- p, k. Z
  11.   uint8_t i,data_read = 0;  
    : r( [% W/ p, x+ K6 W5 [
  12. if(data!=0xA5){
    " N: m$ f% f) F9 K3 f" l% L
  13.     for(i=0;i<8;i++){
    3 m7 Y0 a$ H6 j
  14.      MSPI_CLK_L();                                                   
    & I3 a) W5 T' Y" G1 l, g- j0 i3 n
  15.      if(data&0x80){                                                  : L2 G9 v- D' A7 S; _- x
  16.   MSPI_MOSI_H();# }6 \1 T+ Q) p8 {9 P
  17.       }else{- t' V: _( W, D: k9 P$ M5 ?
  18.   MSPI_MOSI_L();
    , k: k# ]+ o. {8 m+ L1 j
  19.      }( M( e6 s: n5 t0 L0 a4 d4 K
  20.       MSPI_DELAY();/ i  U7 l0 e( h/ x; |
  21.       data = data<<1;                                                  
    1 Q7 T4 s' E" X5 z$ j3 Q
  22.       MSPI_CLK_H();                                                   
    6 J! L* P, k' f+ q3 k0 t
  23.      MSPI_DELAY();                                                        
    ; M" `, L- `0 t, b' z
  24.     }
    - f1 I- Q$ s0 L% R; T$ q3 q
  25.     return data_read;, G! {! Y* J" L% I( O- S
  26.   }else{
    * R3 D2 {- R0 [' V
  27.    for(i=0;i<8;i++){
    % _+ E2 w1 V7 {+ J, _% S
  28.       MSPI_CLK_L();                                      ! S0 @. D9 R9 H3 r' Y7 ~
  29.       MSPI_DELAY();                                      
    0 s) u: E. o$ K7 ?$ M6 t+ a6 K
  30.       data_read = data_read<<1;                                    % p! R6 s7 \# M/ {
  31.       MSPI_CLK_H();                                    4 m9 U1 F9 ~( {+ f
  32.       if(MSPI_READ_IN()){                                                $ Y7 _- M$ p5 A2 F4 e' L* i
  33.   data_read |= 0x01;                                  . \" I: n) {( A0 f
  34.      }) ?+ D; c( C6 p! j
  35.       MSPI_DELAY();  h: i+ C6 g" a, i& T
  36.     }, H7 u- b* i4 |+ _% u1 d
  37.     returndata_read;
    ) ~5 k& v& `8 c6 T) k
  38.   }                                            : F0 C( t  w4 f, h6 R2 Q
  39. }6 ]! M1 w) e9 a/ m7 F5 J! L7 E& g

  40. 5 ^1 R! |1 ?) V# M; o& m- R1 q
  41. /**- s' G$ |* @: _1 n; p" v) |
  42.   * @brief  Initializes the peripheralsused by the SPI FLASH driver.! B) E- O, x/ a
  43.   * @param  None
    : G( z- S+ s2 n- t! s0 M
  44.   * @retvalNone
    * Y$ T& h9 |$ ]6 t6 d
  45.   */
    ' S  l7 q0 q/ g3 A
  46. void FLASH_GPIO_Init(void)) E6 W/ `1 K* Y/ y
  47. {
    # [& S4 ]  @( [6 g
  48. GPIO_InitTypeDef GPIO_InitStructure;
    8 A% W* M+ u' z9 L3 a7 p

  49. / l) d& I. y& T( ~+ R0 J
  50.   /*!< Enable GPIOclocks */
    4 {. W' B* n; Y% S
  51.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);% Q! M7 N* ~' S1 l8 S  ]% k
  52.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    ' j; l) T  O# O
  53.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
    " F+ J7 B7 Q  b% ?: b5 A
  54. 8 _! g# Z/ k' x/ X
  55.   /*!< Configure sFLASH Card CS pin in output pushpull mode*/
    ( y% C  R; D0 q& A' C
  56.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
    6 L' r: h5 B4 H& @" A2 R2 c4 n' O& K5 {% I
  57. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    . T' D- d% s! |" R# k  L0 H5 Z
  58. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;) {( ?) c5 A+ u
  59. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    + U/ K3 L- w- d" M
  60. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;, ]5 `1 _+ M4 B. ~( k6 R4 p
  61.   GPIO_Init(GPIOE,&GPIO_InitStructure);( Y& P" e# ^  g, F( Z# G8 I& K4 M
  62.   2 K% ^' F. T. q& `* u2 W
  63.   /*!< SPI SCK pinconfiguration */
      n6 j0 l- t! Q- y
  64.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    3 |7 t4 L) N+ Y6 }4 e* w1 W) V
  65. GPIO_Init(GPIOA, &GPIO_InitStructure);) i% d; o! `8 R3 ~! M( p) J5 B" }
  66.   MSPI_CLK_H();
    4 c! P5 c( M0 C. {/ \& Y

  67. " }( [/ ~. ^- H. }
  68.   /*!< SPI MOSI pin configuration */
    : F5 ^$ @: t+ F) Y) s
  69. GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_5;2 `2 b4 k! a$ {* U: U3 _5 f2 y# w
  70.   GPIO_Init(GPIOB,&GPIO_InitStructure);
    , t1 g- e( z! P7 X6 e8 T
  71.   MSPI_MOSI_H();
    & ^: c8 B8 {+ B5 c# m8 v
  72.   % x0 g. O# h! |9 y
  73.   /*!<SPI MISO pin configuration */
    7 i* a1 ]0 G4 q: d; b% f# g
  74.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    7 h6 H6 Y: T, G5 N) \, L9 z
  75.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;. {$ h0 @, D/ j) D- M1 z2 G
  76. GPIO_Init(GPIOA, &GPIO_InitStructure);
    * g- _! v& Z& u3 D$ l# {% N
  77. }+ G) x7 v  `# d# o* P

  78. 5 B  }; u: n) m7 w
  79. /**1 c1 ~4 L! J% ?1 \/ U. K! u
  80. * @brief  Initializes the peripherals used by the SPI FLASH driver.
    & S; _( p5 H7 n
  81. * @param  None/ N- s. {0 r; U0 X6 h' h
  82.   * @retval None6 N) k9 w( h" s
  83.   */
    8 D: w/ {# w. M% w/ P
  84. void FLASH_SPIInit(void)
    $ e, j/ {" i; p- r) C) i9 |
  85. {  
    / s' Z( S0 W' U; o% V
  86.   
    - G; x0 b9 i* r2 X- ^7 K; J# |
  87.   FLASH_GPIO_Init();
    & B5 F0 L! t$ f, o9 p2 t# m
  88. ' c9 }# D. \5 J( Y# |4 T  ]1 V
  89.   /*!< Deselect the FLASH: Chip Select high */# C5 w: x( [* Y' A% \
  90. GPIO_SetBits(GPIOE,GPIO_Pin_12);
    , S' M  o0 s( [5 u6 h  A
  91. }
复制代码
3 r  A0 _2 j; {

. t' e$ j- Q. P/ u# G) g; ?" x! y* c2 `, e) y
4.png
16.png
2 收藏 7 评论1 发布时间:2020-9-8 13:47

举报

1个回答
qhq8001 回答时间:2020-9-11 09:58:14
真形象,
& D! p% l, u; O. h- g4 }* k; q

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版