|
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% o2.3、SPI数据交换 SPI数据交换框图
# }' l7 r8 k; p2 o + 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
主机读取一个bit过程 , E5 v. ~* l/ w. r/ k
) 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 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
( f( ?2 P m9 S8 ~: i- A- j03 工作机制 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 图例2 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,从数据手册查到 - P' }7 y0 q5 i/ q
STM32F207VCT6共有3个SPI。
+ o$ @5 ?$ o) I; }+ @
从下表查出每个SPI对应的管脚 ! L% H" k0 j3 i* e* |& d G
STM32标准外设库SPI_InitTypeDef的定义
: S: W7 v, A Q' Q0 t, r1 t
- 3 @: Z0 n5 W$ s+ d4 `
- typedef struct
% c: I$ T% z$ }% B - {
$ r5 g! `' x) e' f) g& h, t - uint16_t SPI_Direction; // 设置SPI 的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式
/ d/ |3 i( f. N2 b: _1 x+ X - uint16_t SPI_Mode; // 设置SPI 的主从模式. I: L. ~' l- @& g p
- uint16_t SPI_DataSize; // 为8 位还是16 位帧格式选择项% L0 C. P$ i) {* W5 z% H# E8 p# h! H
- uint16_t SPI_CPOL; // 设置时钟极性
4 W2 m5 G$ i! p) L; K M v( f2 P - uint16_t SPI_CPHA; // 设置时钟相位- N9 Z# r* H) J
- uint16_t SPI_NSS; //设置NSS 信号由硬件(NSS管脚)还是软件控制
0 D! [1 E1 h4 Z8 e - uint16_t SPI_BaudRatePrescaler; //设置SPI 波特率预分频值# i2 `5 o! y- O+ _8 l
- uint16_t SPI_FirstBit; //设置数据传输顺序是MSB 位在前还是LSB 位在前8 h: `8 r( y% f# F. T. e' B
- uint16_t SPI_CRCPolynomial; //设置CRC 校验多项式,提高通信可靠性,大于1 即可
5 y: u% P8 P! Q2 P: v - }SPI_InitTypeDef;3 c7 \: |/ g% v# z
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的主从模式,这里我们设置为主机模式 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
- void SPIInit( void )
' Y8 t8 S$ h8 ~ - {4 [2 C2 Y8 `2 h$ s! c9 z
- SPI_InitTypeDef SPI_InitStructure;5 k$ E- a3 D1 L0 }3 a
- FLASH_GPIO_Init();
5 O! O3 a8 R' X2 E$ u7 ?1 h - /*!< Deselect the FLASH: Chip Select high */, u) L$ e" x: Q/ L; A3 I+ j
- GPIO_SetBits( GPIOA, GPIO_Pin_4 );
" _$ v/ t1 N3 T8 h( o - /*!< SPI configuration */
8 X7 s0 `( K% X0 B8 V, y% m - SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; /* 双线双向全双工 */
" L( B6 c E4 N2 N - SPI_InitStructure.SPI_Mode = SPI_Mode_Master; /* 主 SPI */! c* a; H: V; E
- SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; /* SPI 发送接收 8 位帧结构 */& B" v' h. ~, y
- SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; /* 串行同步时钟的空闲状态为高电平 */+ \9 m- I: F* ], y/ Z2 V$ [. z' `
- SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; /* 第二个跳变沿数据被采样 */. _; k ]" X8 Y. m+ |, [
- SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; /* NSS 信号由软件控制 */$ K$ z$ j" ?0 q! q) n7 u
- SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; /* 预分频 16 */
- v: s; z; {; h6 Z5 ^( l& F -
. e5 G1 \4 e I' l - SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; /* 数据传输从 MSB 位开始 */; `" U7 Q7 u- I$ \9 }7 k
- SPI_InitStructure.SPI_CRCPolynomial= 7; /* CRC 值计算的多项式 */5 F0 `. C# T$ \3 D- \4 \
- SPI_Init( SPI1, &SPI_InitStructure );
$ I4 }& O" D. y2 `; v# \8 x X - /*!< Enable the sFLASH_SPI */
: b& W. s+ \! \' Q2 O; T ~ - SPI_Cmd( SPI1, ENABLE );0 L: A5 N; g, N1 B L8 Z
- }
复制代码
( 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 , `6 Y% C, L3 T' k. v* t V, Q: m
所以STM32的SPI读取NorFlash的配置如下 9 C: Y' P( `( Z- t' i
抓取波形 , p% a3 Q5 ?/ s
波形如下: 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也看到了时序图 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
* _& r# [, M+ w' d5 v; M" h! A8 V/ E6 d5 r! b
- T# l& X* @% j- s% h" U- /**$ |2 R7 {6 K3 L7 [' h
- 9 h( E0 A. s1 s! B- u7 q" N% l
- * @brief Initializes the peripherals used by the SPIFLASH driver.
7 l9 a3 S! a% Q - * @param None" w! d) V* ]* H- \- E
- * @retval None
! L$ e N O0 |0 ?' w* T( X - */
6 ^* w! m8 F6 B8 t) W - void FLASH_GPIO_Init(void)7 h7 W( ~7 D6 B8 n8 b0 @: m& U
- {
3 s: b. @3 G7 B - GPIO_InitTypeDefGPIO_InitStructure;" q/ r% ?1 W F! Z
-
* b) P: S& T: M, j' j - /*!< Enable the SPI clock */1 y9 y9 ~2 z7 b' |+ S4 N
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
! h) B1 r5 n: u) B8 E4 D - 3 d* e6 I! A7 `
- /*!< Enable GPIO clocks */
4 F) J) j- u7 o/ @ - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
8 k( i. a0 e7 b% P - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
5 W" A# L& }- Y - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);+ e! C# d, A* _. K
- /*!<SPI pins configuration*************************************************/; M/ N7 h q* ~, ^
-
9 @5 f; h! D1 ?9 B" B - /*!< Connect SPI pins to AF5 */
* r0 C- P- e( p* n& u, z4 o, z! }6 N2 { - GPIO_PinAFConfig(GPIOA,5, GPIO_AF_SPI1);
2 x: W- B# H! G/ a; e! h - GPIO_PinAFConfig(GPIOA, 6, GPIO_AF_SPI1);
- B! c/ r( l) S: T" p% I - GPIO_PinAFConfig(GPIOB, 5, GPIO_AF_SPI1);
4 D9 `6 \! l' R2 e -
9 @+ z3 Z( W* E- i" r, k - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;. b, n6 W4 G/ }8 N: ^
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
; u) K0 e5 L: O* b1 u* { - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
# M0 H* [ q7 _$ o: {) s" e+ i - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//GPIO_PuPd_DOWN;* h( w! r% {* B) z) f( J0 T6 H
- 0 J# F: O- g# A2 D5 m1 O0 `# m
- /*!< SPI SCK pin configuration */8 f* d6 j/ U* K0 k5 ^# y9 m
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
2 e+ k" J. C7 H: K h: x- M, B - GPIO_Init(GPIOA,&GPIO_InitStructure);
0 s/ m& n9 ~# v1 ^$ z& G -
" ]1 G4 z7 ~4 G3 s" J - /*!< SPI MISO pinconfiguration */
6 o5 A* S/ h( S/ z) e) } - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;" b9 l8 R$ S; c) U' G5 Z) V* W
- GPIO_Init(GPIOA, &GPIO_InitStructure);
$ D' \, k" h: t2 Q8 c - " l( O/ s) H" S! B' ^5 Y! q M! G+ J, f
- /*!<SPI MOSI pin configuration */
4 R5 R. U% R7 {/ \ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;5 ~ B# Q4 V: P$ S, j1 d
- GPIO_Init(GPIOB, &GPIO_InitStructure);
9 ?# X9 J" R8 @, q( O! `- ] -
! g9 v6 B1 {( v) ^6 q# @ - /*!< Configure sFLASH Card CS pin in output pushpull mode*/; r: M' T l, V: E, M
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
7 _9 M* H4 b# }- M9 E - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;& _3 @/ T7 L! F5 c8 P3 K
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;5 ?2 ]; z3 ^$ A0 }6 R( \( w
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;: m# S( L* X, @7 u* p) s
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;5 d" \! c/ i3 }0 E) |0 u y; E
- GPIO_Init(GPIOE,&GPIO_InitStructure);( i% H6 ]* U7 c) k+ @
- }8 ?3 ?: l3 o G
- 6 G. {: r9 Y5 ~' ^# s) h4 \* I
- /**
7 u8 c( Y. i! k4 z - * @brief Initializes the peripherals used by the SPI FLASH driver.
# t- Q" R" c+ F/ w8 e - *@param None2 ?/ J7 w" y' L' R
- * @retval None
`( E( F6 G' A9 J/ v( c - */# R5 y7 A8 H% g: Y2 s
- void FLASH_SPIInit(void)3 s4 ]! o) V+ }* R" ^8 A
- {
( t0 l# \$ U0 w) ^ -
7 q7 j* c# f3 \6 {" a - SPI_InitTypeDef SPI_InitStructure;$ S P6 I9 e5 j- `
-
) o8 O. G% \- z9 S - FLASH_GPIO_Init();
4 y m8 Y% ?* z- M! r! y1 Z - 7 h$ |$ w! `) w7 ?4 t
- /*!< Deselect the FLASH: Chip Select high */% x2 h2 n" o! {9 N6 n5 |4 k6 y, C
- GPIO_SetBits(GPIOE,GPIO_Pin_12);+ B" T$ |! b' i: l0 ?; b9 k: o# ~
- 3 `* C! H1 |( M+ Q* @
- /*!< SPIconfiguration */
) @& Q. i/ N* {5 [, }9 K. z - SPI_InitStructure.SPI_Direction =SPI_Direction_2Lines_FullDuplex;//双线双向全双工
+ N6 ?. o8 t0 n7 }) Q9 P - SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//主SPI
8 g' H, a* [7 V - SPI_InitStructure.SPI_DataSize =SPI_DataSize_8b;// SPI 发送接收8 位帧结构
- _4 E) G" T- r7 m* n - SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串行同步时钟的空闲状态为高电平2 C& {+ O# a4 A6 A: u
- SPI_InitStructure.SPI_CPHA =SPI_CPHA_2Edge;//第二个跳变沿数据被采样
^" \% i/ o+ j. h' D, \" C - SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//NSS 信号由软件控制6 E' B( O( T. M j) x& y t3 h9 j
- SPI_InitStructure.SPI_BaudRatePrescaler =SPI_BaudRatePrescaler_16;//预分频16
/ m" Z$ h+ A( o7 Z- v -
; f9 k7 D! ]8 L$ J4 k1 I- j" _# X8 ]$ x - SPI_InitStructure.SPI_FirstBit= SPI_FirstBit_MSB;//数据传输从MSB 位开始# t% H+ s% r4 l
- SPI_InitStructure.SPI_CRCPolynomial = 7;//CRC 值计算的多项式. v3 T1 A, K- v5 e8 s
- SPI_Init(SPI1, &SPI_InitStructure);
/ w6 h" q1 G+ n% g. J( D - /*!< Enable thesFLASH_SPI */
# z$ T! Y2 ^0 o( F7 ~# B - 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
) w5 M% @. E3 T- /**0 M: V; o7 ]8 d* d
4 _, j5 ?$ B, @ Z3 n0 Z3 C- * @brief Sends a byte through the SPI interface andreturn the byte received
1 H$ Q' x6 j, W% T8 ~1 A - * from the SPI bus.. m( a/ E* f- D' G* A) z
- *@param byte: byte to send.
h6 i! ?: E7 _( g1 t2 E' b - * @retval The value of thereceived byte.
a/ Y; B: O7 U) n* {0 h - */$ D* Z& v7 c$ [% n% f6 H" q
- uint8_t SPI_ReadWriteByte(uint8_tdata)
8 u% p% a6 V5 V( r+ q - {
+ s3 i) x, o/ L& @ q - uint8_t i,data_read = 0;
; c$ h/ V& w1 L' z! M - if(data!=0xA5){
; F+ Q/ a& m" W, p# }# D' E( P# q - for(i=0;i<8;i++){4 B( @0 s& v$ z8 A% g% V! Q: w3 O
- MSPI_CLK_L(); ; U2 W: I6 h0 C0 }1 m
- if(data&0x80){ 1 I, B4 L$ a7 X
- MSPI_MOSI_H();4 y6 q, E2 I- s6 o) n( m! Q0 k
- }else{
( O* X" {2 O# n5 J - MSPI_MOSI_L();
7 U1 w7 w' Q1 M - }
" [: U/ P I4 w9 ` - MSPI_DELAY();2 t! S2 o/ C, l o N- [
- data = data<<1; 8 C1 P# x, \4 g6 g' H
- MSPI_CLK_H();
2 k( F6 n( f, v5 l7 M; C! ?: I - MSPI_DELAY(); : y" h8 }& g Q
- }
) k; t7 b& ~) Q9 b - return data_read;
" H; ~/ F* i$ r+ k* | - }else{9 W Q! B7 r' F3 {; X6 k8 I( T5 t
- for(i=0;i<8;i++){. R6 p" C' n9 {( M
- MSPI_CLK_L();
4 ~! h' f, ?$ f- Q4 j - MSPI_DELAY();
4 N8 Z) f. w% I" F9 ^ - data_read = data_read<<1;
; C6 d( Y' U3 c% {8 H! w - MSPI_CLK_H(); " K+ U3 k0 f7 r; l* e4 O4 b
- if(MSPI_READ_IN()){
9 a6 X8 n1 J- R - data_read |= 0x01; ( d f# S$ M' S% k" A
- }! a! h! ]" B& e( Y) \ d u& e7 w
- MSPI_DELAY();
& c: i1 r5 ^0 h6 M" v/ Y9 y7 f) Q% z - }
. ?$ j( w' e1 w% N8 l' {4 ?. u - returndata_read;
( } V+ W9 o( |% l - }
- K+ b8 I; I8 @ X+ q - }
2 L: q2 E" G( E* S& R" S: d - % y, B* F3 U9 M e, I1 t# ~
- /**2 |- q8 K( h0 Z$ _; T' a7 _& P
- * @brief Initializes the peripheralsused by the SPI FLASH driver.
: Q5 Y2 _/ k/ _- l! j - * @param None& m# w) A1 L: |. Q: O) R3 o
- * @retvalNone0 |+ K0 U7 L' }; s1 B
- */
8 W8 E% K. N6 {2 L" ]# L( L - void FLASH_GPIO_Init(void)
& c4 F$ q/ E1 \* S( } - {
: T% C, F4 C) S4 F, \' }& L - GPIO_InitTypeDef GPIO_InitStructure;
3 O8 A! G, O0 s1 q% Q
6 y2 w. T" S' L! U3 z- /*!< Enable GPIOclocks */& w+ k2 ~ V5 f- R% O# T
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
; }' H* ^0 v/ H8 e& p/ o - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
+ \# k/ B2 i8 \+ j% H) f - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
' H! \" l6 U3 A/ C* \% L8 q: C -
9 n- n, `, V3 G; I - /*!< Configure sFLASH Card CS pin in output pushpull mode*/
# y3 w t+ K2 u8 s8 W- ~ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;, d! y5 O" l9 g& j: Q3 u/ H
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;+ S& N1 D: y# Z9 b* ^; d; k
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
B4 ^3 B$ ]; u% L# @; Q - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;: y( l/ z$ _* e% h( Z6 J8 o
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;# K3 R# L* }$ |2 k6 m D
- GPIO_Init(GPIOE,&GPIO_InitStructure);
3 q0 t+ R V4 E2 \ - 9 z' y8 @ }( ?( a& l1 j
- /*!< SPI SCK pinconfiguration */6 J* Y2 m, G6 z- v2 o
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;( N. F( \ ^2 z Q/ A
- GPIO_Init(GPIOA, &GPIO_InitStructure); S6 R; g4 @4 R( K- h4 x' b
- MSPI_CLK_H();3 ?5 n2 K1 A6 T
- 7 c. d( T3 U4 d4 f
- /*!< SPI MOSI pin configuration */; Q7 ~: \. H# m1 X
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;5 m2 B Q5 G' }) V& P4 l
- GPIO_Init(GPIOB,&GPIO_InitStructure);* {6 F0 \; K7 e- x6 h
- MSPI_MOSI_H();2 n0 `5 i: U9 D6 {8 f6 I
-
( W. L. P4 f( W& y4 X! d$ X1 c - /*!<SPI MISO pin configuration */) s1 N" D# m. @/ r# ^! X8 \* \ l1 }
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;$ Y7 c8 B2 [) Y9 B8 U& }$ y, \
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
' ?- J g2 [/ ^: X3 |! ~4 Q4 _- S - GPIO_Init(GPIOA, &GPIO_InitStructure);
, f9 R; o- }+ z+ h8 D - }1 A& T8 e& H/ [, j7 u5 C
/ ]0 V0 Y* w* u5 v/ O2 e2 Z- /**) ^' M& g4 r6 J& L
- * @brief Initializes the peripherals used by the SPI FLASH driver.4 D0 g _: Y% {# Z
- * @param None7 @3 F) i5 I( M T* p4 ~$ K; y
- * @retval None
$ ^8 m! F) J6 W1 b - */
, ^# L& A. j. ~2 d4 v) t* ` - void FLASH_SPIInit(void)
9 ]9 M! ~% y) ?8 u - { 7 e4 ~3 c1 G' f- r
-
/ _9 Y# `* H3 w- l6 r; ^* d& N' p - FLASH_GPIO_Init();; o) _4 w/ ^3 x* a, W# }, E% a
- ( }; L1 h0 d: \* Z! F; w2 {2 ]
- /*!< Deselect the FLASH: Chip Select high */
: t6 M9 X- E" a0 B( J - GPIO_SetBits(GPIOE,GPIO_Pin_12);
6 g4 V6 ^# {. T R - }
复制代码
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 |