一、SPI简介 SPI(Serial Peripheral Interface )是串行外围接口设备,是一种高速的,全双工,同步的通信总线,并且在芯片上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是处于这种简单易用的特性,现在越来越多的芯片集成了这种协议。 SPI 是一个环形总线结构,由 ss(cs)、 sck、 sdi、 sdo 构成,其时序其实很简单,主要是在 sck 的控制下,两个双向移位寄存器进行数据交换。2 y- G7 r Z& p8 Y5 h
# G+ Y8 W$ R$ `" U, m+ {4 m4 H1 T, H; r. g1 B, |# g$ y1 Q( X
因为是全双工同步通信,所以在传输数据时,左边主机的数据从移位寄存器进入MOSI线上进入右边的从机,并存入最低位,同时从机的最高位通过MISO传输到主机的最低位,当第二位数据进行发送时,最低位的数据会向左移一位并将新数据存入最低位。 二、通信协议 1、物理层 motorola公司 首先提出的全双工三线同步串行外围接口,采用主从模式( Master Slave)架构;支持多 slave 模式应用,一般仅支持单Master(单主机模式)。 管脚 三线SPI:SCLK(时钟线),MISO(主机接收从机发送),MOSI(主机发送从机接收) 四线SPI:CS(片选线),SCLK,MISO,MOSI 片选:被选,确定该设备处于何种工作模式 连接方式
& e8 p. T0 ]" W7 B2、数据链路层
7 ], |/ |# ]4 G& [0 D SPI采用位协议,------高位在前,低位在后 SPI有四种工作模式,SPI0 SPI1 SPI2 SPI3 SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协 议没有重大的影响。如果 CPOL=0,串行同步时钟的空闲状态为低电平;如果 CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA), m) E0 l1 q, C7 w; x
能够配置用于选择两种不同的传输协议之一进行数据传输。如果 CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如
& V! k5 z! P% h5 d* X+ a c果 CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。 SPI 主模块和与之通信的外设音时钟相位和极性应该一致。3 Z" \; q7 j3 l+ r/ \) V. J6 ]
SPI 时序图详解---SPI 接口在模式 0 下输出第一位数据的时刻
0 _' i2 {2 k$ C: [: s 三、STM32中SPI控制器 数量:2个 SPI1 SPI2 特性:全双工同步通信 8/16是传输帧选择 主从结构 8个主模式波特率预分频系数 主模式和从模式下均可以由软件或硬件进行NSS管理 可编程的时钟极性和相位 可编程的数据顺序, MSB在前或LSB在前 四、配置工作模式 1、NSS管理 SPI可以工作为主机模式和从机模式,可以通过软件模式和硬件模式进行管理。 SPI的NSS引脚分为内部引脚和外部引脚,当内部引脚检测到高电平的时候,设备会工作在主机模式,检测到低电平,工作在从机模式。 先说软件模式,软件模式可以通过SPI_CR1寄存器的SSM为进行设置,当SSM位为1时,SPI的模式管理为软件管理模式,且当SSI位为1时(SSI位仅在SSM位为1时有效),内部的NSS会被驱动为高电平,该设备就设置为主机模式且外部NSS引脚会输出一个低电平信号,当其他的设备检测到低电平信号时,会自动进入从机模式。 5 g* r0 {, ^* Z8 g8 c F
% X$ k; l2 y8 s( Y$ @$ Z
: {& b$ z7 ~: \ k' w/ Q+ z- j) t7 ~, X) z& Z5 c
硬件模式: ' v0 s) k0 U: n; n! C+ P
NSS输出使能通过CR2的SSOE位进行控制,一旦该位被使能,NSS引脚作为一个输出引脚,若SPI工作为主机模式,NSS会输出一个低电平,当其他设备的NSS引脚接到主设备的引脚,会检测到一个低电平,并会自动进入从设备状态。当配置为主模式,NSS配置为输入引脚,(MSTR=1,SSOE=0)时,如果NSS被拉低,则配置为主模式水白,会自动进入从工作模式。 若向通过硬件管理工作模式,只需将需要配置为主模式的NSS引脚接高电平,从模式的NSS接低电平即可。
1 y1 ^& O# d( S9 q$ j# ~1 r五、SPI配置过程 1、查看原理图确定引脚,以及各个引脚的工作模式
7 w4 o K* S0 |1 b. f + q( ^( U) b) D% M5 f
- p5 g0 r7 E; J. k9 |
这里spi的四个引脚分别对应pa4,pa5,pa6,pa7,SPI作为主机,MOSI,cs,clk应给配置为输出模式,miso配置为输入。
. R" d0 N6 U4 y$ @7 H/ q$ d
' c( ]* F C7 P. W8 t8 A所以为了将SPI的功能复用到IO扣上,pa5,pa7要用作复用推挽输出。(pa4为什么配置为通用推挽输出现在我还没弄明白,等明白了再改回来) 2、确定spi的时钟相位以及时钟极性等工作模式 因为时钟相位和时钟极性可以配置出4种spi的模式,而spi的工作模式与数据的采集以及输出有关,所以我们这里要根据外设来确当spi的工作模式。 - #include "spi.h"
7 ^* g' {1 j0 B
: y& i% |* Y( q8 b Z- void SPI_Config(void), D0 a. G' `" H& e
- {
7 ~, w6 n+ Q% h& J2 l4 i6 G - /*$ |8 n; Q8 k1 L6 R$ Q3 l$ _
- pa4 sf cs 通用推挽输出/ Y4 ?3 c) ~4 f, }$ c% i/ O
- pa5 sc clk 复用推挽输出9 x6 P3 I& s* l
- pa6 miso 浮空输入
1 H7 T6 U8 t/ j - pa7 mosi 复用推挽输出
/ ?. u& y8 D9 u9 A& X - */4 x: s. Z R2 a
- //GPIO, `, E% Z# G7 U- n
- GPIO_InitTypeDef GPIOInitStruct;
4 Z8 e2 j0 A& J# z. Z7 f; V, x - //SPI8 R3 k/ n& v/ A t# N
- SPI_InitTypeDef SPIInitStruct;
) c; J/ f! Q8 c; Z+ _" H - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1,ENABLE);& Y- y% H: \& r/ ?6 @0 v
- //pa4 通用推挽输出
2 P0 w' p- @" }7 P5 { - GPIOInitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
" w+ ^' ^8 b) {6 `1 h1 J - GPIOInitStruct.GPIO_Pin = GPIO_Pin_4;
3 z; i2 z2 h; G g6 a9 a7 H6 t - GPIOInitStruct.GPIO_Speed = GPIO_Speed_50MHz;3 G/ o1 G0 ^# s4 W
- GPIO_Init(GPIOA,&GPIOInitStruct);
6 D' q# t+ _( D# Z - //pa5 pa7 复用推挽输出0 n x$ {, c( b9 O
- GPIOInitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
+ l: |3 w7 x, q0 Q6 I% f - GPIOInitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
3 Y2 L1 `( c( s* Q - GPIO_Init(GPIOA,&GPIOInitStruct);
9 g; m8 [, S/ V% U9 ^ - //pa6 浮空输入% G0 y, s) s; g9 l. {
- GPIOInitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
) T$ O7 y; ^- ], D - GPIOInitStruct.GPIO_Pin = GPIO_Pin_6;, O1 x+ V1 }9 a" }0 [! v
- GPIO_Init(GPIOA,&GPIOInitStruct);
" K y; F' \6 z q2 P, C - . e2 m) p C0 G6 o1 \/ u1 t
- //SPI
3 _) i3 [! p9 e5 l) F9 V7 j k - SPIInitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; //分频
" O$ i0 Z, U4 g! w& ?4 i& Z - SPIInitStruct.SPI_CPHA = SPI_CPHA_1Edge; //时钟相位; [; N/ C# H, L. R- T8 i6 p
- SPIInitStruct.SPI_CPOL = SPI_CPOL_Low; //时钟极性
8 u8 u$ e) L m4 A$ y3 P - SPIInitStruct.SPI_DataSize = SPI_DataSize_8b; //数据宽度
/ M8 {7 ?5 i& H" t9 h5 U2 B: J - SPIInitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //全双工" z3 p/ ]. e5 Z
- SPIInitStruct.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前
! ~+ P5 n: c' ~+ g3 ~0 Q# c* ?1 ? - SPIInitStruct.SPI_Mode = SPI_Mode_Master; //主机( c4 C* t! G. ~( a f5 W
- SPIInitStruct.SPI_NSS = SPI_NSS_Soft; //软件模式
' L4 L( {5 D7 `8 e+ F) ?; I - SPI_Init(SPI1,&SPIInitStruct);( n& a9 ~ K9 i' r3 R
- //使能SPI
! _4 o/ d* r( ^& z - SPI_Cmd(SPI1,ENABLE);2 |- r5 P' z- a" ?: p! ?! T5 v
-
; X# p( d9 n5 { - }
复制代码 $ _' p1 X ?1 Z3 @: c
|