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
7 `2 x4 H, l, A; o! x2.3、SPI数据交换" E- h" U/ w! i
SPI数据交换框图
9 x! P0 ?% H- R2 c
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
6 ^" s/ E4 \. x9 p" F主机读取一个bit过程* v: W, }* r7 E& i7 f: Y) U
: C* E1 j2 R/ R5 d# J
当读取7次后,也就是读取7位后+ {8 W. }2 F7 g8 U6 `9 ?
# ]) 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
% 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
5 g9 ^6 E( g/ N$ s3 c: ~
图例2
' O: c/ C" n* j5 @0 d" J9 G
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
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
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# O4 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- 3 y- r2 n7 `' c
- typedef struct{ 4 a' h; h+ g+ f$ X; z
- uint16_t SPI_Direction; // 设置SPI 的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式
5 s' Z) q$ r* E! C - uint16_t SPI_Mode; // 设置SPI 的主从模式
) P# l4 w! J9 E y! X& | - uint16_t SPI_DataSize; // 为8 位还是16 位帧格式选择项
( E. s* B: o8 s# z# s8 J - uint16_t SPI_CPOL; // 设置时钟极性
1 n5 j7 S$ y$ u) n - uint16_t SPI_CPHA; // 设置时钟相位
2 l/ t, E' u+ N6 p5 k) q - uint16_t SPI_NSS; //设置NSS 信号由硬件(NSS管脚)还是软件控制
) P. a: C: W* H' S! S& [ - uint16_t SPI_BaudRatePrescaler; //设置SPI 波特率预分频值 ) b7 ]4 z0 I0 [( C9 [
- uint16_t SPI_FirstBit; //设置数据传输顺序是MSB 位在前还是LSB 位在前
0 b5 a- S/ J/ a5 X! }) r - uint16_t SPI_CRCPolynomial; //设置CRC 校验多项式,提高通信可靠性,大于1 即可
% z. X: z$ k, `) z8 J/ Y+ V; O - }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
- void SPIInit( void )! l) ]8 m& A& `. V$ Y
- { 9 ^1 K$ V6 q" e7 W4 t# Z
- SPI_InitTypeDef SPI_InitStructure;
. V/ D" Q9 o; g( a% M - FLASH_GPIO_Init(); /*!< Deselect the FLASH: Chip Select high */
7 m8 ^( D( z) Y# e! s1 f6 D - GPIO_SetBits( GPIOA, GPIO_Pin_4 ); + E+ `9 M) T) B
- /*!< SPI configuration */
8 j; ?1 c) [* {8 D - SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; /* 双线双向全双工 */
; [, E: @0 n$ l+ ?' v: \1 t1 h' n - SPI_InitStructure.SPI_Mode = SPI_Mode_Master; /* 主 SPI */
h! n- w) A) n+ j8 B4 K$ C - SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; /* SPI 发送接收 8 位帧结构 */
. Q) U0 _" P" v) @% c - SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; /* 串行同步时钟的空闲状态为高电平 */ . |" d4 R) f; d2 f; G5 k; h
- SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; /* 第二个跳变沿数据被采样 */ + W( ]: J' Z4 y4 r) b
- SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; /* NSS 信号由软件控制 */
* s; \/ Q$ \9 s- X2 V - SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; /* 预分频 16 */
; T+ {3 C6 z) Q: m" z8 [- Y - SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; /* 数据传输从 MSB 位开始 */ ! k: t8 o! A( m8 h. l4 t; I
- SPI_InitStructure.SPI_CRCPolynomial= 7; /* CRC 值计算的多项式 */ ) X% a. Q. i( F1 [ R, H0 Y! S+ Z+ L1 x
- SPI_Init( SPI1, &SPI_InitStructure ); . T \, ~+ A/ T& `
- /*!< Enable the sFLASH_SPI */
# M. m3 T9 f1 u8 k4 i8 R - SPI_Cmd( SPI1, ENABLE );
0 B& L7 F1 h2 G# Q7 T - }
复制代码 & ]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
; ^* m" O7 P0 A T, R所以STM32的SPI读取NorFlash的配置如下1 `4 f( C3 Y% S& }0 W
8 ?1 w: x# m/ X- b
抓取波形1 b9 N4 M, u' n5 ]9 p4 m1 P
9 q6 \, K, o; o) o* U1 X- V2 K波形如下: V0 O/ s' D. i
* 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
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
- /**
+ f4 U8 }3 h* V9 G- C9 i( m# x/ Z - * @brief Initializes the peripherals used by the SPIFLASH driver.
8 q& E8 K k9 |5 ] - * @param None / V7 P' D2 E4 I" i7 k5 U
- * @retval None 6 p$ K3 W6 M# _
- */1 x! Y* U" \% R# ?" m9 b) K8 u" h
- void FLASH_GPIO_Init(void)
7 a* t1 t- S: J - { $ B* C' B% Y: R: y$ `' r- @
- GPIO_InitTypeDefGPIO_InitStructure; /*!< Enable the SPI clock */ ' Q7 D" J, q2 C2 d: ~. e8 `3 ]
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); /*!< Enable GPIO clocks */ + n4 \" j7 D) T2 h, k& Y7 B
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + D( D3 Q3 o" `$ D4 _
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
& R: l6 H& t7 ?% |7 w6 R - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
# Z+ U% B+ O0 l, I! m7 w0 f - /*!<SPI pins configuration*************************************************/
. b O% X' O* n! R - /*!< Connect SPI pins to AF5 */ % ], y/ G7 k3 A
- GPIO_PinAFConfig(GPIOA,5, GPIO_AF_SPI1); ; R/ C- ^; B3 E( _, o/ M
- GPIO_PinAFConfig(GPIOA, 6, GPIO_AF_SPI1);
+ d2 V' R' _$ U- R# L - GPIO_PinAFConfig(GPIOB, 5, GPIO_AF_SPI1); ; R3 J( p' N% i! B. _" g
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; ; b& E; @/ j$ {3 l% _8 E
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
( n( j0 B7 j' L% K7 R) l, S7 |, `% Z - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; 3 c& w- J' m3 w$ x
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//GPIO_PuPd_DOWN; % s2 Z: {! h- ^# `, |5 s
- /*!< SPI SCK pin configuration */ 3 [; j1 L3 L6 z- t! w4 W- x) V1 X
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
) H; [2 q- ]# L1 V$ I' r" b - GPIO_Init(GPIOA,&GPIO_InitStructure); - C2 }) X- D5 B' C/ F% V. n: z0 E
- /*!< SPI MISO pinconfiguration */
, Z2 x! L4 ]0 l1 } - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; , i* q/ S% m8 U0 E' \
- GPIO_Init(GPIOA, &GPIO_InitStructure); 7 F5 L' O7 s) Y
- /*!<SPI MOSI pin configuration */
% ]* ?& A2 Y) k! H2 i - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
' M0 C" F2 \0 M _3 M" x - GPIO_Init(GPIOB, &GPIO_InitStructure); & A0 Y3 y$ l; o/ E/ j5 C7 \1 h
- /*!< Configure sFLASH Card CS pin in output pushpull mode*/
& Y) g% u% T3 A6 i* U - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; . \7 ^" p( I7 H; `/ h( B* {" u
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; ; K( w$ D! d5 ~$ v( i+ k: j" w
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; % Y( N' T& u* b+ W5 A* j# `
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
$ F( W1 Q5 d |! S' |" H# ` - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; ; e& h+ u5 t7 |. ]
- GPIO_Init(GPIOE,&GPIO_InitStructure);) o" ]1 ^$ ]6 s. ~3 R) O' P5 k
- }% R) z5 C, m1 S1 J% e
- /**
y- [0 y& W3 a% G* T - * @brief Initializes the peripherals used by the SPI FLASH driver. ) B. p; n1 E) u
- *@param None
7 l% ^% J( X/ v7 O. r9 v+ N+ L8 Y9 v - * @retval None
s. }. p5 {& p* r# f5 ]8 J - */
- M% x, e' P- J; p - void FLASH_SPIInit(void)
- r. \( c1 r1 o% F" D - {
1 s8 V1 J, V. N# E! X - SPI_InitTypeDef SPI_InitStructure;
2 G6 k/ w1 ?+ E; h% \ - FLASH_GPIO_Init(); /*!< Deselect the FLASH: Chip Select high */
% ^2 p! \ v/ a/ D$ b5 V3 ]/ ` - GPIO_SetBits(GPIOE,GPIO_Pin_12); /*!< SPIconfiguration */ 0 R) ]. D* ~0 {
- SPI_InitStructure.SPI_Direction =SPI_Direction_2Lines_FullDuplex;//双线双向全双工 + q4 r" ~: O) w1 z
- SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//主SPI
: q, }2 u) s" P# e/ q - SPI_InitStructure.SPI_DataSize =SPI_DataSize_8b;// SPI 发送接收8 位帧结构
0 g) j( |. @# n) S, @7 J R4 Q: @0 I! `6 C - SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串行同步时钟的空闲状态为高电平
7 a8 K- _$ E5 l- l8 v - SPI_InitStructure.SPI_CPHA =SPI_CPHA_2Edge;//第二个跳变沿数据被采样
# u! a6 z9 G$ B - SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//NSS 信号由软件控制 ( i# o& c6 N1 e3 ^. r4 M2 m& h0 N
- SPI_InitStructure.SPI_BaudRatePrescaler =SPI_BaudRatePrescaler_16;//预分频16
# @. A- R1 g& {* k8 }5 Q' t" K) | - SPI_InitStructure.SPI_FirstBit= SPI_FirstBit_MSB;//数据传输从MSB 位开始 ' @+ J, p0 G2 F4 U1 F
- SPI_InitStructure.SPI_CRCPolynomial = 7;//CRC 值计算的多项式
6 w, J0 S! w2 e' q" R - SPI_Init(SPI1, &SPI_InitStructure); /*!< Enable thesFLASH_SPI */ C/ Q$ D& [6 \& L2 ^; \
- SPI_Cmd(SPI1, ENABLE);& b& M) r3 g. E' o( G3 J2 C5 B
- }
复制代码
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 ~- ( \9 `2 a4 O1 _5 p/ M! t
- /**
; N; [0 c5 `( y7 v) r7 b - * @brief Sends a byte through the SPI interface andreturn the byte received
2 h/ m1 ?) e5 `0 ] - * from the SPI bus. * ~/ ?, L1 k0 u0 @0 ~9 U
- *@param byte: byte to send. 0 g8 C8 B( x. `) |( u' e$ o
- * @retval The value of thereceived byte.
: o( B; d1 c5 Q* ` - */
& m; I1 O. _8 C O* g - uint8_t SPI_ReadWriteByte(uint8_tdata)
( [. R8 T* \9 F9 L1 z - { ( t8 }7 u5 u7 N
- uint8_t i,data_read = 0;
, @/ R" X0 c$ j9 g) n/ j - if(data!=0xA5)$ d \( h# n" N+ E
- { : }1 m: s+ x% [. d& T6 c
- for(i=0;i<8;i++)
: V& P; o# [- s - { 7 R) m. ]& ?" ] ^+ }# s$ Y$ |
- MSPI_CLK_L();
: Y3 Z& T" {/ G+ P - if(data&0x80){ * L- n# E+ u W7 J( s+ ^
- MSPI_MOSI_H(); * h1 z( p. ~9 K9 c
- }
+ N8 L; O1 e9 B" h3 J& V - else
; ^4 `2 d6 { L% D8 t* W - { 9 S6 @ j- Z6 l9 a' t {
- MSPI_MOSI_L();
9 P# ]' I# H; l2 r - }
5 D) |3 N; E( }4 J8 R: G& O: | - MSPI_DELAY();
, b! z' [ N' ?: c4 i9 ]' x - data = data<<1;
6 Z2 n% K7 Y8 j8 } - MSPI_CLK_H(); * [0 L( N+ N p& `- [
- MSPI_DELAY();
8 t+ w. ?/ y; F& K$ N* b, O - }
6 `3 b1 ?4 r# \, z; `3 j - return data_read;
) R, C1 {3 W2 L$ ?+ y - }. t/ F% N. j: s: X& A
- else. X/ E r( T$ t/ u" {
- {
) M3 Q7 p6 k; H - for(i=0;i<8;i++)2 m ~1 L5 D& H9 h+ Z
- {
' R m: J/ S9 S' R0 E m - MSPI_CLK_L(); - w4 m; S+ s) f; m2 S u
- MSPI_DELAY(); 9 }8 p7 [$ A, l3 h, H, A' {- p
- data_read = data_read<<1; : r0 W! g8 ]4 @! |' R8 b$ h
- MSPI_CLK_H();
' {, X4 x' [$ y/ ~1 [ - if(MSPI_READ_IN())5 J5 K" d0 H+ U0 W
- { : k5 d9 J0 {$ F4 N% R
- data_read |= 0x01; " M" l$ [9 N6 _, y' T8 {( a1 o
- } : }; O3 H" _* }8 Q& g4 k! R, X
- MSPI_DELAY();
! h$ q6 w0 t7 r( { - }
+ L' \6 f6 |: ]% k# G+ W3 j# w - returndata_read;
/ G, W; ~ I- N7 T - }
4 ?9 y! M. \# O! E - }
, ?4 y! y, [7 H" L, _ - /**
2 Y* M; F7 h/ h - * @brief Initializes the peripheralsused by the SPI FLASH driver.
. m. s; c5 G7 v! W. Y g - * @param None
/ a" H! ]1 R N5 s0 ] - * @retvalNone ) q! {: W' n) a# \5 l w
- */
' Y5 C7 I' f7 b4 E# I - void FLASH_GPIO_Init(void)
) w) T. h2 a( z4 r - { + E8 j0 Y! R7 F y: }) a0 e3 t
- GPIO_InitTypeDef GPIO_InitStructure; /*!< Enable GPIOclocks */ + s% v8 s+ M6 Z s0 D% k3 U I7 p
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); ! M k' w" v% D
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); % ^2 C. t( a! f6 U
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
2 M3 l% E6 C( K" w - /*!< Configure sFLASH Card CS pin in output pushpull mode*/
: ~ G/ L2 y* @0 Y* S8 B K - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
) ?4 H& L+ |9 D+ q3 d0 T1 t, x - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; / r7 ^; {9 K0 V1 Q
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; 6 A2 n5 ~9 I- w3 H4 g8 z7 D3 e
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
! w" p. C: |! R' J: x+ `% K$ Z6 v& Y - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; ( C# j/ C" Q$ |' [- ?4 K2 H/ I
- GPIO_Init(GPIOE,&GPIO_InitStructure); 3 {" P' ]3 `! w/ p& j
- /*!< SPI SCK pinconfiguration */
9 N/ S N! ]" f' t3 E) x0 F - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
, U! f& L) p! Z* D2 r - GPIO_Init(GPIOA, &GPIO_InitStructure); 9 E, w2 `/ ~1 {0 S& p
- MSPI_CLK_H();
2 v7 s3 ?) z/ M1 }) \, i% z - /*!< SPI MOSI pin configuration */
* [5 S' y+ e7 y/ e - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
8 }/ O6 U0 c( P4 g7 d! R# p% F - GPIO_Init(GPIOB,&GPIO_InitStructure); , V6 B. d' d% `5 b8 N
- MSPI_MOSI_H(); & _2 g- c f, U0 v) ^+ v, s
- /*!<SPI MISO pin configuration */
; \6 ?. ~( @( W7 u! e - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; , a. g3 {: E9 Z; ]
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
) _! |) G4 Y8 u' {+ G8 F - GPIO_Init(GPIOA, &GPIO_InitStructure);
; q$ [ x# r% O; C8 I _4 ` - }. X c/ O- O" R( n# F- Y
- /**
3 c; G5 a8 |7 H% E1 H - * @brief Initializes the peripherals used by the SPI FLASH driver. / A! v0 y6 X* I1 s: a! n$ ?( }- H3 `
- * @param None $ Y5 v- E: X r/ w
- * @retval None ! q! W! U$ X/ l" K6 E2 b( J
- */
( s0 H; t5 K: Y) @7 W6 { - void FLASH_SPIInit(void)
+ w; |' p1 Z2 r% ?6 o+ ~% z8 y - {
5 ?1 D& @. i V! ` - FLASH_GPIO_Init(); /*!< Deselect the FLASH: Chip Select high */ - b4 t; g1 {5 h# C
- GPIO_SetBits(GPIOE,GPIO_Pin_12);+ o$ F0 r$ x9 V9 L+ ]
- }
复制代码
2 \+ w! @9 j1 z/ \) n3 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; [
|