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

【经验分享】STM32开发项目:硬件SPI的配置与使用

[复制链接]
STMCU小助手 发布时间:2022-4-14 10:31
项目背景
- M% j  u0 i4 ]! l& Q$ g% O/ u笔者在一个高速数据采集项目中进行开发时,遇到了快速频繁读取ADC数据的需求,此时的软件模拟SPI已经无法满足,只能尝试采用硬件SPI对ADC进行读写。6 r$ ~3 p3 B2 e  u

6 I/ B# l3 I, ~. [( b* e关于SPI外设
+ Z3 E3 J4 v& H, c0 {+ pSPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设备接口,是一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间,要求通讯速率较高的场合。
: n4 L& f/ Z$ Q6 m5 A
# v+ z- h* e0 v! e8 u3 K0 z2 X与I2C通讯不同,应用SPI接口的器件很多都对通讯速率有一定的要求,使用软件模拟SPI通讯有两个主要的缺点:1.增大MCU的负载,尤其是在大量数据高频收发时;2.数据传输速度无法得到控制与保证。
( H7 S) w& ^# A7 g" |; F
# H# y4 B5 R6 M  O硬件连接2 S& t# I/ _' z; U3 z
SPI通讯使用3条总线及片选线,3条总线分别为SCK、MOSI、MISO以及片选线为SS,它们的作用介绍如下:3 x: I* H0 |( C( @9 ?3 b2 @6 j
  n/ ?' n. O( P
SS( Slave Select):从设备选择信号线,常称为片选信号线,也称为 NSS、CS,以下用 NSS 表示。当有多个 SPI从设备与 SPI主机相连时,设备的其它信号线 SCK、MOSI及 MISO同时并联到相同的 SPI总线上,即无论有多少个从设备,都共同只使用这 3条总线;而每个从设备都有独立的这一条 NSS 信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。I2C 协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而 SPI 协议中没有设备地址,它使用 NSS 信号线来寻址,当主机要选择从设备时,把该从设备的 NSS 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI通讯。所以 SPI通讯以 NSS 线置低电平为开始信号,以 NSS 线被拉高作为结束信号。* ?. j! D2 h4 {& t9 Z! f
SCK (Serial Clock):时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如 STM32 的 SPI 时钟频率最大为fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。
3 f9 r' Y' K4 ~1 A* h9 w* p% b
% a# Q- b, y; SMOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。$ i$ Q& r3 L: o3 N

! d8 [) c/ o; T6 b: bMISO(Master Input,,Slave Output):主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。
9 V. Q& f6 O" N) D- M  D" o* Z$ g' f: ?7 G% b! l
AR16MWG9@I8[TOB6%[1U@IN.png
  u* O2 F9 @( j8 S; w$ R- v
7 m5 x2 _+ o2 U  B6 m  h8 `2 \5 q基本通讯过程
* f; q. E3 v( X2 f" S; a下图所示是一个SPI主机的通讯时序。NSS、SCK、MOSI 信号都由主机控制产生,而 MISO 的信号由从机产生,主机通过该信号线读取从机的数据。MOSI 与 MISO 的信号只在 NSS 为低电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据。( E' C$ O! ^' }3 l1 n" B( H4 d

: q2 M8 m7 D6 C5 _2 V# y/ ` 20200506175200389.png
# ?3 b4 O& `: B/ L
' m7 v9 J8 N0 u' x/ i4 s$ w& W在标号(1)处,NSS 信号线由高变低,是 SPI 通讯的起始信号。NSS 是每个从机各自独占的信号线,当从机在自己的 NSS 线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。在图中的标号(6)处,NSS 信号由低变高,是 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。
3 @) B' D/ ?# S$ O: j! t
$ [& J5 a3 y7 \SPI 使用 MOSI 及 MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。MOSI 及MISO 数据线在 SCK 的每个时钟周期传输一位数据,且数据输入输出是同时进行的。数据传输时,MSB 先行或 LSB 先行并没有作硬性规定,但要保证两个 SPI 通讯设备之间使用同样的协定,一般都会采用上图所示的 MSB 先行模式。1 ]/ r( x. k) ?6 H: a/ M
' Y  C/ h& u, t2 u/ _# D' i
观察图中的(2)~(5)标号处,MOSI 及 MISO 的数据在 SCK 的上升沿期间变化输出,在SCK 的下降沿时被采样。即在 SCK 的下降沿时刻,MOSI 及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效,MOSI及 MISO 为下一次表示数据做准备。SPI 每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制。
* {5 g9 W8 s+ A9 f: Z0 B# ]# N3 C
. J( s. L4 ]0 j& |# W+ v' |4 T0 a工作模式  l$ p9 h3 T) h" u/ k" l
SPI 一共有四种通讯模式,它们的主要区别是总线空闲时 SCK 的时钟状态以及数据采样时刻。为方便说明,在此引入“时钟极性 CPOL”和“时钟相位 CPHA”的概念。时钟极性 CPOL 是指 SPI 通讯设备处于空闲状态时,SCK 信号线的电平信号(即 SPI 通讯开始前、 NSS 线为高电平时 SCK 的状态)。CPOL=0 时, SCK 在空闲状态时为低电平,CPOL=1 时,则相反。时钟相位 CPHA 是指数据的采样的时刻,当 CPHA=0 时,MOSI 或 MISO 数据线上的信号将会在 SCK 时钟线的“奇数边沿”被采样。当 CPHA=1 时,数据线在 SCK 的“偶数边沿”采样。
; ~1 _, |( e- X+ c9 o( e" c4 V) k. V" [8 R- D% D; T
CPHA=0 时的 SPI 通讯模式:
0 l7 y/ {  m$ n
: s0 p& I7 E0 ]' B, g4 d6 g 20200506180753750.png . s- B6 R: {5 Z
- }, M% W& w" Y. X! q; ~; Q
CPHA=1 时的 SPI 通讯模式:% t9 U0 m; ?) \- k8 q5 N

1 |, a( C6 d1 m2 b7 j& L 20200506180851547.png
! ^1 C5 y8 @7 D% o$ d9 g$ U, k' k* |; v5 f6 o$ {+ R
由 CPOL 及 CPHA 的不同状态,SPI 分成了四种模式,如下表所示,主机与从机需要工作在相同的模式下才可以正常通讯,实际中采用较多的是“模式 0”与“模式 3”。
' }% k, c0 U) Z. T, D% y6 D6 H
6 i( J3 C- K* n9 k. i" b' V2 i+ v$ Z 5(]9FB{L9U5MVF%GL%_M769.png 3 s: p4 V% p! s  t: _

8 t3 _- B+ @. J' b配置流程
2 q3 `# }9 {! y4 [+ EGPIO端口功能配置:" T9 q2 f: t* w! z. K- \1 A6 o/ [
; l3 l' }; \( H  @: p
  1. void GPIO_Config()3 {2 F# V! Z6 z" _
  2. {* i$ n) v1 s( P. n! {
  3.         GPIO_InitTypeDef GPIO_InitStructure;
    ; C/ K5 U/ n2 T, ^
  4.         # w/ Z, U' d$ \3 w, C
  5.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);! s1 @4 e/ q" @: q$ k% w/ h: ~5 T
  6.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    + S' L# ^* l4 W; T8 v+ z
  7.         GPIO_AFIODeInit();: m+ X  y( u; M" l) K
  8. * k( E* S5 J7 E0 i8 K& a% a/ ^
  9.         //SPI-Clock:PA5 SPI-MISO:PA6 SPI-MOSI:PA71 y' _, U0 }! J+ C/ L
  10.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    5 g3 {. z! |+ X0 ^  |
  11.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  Z7 ~; S1 {( k& y( f! {
  12.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;, P9 z# j9 M7 M4 c1 O
  13.         GPIO_Init(GPIOA, &GPIO_InitStructure);0 c3 G3 Q1 ^4 D+ _7 s! \' u
  14. }
复制代码
) W& y1 n6 p7 e- \* T/ A  p$ G5 B
SPI外设配置:$ z9 T( M; G5 K# [* O
  f; h1 ~3 _& t& [% |
  1. void SPI_Config()
    . E  a4 D# q1 B1 h  D! [
  2. {
    ) P5 W, [  ^* G6 Z
  3.         SPI_InitTypeDef SPI_InitStructure;1 q$ |  B+ h/ k/ S. m% P
  4.         
    2 e% r- D; a: K& y- G; o0 e9 @
  5.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    ) c" p% Y  o8 a' e& T
  6.         ! A5 F8 T3 d( ?$ i. X
  7.         SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;* M& a' u4 ?) t0 @/ ^- G7 t
  8.         SPI_InitStructure.SPI_Mode = SPI_Mode_Master;& J3 F3 [& I% t- s) t4 o" o
  9.         SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;2 E3 h/ S9 j1 h& X. N0 V
  10.         SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    # I$ \7 N. _/ e* x. W% K  S. y8 A
  11.         SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    2 q+ L4 ^& |9 `$ f
  12.         SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    1 B3 D9 ^. Y; ]7 }
  13.         SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;" x! B$ r9 M% Y6 _" f) j3 \
  14.         SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;, U" O  E: B2 X5 x
  15.         SPI_InitStructure.SPI_CRCPolynomial = 7;/ o) E" V6 S; ~6 m- e
  16.         
    1 K9 R3 T1 t" R4 E
  17.         SPI_Init(SPI1, &SPI_InitStructure);
    . b; ]1 w$ T! ~5 K
  18. $ s/ U& c0 d! C  z# ^
  19. //        SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, ENABLE);5 y7 G7 Q/ h5 v1 `7 E
  20. //        SPI_I2S_ClearFlag(SPI1, SPI_I2S_IT_RXNE | SPI_I2S_IT_TXE);
    ! R6 b; n- V1 Z  b! d: @9 l
  21. }8 [+ b3 a& f5 U8 a) A$ K
复制代码
0 _0 o& a. ^3 v" i. ~9 G' C1 Q
在某处使能SPI:
) K1 ]" d  v0 p9 X  V# G4 q3 _& z: ~, x- k
  1.         SPI_Cmd(SPI1, ENABLE);
复制代码
% m- y3 p" P) G" X& \
如果使能了SPI中断,需要在NVIC中配置其中断的主从优先级。# P, w7 q, t: D" U, X
5 D$ H/ z4 z5 D5 Y4 G: M- {
特别注意
( D: q8 {2 s8 r/ a7 w8 E& P硬件SPI写入数据的时候记得读取,不然会一直读出0xFF。因为SPI为双线结构,只要时钟线有信号,两条线上都会有信号,如果只发送数据而不同步读取数据,会造成溢出标志OVR被置1。
( S2 \! q. D+ ]) P! G3 W/ X! {
: X7 F8 S+ B. f& k% J4 z" s. JOverrun flag (OVR)
, s! z8 n7 N. z% b. A: l  iThis flag is set when data are received and the previous data have not yet been read from SPI_DR. As a result, the incoming data are lost. An interrupt may be generated if the ERRIE bit is set in SPI_CR2.( I. `( }( b4 W/ Z  Y1 K1 H
In this case, the receive buffer contents are not updated with the newly received data from the transmitter device. A read operation to the SPI_DR register returns the previous correctly received data. All other subsequently transmitted half-words are lost.
, _7 B  P+ P: r1 g- ~Clearing the OVR bit is done by a read operation on the SPI_DR register followed by a read access to the SPI_SR register.% v" U! h" C9 e. |/ r
$ H  e% T1 u. m* ?8 k8 q- g# t+ H; i  d
使用下面的函数可以通过硬件SPI安全地读写数据:
( U8 G- ^6 B) w2 w' X, \; ^3 e1 I# P! x! @' D) e) t
  1. uint8_t SPI_I2S_ReadWriteData(uint8_t Tdata)
    # R6 V- O/ s+ C& E/ `. L
  2. {
    ( b2 A& C# u/ c4 Y! k$ K/ z
  3.         while ((SPI1->SR & SPI_I2S_FLAG_TXE) == (uint16_t) RESET)
    & U' }+ l, N0 b0 C( \
  4.                 ;
    5 [; S0 u8 }& i5 W/ f: {
  5.         SPI1->DR = Tdata;
    5 w! R( z, {5 H6 `
  6.         while ((SPI1->SR & SPI_I2S_FLAG_RXNE) == (uint16_t) RESET)
    2 G6 N4 j2 r  u) a
  7.                 ;
    . ~  x" ?' c( U1 m  ~
  8.         ///注意:接收区满了要及时读出来,否则将不能接收下一个字节数据
    ) u# B5 D9 r: F( N" b
  9.         return SPI1->DR;  v+ R3 j2 ?' v. y3 \
  10. }
复制代码

% X; c' Z, a8 r
# Y6 [, C5 X# y" e% j: B6 [( A" y" z9 k& E% _3 X: @- u* v
7 r& @% {2 }& }! K
赞 收藏 评论0 发布时间:2022-4-14 10:31

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版