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

STM32F4系列-SPI通讯应用-十

[复制链接]
STMCU小助手 发布时间:2022-10-24 19:02
这一章来说一说SPI通信,同样的很多模块也用到了SPI通信,比如0.96寸的OLED模块。玩过单片机的小伙伴都知道OLED有4针的也有7针的,4针的就是IIC通信,7针的就是SPI通信。
" ?" D. a. k8 p0 n7 z

, O$ A# b0 k; i* [1 Z
一、 SPI 简介
  SPI 是英语 Serial Peripheral interface 的缩写,顾名思义就是串行外围设备接口。是 Motorola首先在其 MC68HCXX 系列处理器上定义的。SPI 接口主要应用在 EEPROM,FLASH,实时时钟,AD 转换器,还有数字信号处理器和数字信号解码器之间。SPI是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为 PCB 的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,STM32 也有 SPI 接口。SPI通讯使用 3 条总线及片选线,3条总线分别为 SCK、MOSI、MISO、SS。5 c# ^/ ^( X8 v, y# j1 \6 i2 P' ~+ H

; U$ Q4 J' q" x7 y1 j
微信图片_20221024184115.png ' J+ t& C1 `5 y, y! o& n
  (1) SCK (Serial Clock):时钟信号线,用于通讯数据同步。它由通讯主机STM32产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如STM32的SPI 时钟频率最大为fpclk /2,两个设备之间通讯时,通讯速率受限于低速设备。! o* `. w6 x9 `+ z+ v% A
  (2) MOSI (Master Output, Slave Input):主设备STM32输出/从设备输入引脚。STM32的数据从这条信号线输出,从机由这条信号线读入STM32发送的数据,即这条线上数据的方向为STM32到从机。
5 o* L9 ]6 v. E+ r  (3) MISO(Master Input,,Slave Output):主设备STM32输入/从设备输出引脚。STM32从这条信号线读入数据,从机的数据由这条信号线输出到STM32,即在这条线上数据的方向为从机到STM32。. M1 ^% p6 n7 a( t+ X6 S& ~
  (4) SS( Slave Select):片选信号线,也称为 NSS、CS。当有多个SPI 从设备与 SPI 主机STM32相连时,设备的其它信号线 SCK、MOSI及 MISO同时并联到相同的 SPI总线上,即无论有多少个从设备,都共同只使用这 3 条总线;而每个从设备都有独立的这一条 NSS 信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。I2C 协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而 SPI 协议中没有设备地址,它使用 NSS 信号线来寻址,当主机要选择从设备时,把该从设备的 NSS 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI 通讯。所以SPI通讯以 NSS 线置低电平为开始信号,以 NSS 线被拉高作为结束信号。4 t* E  a9 \$ l4 E: s9 U+ r/ p
  m; ^2 j: \& @# x
二、 具体代码编写
" E% i- N1 X5 x' X1 G7 H0 k7 x1 T1.SPI引脚初始化
  我们使用 STM32 的 SPI1 的主模式,第一步就要是能 SPI1 的时钟,SPI1 的时钟通过 APB2ENR 的第12位来设置。其次要设置 SPI1 的相关引脚为复用输出,这样才会连接到 SPI1 上否则这些 IO 口还是默认的状态,也就是标准输入输出口。这里我们使用的是 PA5、PA6、PA7 这 3 个,所以设置这三个为复用 IO。
  1. void SPI1_Init(void)) N' M  x$ g9 |2 F+ J
  2. {& P, Z/ m4 n! H4 l! x0 z  w
  3. GPIO_InitTypeDef GPIO_InitStructure;    - I4 e$ }7 n) n
  4. RCC_APB2PeriphClockCmd(    RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE );  & K' H+ @1 M3 j. O4 p
  5. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;. o' M/ A2 j* t# @; G2 @
  6. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
    " N6 \( Y' K( x4 G; X( w$ v2 [
  7. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    % v% L" s; T. e' w, B" e( x: d
  8. GPIO_Init(GPIOA, &GPIO_InitStructure);   
    , s) @' H" R4 f+ y0 J7 c$ Z- m! ^. K2 ]
  9. GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);
    + W; n0 g2 d* U2 ^2 N
  10. }
复制代码

# u/ y8 h) a4 c9 _$ _' Q1 u2.设置SPI1工作模式
  接下来我们要初始化 SPI1,这在库函数中是通过 SPI_Init 函数来实现的,和STM32的其他外设一样同样需要配置这些参数,使用中我们不需要关心这些具体的设置。设置 SPI1 为主机模式,设置数据格式为 8 位,然设置 SCK 时钟极性及采样方式。并设置 SPI1 的时钟频率(最大 18Mhz),以及数据的格式(MSB 在前还是LSB 在前)。这在库函数中是通过 SPI_Init 函数来实现的。
SPI_InitTypeDef  SPI_InitStructure;$ R" h+ M( s2 h7 h5 Y
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工$ g& f5 \3 ~8 E/ p1 y% b2 m: `" x
  1. SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//设置SPI工作模式:设置为主SPI
    , R1 C2 |1 F1 F8 _# M0 ~3 G
  2. SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//设置SPI的数据大小:SPI发送接收8位帧结构
    . i; i1 r- G  d& P
  3. SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//选择了串行时钟的稳态:时钟悬空高
    3 s/ T! m& ^; X3 p: ^  {
  4. SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//数据捕获于第二个时钟沿5 R3 M5 S, x9 d# t% M  B0 k& K0 `
  5. SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制* @! `5 A2 u: v( l! L! N" b( u4 [( a
  6. SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//定义波特率预分频的值:波特率预分频值为256
    8 [! V2 O# J5 N  c: v. v$ d
  7. SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始, q+ u7 M. |5 A& Q- H/ v
  8. SPI_InitStructure.SPI_CRCPolynomial = 7;//CRC值计算的多项式
    1 Y; E  C) n4 U6 P+ l, P
  9. SPI_Init(SPI1, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
复制代码

: B* A$ Z% f/ q9 a7 B0 V+ I3.使能SPI1
初始化完成之后接下来是要使能 SPI1 通信了,在使能 SPI1 之后,我们就可以开始 SPI 通讯了。
  1. SPI_Cmd(SPI1, ENABLE); //使能SPI外设
复制代码

5 H" Q' ?& K. _# V: y2 k; `2 M2 ~6 W" ^/ H! Z, G0 N
4.使用SPI读写一个字节
' O0 _6 r, E" R1 S$ q6 P* r' ~
  1. u8 SPI1_ReadWriteByte(u8 TxData)
    - |' V: U2 c6 g3 i0 I
  2. {        8 ?" b$ z$ V, B9 X, V" _
  3.     u8 retry=0;                 9 h3 X2 d- R$ R; {" l8 f! S
  4.     while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET)//检查指定的SPI标志位设置与否:发送缓存空标志位
    * g1 B# I# s% A$ a5 k; B' v
  5.     {
    6 t* K( ^7 X+ m) V: p+ M* |1 ~
  6.         retry++;
    ; o3 K. @/ z5 ?2 N5 ]; X5 ^
  7.         if(retry>200)return 0;- R- _2 a8 ^* `8 j
  8.     }            
    - Q0 G8 s/ _3 t% N; I, o- J/ \6 P
  9.     SPI_I2S_SendData(SPI1, TxData);//通过外设SPIx发送一个数据: z2 E1 f3 w" ~
  10.     retry=0;) v& m- U+ h% u  f4 X9 x
  11.     while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)//检查指定的SPI标志位设置与否:接受缓存非空标志位) }) ^+ r( W: v
  12.     {
    4 ^& b/ U, R2 L1 U& z, O' {
  13.         retry++;) F! b& K! k- R8 n" Q" i
  14.         if(retry>200)return 0;
    : S/ }  H' n  h: t) ^
  15.     }                              
    % k, U* G0 j% ~/ H9 c* w' R: @
  16.     return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据                     
    ! Z" w/ B, N8 [, y3 c3 e
  17. }
复制代码

% ]* p. u9 O/ m% ?/ g# a- Y# d
  本函数中不包含 SPI 起始和停止信号,只是收发的主要过程,所以在调用本函数前后要做好起始和停止信号的操作;
- P/ a1 ]) O  c  M/ H! J  循环调用库函数SPI_I2S_GetFlagStatus 检测事件,若检测到事件,则进入通讯的下一阶段,若未检测到事件则停留在此处一直检测,当检测200次都还没等待到事件则认为通讯失败,退出通讯;
% M+ P& w9 X8 I8 b0 p$ w  通过检测TXE标志,获取发送缓冲区的状态,若发送缓冲区为空,则表示可能存在的上一个数据已经发送完毕;等待至发送缓冲区为空后,调用库函数 SPI_I2S_SendData 把要发送的数据“TxData”写入到 SPI的数据寄存器 DR,写入 SPI数据寄存器的数据会存储到发送缓冲区,由 SPI外设发送出去;
; P0 |1 m# U* K. R7 Y  L# n: s  写入完毕后通过循环200次等待RXNE 事件,即接收缓冲区非空事件。由于 SPI 双线全双工模式下MOSI 与 MISO 数据传输是同步的,当接收缓冲区非空时,表示上面的数据发送完毕,且接收缓冲区也收到新的数据;+ J1 p, R, h, z0 A9 u
  等待至接收缓冲区非空时,通过调用库函数 SPI_I2S_ReceiveData 读取 SPI 的数据寄存器 DR,就可以获取接收缓冲区中的新数据了。代码中使用关键字“return”把接收到的这个数据作为SPI_SendByte 函数的返回值。
- V2 {3 s! k3 f1 G: S: u, l  以上的步骤我们就搞定了SPI 的基本收发单元,还需要了解如何对FLASH 芯片进行读写。FLASH 芯片自定义了很多指令,我们通过控制 STM32 利用 SPI 总线向 FLASH 芯片发送指令,FLASH芯片收到后就会执行相应的操作。而这些指令,对主机端(STM32)来说,只是它遵守最基本的 SPI 通讯协议发送出的数据,但在设备端(FLASH 芯片)把这些数据解释成不同的意义,所以才成为指令。查看FLASH 芯片的数据手册可了解各种它定义的各种指令的功能及指令格式。这里我就不过多的将写了。
( h& i5 b. a! F+ I8 S

, [' `* C0 D2 l& _转载自: 果果小师弟
: x4 ]" o% m5 P& M5 g1 f6 K; k3 p1 m! m5 F, \
赞 收藏 评论0 发布时间:2022-10-24 19:02

举报

0个回答

所属标签

相似技术帖

官网相关资源

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