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

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

[复制链接]
STMCU小助手 发布时间:2021-12-12 21:44
SPI协议的原理,网上大把的资料可以找到,这里记录一下SPI的初始化过程,以即以读取W25Q16型号为例的一个简单的SPI读写过程。
9 X* O: m# `/ |( r9 Y8 rCubeMX配置:
6 A& M6 S+ Q/ h/ y$ b( Z
5 s! u% E' \) V/ K: t. i9 W
20200423110312574.png

* C  J9 K( W4 Z, O) P& m
3 H2 E) G+ ~% V: r, d  l# `SPI模式
6 ^5 E6 L( Z, w! }9 z有只发送、只接收、半双工和全双工模式;! c# m) @5 G9 M2 ^: y- a
根据自己的需求,和SPI设备支持的类型,这里选择全双工的SPI;
  F: g5 n! Z/ j. ^2 {3 |3 M' O0 p& G* y  u7 n
片选信号
, b5 o' W  b; o因为我们这个SPI总线上会挂载多个从设备,而且只有一个主机,所以,这里禁用硬件片选,程序里面读写之前使用软件进行片选;" `  Z7 ]  @( l' `8 J

" _; T" i: [/ r0 i5 R2 H帧格式
7 J! t* |/ v& M* |这里选择Motorola帧格式;/ P% D0 E1 g, P1 P' ?
选择TI格式不能设置CPOL和CPHA,而且必须要开启硬件NSS这里才能选择TI模式。
# N! @4 R8 c# A: f8 |博主在网上搜了一些关于TI的SSP协议相关的资料,只有简简单单的一句:SSP总线兼容SPI,SSI 和Microwire 总线的接口。
& d2 ^& r9 F7 A9 q博主又搜了以下Microwire协议,和SPI一样,只不过片选信号是高电平有效。2 c  s! P  M& }4 I" L( Y, R4 D! I
这是手册里关于TI模式的说明部分和波形图:
6 q' y; I+ i! m& m! v+ M+ @0 f$ V' w
20200423112839303.png
$ f. d, w5 p2 i$ y
, u$ e* M1 z# Z
20200423112906213.png
( ^3 k) \- P: r7 f

6 _. q3 l9 y% A1 }也不清楚这个SCK的触发信号是干嘛的,所以这里老老实实选择Motorola就行了。9 D' o* f! l! ?! Y; a* O

/ e  g4 x# n! ~) `, O& q5 Y! r# N数据长度: g+ T* z1 j9 r" L* j8 G& h. f
4-16bit可选,根据实际,选择8bit;
1 s# }. l2 \: ~; F  ]1 A2 Y5 W. A5 c2 t  ]% z" s; z
数据传输模式
4 y! y! H+ J; o+ ?) l第一个传输的bit是高位还是低位,这里根据设备来选择,好像MSB(先发送高位)的比较多,注意这里的会同时设置发送和接收,如果设置错了,发送的指令对方就无法识别,可能收到的就是0xFF这种值。: N8 j) B. f3 J. X: G4 `5 o/ K

" K% i* X  P, M" f" R3 p; [SPI速率* b  L4 g6 ^7 q! M3 C" X- S
SPI的波特率是由时钟分频得到的,CubeMX很方便的还帮我们计算出来了时钟速率,可以根据自己的需求设置。
# j2 U1 o( G+ B% ?1 v) o, h# _- Z8 Z/ _
时钟极性和时钟相位/ c- i" q) r& [+ }  e, j1 Z
根据极性和相位的不同,可以有4种不同的工作模式,这个根据芯片手册支持的模式选择,注意,一条总线上尽量挂载支持相通模式的设备,这样就不用每次在设备切换之前重新配置SPI外设。
9 j9 t+ t6 ?/ Z- Q- ~- R- Q# N& [! j3 p, g
CRC校验5 R' D, j& ]: W; t# {
暂时还没研究到这里……
. P* C: N% X7 w( g+ ^( B3 G9 X9 _5 R; u
NSS脉冲
5 h1 z7 }9 k5 `& J7 Q9 f
参考SPI的NSS 脉冲模式的作用,这里用不到;
" t( ], |2 q+ r2 _+ a1 t1 K7 _$ X& `, ^
NSS信号类型
# |7 t* ]4 j; g- J8 E8 L& }0 l
因为前面禁用了硬件片选,所以这里只能设置位软件模式。6 L; r( s) Y3 Z, n9 l

) C$ D# H, _+ r, D" g) m6 w除此意外,还需要把连接硬件片选信号的GPIO,设置为输出模式。
( S* O& J+ V* N( [; R/ o" {; H/ PCubeMX生成的代码如下:
* a0 d2 i3 z0 v, r0 W
9 }, `7 \  D! u9 X
  1. SPI_HandleTypeDef SPI3_Handler;  //SPI2句柄
    6 U4 s6 X+ D3 m6 [3 @% Q5 {

  2. + q' G( P4 p/ u. P$ ?; |
  3. void SPI3_Init(void)
    ; r7 X4 u5 a- `( u1 k9 T
  4. {
    * P9 R8 X$ h! y- E* i, E+ F  Z
  5.     SPI3_Handler.Instance = SPI3;
    : I) a$ E& L, J1 O7 ~( V4 i" C, q& l
  6.     SPI3_Handler.Init.Mode = SPI_MODE_MASTER;' C" }6 O' V. T* q
  7.     SPI3_Handler.Init.Direction = SPI_DIRECTION_2LINES;
    : e* |- n8 `, j* P( b+ Q2 D
  8.     SPI3_Handler.Init.DataSize = SPI_DATASIZE_8BIT;
    1 |6 Z/ J* j* ~5 K* u. m" l5 b% t
  9.     SPI3_Handler.Init.CLKPolarity = SPI_POLARITY_LOW;
    " ^* \# c* U; q  Q
  10.     SPI3_Handler.Init.CLKPhase = SPI_PHASE_1EDGE;" I- m8 y5 G9 C2 Y
  11.     SPI3_Handler.Init.NSS = SPI_NSS_SOFT;! d9 {& `; E1 K* x1 Y- n- ~
  12.     SPI3_Handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;- I/ C5 r7 u3 o4 ?- n9 d
  13.     SPI3_Handler.Init.FirstBit = SPI_FIRSTBIT_MSB;
    1 s6 D- B; ^9 w8 Y
  14.     SPI3_Handler.Init.TIMode = SPI_TIMODE_DISABLE;
    0 t% @4 P# a8 @- g: v6 ^
  15.     SPI3_Handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;9 u: M: C" r! `( r; F; l
  16.     SPI3_Handler.Init.CRCPolynomial = 7;. c0 s5 W( @$ N5 T) K
  17.     SPI3_Handler.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
    5 Z. e  ?6 d# Q# d" j4 o* b
  18.     SPI3_Handler.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;) m% Q% v2 ]3 Q. `8 q8 O
  19.     HAL_SPI_Init(&SPI3_Handler);8 H6 v" c/ u: g* g

  20. . V# t4 @3 Y2 W( ]; z5 u7 l" m
  21.     __HAL_SPI_ENABLE(&SPI3_Handler);               
    - i( H" X( y" N# ^/ d9 \# k
  22. }
      @3 A7 Z3 f! s
  23. * p+ m4 V* R0 ?$ M" R% k" O2 ]0 }
  24. void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)' F. h3 C- o5 X5 _
  25. {
      Q* N* r# M; p. ]! g
  26.   GPIO_InitTypeDef GPIO_InitStruct = {0};
    & t8 O; T/ e" [, z
  27.   if(hspi->Instance==SPI3)  ~# Q9 v/ u7 A# `6 o% s5 d
  28.   {) o/ @  |' O+ E
  29. 5 U$ p$ k- b4 P0 K  i
  30.     __HAL_RCC_SPI3_CLK_ENABLE();! F+ \. a5 L2 k6 c  p! a6 y
  31. ! a: g4 ]/ q+ J1 v; Y9 H3 i
  32.     __HAL_RCC_GPIOB_CLK_ENABLE();1 w; {2 O0 V: X. L

  33. + B. ^8 {- C* Q* G! ~/ d8 z5 E
  34.     GPIO_InitStruct.Pin = GPIO_PIN_2;' Q) z' L1 h3 @0 y. ~! q5 K
  35.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;5 A) S! w& F& f4 o# s: s+ D
  36.     GPIO_InitStruct.Pull = GPIO_NOPULL;
    ' v1 t" ^7 Y7 P& t8 B8 k3 \
  37.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    7 L6 h( l7 J. T( {8 C* \( z  ^
  38.     GPIO_InitStruct.Alternate = GPIO_AF7_SPI3;
    & |- Q$ ]( |$ B
  39.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);! @" n3 _! t4 K, f
  40. 1 L: e9 [# H6 |1 l& u. v
  41.     GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4;3 q1 N1 M- J' V
  42.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;; u- G' S! P+ v1 ]$ x
  43.     GPIO_InitStruct.Pull = GPIO_NOPULL;
    7 y3 }& _' L4 I& m, O
  44.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;; @( q& u! G7 A6 h
  45.     GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
    % C. Y6 {, _3 f$ B# y! [, t6 B  r
  46.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);5 A0 @9 s$ F4 y- f7 ^8 I! K
  47.   }
    & |, z" Y& j6 |  `' m% s# ~& R3 R
  48. }3 O4 H0 `# E5 n" {
  49. 2 x" F* [. Q7 M/ Q3 a8 {

  50. : z9 H2 `- B! x$ o7 i( J/ S  t5 b
  51. static void MX_GPIO_Init(void). Q8 }$ y7 g* o' s" ?0 C
  52. {7 _3 F1 h" W" X9 Z' k8 N0 @' |
  53.   GPIO_InitTypeDef GPIO_InitStruct = {0};
    + o- V7 w& f8 B+ E6 j

  54. - z# p# J: ], |  p3 P* M
  55.   /* GPIO Ports Clock Enable */
    7 T8 L; z# O3 Q% k+ Q- k
  56.   __HAL_RCC_GPIOA_CLK_ENABLE();
    & b9 v3 \' Q( B4 u+ {: G: E) Q" D

  57.   I( M( W' N3 T# y  h+ l, C
  58.   /*Configure GPIO pin Output Level */
    3 F' e3 i4 U8 P  p$ s, f, G
  59.   HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    ( H& `4 r' [7 E( b

  60. # n4 F6 Q/ Q, ]0 \- L
  61.   /*Configure GPIO pin : PA4 */9 j$ d' |. [! ?. ?' b4 j' T
  62.   GPIO_InitStruct.Pin = GPIO_PIN_4;7 J+ h4 q- Z$ I9 d7 g0 G
  63.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    . j5 c8 T2 E+ F
  64.   GPIO_InitStruct.Pull = GPIO_NOPULL;
    # ^0 A0 k7 P9 h/ {
  65.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    8 q. A. _9 |! J- J- M9 a
  66.   HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    $ R. j$ U+ u) F. P9 k+ X: `" B

  67. 4 F* U; x# v9 c1 }
  68. }
复制代码

; O8 k$ Y' g4 S, K) d5 h) |同一总线上不同设备,工作在不同模式的时候,速率可能不一样,需要一个重新设置速率的函数:
: q! r( [1 m% f* ~' j
5 e  Y( v: u  T. t
  1. void SPI3_SetSpeed(u8 SPI_BaudRatePrescaler)9 O( I+ y$ X5 [/ t! l, x
  2. {
    % B9 L, i& i5 q# ^1 F2 A' I3 g2 S
  3.     assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性5 _* B2 T$ p) o* j- @
  4.     __HAL_SPI_DISABLE(&SPI3_Handler);            //关闭SPI
    $ {) _$ K9 w! ~+ G% ?8 S7 b
  5.     SPI3_Handler.Instance->CR1 &= 0XFFC7;        //位3-5清零,用来设置波特率8 P" M* V( p2 n: G# F9 S4 `& k5 F# f4 p
  6.     SPI3_Handler.Instance->CR1 |= SPI_BaudRatePrescaler; //设置SPI速度9 E# q5 f" u6 }9 ?: j) J3 u5 A
  7.     __HAL_SPI_ENABLE(&SPI3_Handler);             //使能SPI/ |2 n' m" c: @
  8. - B9 d- A* ]1 B) ^7 g4 Y/ C! C4 _
  9. }
复制代码

9 l" d. m% T& Q6 `因为全双工的读写是同时的,封装一个读写函数:
! a) N+ z7 `8 W7 x/ N4 x7 B' L; k+ u; h: i8 L0 C* M
  1. //SPI2 读写一个字节5 z$ q  B  H- h' b1 l7 K! j
  2. //TxData:要写入的字节
    . `/ l8 M8 r: h: n
  3. //返回值:读取到的字节( ^& \* A" o- p# @- [  O
  4. u8 SPI3_ReadWriteByte(u8 TxData)
    , M0 G2 n4 z' E4 t  j' @% [
  5. {
      _4 I" E2 p1 i& h& `; C$ {3 a
  6.     u8 Rxdata;2 Y5 R1 ~# {  s2 U* p
  7.     HAL_SPI_TransmitReceive(&SPI3_Handler, &TxData, &Rxdata, 1, 1000);' ~$ F! F" O& O6 X+ a# s
  8.     return Rxdata;                      //返回收到的数据
    1 d7 h& m" u0 `7 z
  9. }
复制代码
! `" g) G; [7 @& H9 ~' u. i) W- v& W
根据W25QXX的数据手册,写一个读ID的函数:# }9 s7 s9 c% D+ W4 x+ d
  1. //读取芯片ID0 m- P- R3 w6 n. l" Q! w- Z5 `( N
  2. //返回值如下:                                   
    8 J6 C0 n5 j+ Z# s/ \
  3. //0XEF13,表示芯片型号为W25Q80  2 c5 x+ Q9 ]' a0 {6 Z
  4. //0XEF14,表示芯片型号为W25Q16   
    3 D! w" R  F8 r
  5. //0XEF15,表示芯片型号为W25Q32  
    6 R& f- T! h! H1 N+ ]3 M8 X
  6. //0XEF16,表示芯片型号为W25Q64
    ( u7 y4 N& t, I
  7. //0XEF17,表示芯片型号为W25Q128           
    * F) q+ v6 M5 w# S
  8. u16 W25QXX_ReadID(void)
    " P! n& S5 H8 m- o( [! s0 H6 J
  9. {
    * y: H/ p6 Q* u* h& G# U$ }
  10.         u16 Temp = 0;          + H2 F( L: ]1 W( \( ]7 R
  11.         HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);            / a9 {" T' x4 x7 S2 F" h% ~8 B
  12.         SPI3_ReadWriteByte(0x90);//发送读取ID命令            
    3 p( y; E- A3 W+ @: R
  13.         SPI3_ReadWriteByte(0x00);             4 T; v2 O: H/ y+ v4 B5 n
  14.         SPI3_ReadWriteByte(0x00);            
    , ~4 L* T. E, F$ [8 r$ p
  15.         SPI3_ReadWriteByte(0x00);                                    
    & |+ \5 {) a& A+ |
  16.         Temp|=SPI3_ReadWriteByte(0xFF)<<8;  2 N1 x, e# i% A/ t& i
  17.         Temp|=SPI3_ReadWriteByte(0xFF);         
    4 K5 M* \. ~) p5 ^& ^7 ^0 U
  18.         HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);  # W! ?  A1 Z  o+ m
  19.         return Temp;" k. @! `3 f, s
  20. }  
复制代码

! }+ I+ d! C5 X最后写一个在主函数中调用的接口函数。  P! a' i- b4 G& F& X
1 ]4 F. L1 f0 s7 L8 z8 ^  F0 |- L
  1. void W25QXX_Init(void)
    ! v8 B) z& z$ Q" P
  2. {" b/ m7 N( t+ r( R
  3.         MX_GPIO_Init();2 @+ K5 S! f, p7 S# K6 _6 s
  4.         HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
    ; x' L+ M5 E" X. r, t
  5.         SPI3_Init();
    8 Z6 ?0 w+ O. Y% N# q  i
  6.         SPI3_SetSpeed(SPI_BAUDRATEPRESCALER_4);* Q. A9 A( O- I  l
  7.         uint16_t W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.  
    0 z" C4 M2 G- F* y6 @8 |
  8.         printf("W25QXX ID:0x%4x\r\n",W25QXX_TYPE);
    3 W5 g3 g9 ], d' H1 e/ Q* l
  9. }
复制代码

/ i. L+ m6 `4 d下载运行,读取到了芯片ID。
; q; A$ ^# G1 S2 ~/ z# e4 z# L& B5 j$ J# ^6 v, U4 G7 n0 l
20200423121114508.png

, y; [8 k0 H7 Y% S7 f0 J3 d7 {$ a
  ?+ g! z' O2 m3 W
收藏 评论0 发布时间:2021-12-12 21:44

举报

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