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

【经验分享】STM32F7实现SPI读写,读取W25Q16型号

[复制链接]
STMCU小助手 发布时间:2021-12-12 21:44
SPI协议的原理,网上大把的资料可以找到,这里记录一下SPI的初始化过程,以即以读取W25Q16型号为例的一个简单的SPI读写过程。
6 J/ W+ n9 g/ T$ _. [CubeMX配置:
0 S1 K6 Z/ }) g' h6 K# B
! g/ G8 U. U7 J6 ~
20200423110312574.png
' b. D2 A5 U5 I

. k. a1 ?2 z, f% y: S6 l% N8 tSPI模式
* _7 }& e( ?% B. `2 s有只发送、只接收、半双工和全双工模式;2 Y. `$ n: s% I9 Q) A
根据自己的需求,和SPI设备支持的类型,这里选择全双工的SPI;) u; k( x4 x# C0 h, X% `) Z

7 b' n. r- {0 S. M& g7 {8 o片选信号
% ^  g5 G, h; Y, M: u/ }+ h因为我们这个SPI总线上会挂载多个从设备,而且只有一个主机,所以,这里禁用硬件片选,程序里面读写之前使用软件进行片选;2 ]& o. a/ S. j# [2 A- ?
2 i* Q7 O1 N, d7 s
帧格式
$ }% h) e1 w+ ]  n6 e# y. m% i这里选择Motorola帧格式;- s. [3 }: b1 Q8 v
选择TI格式不能设置CPOL和CPHA,而且必须要开启硬件NSS这里才能选择TI模式。
/ Q2 o' D- l. d& E博主在网上搜了一些关于TI的SSP协议相关的资料,只有简简单单的一句:SSP总线兼容SPI,SSI 和Microwire 总线的接口。
( b8 q6 v+ ~" J. A0 o1 Y/ j. @博主又搜了以下Microwire协议,和SPI一样,只不过片选信号是高电平有效。
9 p5 ?! ]) K, b. y! O& Q5 ^/ }这是手册里关于TI模式的说明部分和波形图:: N2 h6 T# H4 P# \6 ]; J
3 O% ~. r$ ]7 H1 l; m
20200423112839303.png
) z7 X; N6 {, P" P% |- S5 |7 X
( D. A* L2 Q$ y9 Y, f& S: f  _
20200423112906213.png
$ I, ^! _/ J. @+ ^

5 t& m; R( j6 P也不清楚这个SCK的触发信号是干嘛的,所以这里老老实实选择Motorola就行了。: J; w* J6 W9 }0 j5 {$ y0 L1 B
2 e# U  a6 O0 S0 x
数据长度4 l& C7 m9 P% I+ n: ]  Q
4-16bit可选,根据实际,选择8bit;) c1 q+ K+ o3 E

, l4 u2 `9 @9 U2 \' c数据传输模式3 U7 w! `! K+ a( H$ `, W$ q
第一个传输的bit是高位还是低位,这里根据设备来选择,好像MSB(先发送高位)的比较多,注意这里的会同时设置发送和接收,如果设置错了,发送的指令对方就无法识别,可能收到的就是0xFF这种值。
! O% O( Z6 i2 T8 R( C+ W$ h7 S5 `+ l. z; F5 p6 b
SPI速率7 L( b& _+ t1 `3 M5 H
SPI的波特率是由时钟分频得到的,CubeMX很方便的还帮我们计算出来了时钟速率,可以根据自己的需求设置。
: ^0 ?  C8 O2 F. n, D0 G, b* a6 g1 L2 i, F# M, T
时钟极性和时钟相位
% p2 o4 K% V/ q0 S$ \, C根据极性和相位的不同,可以有4种不同的工作模式,这个根据芯片手册支持的模式选择,注意,一条总线上尽量挂载支持相通模式的设备,这样就不用每次在设备切换之前重新配置SPI外设。
6 X& m9 ]! H! ?) o  y  m) _6 _5 e6 ]6 d
CRC校验( m: g" F0 s9 n! A8 l8 f
暂时还没研究到这里……4 X6 _% P8 g. ^
) m& q3 w" L+ D
NSS脉冲

! a/ _4 E1 W) D  T) _% A参考SPI的NSS 脉冲模式的作用,这里用不到;. k: x0 \! `, Z: E& {4 Y8 R
. Z% a% t4 v% W0 ~& @+ [3 P  P
NSS信号类型

) F) Z+ z" R6 D) f! c因为前面禁用了硬件片选,所以这里只能设置位软件模式。' E8 o( q6 s# l6 s0 g1 f/ U" Z

0 |7 m. E0 T  u) n' {/ E% J) \除此意外,还需要把连接硬件片选信号的GPIO,设置为输出模式。8 V( ^9 a3 `0 B' p9 Q, a
CubeMX生成的代码如下:
0 E# ^2 Q# u! ]# o. U' ?- ]* K
  1. SPI_HandleTypeDef SPI3_Handler;  //SPI2句柄
    2 {& J: K7 {  m3 v
  2. " t6 p& g: B- n
  3. void SPI3_Init(void)
    ! q+ Y2 Q: c! z2 k! M8 ^3 K
  4. {
    4 U& @- D; ?; E9 J! L
  5.     SPI3_Handler.Instance = SPI3;
    1 t0 i& N; `$ j: u
  6.     SPI3_Handler.Init.Mode = SPI_MODE_MASTER;% e( `- L9 R& J1 y! H. V9 M5 o
  7.     SPI3_Handler.Init.Direction = SPI_DIRECTION_2LINES;
    8 K4 ^$ M5 O0 q$ s# f% |
  8.     SPI3_Handler.Init.DataSize = SPI_DATASIZE_8BIT;
    6 F  _; J* d( k  ?
  9.     SPI3_Handler.Init.CLKPolarity = SPI_POLARITY_LOW;6 [& T- j" }5 T2 ]
  10.     SPI3_Handler.Init.CLKPhase = SPI_PHASE_1EDGE;
    ) O7 |6 }/ d4 }, _* ^
  11.     SPI3_Handler.Init.NSS = SPI_NSS_SOFT;
    & F% T- v4 q9 [6 R4 o
  12.     SPI3_Handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;8 w6 R$ m' _$ `0 T4 A  Z  a
  13.     SPI3_Handler.Init.FirstBit = SPI_FIRSTBIT_MSB;* k: `* J/ d% X
  14.     SPI3_Handler.Init.TIMode = SPI_TIMODE_DISABLE;+ @# M+ Y# n% P- ^" P& p
  15.     SPI3_Handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    7 j2 @# j; c( E, o' a3 x" u9 v
  16.     SPI3_Handler.Init.CRCPolynomial = 7;
    9 S$ |% Y  J; f
  17.     SPI3_Handler.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
    5 w% C" z  f+ P5 S( h
  18.     SPI3_Handler.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
    - U+ u, |. l2 b
  19.     HAL_SPI_Init(&SPI3_Handler);0 v) y8 \  p' C
  20. 1 ~, ]- `- {6 I9 G* J
  21.     __HAL_SPI_ENABLE(&SPI3_Handler);               
    / G8 d6 E. U& J1 H" s
  22. }: Q) W( U0 e, Q+ S9 L2 r

  23. $ X  X; ~; u6 V- S6 L: v
  24. void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
    2 l4 B; r8 W; F3 D+ c+ c0 i
  25. {
    7 Q9 b& g( l" @* a
  26.   GPIO_InitTypeDef GPIO_InitStruct = {0};: ~9 D/ A1 `6 k2 ^1 L5 s2 n4 Q4 \
  27.   if(hspi->Instance==SPI3)' v1 N' M% A! S, H# ?! U  y
  28.   {
    ' A- {& q4 L9 @; z) x" T

  29. 5 [1 j6 U# g8 q
  30.     __HAL_RCC_SPI3_CLK_ENABLE();
    : a& k8 w6 f) r2 N, P

  31. $ r/ k  c# s8 ~" ], P' u
  32.     __HAL_RCC_GPIOB_CLK_ENABLE();9 O5 N* F/ ?4 h+ b) Q6 e
  33. ; X1 }) P) \/ }' M- x
  34.     GPIO_InitStruct.Pin = GPIO_PIN_2;
    $ ~0 a/ n: q% g- d) L5 z
  35.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    ; m1 D# N: `3 z. A
  36.     GPIO_InitStruct.Pull = GPIO_NOPULL;
    $ s' m9 s5 Q6 Z8 f9 R8 n3 n
  37.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;5 H. g" Y# j8 y7 o% j' ^
  38.     GPIO_InitStruct.Alternate = GPIO_AF7_SPI3;
    + d/ Z+ _4 P4 k8 Z* Z( P
  39.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);" A  v+ ^- t0 l8 k9 W
  40. : K$ ~0 ?1 I, s$ K9 Z
  41.     GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4;
    5 l4 I8 P3 Y5 o, v1 t/ A, e' v( d2 R
  42.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    ' Z; L1 D6 ^+ n) D6 j& ?' N
  43.     GPIO_InitStruct.Pull = GPIO_NOPULL;
    ! K* Q) f: m8 J- h/ f' z
  44.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;( d$ Y5 F. O8 V0 D3 [* G; L' M, T( U
  45.     GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
    + c7 i" X8 l5 q! m! M
  46.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);& r* E* u. H* n3 s2 C
  47.   }
    , I6 W0 J/ |* U0 s( P
  48. }
    3 W( l, R  B3 [1 u- ]! ^1 R

  49. " h$ O, I" k! A6 ?( p: F
  50. - R: B- {5 Z3 Y  w  y% O; @
  51. static void MX_GPIO_Init(void)
    1 ~/ e' T8 E1 u) ^4 d
  52. {5 \! D- f% z4 s1 K- i- y$ i2 r
  53.   GPIO_InitTypeDef GPIO_InitStruct = {0};
    % E$ J$ V3 s; Z9 K
  54. 7 |3 v0 R% E& n; J0 J4 d( \
  55.   /* GPIO Ports Clock Enable */* e( \  T' \8 w1 j+ f* v, z
  56.   __HAL_RCC_GPIOA_CLK_ENABLE();8 F/ Z1 Q5 E7 g8 v
  57. % {  U3 R# e+ d4 x; [8 h/ g- s
  58.   /*Configure GPIO pin Output Level */
    9 C' T! {4 F9 n4 o1 s
  59.   HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    5 s9 [! {+ d& }7 D; v+ {
  60. ; g8 s2 T, {* I- p2 S9 F
  61.   /*Configure GPIO pin : PA4 */; q" e2 E4 Z$ K4 Z; A8 t/ w
  62.   GPIO_InitStruct.Pin = GPIO_PIN_4;6 _( {( ?0 B! r5 B* @
  63.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;+ c2 C* T2 a4 D
  64.   GPIO_InitStruct.Pull = GPIO_NOPULL;7 X: X6 f6 j+ S: M
  65.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    & B7 r6 [% D6 T1 e5 i9 T' j7 K
  66.   HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);7 g% L' v5 _, Z; p6 ^
  67. 8 G7 U" a) \: F* @4 U0 e
  68. }
复制代码

3 c4 B, x) ?! E8 Q$ i  `% a同一总线上不同设备,工作在不同模式的时候,速率可能不一样,需要一个重新设置速率的函数:
! D. X$ N* y. W" F5 u4 E" `8 V! ^2 Q7 q# C8 \: N' K/ `7 Q2 j# z8 R
  1. void SPI3_SetSpeed(u8 SPI_BaudRatePrescaler)
    3 F6 B8 i2 l$ @# C7 j
  2. {
    2 }/ N* h$ y2 E: M7 }' W! j
  3.     assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性+ W# `& `- d9 @; h  }+ @) G2 g
  4.     __HAL_SPI_DISABLE(&SPI3_Handler);            //关闭SPI; U% b* T0 P! X) T4 g0 q
  5.     SPI3_Handler.Instance->CR1 &= 0XFFC7;        //位3-5清零,用来设置波特率
    9 d2 z% x; _# n% c, c5 N5 y6 P
  6.     SPI3_Handler.Instance->CR1 |= SPI_BaudRatePrescaler; //设置SPI速度* M8 D" L8 O$ y$ a' Q5 H
  7.     __HAL_SPI_ENABLE(&SPI3_Handler);             //使能SPI
    $ o- }  v8 k, k3 w# m
  8. # {" P4 u& n7 R1 r& @! L% ]: _& h! u! K
  9. }
复制代码

4 F4 c/ w* o0 p9 L' l因为全双工的读写是同时的,封装一个读写函数:
; s3 d# U4 s" X8 m" R  Z) f' z: f1 f! F5 ]4 }. E0 F7 K0 V) }
  1. //SPI2 读写一个字节, w8 f8 y' r1 U/ V+ l+ u
  2. //TxData:要写入的字节. [" U2 j1 R8 l7 Z
  3. //返回值:读取到的字节
    % d. M6 e2 C  T4 }7 e, g5 C
  4. u8 SPI3_ReadWriteByte(u8 TxData); q, b& o6 w1 y
  5. {0 {' \. B9 S/ \( g
  6.     u8 Rxdata;
    6 }+ d( j9 d" Q. v. U
  7.     HAL_SPI_TransmitReceive(&SPI3_Handler, &TxData, &Rxdata, 1, 1000);/ f8 B. Y2 @2 \) ?, D1 y  i# H
  8.     return Rxdata;                      //返回收到的数据
    ! s1 n7 `* q: m8 |, C8 k
  9. }
复制代码
9 `: \: l7 Z& u. n6 J3 I
根据W25QXX的数据手册,写一个读ID的函数:
3 z6 c7 _' p0 q* F5 a" t& T
  1. //读取芯片ID$ I( f" I: L* {& w3 V1 {# L
  2. //返回值如下:                                   
    7 w: i7 w$ ?9 H7 T5 e
  3. //0XEF13,表示芯片型号为W25Q80  & s3 h! L, |0 ^; [+ _5 q
  4. //0XEF14,表示芯片型号为W25Q16    % W- y" A0 N: _  _6 ?  u" r
  5. //0XEF15,表示芯片型号为W25Q32  ; `) t0 i1 N* K! L# T# @( e
  6. //0XEF16,表示芯片型号为W25Q64
    3 C9 Z8 o+ n& p
  7. //0XEF17,表示芯片型号为W25Q128           
    0 E' K. _! q0 @
  8. u16 W25QXX_ReadID(void)3 z8 t: r9 u2 I+ f3 ]6 J; J" t
  9. {
    ; K6 e% X" J3 U7 b
  10.         u16 Temp = 0;          + ~& s, \' o; a8 {7 C+ R9 }1 L
  11.         HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);            3 ~; L; r  l4 v" X5 D3 Q
  12.         SPI3_ReadWriteByte(0x90);//发送读取ID命令            , L2 U1 t8 I+ V; l
  13.         SPI3_ReadWriteByte(0x00);            
    " l3 y/ K5 s5 k4 p+ Y: I
  14.         SPI3_ReadWriteByte(0x00);            
    3 h! I5 O4 v" C# }  z, {& p. c' J
  15.         SPI3_ReadWriteByte(0x00);                                     - ]# n$ \; I; k! E9 Z3 @$ C
  16.         Temp|=SPI3_ReadWriteByte(0xFF)<<8;  # S2 P: i, i4 e+ m8 a, R8 D& |
  17.         Temp|=SPI3_ReadWriteByte(0xFF);         
    7 V$ u7 E' Y4 G* p% {
  18.         HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);  . g# u3 Q* I, {2 g' T
  19.         return Temp;& i4 Q' O) ~. h, d5 ?- R
  20. }  
复制代码

+ g- v7 p9 A3 \, I3 _  |最后写一个在主函数中调用的接口函数。6 X( _8 k  W0 a. Q. @

1 I6 ]7 k+ A/ y6 W/ I: w' O. c
  1. void W25QXX_Init(void)
    3 J* }2 t" ?" Y& t; V
  2. {2 L7 @0 g  ?& K4 P
  3.         MX_GPIO_Init();
    1 z; P2 A% H8 ~. \
  4.         HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);% j! J1 O+ Y, v+ E
  5.         SPI3_Init();! H* B$ T5 k( P% `
  6.         SPI3_SetSpeed(SPI_BAUDRATEPRESCALER_4);
    3 x) p# \' T, F0 h; N
  7.         uint16_t W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.  
    * U# B& L; [/ F$ N3 ]0 ~* x! b! C
  8.         printf("W25QXX ID:0x%4x\r\n",W25QXX_TYPE);
    0 `  u3 t0 l. \
  9. }
复制代码
% e- J% a' I7 C' z7 B: Y8 ^0 r
下载运行,读取到了芯片ID。
# i2 ~! h( ^- z7 g" ], Z3 v- O3 G, z( i2 U
20200423121114508.png
4 P( B: n2 Y7 D- m0 I9 G/ K7 t  j9 {
4 [8 |7 s7 V. i* M& z. N1 r
收藏 评论0 发布时间:2021-12-12 21:44

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版