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

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

[复制链接]
STMCU小助手 发布时间:2021-12-12 21:44
SPI协议的原理,网上大把的资料可以找到,这里记录一下SPI的初始化过程,以即以读取W25Q16型号为例的一个简单的SPI读写过程。
8 C& u+ a' z. m% nCubeMX配置:
* n; u9 V; `. j- U0 i( y5 |* u+ c8 F- z
20200423110312574.png

. \" }  P: [3 V  k3 X: v) y$ G* r/ h" H) f1 O
SPI模式
  f: O& j+ ?& q2 X8 R! L有只发送、只接收、半双工和全双工模式;4 l: ^8 P, N# Z2 V) T
根据自己的需求,和SPI设备支持的类型,这里选择全双工的SPI;. s4 M7 A( p& f6 v& x
% g, J2 c$ ~8 K
片选信号
  ]9 [4 [% C( p" h2 h5 y" N( V因为我们这个SPI总线上会挂载多个从设备,而且只有一个主机,所以,这里禁用硬件片选,程序里面读写之前使用软件进行片选;
7 W* `4 u# r5 a
$ M3 z  i3 W; y; g* Z0 x3 N3 ^( o帧格式2 }; W  ~% `. P( |4 |$ S! S+ g
这里选择Motorola帧格式;
- s7 V! K% x4 F# n8 U: b选择TI格式不能设置CPOL和CPHA,而且必须要开启硬件NSS这里才能选择TI模式。
+ Y2 m0 Z( i4 h: i博主在网上搜了一些关于TI的SSP协议相关的资料,只有简简单单的一句:SSP总线兼容SPI,SSI 和Microwire 总线的接口。2 ?* u# V: ^4 \, A5 J, c9 F
博主又搜了以下Microwire协议,和SPI一样,只不过片选信号是高电平有效。
  \5 d% w- K' J! D这是手册里关于TI模式的说明部分和波形图:
7 o$ {& {0 B0 |8 i4 t' Q$ Q0 U
. F6 n6 z# e/ ^2 W
20200423112839303.png
: j# ^6 [. I. ^9 G

0 R6 ]" y' H+ o
20200423112906213.png

$ C! g/ n9 T, G& j6 g7 x" }0 R& L; Y$ ^5 L# [/ a4 v; e& q
也不清楚这个SCK的触发信号是干嘛的,所以这里老老实实选择Motorola就行了。4 A$ b% K6 }! w! ~
% k1 J' n& |" P+ v6 _
数据长度/ e) q% V5 I+ ^# ]" X' J
4-16bit可选,根据实际,选择8bit;
. Q* N$ X6 E9 p4 K# v* D* u
: S1 f( b) @/ i9 F+ o数据传输模式
- j+ K6 e1 O- P! F: N! ~第一个传输的bit是高位还是低位,这里根据设备来选择,好像MSB(先发送高位)的比较多,注意这里的会同时设置发送和接收,如果设置错了,发送的指令对方就无法识别,可能收到的就是0xFF这种值。
8 x/ B, h2 j& ~3 p1 p- _' r
2 t9 ?1 p8 F1 a5 cSPI速率
' m/ A, l$ t' K. ]& o+ h0 G0 c8 ySPI的波特率是由时钟分频得到的,CubeMX很方便的还帮我们计算出来了时钟速率,可以根据自己的需求设置。8 l$ y6 P2 U/ G$ G4 C

2 s& ~9 f/ A5 v: K* K时钟极性和时钟相位& c; }0 ^" x. i! Y2 r" |
根据极性和相位的不同,可以有4种不同的工作模式,这个根据芯片手册支持的模式选择,注意,一条总线上尽量挂载支持相通模式的设备,这样就不用每次在设备切换之前重新配置SPI外设。' X: I# R- ]% n! W! N

4 ?! Y, O- m' f9 tCRC校验, S1 c& b3 m2 ]8 z
暂时还没研究到这里……
6 A. g  @: X0 j* t! W( K) F( Z3 M1 W' K
NSS脉冲
7 Q1 @+ X" R; h- a
参考SPI的NSS 脉冲模式的作用,这里用不到;
. h0 G+ d2 T+ A/ x9 s( j3 u1 s) H, Y5 R
NSS信号类型

4 t8 I' X. {) m' t& F4 @, M! O因为前面禁用了硬件片选,所以这里只能设置位软件模式。: s7 r% d; @5 _
7 m9 S+ X' D8 f, }7 B) g
除此意外,还需要把连接硬件片选信号的GPIO,设置为输出模式。  R' W1 X+ g& W! d, r, s; s* U' }/ ]4 ]1 `
CubeMX生成的代码如下:
. S) F5 _0 \# |) N2 t4 X8 ^0 ?* R) J+ V0 @3 _( C: b1 x
  1. SPI_HandleTypeDef SPI3_Handler;  //SPI2句柄
      ]) J. m* I# z; c. q9 G/ ]
  2. , ~) B# g( w+ \' b7 Y7 X
  3. void SPI3_Init(void)
    8 K1 k& z+ \$ @4 b, l8 S4 s
  4. {' |2 r7 w5 D0 B* l* [
  5.     SPI3_Handler.Instance = SPI3;
    + P4 u. L0 ]7 L% m4 D, P
  6.     SPI3_Handler.Init.Mode = SPI_MODE_MASTER;& H  y& S8 M2 B9 \' H% e
  7.     SPI3_Handler.Init.Direction = SPI_DIRECTION_2LINES;
    - V3 w5 i  y) t% _
  8.     SPI3_Handler.Init.DataSize = SPI_DATASIZE_8BIT;
    : d% u* r, `3 ]' e6 ?. k
  9.     SPI3_Handler.Init.CLKPolarity = SPI_POLARITY_LOW;9 s& s2 L9 ^# o7 H' F' ?1 H
  10.     SPI3_Handler.Init.CLKPhase = SPI_PHASE_1EDGE;
    0 K' J  ^4 _, C
  11.     SPI3_Handler.Init.NSS = SPI_NSS_SOFT;
    " I3 c1 U# o6 u& G: Q
  12.     SPI3_Handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;* N9 V. l* _8 d
  13.     SPI3_Handler.Init.FirstBit = SPI_FIRSTBIT_MSB;
    8 |, ?, ]- e0 J2 N
  14.     SPI3_Handler.Init.TIMode = SPI_TIMODE_DISABLE;
    * |( i" v* b& U2 S$ a4 m% B* q
  15.     SPI3_Handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    : \: j/ f7 V" n+ E/ G0 i3 C
  16.     SPI3_Handler.Init.CRCPolynomial = 7;
    ! H& _: w4 `& E: }- f9 K8 `1 x
  17.     SPI3_Handler.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
    5 t1 ?6 W, d  G9 p4 \2 L
  18.     SPI3_Handler.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
    9 N8 S# s# M. }! @8 o8 X
  19.     HAL_SPI_Init(&SPI3_Handler);
    9 v8 }- j& M* p4 q, N% D
  20. ' {0 N& C# J- c2 |, G
  21.     __HAL_SPI_ENABLE(&SPI3_Handler);               
    ! [- o6 I4 q' d& v. n; C, P
  22. }4 D+ H9 D- X0 W( ?1 g( w( k9 q) Y

  23. - `; N( U' b# X7 o; q- |/ T
  24. void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)5 V* Y. ]3 A. E4 r# I/ z0 K6 p
  25. {" X0 \- i* M, R3 ?" a9 V
  26.   GPIO_InitTypeDef GPIO_InitStruct = {0};
    2 l- d' H' e6 l
  27.   if(hspi->Instance==SPI3)( N3 q( B& j* O5 @2 F
  28.   {- r# B7 d$ @5 X" P* P
  29.   a/ Y% L. e. ?0 @7 `/ j) S6 m# d
  30.     __HAL_RCC_SPI3_CLK_ENABLE();
    1 _0 ^  d+ W, l: X! y  t/ j
  31. , ^( R. M8 k8 Z. }4 L; P% m$ D& J; b
  32.     __HAL_RCC_GPIOB_CLK_ENABLE();
    3 s  E# e, f$ |2 `
  33. 8 E* w8 i# c" b% |- _
  34.     GPIO_InitStruct.Pin = GPIO_PIN_2;
    * G' i; ~) H- `9 u
  35.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;7 j7 }9 C; ^* B; z( [5 [
  36.     GPIO_InitStruct.Pull = GPIO_NOPULL;
    - H) L! h! |& k1 j0 `9 ]7 Y* r
  37.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;. s/ x& C$ u; y! ~0 Q) Y0 d0 V- \
  38.     GPIO_InitStruct.Alternate = GPIO_AF7_SPI3;5 w  [# s+ n9 Y3 p
  39.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);# m( }8 R. T; x) j
  40. $ T2 ~& _* s" _% N
  41.     GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4;
    ) |9 m* x5 L" y2 ~4 V' w# w6 ~* e
  42.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;8 B, }7 D& ?) F. W& J! N
  43.     GPIO_InitStruct.Pull = GPIO_NOPULL;
    # ~, E4 P6 O7 e7 Z
  44.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    * X7 e/ v; K7 T$ q" d; H3 U
  45.     GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
    9 u) {8 L1 n1 t; r/ j2 l8 q. r. D
  46.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    % i: R2 R/ D' x
  47.   }5 {9 I( B% d1 |: K8 N
  48. }4 O& h, L/ K, b1 f5 B

  49. * D( q/ ]* `* Y4 t7 e: x

  50.   N+ F2 f3 E* _8 M) D, t0 H0 V
  51. static void MX_GPIO_Init(void)9 L# T/ B/ ?6 A5 \* S% _# J
  52. {1 c* k5 J6 b7 C& u% q3 M
  53.   GPIO_InitTypeDef GPIO_InitStruct = {0};
    4 p% i( }* R& o: v: U

  54. 6 {8 L- T1 I2 H3 E; @
  55.   /* GPIO Ports Clock Enable */+ b1 A. x+ v# c; c8 V
  56.   __HAL_RCC_GPIOA_CLK_ENABLE();
    - f$ B) |' E9 g2 \3 z; ]. Y

  57. + A1 L$ P$ x1 e2 n% O
  58.   /*Configure GPIO pin Output Level */
    $ g& b( ?( k* ~9 l/ m; C6 G
  59.   HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);$ |1 g" U% \& ?. u- k4 f

  60. ) }9 d+ v$ b1 z$ A' Q/ L" \5 T  S9 K
  61.   /*Configure GPIO pin : PA4 */
    0 o4 |0 r) K/ C$ R6 a6 H6 W
  62.   GPIO_InitStruct.Pin = GPIO_PIN_4;
    # L, ~) C" S' a
  63.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    1 X9 c, E: ^) M$ s0 v& ~% o
  64.   GPIO_InitStruct.Pull = GPIO_NOPULL;. a( [& c$ o6 \6 h& L5 }( E- f
  65.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;5 O! e  p" T% ?! s2 T) }
  66.   HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    3 h- ^  }6 F! f* a

  67. 4 k+ |4 r  t  p. _/ N+ \9 Z
  68. }
复制代码

0 B  \8 F5 f, |3 D! F1 b同一总线上不同设备,工作在不同模式的时候,速率可能不一样,需要一个重新设置速率的函数:, z+ y1 x8 H1 e, z8 c9 e
# o2 G7 q2 y) x: {
  1. void SPI3_SetSpeed(u8 SPI_BaudRatePrescaler)( s# O$ c# t* F% y: D* \0 A/ Y
  2. {2 x. z- j3 `  C# }0 e- B2 ]
  3.     assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性9 V3 q2 F* e+ {8 V; U. V# Z, L( v/ A
  4.     __HAL_SPI_DISABLE(&SPI3_Handler);            //关闭SPI
    ! [6 s. H0 m# c$ D9 f  t& t
  5.     SPI3_Handler.Instance->CR1 &= 0XFFC7;        //位3-5清零,用来设置波特率
    6 _) e( O5 T8 R* }! T  m
  6.     SPI3_Handler.Instance->CR1 |= SPI_BaudRatePrescaler; //设置SPI速度
    1 V- f5 s2 n4 ]6 T" W
  7.     __HAL_SPI_ENABLE(&SPI3_Handler);             //使能SPI; ]1 O9 D- X  L/ [/ W
  8. 6 |! R& H* Y- |% u
  9. }
复制代码
7 e; L8 x/ a6 q' \* N
因为全双工的读写是同时的,封装一个读写函数:
9 U/ o1 R, n- e7 {
# Y% x1 }* v; A. K
  1. //SPI2 读写一个字节0 ^. ?2 ]3 O  A! H) B
  2. //TxData:要写入的字节
    4 i9 F7 D. b* p0 m% I
  3. //返回值:读取到的字节! y: P! F) c4 a  C6 h
  4. u8 SPI3_ReadWriteByte(u8 TxData)0 z0 G6 T0 }, e7 \! `6 V  z0 t
  5. {7 ^& }/ A: |2 t1 }
  6.     u8 Rxdata;1 y4 V0 T7 x  t
  7.     HAL_SPI_TransmitReceive(&SPI3_Handler, &TxData, &Rxdata, 1, 1000);9 L, N7 a# M8 i* b+ K$ h
  8.     return Rxdata;                      //返回收到的数据, }/ Q2 _% `8 B1 h/ ], c
  9. }
复制代码
+ E/ u* J% \# U" Z- B/ j
根据W25QXX的数据手册,写一个读ID的函数:& [, T3 M; N  f2 J4 e) }
  1. //读取芯片ID$ p/ C% f% J2 B1 x$ p- }
  2. //返回值如下:                                   
    % }0 Q" U5 h8 h
  3. //0XEF13,表示芯片型号为W25Q80  
    1 B9 s: X# Q$ L; y. E
  4. //0XEF14,表示芯片型号为W25Q16   
    & {" c' u. X' S1 y- t5 ?
  5. //0XEF15,表示芯片型号为W25Q32  , F/ Q& \  H- D- r# p! q. D
  6. //0XEF16,表示芯片型号为W25Q64
    4 N9 l! [' J. e
  7. //0XEF17,表示芯片型号为W25Q128           
    6 V* @  P5 \2 K5 d
  8. u16 W25QXX_ReadID(void)0 Z, {* P' F7 L
  9. {3 j$ ~0 c+ k7 c
  10.         u16 Temp = 0;         
    - J4 }$ k$ B1 p* T2 _% y
  11.         HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);            $ U- `1 ^8 Q7 h7 q8 o
  12.         SPI3_ReadWriteByte(0x90);//发送读取ID命令            & o: U  A; A' ^2 W, i
  13.         SPI3_ReadWriteByte(0x00);            
    ) d  P( \/ U) Q' t: I
  14.         SPI3_ReadWriteByte(0x00);            
    2 J6 l1 f- H1 `3 m8 T
  15.         SPI3_ReadWriteByte(0x00);                                     % e2 p6 L. t8 I7 u
  16.         Temp|=SPI3_ReadWriteByte(0xFF)<<8;  # q7 A" Y3 x/ N! m; l* H
  17.         Temp|=SPI3_ReadWriteByte(0xFF);         
    8 `8 U1 e) e9 v% r; a7 |
  18.         HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);  
    ) y  u2 p. B, s5 B+ h; ]7 j
  19.         return Temp;
    + L3 Y8 k) g- w7 p( d( ?
  20. }  
复制代码
. D) j* l: N$ [# P' X+ X' r
最后写一个在主函数中调用的接口函数。
! d  Q- X8 w9 F% R5 q9 z# p+ X2 l
' q( ^# L& R* ^6 w+ a4 O: b' j
  1. void W25QXX_Init(void)
    ( `" I, O7 W7 F6 ~2 P! n: Q
  2. {
    : d) |1 |. Z2 S* h" I) w, L+ [  v
  3.         MX_GPIO_Init();2 v8 z1 a- K0 V( b# ?+ s7 u
  4.         HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
    6 l* E  z! L9 ?, I# l# W4 C
  5.         SPI3_Init();2 l1 E! @+ h6 U
  6.         SPI3_SetSpeed(SPI_BAUDRATEPRESCALER_4);
    . z; n$ i) _% P8 y& ?( A% t
  7.         uint16_t W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.  
    ( n: u6 D$ ~$ G6 x9 h, F
  8.         printf("W25QXX ID:0x%4x\r\n",W25QXX_TYPE);
    3 U# g! |7 n5 `
  9. }
复制代码
) S/ i* x, @# |7 v7 y: t$ L
下载运行,读取到了芯片ID。
+ y  a4 I2 ?5 h
' b$ ]) f  U- y- x; {* B
20200423121114508.png
  i2 e* o( p, \) Q* O

! v; O, O6 G! T  R
收藏 评论0 发布时间:2021-12-12 21:44

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版