一、SPI简介 SPI(Serial Peripheral Interface )是串行外围接口设备,是一种高速的,全双工,同步的通信总线,并且在芯片上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是处于这种简单易用的特性,现在越来越多的芯片集成了这种协议。 SPI 是一个环形总线结构,由 ss(cs)、 sck、 sdi、 sdo 构成,其时序其实很简单,主要是在 sck 的控制下,两个双向移位寄存器进行数据交换。! _& q$ ]1 `3 P$ P! z. Z
& J: J" ?! [0 N4 X
7 H5 {8 C2 p4 ~; a" q/ U/ `因为是全双工同步通信,所以在传输数据时,左边主机的数据从移位寄存器进入MOSI线上进入右边的从机,并存入最低位,同时从机的最高位通过MISO传输到主机的最低位,当第二位数据进行发送时,最低位的数据会向左移一位并将新数据存入最低位。 二、通信协议 1、物理层 motorola公司 首先提出的全双工三线同步串行外围接口,采用主从模式( Master Slave)架构;支持多 slave 模式应用,一般仅支持单Master(单主机模式)。 管脚 三线SPI:SCLK(时钟线),MISO(主机接收从机发送),MOSI(主机发送从机接收) 四线SPI:CS(片选线),SCLK,MISO,MOSI 片选:被选,确定该设备处于何种工作模式 连接方式 / O9 g; S1 |+ U/ ?
2、数据链路层
/ k% C5 C6 l" T! `" ? SPI采用位协议,------高位在前,低位在后 SPI有四种工作模式,SPI0 SPI1 SPI2 SPI3 SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协 议没有重大的影响。如果 CPOL=0,串行同步时钟的空闲状态为低电平;如果 CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)
; ]0 e5 ^0 {% \9 g$ N能够配置用于选择两种不同的传输协议之一进行数据传输。如果 CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如* Q0 j q1 _" P+ h+ @
果 CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。 SPI 主模块和与之通信的外设音时钟相位和极性应该一致。* ^) p0 r( k; ?. h8 O# q
SPI 时序图详解---SPI 接口在模式 0 下输出第一位数据的时刻
& v7 V6 n+ G. Z/ \) D# E 三、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引脚会输出一个低电平信号,当其他的设备检测到低电平信号时,会自动进入从机模式。 * a; x: T# l) i( M4 ~' P
" _" k1 ]4 X! z3 ?9 R
2 g3 y8 a. z0 _3 S: x/ [8 l; \7 j0 [. Q: I$ d6 H
硬件模式:
3 \0 l+ r9 I2 R NSS输出使能通过CR2的SSOE位进行控制,一旦该位被使能,NSS引脚作为一个输出引脚,若SPI工作为主机模式,NSS会输出一个低电平,当其他设备的NSS引脚接到主设备的引脚,会检测到一个低电平,并会自动进入从设备状态。当配置为主模式,NSS配置为输入引脚,(MSTR=1,SSOE=0)时,如果NSS被拉低,则配置为主模式水白,会自动进入从工作模式。 若向通过硬件管理工作模式,只需将需要配置为主模式的NSS引脚接高电平,从模式的NSS接低电平即可。 7 e( Q9 h9 r- i5 T+ q; w
五、SPI配置过程 1、查看原理图确定引脚,以及各个引脚的工作模式8 j# D1 i2 D6 E' h* n$ f
2 L* B6 d0 g. l. U* _% {+ p2 M6 p% U
这里spi的四个引脚分别对应pa4,pa5,pa6,pa7,SPI作为主机,MOSI,cs,clk应给配置为输出模式,miso配置为输入。
8 M' K( _6 c% n. Y2 A5 P! s5 z7 ?% f( y6 a" P$ g8 F: e/ o4 v
所以为了将SPI的功能复用到IO扣上,pa5,pa7要用作复用推挽输出。(pa4为什么配置为通用推挽输出现在我还没弄明白,等明白了再改回来) 2、确定spi的时钟相位以及时钟极性等工作模式 因为时钟相位和时钟极性可以配置出4种spi的模式,而spi的工作模式与数据的采集以及输出有关,所以我们这里要根据外设来确当spi的工作模式。 - #include "spi.h"
. h# x. p' R! r+ g
7 L* h4 [0 B+ u' y- void SPI_Config(void)5 e* S0 j! l+ T. l
- {
9 C L+ b! s. w* n - /*0 u7 V. l$ B2 Q0 w$ t" r0 Y
- pa4 sf cs 通用推挽输出
4 i5 V3 p* T i" e - pa5 sc clk 复用推挽输出
" _" P( f2 Q) e - pa6 miso 浮空输入
4 |; R! D9 N; @, U8 l" F) R- z0 @ - pa7 mosi 复用推挽输出 b4 \1 X" }2 A \% p1 j5 h
- */; ]) d* [( X' t2 A% U
- //GPIO8 O% O" j+ x2 R: g
- GPIO_InitTypeDef GPIOInitStruct;
, Y) v6 g" U% e5 ^* T ^ - //SPI1 [' Q' y! e+ U# K# X
- SPI_InitTypeDef SPIInitStruct;8 e5 L. r _& Y9 ], b+ d; r$ _, |
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1,ENABLE);/ h: k) Z2 O6 H' Q/ e+ ~
- //pa4 通用推挽输出$ Z: U3 ~% q4 x8 P$ H
- GPIOInitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
8 f2 I) w/ e4 B7 z+ a: f; z* x - GPIOInitStruct.GPIO_Pin = GPIO_Pin_4;
8 {0 G4 {" R) S! L* }5 z - GPIOInitStruct.GPIO_Speed = GPIO_Speed_50MHz;! `" v: @) @ E0 m+ ^
- GPIO_Init(GPIOA,&GPIOInitStruct);, S' K/ O' u# [
- //pa5 pa7 复用推挽输出% x0 N6 n6 N: J D3 z' _
- GPIOInitStruct.GPIO_Mode = GPIO_Mode_AF_PP;" P( J0 f+ ~' ? z! B
- GPIOInitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
f% O" z; T7 [ - GPIO_Init(GPIOA,&GPIOInitStruct);1 G# o# a) A1 W
- //pa6 浮空输入% C# o- N+ ~ Y. C( P. N
- GPIOInitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;% f( J2 p% h+ ^
- GPIOInitStruct.GPIO_Pin = GPIO_Pin_6;* h! F1 G9 s7 f
- GPIO_Init(GPIOA,&GPIOInitStruct);
; A" b4 C( `( M5 h5 M0 w6 Z! Z. l - 0 p6 w: m0 b8 p
- //SPI
3 H7 W! \9 ]9 P! u - SPIInitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; //分频
5 L& D8 b4 m8 c* ]" B) q+ ]" W; q - SPIInitStruct.SPI_CPHA = SPI_CPHA_1Edge; //时钟相位! ?2 q/ @! g& R( H' F/ h& v2 K6 a4 ~
- SPIInitStruct.SPI_CPOL = SPI_CPOL_Low; //时钟极性
) g" v5 S% q: K7 U1 C% B2 S; \ - SPIInitStruct.SPI_DataSize = SPI_DataSize_8b; //数据宽度" H& p, |- G, W. p6 I2 m
- SPIInitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //全双工
# a5 U+ Y0 X$ i) k$ P. X4 q; N - SPIInitStruct.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前
* {# Q* T: H% B. j6 p+ [2 v# J - SPIInitStruct.SPI_Mode = SPI_Mode_Master; //主机" x; D2 h$ L! ^( f( ~# \+ E; k
- SPIInitStruct.SPI_NSS = SPI_NSS_Soft; //软件模式2 B3 b2 S1 T3 ]
- SPI_Init(SPI1,&SPIInitStruct);
( C% C9 C6 x8 M2 R - //使能SPI
. |7 _ t. f" g$ M9 \6 G - SPI_Cmd(SPI1,ENABLE);
- e8 ~8 o% p! X1 ^5 i - 2 X/ }. M8 r/ c( p. t" ]9 _
- }
复制代码 9 L; B4 b5 q! i
|