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

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

[复制链接]
STMCU小助手 发布时间:2021-12-12 21:44
SPI协议的原理,网上大把的资料可以找到,这里记录一下SPI的初始化过程,以即以读取W25Q16型号为例的一个简单的SPI读写过程。* Y! ]5 v6 b0 Z$ v' G% G
CubeMX配置:
& j9 H7 n$ p# r, k! W) W. g, f/ G. T" U
20200423110312574.png
. Z; n: |, H( b7 v: I+ R

+ O( Q2 g, q% J( J; q$ ASPI模式+ W9 G: S" R1 s* B
有只发送、只接收、半双工和全双工模式;
0 D/ x0 U  W( f) K7 F7 A6 y+ z根据自己的需求,和SPI设备支持的类型,这里选择全双工的SPI;# g. l) [# y2 u5 t8 |3 j
( [1 V7 |- j5 N, Y! L
片选信号
0 P# {! f, X+ k因为我们这个SPI总线上会挂载多个从设备,而且只有一个主机,所以,这里禁用硬件片选,程序里面读写之前使用软件进行片选;
0 L6 i! }) H' G% T+ U  v. x0 o( p: M4 O1 q* j8 [0 N) `
帧格式
. m& n* i# I3 \  f* h6 Z这里选择Motorola帧格式;
& C6 k4 s9 o6 Z: Y/ q选择TI格式不能设置CPOL和CPHA,而且必须要开启硬件NSS这里才能选择TI模式。
6 W* j  d' h0 O9 h博主在网上搜了一些关于TI的SSP协议相关的资料,只有简简单单的一句:SSP总线兼容SPI,SSI 和Microwire 总线的接口。. W% N6 f7 B- {* z
博主又搜了以下Microwire协议,和SPI一样,只不过片选信号是高电平有效。( @* |9 u' Z! b+ o
这是手册里关于TI模式的说明部分和波形图:( i1 N3 C- Z0 S: N, o- }8 P

+ b1 o- ~0 ]  \8 P4 B/ f2 C) q
20200423112839303.png

- l% d0 x4 Z; c0 r& S7 M$ Q; r6 y1 Y& @; q( N
20200423112906213.png
1 T' E4 G6 z9 F7 G2 ~
& t- g4 |/ ?1 s" Q9 B1 q
也不清楚这个SCK的触发信号是干嘛的,所以这里老老实实选择Motorola就行了。/ {( E; ?" T+ N3 v; f4 a$ j5 P

' D* _6 h' d' g4 d8 ~5 {4 R( w% F数据长度+ _2 x, k1 P' A6 g
4-16bit可选,根据实际,选择8bit;
) ?. G$ q( b! s* b+ K6 s0 ]1 r3 t0 M
数据传输模式
0 g2 ?; l8 H/ n; y" f第一个传输的bit是高位还是低位,这里根据设备来选择,好像MSB(先发送高位)的比较多,注意这里的会同时设置发送和接收,如果设置错了,发送的指令对方就无法识别,可能收到的就是0xFF这种值。! ?2 r( r& j" f: a& ^
) m) w/ |  V6 j. Q/ j  o+ a7 r
SPI速率
; r. E( _6 K+ k- K! K$ g) aSPI的波特率是由时钟分频得到的,CubeMX很方便的还帮我们计算出来了时钟速率,可以根据自己的需求设置。% g. B% f" R' s1 n( o) [

9 j3 j2 C- k' ~2 ]时钟极性和时钟相位
5 M, @7 b* C- d' @- N根据极性和相位的不同,可以有4种不同的工作模式,这个根据芯片手册支持的模式选择,注意,一条总线上尽量挂载支持相通模式的设备,这样就不用每次在设备切换之前重新配置SPI外设。
% B4 C# L8 P* D  o: }: `
7 s& @& L% Q0 V3 {# KCRC校验2 j' S/ I; ~9 g9 q' r: R
暂时还没研究到这里……+ i7 u( m' z; R( |; \7 G: L
) r) Z% _0 _7 Y( o( D" b, Z
NSS脉冲

: a0 C% d$ D# h参考SPI的NSS 脉冲模式的作用,这里用不到;3 F. Q" x6 g) u3 b: C0 j

3 u- G# c! I2 M4 B+ KNSS信号类型
% q* O6 D3 C% _$ V% B/ P7 l
因为前面禁用了硬件片选,所以这里只能设置位软件模式。  [# S% z8 V- v! ^+ x9 M7 j

2 m0 ~* ?# @# Y) t' `除此意外,还需要把连接硬件片选信号的GPIO,设置为输出模式。: E1 M1 T4 r, ~/ x+ Y/ O/ e) X) l
CubeMX生成的代码如下:
" P5 X' T  W- z' m9 ~5 O
: q$ b: C4 v) w+ u$ @6 q4 `
  1. SPI_HandleTypeDef SPI3_Handler;  //SPI2句柄- N' z' b. u" f4 J

  2. 5 {+ N' {2 R  M' U5 f, Q" ^4 H  \
  3. void SPI3_Init(void): F+ c  R1 x8 E; q
  4. {! C1 t1 A/ X% \- l) o+ T
  5.     SPI3_Handler.Instance = SPI3;
    " y% m* X3 G) H% S) \
  6.     SPI3_Handler.Init.Mode = SPI_MODE_MASTER;
    - u. O" H( v' E
  7.     SPI3_Handler.Init.Direction = SPI_DIRECTION_2LINES;
    ; t& V/ R8 d+ w
  8.     SPI3_Handler.Init.DataSize = SPI_DATASIZE_8BIT;& R4 b7 G7 c' ~; E, v
  9.     SPI3_Handler.Init.CLKPolarity = SPI_POLARITY_LOW;: W* D; t0 j3 U: M( ]% a+ w9 \
  10.     SPI3_Handler.Init.CLKPhase = SPI_PHASE_1EDGE;+ s4 S; w5 s. G' M
  11.     SPI3_Handler.Init.NSS = SPI_NSS_SOFT;
    7 }1 Q& h- C1 M) {1 ^7 n
  12.     SPI3_Handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
    : f9 m4 }$ E) O2 d
  13.     SPI3_Handler.Init.FirstBit = SPI_FIRSTBIT_MSB;
    ' q+ D; r$ ]( t0 O
  14.     SPI3_Handler.Init.TIMode = SPI_TIMODE_DISABLE;
    5 z. U5 }& e+ q' @2 K3 M  F( H, M
  15.     SPI3_Handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;' [" X& U2 u) S8 u
  16.     SPI3_Handler.Init.CRCPolynomial = 7;, b7 Z& d+ o% v, e  I
  17.     SPI3_Handler.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
    . N3 P) F- j; `) G3 k
  18.     SPI3_Handler.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;/ a! ?5 k/ }8 s
  19.     HAL_SPI_Init(&SPI3_Handler);- t  @' g( i3 T* j' _

  20. 9 b- |! Y' T8 ]2 S
  21.     __HAL_SPI_ENABLE(&SPI3_Handler);               
    : b, l& j  q% L, R$ ?
  22. }3 r! s- U, j9 O3 N  S
  23. ! p+ L8 _- A9 j: R
  24. void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
    8 ^+ ^9 e* d/ T: J. f! ~
  25. {
    1 U0 ^1 h! Z# L; ?% I
  26.   GPIO_InitTypeDef GPIO_InitStruct = {0};
    / s  i; G! G: Y: }
  27.   if(hspi->Instance==SPI3)
    5 b; x: l! V) C/ I' q! L
  28.   {
    9 z/ r+ I" F: N9 }! g( T' C3 R

  29. $ A' K$ l, w& X7 ?! ]
  30.     __HAL_RCC_SPI3_CLK_ENABLE();
    - C! f# X: F; A5 a5 \, K
  31. + n9 W2 {. [* G/ x
  32.     __HAL_RCC_GPIOB_CLK_ENABLE();% d9 x( t8 q) g3 W. O7 I
  33. ' B& |4 Y3 H7 A9 e" T; g5 \* Z3 R
  34.     GPIO_InitStruct.Pin = GPIO_PIN_2;
    ! `9 n! z" p: i
  35.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;$ P' z+ S* U+ Q! G% i) k6 o
  36.     GPIO_InitStruct.Pull = GPIO_NOPULL;
    % ^- h5 g2 _; {2 F( s$ V) x( W5 e
  37.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;5 ~4 W$ K) w; o( B
  38.     GPIO_InitStruct.Alternate = GPIO_AF7_SPI3;: `! D, }/ \/ g+ B- e/ b
  39.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    ; G" `' c) N: T8 J

  40. $ ?  f4 \! Y  Y$ n0 v
  41.     GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4;
    : S3 S) X# M& t7 h( N
  42.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;. S7 r5 P/ e2 r
  43.     GPIO_InitStruct.Pull = GPIO_NOPULL;: O3 \$ k- I' U3 B" S
  44.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
      K2 Q' F+ R: i) [1 L$ }  C
  45.     GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;  i1 w0 I/ \3 I! V& q
  46.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    # H6 e( U, n- L& K* v9 l
  47.   }$ |! L$ u' }5 t
  48. }
    6 ]  d9 i& w. w) y

  49. 1 C" r/ E. t3 S  Y6 Q$ F9 d2 ^
  50. 5 }% R7 V( Z+ S( D2 W
  51. static void MX_GPIO_Init(void)+ f' f0 i. }( A5 m# H5 y! @7 Y* o) l7 s
  52. {% o7 C+ m8 r/ `9 ]; I6 c" w
  53.   GPIO_InitTypeDef GPIO_InitStruct = {0};5 O) _" q6 G" Z' @
  54. , D* b: g: K! ]  I& Y7 h9 H
  55.   /* GPIO Ports Clock Enable */
    ) K! A/ d! Z) }8 k# y1 D3 ?5 W" `
  56.   __HAL_RCC_GPIOA_CLK_ENABLE();4 |$ I9 d; t, |2 e9 u, ^- B
  57. ! ]8 q5 b, g# O, }# X
  58.   /*Configure GPIO pin Output Level */% v, ]+ }4 t+ m8 `1 }4 h
  59.   HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);3 O6 z1 y+ v& ^8 }. v

  60. & u  z/ o8 H" {( E
  61.   /*Configure GPIO pin : PA4 */
    + E9 v5 l; j! ]  X" K
  62.   GPIO_InitStruct.Pin = GPIO_PIN_4;
    % n: d6 X3 X3 q# }& c. n! s8 A
  63.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;1 f) R+ U  m2 b$ T9 A2 J
  64.   GPIO_InitStruct.Pull = GPIO_NOPULL;9 i* }9 ^0 ~! c
  65.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    9 b7 N9 [  p  M
  66.   HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);$ v$ w8 O: }/ U5 N

  67. 4 G! l, k7 m1 a5 |
  68. }
复制代码
8 @/ Q. \3 V' y& J; P
同一总线上不同设备,工作在不同模式的时候,速率可能不一样,需要一个重新设置速率的函数:: T% y0 K' Q8 O4 t$ U% a& S) }( F

+ B7 C4 S/ ]7 @) `
  1. void SPI3_SetSpeed(u8 SPI_BaudRatePrescaler)
    : A0 B1 x% }, v
  2. {9 L9 Z9 o" y/ t% ~" v) {( H" F' I
  3.     assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性
    3 i1 Y% y1 _  W2 P+ N# L8 H7 }
  4.     __HAL_SPI_DISABLE(&SPI3_Handler);            //关闭SPI+ ^" n: w7 O' c7 ^6 v' L
  5.     SPI3_Handler.Instance->CR1 &= 0XFFC7;        //位3-5清零,用来设置波特率
    $ q* g  R4 c. G4 E5 n( F( i: P# E
  6.     SPI3_Handler.Instance->CR1 |= SPI_BaudRatePrescaler; //设置SPI速度
    8 A- u( J* c2 G) E: S& l
  7.     __HAL_SPI_ENABLE(&SPI3_Handler);             //使能SPI& a$ ^. @5 v* Z
  8. + t3 B3 W% Y; F" M( T; Q
  9. }
复制代码
& z- p) m: ?8 Y6 @7 E
因为全双工的读写是同时的,封装一个读写函数:
$ Q+ k3 @- w5 d7 o2 ]& B/ c4 ^
( D. P  k3 D& U1 D" r2 I8 ?- n
  1. //SPI2 读写一个字节
    / i$ G! H# d* J  I/ U
  2. //TxData:要写入的字节
    # V1 m! M0 p* v+ G" i- N. d
  3. //返回值:读取到的字节
    , c0 G" l! t* V% \2 x9 |
  4. u8 SPI3_ReadWriteByte(u8 TxData)
    9 Q" P; L4 D: ^; Z; N; X% s
  5. {# G: r- p7 ^/ p4 X
  6.     u8 Rxdata;
    # v- L4 C' M& [) ^
  7.     HAL_SPI_TransmitReceive(&SPI3_Handler, &TxData, &Rxdata, 1, 1000);
    % g5 {7 M3 h4 r' {+ S% S
  8.     return Rxdata;                      //返回收到的数据3 S/ X1 a7 H' H) d& A) ?- J5 {" J
  9. }
复制代码

8 h$ u) N* |) D3 e根据W25QXX的数据手册,写一个读ID的函数:
$ @% R: V. |% o* B: P
  1. //读取芯片ID
    $ B. E5 s9 ]: O. l1 g% o2 g6 f1 Y
  2. //返回值如下:                                   
    7 \( F) N5 ?/ z' C- z( J0 p% Y) x. G3 E
  3. //0XEF13,表示芯片型号为W25Q80  
    0 |( ~% I5 n! }8 U* Q, x9 `7 {
  4. //0XEF14,表示芯片型号为W25Q16    0 Z8 a5 v( U/ }
  5. //0XEF15,表示芯片型号为W25Q32  
    ) W8 }9 O- A( |4 D$ r
  6. //0XEF16,表示芯片型号为W25Q64 ) e" G0 n2 t6 z: H2 f1 x
  7. //0XEF17,表示芯片型号为W25Q128           
    7 y" y" u! H: O$ J
  8. u16 W25QXX_ReadID(void)
    ' K& _0 k  y$ E% ~
  9. {
    / E0 }" v+ o, i2 Z5 G- X. R
  10.         u16 Temp = 0;         
    + T: R+ S8 Z- Q2 Z$ H, N; g
  11.         HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);            - B9 f* V1 G/ N  s9 C
  12.         SPI3_ReadWriteByte(0x90);//发送读取ID命令            
      d& I+ E' N- n( T
  13.         SPI3_ReadWriteByte(0x00);            
    ; u3 T% R) J' K) O4 L5 q/ T  |
  14.         SPI3_ReadWriteByte(0x00);             9 R/ ?7 e1 V2 K6 D: p1 k: j
  15.         SPI3_ReadWriteByte(0x00);                                     , [  I$ e* V( T6 O
  16.         Temp|=SPI3_ReadWriteByte(0xFF)<<8;  0 W* W/ f4 I9 u
  17.         Temp|=SPI3_ReadWriteByte(0xFF);           `- a$ p6 b2 M- i3 e$ h- U# m
  18.         HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);  2 ~; e* W& C+ y6 t! T$ h- N; _: E
  19.         return Temp;* x; o) r/ K3 a7 b
  20. }  
复制代码
8 a( P0 [# j  A" M8 r$ F$ u% u% E
最后写一个在主函数中调用的接口函数。
- O( \+ k: \% T) |; E
) ^$ o. F$ U9 x# G, M  X. C
  1. void W25QXX_Init(void)! Y* A3 `: {0 h1 ]8 l
  2. {
    2 E* T) e' m( i. V8 ]/ @
  3.         MX_GPIO_Init();/ M& L: {) y9 A+ L# w0 [) U
  4.         HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
    ; k/ @% J/ ^! v0 j* y8 c
  5.         SPI3_Init();! n: H% Q3 f" N8 n# I( o; e- D
  6.         SPI3_SetSpeed(SPI_BAUDRATEPRESCALER_4);
    5 K! z2 t, o' j! d7 y0 B$ {0 E
  7.         uint16_t W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.  
    : |# @2 ?0 d5 Y
  8.         printf("W25QXX ID:0x%4x\r\n",W25QXX_TYPE);4 h8 I3 B  B4 J. W/ B
  9. }
复制代码

" q, k! a" o2 u/ s% J下载运行,读取到了芯片ID。
: b9 {% o; `! X; L% T# @7 k' |& M
% Z; ~1 g- \5 }5 E! Q* f1 Y, o
20200423121114508.png

, M8 L7 Z% T8 p" e- E, u4 [7 U/ X3 }) ]
收藏 评论0 发布时间:2021-12-12 21:44

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版