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

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

[复制链接]
STMCU小助手 发布时间:2021-12-12 21:44
SPI协议的原理,网上大把的资料可以找到,这里记录一下SPI的初始化过程,以即以读取W25Q16型号为例的一个简单的SPI读写过程。
& R: T9 K* T/ y0 q. m# x4 sCubeMX配置:  o1 }8 c  j6 p3 o  e* W4 f

: s) ~( H, s; E+ Z$ ^
20200423110312574.png
6 y5 U' s& Z. q& B
& y7 V$ |& L* q4 f) w: l3 e
SPI模式* p! V+ m: v. c
有只发送、只接收、半双工和全双工模式;  g; R# d: i9 b& A! \8 Q
根据自己的需求,和SPI设备支持的类型,这里选择全双工的SPI;" k! j4 g* s3 m6 {( f$ H7 l. Q

9 S1 [3 V8 w' P, r  [$ F& a( U片选信号
  b: A- u5 m+ H# Z3 q因为我们这个SPI总线上会挂载多个从设备,而且只有一个主机,所以,这里禁用硬件片选,程序里面读写之前使用软件进行片选;2 {  T9 M/ J. m0 L8 j

: l. k5 `$ _/ y帧格式
% D- d1 p! Q2 L# ?$ p; {: e5 V4 I这里选择Motorola帧格式;
3 |# |3 L+ M; H/ p$ \6 E& }( P选择TI格式不能设置CPOL和CPHA,而且必须要开启硬件NSS这里才能选择TI模式。+ ^6 a2 a, E( ^" A: V9 c! D7 S
博主在网上搜了一些关于TI的SSP协议相关的资料,只有简简单单的一句:SSP总线兼容SPI,SSI 和Microwire 总线的接口。
9 F, w8 r5 x9 |* `6 R博主又搜了以下Microwire协议,和SPI一样,只不过片选信号是高电平有效。
4 a" ^' i+ h% O0 n/ h( ]3 T6 E这是手册里关于TI模式的说明部分和波形图:. q3 \3 u) `- A

2 C( A- h+ z$ P7 q
20200423112839303.png

# L: R5 F! L6 k& j- s/ _8 W5 ~& Z  g
20200423112906213.png

9 s8 l' h8 O2 h2 X0 g
) d2 Y/ k1 v7 [8 i也不清楚这个SCK的触发信号是干嘛的,所以这里老老实实选择Motorola就行了。3 w  a8 e8 N0 o2 C  Y8 S) s: B

+ s9 X; J4 m  X: }, v! y4 i: w  j# t数据长度" p3 B. h, e' ]
4-16bit可选,根据实际,选择8bit;
: h: O7 y7 F! c! ]" r- X  V2 u% s  c1 S) b$ \+ X4 k' X
数据传输模式
3 n" ~2 ~9 R$ I6 p' o2 e9 H; l0 Q) u第一个传输的bit是高位还是低位,这里根据设备来选择,好像MSB(先发送高位)的比较多,注意这里的会同时设置发送和接收,如果设置错了,发送的指令对方就无法识别,可能收到的就是0xFF这种值。
; K! _3 \9 c# `$ w% s  D' m
7 ?- N/ ?% L% Z  `9 RSPI速率
6 w. A; d$ b- l% ~- H0 a& JSPI的波特率是由时钟分频得到的,CubeMX很方便的还帮我们计算出来了时钟速率,可以根据自己的需求设置。
5 ?  K% i! `$ y3 g& O) A( q" k$ D4 z! e9 L1 p3 q5 v
时钟极性和时钟相位! O1 M; D5 _5 o  P8 Q7 e4 v( E
根据极性和相位的不同,可以有4种不同的工作模式,这个根据芯片手册支持的模式选择,注意,一条总线上尽量挂载支持相通模式的设备,这样就不用每次在设备切换之前重新配置SPI外设。
8 R$ V. u9 Z( w$ I" J0 ]' t' f
2 @$ m1 f# D. D. K# ^; G  L* I4 dCRC校验; F, K' F1 Y1 Y# U; y) `" a9 e
暂时还没研究到这里……
. o; V. K' p9 R: S2 [: ~/ T3 x5 B2 `
& n/ C1 A; J& J) k8 ONSS脉冲

- V. u$ {% L8 O参考SPI的NSS 脉冲模式的作用,这里用不到;
- T( e6 x9 c! M+ j. {. M2 g! H! t, x8 T
NSS信号类型

, s5 H7 b6 d( P, q; p. U( D7 c' k因为前面禁用了硬件片选,所以这里只能设置位软件模式。4 W/ y- U! g5 g* ]4 [$ k6 l8 r8 [
- Y$ ~9 a+ q$ T% h
除此意外,还需要把连接硬件片选信号的GPIO,设置为输出模式。
. C, Y  ^, O; J( S( X" ]/ `6 `; K$ m  vCubeMX生成的代码如下:5 {  H+ a4 [5 ~+ L1 `5 B
8 D; M9 X! q+ |
  1. SPI_HandleTypeDef SPI3_Handler;  //SPI2句柄
    + u2 S$ i: c6 r

  2. # }& p4 H) _. K9 V! _6 V
  3. void SPI3_Init(void)
    # p- d2 o2 {$ s9 ?: I
  4. {
    8 y& g7 x* P% \( D
  5.     SPI3_Handler.Instance = SPI3;
    ) V9 `1 c' f4 }5 P0 ]" O, s) \
  6.     SPI3_Handler.Init.Mode = SPI_MODE_MASTER;
    2 f: C5 d1 P2 w; v' {1 E
  7.     SPI3_Handler.Init.Direction = SPI_DIRECTION_2LINES;: U8 R0 L6 m: G1 }4 z1 l
  8.     SPI3_Handler.Init.DataSize = SPI_DATASIZE_8BIT;
    9 M7 G. Q( _  Q8 q  K
  9.     SPI3_Handler.Init.CLKPolarity = SPI_POLARITY_LOW;
    ) `: b9 o" z4 A1 c3 l) Y5 W# o
  10.     SPI3_Handler.Init.CLKPhase = SPI_PHASE_1EDGE;
    3 d8 u9 K0 l: }9 E* B9 E& t
  11.     SPI3_Handler.Init.NSS = SPI_NSS_SOFT;. l' `9 o1 k" {: R' l4 Z3 U
  12.     SPI3_Handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;- r3 p! P9 z1 q0 v5 n. q/ G
  13.     SPI3_Handler.Init.FirstBit = SPI_FIRSTBIT_MSB;
    1 Q% v4 ?4 u6 Q( A7 |7 {
  14.     SPI3_Handler.Init.TIMode = SPI_TIMODE_DISABLE;8 |4 c# Q3 T( o2 O% [( ?
  15.     SPI3_Handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;' f: c1 M2 l, ]) n+ M
  16.     SPI3_Handler.Init.CRCPolynomial = 7;" \# N) p4 P# H1 |& d1 X9 j6 R
  17.     SPI3_Handler.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
    ! ?6 {1 U  D' w0 \
  18.     SPI3_Handler.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;( e$ b; Y, A' M% z, ~5 I; C
  19.     HAL_SPI_Init(&SPI3_Handler);3 d8 t8 c6 ~4 N& p. x

  20. , k& A& u( i6 o
  21.     __HAL_SPI_ENABLE(&SPI3_Handler);               
    - `$ q, g' Q2 a3 A. y
  22. }3 l7 b6 b' u4 T4 O" t+ B$ A
  23. 4 Y+ [3 H& q8 m. Z! w& t& y
  24. void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)* V: }- }! k  K5 w. Z9 T; ?
  25. {
    5 `- ^- ^$ e8 Z% g# f3 ~9 K
  26.   GPIO_InitTypeDef GPIO_InitStruct = {0};
    $ \1 `7 _& }1 X7 w- S
  27.   if(hspi->Instance==SPI3)9 I9 Y: F" q: `0 r6 Z
  28.   {/ F/ e3 S# n* P' K9 N% t) N
  29.   h* e6 m% m+ W/ V
  30.     __HAL_RCC_SPI3_CLK_ENABLE();/ v. M: K$ w# W" W6 o* E
  31. " u* K, O. O9 k0 \
  32.     __HAL_RCC_GPIOB_CLK_ENABLE();7 R7 w3 B' ~5 R4 c6 A  \
  33. ( O9 F6 E; J* n
  34.     GPIO_InitStruct.Pin = GPIO_PIN_2;
    $ D( b9 a8 u& a
  35.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;; d* j1 X% A; y: H. [, J0 ~4 V
  36.     GPIO_InitStruct.Pull = GPIO_NOPULL;3 ]5 p. E2 F7 [8 A& T+ u1 `' h* Z
  37.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    1 V) Q. P8 b9 k! @# J4 C
  38.     GPIO_InitStruct.Alternate = GPIO_AF7_SPI3;% x; E# K, W' c8 ?( H
  39.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    % k6 P- J' S: A* ]
  40. # G; h  e4 X1 a2 d. f9 S6 J
  41.     GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4;( K1 e$ S5 I6 O& V
  42.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;0 W1 u0 ^, v5 j
  43.     GPIO_InitStruct.Pull = GPIO_NOPULL;
    - p' }. q: n$ E; P% E' {
  44.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;$ z/ f+ f& Z5 g. O' n7 q8 u* E
  45.     GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;9 p$ m1 j6 S7 b
  46.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);+ a: C4 k! M, ~$ u" Q
  47.   }
    ) ?( p& f% o" N5 B; K" O2 c8 V; r
  48. }, w) N; v1 W2 V; m$ E0 Y

  49. / A4 G" y& w* h: w: H

  50. / Q7 U. A' T& m4 r9 c
  51. static void MX_GPIO_Init(void)8 H8 {& u8 u$ x$ q3 D
  52. {
    / x8 t# \% p# I2 @
  53.   GPIO_InitTypeDef GPIO_InitStruct = {0};
    ) O$ b/ }* X  U1 z  w+ l

  54. 7 A  ]$ x! j3 \
  55.   /* GPIO Ports Clock Enable */. }: u9 z& ?7 ?: A
  56.   __HAL_RCC_GPIOA_CLK_ENABLE();# |  ^! A" g- C1 z, ]
  57. * k5 U" d7 t4 r
  58.   /*Configure GPIO pin Output Level */
    / c  D0 {6 C7 d, _. u
  59.   HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    ; `) x; J5 e* ]  @* M
  60. $ c2 y: M  s3 k
  61.   /*Configure GPIO pin : PA4 */# x2 K# b& t, g$ ^; c
  62.   GPIO_InitStruct.Pin = GPIO_PIN_4;
    * H5 ^* E3 A$ F9 D; }  Z( ~
  63.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;; r% N! s5 K: o( ?  f
  64.   GPIO_InitStruct.Pull = GPIO_NOPULL;
    7 r8 K% O- p2 ]4 N6 F
  65.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;) V2 M& A6 ?/ w& ?) _$ N6 Z
  66.   HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    ( q9 o8 b) A4 \9 W3 m4 i

  67. 5 ?" x2 A# `! a+ r: [6 e
  68. }
复制代码

* R% ^5 h7 ?8 A( ^" A同一总线上不同设备,工作在不同模式的时候,速率可能不一样,需要一个重新设置速率的函数:% B6 J" h: q; W1 q
$ M* Z$ T6 ?* Z, E6 B. |% t( M
  1. void SPI3_SetSpeed(u8 SPI_BaudRatePrescaler)4 k' G0 q4 A) L/ u
  2. {+ U: x1 E! X$ @) v6 W. Z1 t1 A
  3.     assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性
    8 q* J/ M7 o; Z% C# n$ w: M
  4.     __HAL_SPI_DISABLE(&SPI3_Handler);            //关闭SPI
    4 K8 w; o" Q% R  o9 \) h. B2 I
  5.     SPI3_Handler.Instance->CR1 &= 0XFFC7;        //位3-5清零,用来设置波特率
    2 B/ c* X; r, D$ s3 q& A
  6.     SPI3_Handler.Instance->CR1 |= SPI_BaudRatePrescaler; //设置SPI速度0 G, J# X) ?3 `. u* ?- V; Z" `
  7.     __HAL_SPI_ENABLE(&SPI3_Handler);             //使能SPI- x9 ^, L8 t& w( L5 s! I  P

  8. 6 D* U8 F+ O( G" l
  9. }
复制代码

% K; v# _/ u, X  h' T) |因为全双工的读写是同时的,封装一个读写函数:
% _+ {) Y' C- C$ E  a4 D, N( S. G3 h9 H# S
  1. //SPI2 读写一个字节% U* Y# ]$ D; s$ W, I
  2. //TxData:要写入的字节+ V; \  n; a+ p  Q
  3. //返回值:读取到的字节
    ; V) h1 g  m) J& V" t: k7 t# \; z; h( W" w
  4. u8 SPI3_ReadWriteByte(u8 TxData)3 s7 s6 }+ g" M
  5. {+ i' j/ m& j* d. H" o& H
  6.     u8 Rxdata;) P4 S2 L" P5 }' s4 }
  7.     HAL_SPI_TransmitReceive(&SPI3_Handler, &TxData, &Rxdata, 1, 1000);
    9 }0 g# m  J9 M2 G/ r( h
  8.     return Rxdata;                      //返回收到的数据
    2 M: T% p4 i! F: p+ j: |
  9. }
复制代码
) B$ B2 D/ ~( }* z
根据W25QXX的数据手册,写一个读ID的函数:
. A8 `9 H4 Z! l0 N$ i# Q# O/ Q
  1. //读取芯片ID- J! h( f4 P+ h+ i
  2. //返回值如下:                                   5 L6 X; l8 J! T6 M
  3. //0XEF13,表示芯片型号为W25Q80  & j( g0 G3 Y* {! a1 Y! a+ |
  4. //0XEF14,表示芯片型号为W25Q16    & U/ A, f; Y) Y- h
  5. //0XEF15,表示芯片型号为W25Q32  0 i; m; [: U% G) N6 B2 }! H
  6. //0XEF16,表示芯片型号为W25Q64
    ; R* |; k6 e! [$ t/ q" \0 U7 L! _8 m
  7. //0XEF17,表示芯片型号为W25Q128           
    & ~  P3 k/ l( R+ x
  8. u16 W25QXX_ReadID(void)+ h6 M  b3 ~: M
  9. {
    1 t+ A6 J0 J% ~! ]
  10.         u16 Temp = 0;          ! c- t' f/ Z4 k
  11.         HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);            
    ! b3 s2 n% D% q/ A
  12.         SPI3_ReadWriteByte(0x90);//发送读取ID命令            
    6 g. z+ |7 Y1 p3 E% |
  13.         SPI3_ReadWriteByte(0x00);            
    ( \5 f. ~  |0 c) o; ]' r" _/ ~
  14.         SPI3_ReadWriteByte(0x00);             4 ]* y3 u$ j: W# @' a
  15.         SPI3_ReadWriteByte(0x00);                                     * `* T; M" @$ Z; s
  16.         Temp|=SPI3_ReadWriteByte(0xFF)<<8;  
    ) n& L" G% W3 O+ a# o, B' G1 ~
  17.         Temp|=SPI3_ReadWriteByte(0xFF);         3 x: \- O1 [- t! c2 v
  18.         HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);  
      A5 @- {* O/ K
  19.         return Temp;
    6 |! d8 o( a, ?- E
  20. }  
复制代码
( ]( f6 g; X! ~$ p" D& Y% w0 y) C
最后写一个在主函数中调用的接口函数。
3 j7 @2 T" n. }/ K0 L. V
! Y2 d* W8 F4 g% y& k
  1. void W25QXX_Init(void)
    # P' d% R0 L0 G
  2. {2 n+ H" z  G, J" D- j
  3.         MX_GPIO_Init();- H& n1 a+ R9 E
  4.         HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);0 S# ^1 y3 i7 {
  5.         SPI3_Init();
    3 x- p) A8 g0 g6 B
  6.         SPI3_SetSpeed(SPI_BAUDRATEPRESCALER_4);
    ) J) R, E7 V5 \9 g2 u# W0 w3 w
  7.         uint16_t W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.  
    2 i. D" p. R3 ^. x' W* d  j, V
  8.         printf("W25QXX ID:0x%4x\r\n",W25QXX_TYPE);7 ~1 H6 T+ |2 P1 B
  9. }
复制代码
- J# `8 M( @0 G: t
下载运行,读取到了芯片ID。" P' M* V+ R$ U( |9 f. X
5 F' N: H8 j" I9 P7 B  j+ F
20200423121114508.png

  f- _1 Q- K- r$ |$ Y! C4 M
5 y7 Z  o) H' ]
收藏 评论0 发布时间:2021-12-12 21:44

举报

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