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

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

[复制链接]
STMCU小助手 发布时间:2022-4-14 10:31
项目背景1 `& m" K% y; W  X4 U
笔者在一个高速数据采集项目中进行开发时,遇到了快速频繁读取ADC数据的需求,此时的软件模拟SPI已经无法满足,只能尝试采用硬件SPI对ADC进行读写。
7 I6 N! `! C  A9 w; r! L1 k4 I5 n1 P8 |; Y8 [' N
关于SPI外设; C! @3 O# B& R. x; U; i
SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设备接口,是一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间,要求通讯速率较高的场合。
8 J4 c$ ~5 W0 ]9 ]- \" a# Y3 ^0 S& B  @( x0 U/ G/ j, ]
与I2C通讯不同,应用SPI接口的器件很多都对通讯速率有一定的要求,使用软件模拟SPI通讯有两个主要的缺点:1.增大MCU的负载,尤其是在大量数据高频收发时;2.数据传输速度无法得到控制与保证。
8 U8 Q) i' d, N* H- O. u" J/ {1 b* K' Z0 E+ V. ?7 r% L
硬件连接4 P1 b; [; {9 X- q0 a. _. N
SPI通讯使用3条总线及片选线,3条总线分别为SCK、MOSI、MISO以及片选线为SS,它们的作用介绍如下:
' Q! R' q2 H& k. c& p2 J
9 |: q1 y3 ]# p6 s2 j4 O& v8 `! bSS( Slave Select):从设备选择信号线,常称为片选信号线,也称为 NSS、CS,以下用 NSS 表示。当有多个 SPI从设备与 SPI主机相连时,设备的其它信号线 SCK、MOSI及 MISO同时并联到相同的 SPI总线上,即无论有多少个从设备,都共同只使用这 3条总线;而每个从设备都有独立的这一条 NSS 信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。I2C 协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而 SPI 协议中没有设备地址,它使用 NSS 信号线来寻址,当主机要选择从设备时,把该从设备的 NSS 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI通讯。所以 SPI通讯以 NSS 线置低电平为开始信号,以 NSS 线被拉高作为结束信号。, n, y+ c$ X* {3 \4 c# _9 g
SCK (Serial Clock):时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如 STM32 的 SPI 时钟频率最大为fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。; ~* h3 U- H! w: v

) {% \, o2 [; l# g) P6 ~9 T  F! _MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。9 J. ]  q6 i' I

# Q! N* O4 ^9 h& i) A' w% [MISO(Master Input,,Slave Output):主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。4 K- b% P, g1 n) Z  `' e: ?
: D; u; G7 g3 j9 o! |. w
AR16MWG9@I8[TOB6%[1U@IN.png ; m; r% R. Q2 z+ R' B  z# k' f

6 a# `; S7 A6 z7 U( K+ N基本通讯过程" x+ }, Q1 y, o0 p# o% Y( y
下图所示是一个SPI主机的通讯时序。NSS、SCK、MOSI 信号都由主机控制产生,而 MISO 的信号由从机产生,主机通过该信号线读取从机的数据。MOSI 与 MISO 的信号只在 NSS 为低电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据。  j: S/ N8 q' v  B9 o$ M
8 e& K% T$ r9 [, ^6 H# C( Q
20200506175200389.png 0 F+ y: W0 f$ C$ q2 W: n2 [
! G% Y2 L# H% N" W
在标号(1)处,NSS 信号线由高变低,是 SPI 通讯的起始信号。NSS 是每个从机各自独占的信号线,当从机在自己的 NSS 线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。在图中的标号(6)处,NSS 信号由低变高,是 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。0 i% |# t4 k  R* N% N( j- w- k
  O8 w7 E$ d# j& i$ z/ ~
SPI 使用 MOSI 及 MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。MOSI 及MISO 数据线在 SCK 的每个时钟周期传输一位数据,且数据输入输出是同时进行的。数据传输时,MSB 先行或 LSB 先行并没有作硬性规定,但要保证两个 SPI 通讯设备之间使用同样的协定,一般都会采用上图所示的 MSB 先行模式。
+ t: u8 F2 o+ c. U1 z8 g
" j5 z$ |3 N& c* ]5 x( h观察图中的(2)~(5)标号处,MOSI 及 MISO 的数据在 SCK 的上升沿期间变化输出,在SCK 的下降沿时被采样。即在 SCK 的下降沿时刻,MOSI 及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效,MOSI及 MISO 为下一次表示数据做准备。SPI 每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制。
* N3 T9 s% e/ a* M' J
' |  G. Z/ C' l+ p工作模式' v* O# O) t8 P/ {: m) d, e0 D) X* [
SPI 一共有四种通讯模式,它们的主要区别是总线空闲时 SCK 的时钟状态以及数据采样时刻。为方便说明,在此引入“时钟极性 CPOL”和“时钟相位 CPHA”的概念。时钟极性 CPOL 是指 SPI 通讯设备处于空闲状态时,SCK 信号线的电平信号(即 SPI 通讯开始前、 NSS 线为高电平时 SCK 的状态)。CPOL=0 时, SCK 在空闲状态时为低电平,CPOL=1 时,则相反。时钟相位 CPHA 是指数据的采样的时刻,当 CPHA=0 时,MOSI 或 MISO 数据线上的信号将会在 SCK 时钟线的“奇数边沿”被采样。当 CPHA=1 时,数据线在 SCK 的“偶数边沿”采样。: r8 b4 |. G+ b; n% s
7 ]# Y; U+ Z  W& X4 H% @. n5 @
CPHA=0 时的 SPI 通讯模式:
# q; d# W5 t2 `2 q* |6 a: K3 F, f( `1 o. [$ F: s, e7 X) ~
20200506180753750.png
* {$ s6 r! t- T( g6 m# c
( G) Y" R9 s. S* s+ XCPHA=1 时的 SPI 通讯模式:
0 N* m2 }9 Y" y$ Y$ h& k5 {  D2 Y- W3 `( n
20200506180851547.png . X" v5 ?$ k! |+ R% D

% q4 N0 U7 |5 R- o5 W由 CPOL 及 CPHA 的不同状态,SPI 分成了四种模式,如下表所示,主机与从机需要工作在相同的模式下才可以正常通讯,实际中采用较多的是“模式 0”与“模式 3”。& n0 h" r0 M  D3 G! w
) Y" p7 j% G' |  X# ?0 o
5(]9FB{L9U5MVF%GL%_M769.png
% n0 l* N8 N! M% A: I6 v6 i
% a7 }$ D! }; t+ l) r配置流程
/ ~. J" p5 d- D: f5 B* U& [( j0 i; lGPIO端口功能配置:
$ d9 R8 F( }2 |6 u7 ?
3 E! s2 V4 z( L0 x# Y  p
  1. void GPIO_Config()( u- k( |- J2 v8 ?, H% a
  2. {
    / g. v" _( v  Q
  3.         GPIO_InitTypeDef GPIO_InitStructure;
    4 Q8 B( {+ t- A* K
  4.         
    + Z, o# y5 L2 D" ?0 K
  5.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    - o, K! B3 b8 j* {
  6.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);1 o2 Y* W: h/ i5 M1 A
  7.         GPIO_AFIODeInit();: V/ `$ d, m) h, Q" z

  8. - @+ X% v$ m6 Z- e
  9.         //SPI-Clock:PA5 SPI-MISO:PA6 SPI-MOSI:PA7
    * p; T" y; S! z
  10.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    0 j# G* U+ a: i4 }+ C& p
  11.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;7 k- b8 l" l+ L! P! z
  12.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;& u/ |/ e( J2 o2 d  E# Q
  13.         GPIO_Init(GPIOA, &GPIO_InitStructure);
    + N! l* c- G/ m* c9 l# b
  14. }
复制代码

) o) [& M/ d3 mSPI外设配置:( \4 n3 B. D9 @( ]5 {7 C% v& x9 O
5 g' Q9 r4 ^4 [* p- l4 r' C4 k2 H3 N
  1. void SPI_Config()
    % U& ~2 D+ [/ b8 U7 h
  2. {: o/ G) k, _* G1 r6 ]* J
  3.         SPI_InitTypeDef SPI_InitStructure;
    % B- I1 R4 B+ {. T" `, m
  4.         
    : Z$ {5 m' q, C6 e: }: g0 Y  f
  5.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    8 N' N+ n! B) Y" Y; C
  6.         
    7 c3 g, I& k7 B9 T1 D2 ?2 U9 P
  7.         SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    3 m! D8 n% K2 E# B2 z7 E
  8.         SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    3 x- L0 E/ ]9 j# ?' L* ~: X2 ~% q
  9.         SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    7 e) L! R, Y% L" c/ r
  10.         SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;! ]# r+ J" s- X- ~3 e, a- h
  11.         SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    , N5 H% J" U/ J* ^
  12.         SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;; O5 `$ J. J' F9 O" h! z) x2 p. `
  13.         SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;4 S9 f( r, @) ^& x& f6 f8 u, L
  14.         SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;9 t& p+ |: g% K& A) ]
  15.         SPI_InitStructure.SPI_CRCPolynomial = 7;
    ' r6 a- J- H! ^0 \* e; C( z' U
  16.         
    $ _5 E1 t! @- K7 Q6 ]
  17.         SPI_Init(SPI1, &SPI_InitStructure);! V; R2 j! }8 o/ u

  18. 4 I# V# p$ B) _9 {
  19. //        SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, ENABLE);4 t  B4 B3 E4 r7 A  {& i
  20. //        SPI_I2S_ClearFlag(SPI1, SPI_I2S_IT_RXNE | SPI_I2S_IT_TXE);3 @3 Q' P5 a& J3 }
  21. }
    ) k. d& ?& z3 Y. Z# i
复制代码

  L( B& a5 C+ r% ^' H% e: L在某处使能SPI:
" X# {5 ~  W- Q6 |8 W* H& }- |9 z& t' R& v1 s6 k: j& ~
  1.         SPI_Cmd(SPI1, ENABLE);
复制代码

2 B' i8 T3 P, g7 G如果使能了SPI中断,需要在NVIC中配置其中断的主从优先级。9 `1 `: Q$ B- P

4 ^4 R( a* j* f: [, A* Z特别注意6 c: h) v/ t3 s. w6 g9 _
硬件SPI写入数据的时候记得读取,不然会一直读出0xFF。因为SPI为双线结构,只要时钟线有信号,两条线上都会有信号,如果只发送数据而不同步读取数据,会造成溢出标志OVR被置1。0 b7 G6 c: g, a' z2 z* B  t% L5 D

9 ~7 P% I( s5 x5 m+ e$ Y4 yOverrun flag (OVR)
" F, E' Y/ U7 y  ^This 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.& D1 F- P4 x/ N* S7 r0 B
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.
1 _0 h  j* K, qClearing the OVR bit is done by a read operation on the SPI_DR register followed by a read access to the SPI_SR register." E2 c  \2 \& v: i9 C' v" I( U

! P: S& [! Z% J; ]. G使用下面的函数可以通过硬件SPI安全地读写数据:  O* M" X1 Z' ?
, h2 Y0 v! b! |% w/ k1 E
  1. uint8_t SPI_I2S_ReadWriteData(uint8_t Tdata)
    + e5 T6 t, Z6 G
  2. {* V' h, P$ t5 x2 a/ q- Z# M
  3.         while ((SPI1->SR & SPI_I2S_FLAG_TXE) == (uint16_t) RESET)2 {) D* x9 r2 t- n
  4.                 ;
    % E, K0 E0 e9 `1 v+ z2 a% {0 H8 x; F
  5.         SPI1->DR = Tdata;! H! Z% g% p' Z3 {+ T9 w
  6.         while ((SPI1->SR & SPI_I2S_FLAG_RXNE) == (uint16_t) RESET)# r: ?9 z1 f  k$ L- b  K
  7.                 ;) l2 f6 _( ^& T% d+ {, c$ v: J
  8.         ///注意:接收区满了要及时读出来,否则将不能接收下一个字节数据
    . _0 E: _9 L: C7 V9 ~) U' ^$ x1 w+ H: R
  9.         return SPI1->DR;
    & S  }% D) t( {7 u! f7 K
  10. }
复制代码

, {" {6 j4 Y3 [! Q- i/ a' s9 H+ n: o$ F- w  o1 q" G
3 r! k. ]. K* h. f# x

$ n4 i0 ^3 ], S/ y1 k, @3 K/ M/ ^
赞 收藏 评论0 发布时间:2022-4-14 10:31

举报

0个回答

所属标签

相似技术帖

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