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

【经验分享】STM32F7xx —— QSPI

[复制链接]
STMCU小助手 发布时间:2021-12-11 12:00
一、QSPI
$ D4 A% _3 m5 P! q( W        SPI 是 Queued SPI 的简写,是 Motorola公司推出的 SPI 接口的扩展,比 SPI 应用更加广泛。在 SPI 协议的基础上,Motorola 公司对其功能进行了增强,增加了队列传输机制,推出了队列串行外围接口协议(即 QSPI 协议),使用该接口,用户可以一次性传输包含多达16个8位或16位数据的传输队列。一旦传输启动,直到传输结束,都不需要CPU干预,极大的提高了传输效率。该协议在ColdFire系列MCU得到广泛应用。与SPI相比,QSPI的最大结构特点是以80字节的RAM代替了SPI的发送和接收寄存器。QSPI 是一种专用的通信接口,连接单、双或四(条数据线) SPI Flash 存储介质。8 J% A: j. b: G# x# }1 k
  A8 L7 s/ v0 U& G2 G6 _
该接口可以在以下三种模式下工作:
1 w( V9 t9 Z. f# o, b① 间接模式:使用 QSPI 寄存器执行全部操作/ o4 M" c( E# K# P" y2 ^  q; A% C2 m
② 状态轮询模式:周期性读取外部 Flash 状态寄存器,而且标志位置 1 时会产生中断(如擦除或烧写完成,会产生中断)
1 l* C. |1 z, K# S- k/ W  T, `  q1 C③ 内存映射模式:外部 Flash 映射到微控制器地址空间,从而系统将其视作内部存储器。
4 [' I) k6 Z2 o5 n7 }  q6 z# B, }+ c2 T9 g  p
QSPI通过6根线与SPI芯片通信,下图是内部框图:3 l' H7 P- l4 s+ i

1 n9 I! X" S9 p" M- `
20190123173205949.png

/ B. J* _, K% \8 z; T2 f+ ^$ t% f5 m- f& ~' K: [" y5 c
QSPI每条命令,必须包含一个或多个阶段:指令、地址、交替字节、空指令和数据。
; l+ d0 K. \( t7 O7 Z9 t
1 Z) R0 j9 r( h8 Z- V# V# AQSPI发送命令:等待QSPI空闲;设置命令参数。% D9 ]$ ^9 w  l0 P1 O

. a. G( L1 [) j9 [) B/ n1 OQSPI读数据:设置数据传输长度;设置QSPI工作模式并设置地址;读取数据。$ [8 e+ }2 P; f! j. ^" D

! c- o5 Y; W- z) ^: K6 JQSPI写数据:设置数据传输长度;设置QSPI工作模式并设置地址;写数据。' H6 `5 V- J5 H0 G6 X) D2 u
+ v& a" H8 _* B2 H, |( u" Y

1 p" k$ T$ u5 O& ~4 |5 z/ l" ?二、几个重要的函数2 k/ v0 H% w/ h; V! T
  1. HAL_StatusTypeDef     HAL_QSPI_Init     (QSPI_HandleTypeDef *hqspi); // 初始化7 P* I3 B8 w9 l, B7 a& O5 h8 n
  2. 4 w, z* Q- l6 d
  3. HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout); // 发送命令
    ! ^/ l2 x- F2 s8 m, y; s. b

  4. / p# e! ?) N' ?! F- [
  5. HAL_StatusTypeDef     HAL_QSPI_Transmit     (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 发送数据
    + n8 `  d$ c% u" d  q
  6. 0 Z, S; J& |( }1 a9 k9 H) ^% G
  7. HAL_StatusTypeDef     HAL_QSPI_Receive      (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 接收数据
复制代码

& }  N7 _6 Q8 c" \  K三、几个重要的结构
& m5 X3 l& M0 o$ I% U
  1. // QSPI操作句柄, \! \2 O$ s8 e+ y3 U+ W
  2. typedef struct% E4 m+ V1 X: g" b" H. ~
  3. {
    # b1 [) o8 Z% p* X
  4.   QUADSPI_TypeDef            *Instance;        /* QSPI registers base address        */
    , F& p; B" x/ D  a/ g
  5.   QSPI_InitTypeDef           Init;             /* QSPI communication parameters      */
    & k3 \; j  Q% V4 ~5 l* i# ^
  6.   uint8_t                    *pTxBuffPtr;      /* Pointer to QSPI Tx transfer Buffer */2 @3 c- T1 Y# }, H: p+ t( Y& q
  7.   __IO uint16_t              TxXferSize;       /* QSPI Tx Transfer size              */
    $ I5 O8 ]9 X9 v9 v" O$ |+ |
  8.   __IO uint16_t              TxXferCount;      /* QSPI Tx Transfer Counter           */% o& P" N" T7 e: j) w( Z' I9 u
  9.   uint8_t                    *pRxBuffPtr;      /* Pointer to QSPI Rx transfer Buffer */
    * S# a- ?  {: Q3 z6 e
  10.   __IO uint16_t              RxXferSize;       /* QSPI Rx Transfer size              */
    # c  d9 A. F3 K4 f3 \
  11.   __IO uint16_t              RxXferCount;      /* QSPI Rx Transfer Counter           */. j1 [" x) F; x2 I% L: s2 j5 J
  12.   DMA_HandleTypeDef          *hdma;            /* QSPI Rx/Tx DMA Handle parameters   */
    1 ?: Z5 y0 Y2 F4 [
  13.   __IO HAL_LockTypeDef       Lock;             /* Locking object                     */
    ( [: ?0 t9 ^0 h% k9 V" [
  14.   __IO HAL_QSPI_StateTypeDef State;            /* QSPI communication state           */
    + B; i" _* a+ {( U7 o* n
  15.   __IO uint32_t              ErrorCode;        /* QSPI Error code                    */
    # X% ~: v4 X8 @, b1 ?& B/ {5 y5 p
  16.   uint32_t                   Timeout;          /* Timeout for the QSPI memory access */
    8 l2 `+ j8 x$ f" Q/ z4 Y
  17. }QSPI_HandleTypeDef;7 v/ l; L# L% u+ _' B' w
  18. . h% d* @! a+ o& Q
  19. // Instance:QSPI基地址  ---  QUADSPI
    ' P- b  a  X2 y& Q' l+ G
  20. // Init:设置QSPI参数
    ! F7 f* R2 p9 b( F
  21. // pTxBuffPtr,TxXferSize,TxXferCount:发送缓存指针 发送数据量 剩余数据量1 s' v. [& p: a4 ^9 ^" c
  22. // pRxBuffPtr,RxXferSize,RxXferCount:接收缓存指针 发送数据量 剩余数据量
    ) \# w9 v/ W) q, Q
  23. // hdma与DMA相关, 其他为过程变量不需要关心
复制代码
  1. // 参数配置 时钟分频系数  FIFO阈值  采样移位  FLASH大小  片选高电平时间  时钟模式  闪存ID  双闪存模式设置
    + R4 U# A7 H/ a" X5 P, ^
  2. typedef struct
    - _6 l' g$ Z9 P
  3. {: U3 g7 B7 M1 ]
  4.   uint32_t ClockPrescaler;     /* Specifies the prescaler factor for generating clock based on the AHB clock.* Z1 f$ X5 k3 n9 \. U! @
  5.                                   This parameter can be a number between 0 and 255 */
    ; m9 S0 L, W: N. D1 y: a
  6. : l5 y/ g6 e. H7 {6 N: n2 F3 D7 f# ?
  7.   uint32_t FifoThreshold;      /* Specifies the threshold number of bytes in the FIFO (used only in indirect mode)8 _0 S. n8 k6 t" j
  8.                                   This parameter can be a value between 1 and 32 */
    : k$ \$ H" P+ U+ j" q

  9. " O; n5 X8 d& V4 i6 P
  10.   uint32_t SampleShifting;     /* Specifies the Sample Shift. The data is sampled 1/2 clock cycle delay later to + l& J! [9 H8 I/ @1 k
  11.                                   take in account external signal delays. (It should be QSPI_SAMPLE_SHIFTING_NONE in DDR mode)) P7 b: ~8 [/ g* C2 S! r4 r
  12.                                   This parameter can be a value of @ref QSPI_SampleShifting */0 \' T% X- t! H: H7 p9 A, c
  13. 6 X7 W  ?8 f3 @
  14.   uint32_t FlashSize;          /* Specifies the Flash Size. FlashSize+1 is effectively the number of address bits
    4 b0 ]; S* U% P
  15.                                   required to address the flash memory. The flash capacity can be up to 4GB 7 m+ I# Y. v. V: H
  16.                                   (addressed using 32 bits) in indirect mode, but the addressable space in
    , `4 c0 Q( t; H( [7 }# D) j- C8 C
  17.                                   memory-mapped mode is limited to 256MB1 s$ e2 j0 }  [; n% W
  18.                                   This parameter can be a number between 0 and 31 */
    * p* R( @  n6 H7 ]4 z" M
  19. 1 V$ ^( q9 ^  n! L& W
  20.   uint32_t ChipSelectHighTime; /* Specifies the Chip Select High Time. ChipSelectHighTime+1 defines the minimum number
    8 N5 x& v6 Q7 {/ z# K$ X( _
  21.                                   of clock cycles which the chip select must remain high between commands.3 s8 x9 o- E" X
  22.                                   This parameter can be a value of @ref QSPI_ChipSelectHighTime */
    ' [( m5 o, x) m$ s
  23. 5 _. B8 p5 n3 A9 @, l( r) J% z
  24.   uint32_t ClockMode;          /* Specifies the Clock Mode. It indicates the level that clock takes between commands.
    6 E  f8 k2 V* d. U7 `" K
  25.                                   This parameter can be a value of @ref QSPI_ClockMode */  u' Y% i% a6 M4 R  @  U
  26. * o7 H+ y: K- j/ f8 w
  27.   uint32_t FlashID;            /* Specifies the Flash which will be used,
    : |9 c! `; [9 S4 Z: J
  28.                                   This parameter can be a value of @ref QSPI_Flash_Select */
    * a1 O5 `  }/ \

  29. " o4 ?0 U7 N7 D5 R  y8 H) h" [
  30.   uint32_t DualFlash;          /* Specifies the Dual Flash Mode State' T) j$ h* h6 r: K1 C+ |! e0 G
  31.                                   This parameter can be a value of @ref QSPI_DualFlash_Mode */                                               6 M8 M+ h# d3 w9 W/ {
  32. }QSPI_InitTypeDef;
复制代码
  1. // 采样移位$ H% m  o; l+ w: C- C. q. S
  2. #define QSPI_SAMPLE_SHIFTING_NONE           ((uint32_t)0x00000000U)        /*!<No clock cycle shift to sample data*/
    $ Q! G) Q) ]. K
  3. #define QSPI_SAMPLE_SHIFTING_HALFCYCLE      ((uint32_t)QUADSPI_CR_SSHIFT) /*!<1/2 clock cycle shift to sample data*/
复制代码

  n( u. x+ a* l) V  d' l四、QSPI接口设计(仅供参考)
) R2 A4 e0 O  J( I& B6 k! X+ Z
  1. #define QSPI_CLK_ENABLE()         __HAL_RCC_QSPI_CLK_ENABLE();
    ( W2 l% ~5 s2 i( H% D
  2. . m0 @$ J6 ?" }2 z  f& n" J
  3. #define QSPI_BK1_NCS_PORT         GPIOB0 s( U2 x5 b3 |( l2 f2 ]9 H; ^9 h: T
  4. #define QSPI_BK1_NCS_PIN          GPIO_PIN_65 |9 B$ M* s" l& u6 ~+ k
  5. #define QSPI_BK1_NCS_AF           GPIO_AF10_QUADSPI
    : a2 {3 k- y; D! ]* r9 R! n
  6. #define QSPI_BK1_NCS_CONFIG()     GPIOConfigExt(QSPI_BK1_NCS_PORT, QSPI_BK1_NCS_PIN, GPIO_MODE_AF_PP, GPIO_PULLUP, QSPI_BK1_NCS_AF), v+ W& X# f& u4 Y' G
  7. 8 x) @0 @3 x( n- C5 F8 m1 ~
  8. #define QSPI_BK1_CLK_PORT         GPIOB! N! H" E( `" s! m; q7 q. [
  9. #define QSPI_BK1_CLK_PIN          GPIO_PIN_2
    * a' D3 Q2 j$ ~0 G6 y' G* r; }
  10. #define QSPI_BK1_CLK_AF           GPIO_AF9_QUADSPI4 R* Z% n+ p  f+ H+ \
  11. #define QSPI_BK1_CLK_CONFIG()     GPIOConfigExt(QSPI_BK1_CLK_PORT, QSPI_BK1_CLK_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_CLK_AF)% o& Q  P6 O4 c

  12. 9 d6 @; Z( @# F0 x# r/ L/ x
  13. #define QSPI_BK1_IO0_PORT         GPIOF) x- z9 S/ U. ]5 q1 m: m
  14. #define QSPI_BK1_IO0_PIN          GPIO_PIN_85 D+ W; |+ K4 R8 y
  15. #define QSPI_BK1_IO0_AF           GPIO_AF10_QUADSPI0 i$ j0 R) {7 v& Z: H( I: n* n
  16. #define QSPI_BK1_IO0_CONFIG()     GPIOConfigExt(QSPI_BK1_IO0_PORT, QSPI_BK1_IO0_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_IO0_AF)
    ) A9 I. U" [+ n

  17. & G4 l; M" o( ?% M5 I' C8 ]
  18. #define QSPI_BK1_IO1_PORT         GPIOF
    . ^0 V; U3 u. p( j6 `
  19. #define QSPI_BK1_IO1_PIN          GPIO_PIN_9( ~! ?9 K0 ]( a
  20. #define QSPI_BK1_IO1_AF           GPIO_AF10_QUADSPI
    / h! ?9 C2 @; P2 r! N" ^5 V# X
  21. #define QSPI_BK1_IO1_CONFIG()     GPIOConfigExt(QSPI_BK1_IO1_PORT, QSPI_BK1_IO1_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_IO1_AF)
    $ Y" h( B' h: f( T9 P( Y" o! |7 f

  22. % m- {: k- j. n
  23. #define QSPI_BK1_IO2_PORT         GPIOF
    4 X9 R- S3 r6 B' Q8 c8 k; {2 X* H
  24. #define QSPI_BK1_IO2_PIN          GPIO_PIN_7
    & u' `: i( W2 l5 i8 I+ h) |8 A
  25. #define QSPI_BK1_IO2_AF           GPIO_AF9_QUADSPI0 F+ Q7 X; H  C
  26. #define QSPI_BK1_IO2_CONFIG()     GPIOConfigExt(QSPI_BK1_IO2_PORT, QSPI_BK1_IO2_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_IO2_AF)' X' ]( K7 O4 a. H5 K: v0 J
  27. 2 @) N0 v- n4 w. X+ K+ B
  28. #define QSPI_BK1_IO3_PORT         GPIOF5 F. R% H; r% o
  29. #define QSPI_BK1_IO3_PIN          GPIO_PIN_6: `) a, H. V" r& p2 D
  30. #define QSPI_BK1_IO3_AF           GPIO_AF9_QUADSPI
    : R& Y8 j3 W" @0 X/ k: W* O
  31. #define QSPI_BK1_IO3_CONFIG()     GPIOConfigExt(QSPI_BK1_IO3_PORT, QSPI_BK1_IO3_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_IO3_AF)
复制代码
  1. // 封装几个必要的接口( C5 B9 k( a9 v8 X- l
  2. static QSPI_HandleTypeDef qspi_handle;4 G/ }* b2 l: J; A7 V0 l/ S$ k; \% z
  3. 8 C' P+ |, R' `* I7 r/ Y, {; t
  4. static void qspi_gpio_init(void)5 l4 J, q$ k; d3 `3 d0 s
  5. {3 F# p% G/ R$ x/ E4 g* H9 A
  6.   QSPI_CLK_ENABLE();. w6 _: J5 c& k8 A7 S- _4 j
  7. * _$ i5 A. g, k2 h1 m
  8.   QSPI_BK1_NCS_CONFIG();
    2 A4 d- g" g" o% C/ ~, k
  9.   QSPI_BK1_CLK_CONFIG();+ [1 ]$ g$ z4 }; n+ j/ y" A% c2 T
  10.   QSPI_BK1_IO0_CONFIG();; O; Y, C- L! ?6 A" f& I
  11.   QSPI_BK1_IO1_CONFIG();
    9 [% V6 P% \3 a% K( J. p' r* Y7 J
  12.   QSPI_BK1_IO2_CONFIG();) D! h! `' `5 z. P2 p1 v3 f
  13.   QSPI_BK1_IO3_CONFIG();
    $ t% q: z4 I( x* z# I/ Z5 ~4 z
  14. }
    , t/ B( y( L7 h9 d
  15. ; R. b3 ~+ U  Q3 E
  16. static void qspi_mode_init(void)1 d* ~( V  g  i
  17. {
    * l7 s0 ?4 t& l) u6 m8 B
  18.   qspi_handle.Instance = QUADSPI;                        // QSPI
    5 [0 `! L) J4 h+ ~% V  m3 |
  19.   qspi_handle.Init.ClockPrescaler = 2;                   // QPSI分频比,W25Q256最大频率为104M 所以此处应该为2,QSPI频率就为216/(2+1)=72MHZ* A" A: L0 w) G$ b7 y4 G) e
  20.   qspi_handle.Init.FifoThreshold = 4;                    // FIFO阈值为4个字节# S( Q6 V, {7 Y# E( K- N; o
  21.   qspi_handle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; // 采样移位半个周期(DDR模式下,必须设置为0)
    9 w0 t8 {% g; k6 r2 D
  22.   qspi_handle.Init.FlashSize = POSITION_VAL(0X2000000) - 1; // SPI FLASH大小,W25Q256大小为32M字节. m" j# u  m. u2 M" u
  23.   qspi_handle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_4_CYCLE; // 片选高电平时间为4个时钟(13.8*4=55.2ns),即手册里面的tSHSL参数
    # r; D$ w& {2 d9 Q5 ^/ `- p
  24.   qspi_handle.Init.ClockMode = QSPI_CLOCK_MODE_0;        // 模式0( ], E8 W8 @6 v) w3 C0 C# ?& L! I
  25.   qspi_handle.Init.FlashID = QSPI_FLASH_ID_1;            // 第一片flash, U# M& o$ c0 ]
  26.   qspi_handle.Init.DualFlash = QSPI_DUALFLASH_DISABLE;   // 禁止双闪存模式7 Q- y1 p3 Q5 q$ ~
  27.   HAL_QSPI_Init(&qspi_handle);      //QSPI初始化
    , w4 D) ?( `% z" h  C4 O- D
  28. }
    3 T+ r" b! @, O( y

  29. : G& W& {; J' b& j' S# |& g% I
  30. void QSPIInit(void)1 ?2 G9 Y- F. g8 H  r
  31. {
    / J* k! I. p* i' w
  32.   qspi_gpio_init();
    & }% E6 F$ D9 S8 u# B+ `% w5 {
  33.   qspi_mode_init();
    4 E' [- _; Y0 a9 q: o
  34. }2 f2 A/ E6 ?8 V, ?" x2 b' v1 m

  35. $ ]" J8 g: d$ R6 J2 b
  36. // QSPI发送命令+ T- o" p/ p+ |1 [5 D! R% M
  37. // instruction:要发送的指令! D5 A! C# M6 @" X. t' d5 p0 @" H, D
  38. // address:发送到的目的地址* b& w: Q) l9 w, u, k
  39. // dummyCycles:空指令周期数& ^! _& J, q) H+ ~5 z2 i$ Q
  40. // instructionMode:指令模式;QSPI_INSTRUCTION_NONE,QSPI_INSTRUCTION_1_LINE,QSPI_INSTRUCTION_2_LINE,QSPI_INSTRUCTION_4_LINE
    + n2 [( M, C: g0 A! ]8 t/ a
  41. // addressMode:地址模式; QSPI_ADDRESS_NONE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_2_LINE,QSPI_ADDRESS_4_LINE
    ) E7 ]9 N& F1 }4 h+ V
  42. // addressSize:地址长度;QSPI_ADDRESS_8_BITS,QSPI_ADDRESS_16_BITS,QSPI_ADDRESS_24_BITS,QSPI_ADDRESS_32_BITS* e1 X/ u" Z" V2 A" x
  43. // dataMode:数据模式; QSPI_DATA_NONE,QSPI_DATA_1_LINE,QSPI_DATA_2_LINE,QSPI_DATA_4_LINE
    : p1 [6 h, g4 J% P1 B7 S6 b$ j
  44. void QSPISendCMD(uint32_t instruction, uint32_t address, uint32_t dummyCycles, uint32_t instructionMode, uint32_t addressMode, uint32_t addressSize, uint32_t dataMode)
    . R$ L  {% g" S: ]" v- K+ [  x- K$ z: f
  45. {
    & c! w  S- a, K" k# h, E$ J
  46.   QSPI_CommandTypeDef Cmdhandler;
    6 D& L' N; v  ^7 t

  47. " [7 L1 {0 y6 p" ?3 Z- m
  48.   Cmdhandler.Instruction = instruction;           // 指令
    3 @& h0 K4 w' v  s3 w& C) Y
  49.   Cmdhandler.Address = address;                   // 地址
    9 U! R0 M7 i3 N
  50.   Cmdhandler.DummyCycles = dummyCycles;           // 设置空指令周期数
    " H5 w9 g% }1 z+ y
  51.   Cmdhandler.InstructionMode = instructionMode;   // 指令模式
    ! A6 Y" o7 t7 C4 C4 l9 M
  52.   Cmdhandler.AddressMode = addressMode;           // 地址模式( m: R' ]2 {8 q) S( r) p/ r' ]
  53.   Cmdhandler.AddressSize = addressSize;           // 地址长度
    : Z- c7 b! d$ ^5 a% G+ ^
  54.   Cmdhandler.DataMode = dataMode;                 // 数据模式
    ( k- U+ Q1 B8 g/ w
  55.   Cmdhandler.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 每次都发送指令- M5 B  R# N- M+ g  d
  56.   Cmdhandler.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 无交替字节: a+ _( P6 M" v# Z% Z6 F
  57.   Cmdhandler.DdrMode = QSPI_DDR_MODE_DISABLE;           // 关闭DDR模式/ `3 G, J: v3 O6 k  m9 B
  58.   Cmdhandler.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    5 E; T( @0 s6 `( x+ l' D9 v9 }8 P  H

  59. . ?  ~& M# l% t6 Y2 Z
  60.   HAL_QSPI_Command(&qspi_handle, &Cmdhandler, 5000);
    $ }$ Y" d4 W9 x: ~& z
  61. }
    8 g' L3 U( X; k7 Q2 x( R
  62. * R+ D1 f# C- g9 j& ?
  63. // 接收指定长度数据
    : v+ c  h+ f7 _7 q6 B6 O% U" t
  64. uint8_t QSPIReceive(uint8_t *buffer, uint32_t length)4 ~8 _) V" I# \8 l' J, O5 U2 X6 H
  65. {* [+ a5 Q# j7 a) f1 i4 A
  66.   qspi_handle.Instance->DLR = length - 1;% K6 V. l2 I! U9 w% k7 y; @0 s
  67.         0 S+ A/ Y3 q; `3 T1 }8 u; l
  68.   if(HAL_QSPI_Receive(&qspi_handle, buffer, 5000) == HAL_OK)
    ; A. O3 E: ^0 z" S$ f1 V5 V
  69.   {& `+ v/ _) l( Y
  70.     return 0;  //接收数据
    ' ?* ^: Q! ~, H- v% f
  71.   }
    7 B8 U8 v' d! L8 j7 X0 P, @
  72.   else
    ( w3 F" {  V9 k3 h
  73.   {/ }6 q$ j# u/ Y1 T" P, r6 J) U8 P
  74.     return 1;& k2 k4 ]$ L$ ~% F  U2 v
  75.   }  w6 j: w/ V2 L! J8 T
  76. }$ A3 N0 ]  d( u. \" G8 O! J, {
  77. 7 O/ {, }; L& F6 k( x* O2 a" T& Z
  78. // 发送指定长度数据2 ]& P2 ^+ u7 |  g4 N9 E; i% k9 v
  79. uint8_t QSPITransmit(uint8_t *buffer, uint32_t length)9 e% Y) m& K9 A2 z" S
  80. {6 i5 h  m- @0 H  C: X
  81.   qspi_handle.Instance->DLR = length - 1;
    " r6 A& V% [) z! ^8 ^
  82.   if(HAL_QSPI_Transmit(&qspi_handle, buffer, 5000) == HAL_OK)
    5 o, j" x2 Y. Q( l# c; J$ N# t
  83.   {
    , |$ y& L3 v/ K4 B8 B  S" ]+ A/ l# k
  84.     return 0;  //发送数据
    2 L3 g& a6 e) ~; u
  85.   }
    ( N1 G  Z( ]' {) t
  86.   else
    1 w' z9 b5 p7 {4 y% f1 E* m
  87.   {
    , C6 \% L8 P/ t  m- Q2 P4 O
  88.     return 1;. c. a6 Y# D. R6 a
  89.   }5 d* a- P, N' S( m# Z& D
  90. }
复制代码
- P; _: }+ Q3 K0 r! J7 W
五、QSPI驱动W25Q256" M# d0 C2 D2 }6 l) O
W25Q256:32M  512块,每块16个扇区,每个扇区4K。具体指令看手册。封装了如下接口(初始化读写擦除等接口是以后移植文件系统的基础):$ _% A- r. d% r# m, y9 Z3 L4 O
) ]- X8 h6 B+ Y( ]  X8 `
  1. #define W25QXX_WRITE_ENABLE      0x06* n+ Y6 Z9 ]  }
  2. #define W25QXX_WRITE_DISABLE     0x04
    " }8 ^" M' w6 m. w# C6 j
  3. #define W25QXX_READ_STATUS_REG1  0x055 ?) Y6 c) i+ d; B7 g
  4. #define W25QXX_READ_STATUS_REG2  0x35( J# I1 K5 M8 Z
  5. #define W25QXX_READ_STATUS_REG3  0x15
    ; L4 J- _/ g6 M; r9 u
  6. #define W25QXX_WRITE_STATUS_REG1 0x01+ L$ Y, ~# ]* ~4 S
  7. #define W25QXX_WRITE_STATUS_REG2 0x31
    * F+ P  D0 {( U! ~2 U: R* X
  8. #define W25QXX_WRITE_STATUS_REG3 0x11
    $ o* I% F& {0 f4 U1 c1 U0 d- e$ A
  9. #define W25QXX_READ_DATA         0x03: L9 l6 s. B' X( D$ p
  10. #define W25QXX_FAST_READ_DATA    0x0B* H* s1 h2 ]$ E
  11. #define W25QXX_FAST_READ_DUAL    0x3B7 l2 F$ `8 V  l- U. t0 Q
  12. #define W25QXX_PAGE_PROGRAM      0x026 O: S! r' R& E
  13. #define W25QXX_BLOCK_ERASE       0xD8
    + T, p4 v' n" g' k% n& P- ^- a! u
  14. #define W25QXX_SECTOR_ERASE      0x20
    ; o- H* K2 g+ t4 k% g
  15. #define W25QXX_CHIP_ERASE        0xC7
    4 T* b7 j2 H. B: C- V- `
  16. #define W25QXX_DEVICEID          0xAB
      o) M- e( T/ |/ j" S* c% S& y
  17. #define W25QXX_MANUFACT_DEVICEID 0x90
    3 e) w, ~- L! u% G, C) V
  18. #define W25QXX_JEDEC_DEVICEID    0x9F
    ' L5 f4 ^5 p, Q$ C0 F
  19. #define W25QXX_EABLE_4BYTE_ADDR  0xB74 w/ L1 W* f' n0 T. m
  20. #define W25QXX_EXIT_4BYTE_ADDR   0xE9
    6 T) E, `3 \6 U; l8 f3 h+ e. p
  21. #define W25QXX_SET_READ_PARAM    0xC09 b; H& q5 q. x
  22. #define W25QXX_ENTER_QPIMODE     0x38
    ! S/ x) b2 h8 p6 `% ~2 }1 Y
  23. #define W25QXX_EXIT_QPIMODE      0xFF# D( [% Z: o; G9 ^
  24. 4 C( _6 n3 l* p( a9 l# o% ?
  25. uint8_t w25qxx_qpi_mode = 0; // QSPI模式标志:0,SPI模式;1,QPI模式.1 D/ m8 e7 l: P. s/ t. J

  26. 4 ]8 q7 _) M, r& z
  27. // 读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器6 X8 @5 N6 c" K% e9 R% T0 X
  28. // 状态寄存器1:7 h: e) R6 O7 d" }8 v  ~7 h7 W
  29. // BIT7  6   5   4   3   2   1   0: N& W8 }  Q" ?- y, b/ {) K- ^
  30. // SPR   RV  TB BP2 BP1 BP0 WEL BUSY  Q- B! c1 }& [, ]3 H" W. \1 j
  31. // SPR:默认0,状态寄存器保护位,配合WP使用
    $ A: j! @: v1 S- L( u
  32. // TB,BP2,BP1,BP0:FLASH区域写保护设置# n. m0 B" \9 m9 |3 }! b. ^! \0 b) {
  33. // WEL:写使能锁定; R& a  j5 z$ S* A6 _/ d% G
  34. // BUSY:忙标记位(1,忙;0,空闲)! w! w& l& S$ R* r. L# f
  35. // 默认:0x001 J7 U+ Z: G' S7 Y
  36. // 状态寄存器2:& C* g: _% c. I
  37. // BIT7  6   5   4   3   2   1   0$ b: e) g5 ~8 b- f( I5 z
  38. // SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
    6 _* e: p2 A8 h$ i1 I  e
  39. // 状态寄存器3:
    0 a- q! }; K/ p# T: X( k9 ^
  40. // BIT7      6    5    4   3   2   1   0
    . b) i9 c. a" h; F0 Y' P/ C5 A  N
  41. // HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS7 \$ @  w4 V3 M- }
  42. // reg:状态寄存器号,范:1~3
    & p: ?1 A$ _  h3 Z
  43. // 返回值:状态寄存器值
    ; h1 b' m. b" _
  44. static uint8_t w25qxx_read_status(uint8_t reg)
    3 u9 I  e% W2 d+ r6 s* D* D+ x
  45. {
      I* R7 F) t( J% U1 {7 U1 Y
  46.   uint8_t value = 0, command = 0;
    . n# a, S% s( ]/ F
  47. " f$ ~. b# d, u3 d
  48.   switch(reg)" q, e& K, L% `& t- c
  49.   {
    3 r# J$ @- m6 S/ |. g4 p, s
  50.   case 1:) X9 U; X! ^9 q; j/ ~
  51.     command = W25QXX_READ_STATUS_REG1;  // 读状态寄存器1指令
    & }' x) {0 K0 Y& a9 N4 g" R
  52.     break;7 P9 D7 \; H3 F" t$ h  |- t
  53. 3 S/ r4 ]. f2 j, Q
  54.   case 2:  n3 o/ K. U8 g
  55.     command = W25QXX_READ_STATUS_REG2;  // 读状态寄存器2指令1 x  ]' G" e1 e3 _; X
  56.     break;" x& o& {$ J  t1 Q: n  C" @* `& h, y

  57. 9 j% {& v4 w+ k; D
  58.   case 3:
    . H# l9 A1 S: ]
  59.     command = W25QXX_READ_STATUS_REG3;  // 读状态寄存器3指令
    , E$ \6 W& |* T& V
  60.     break;
    7 r0 z" [: S4 L0 A# M. {* l
  61. ( m* z! D8 p& U
  62.   default:: t! I' e+ e8 u% E
  63.     command = W25QXX_READ_STATUS_REG1;* p8 f  s' _& I7 @: l' j" q, m, ]
  64.     break;" c& Z6 o1 b4 J! t
  65.   }. t/ C" `' f3 S% y5 y9 L* J
  66. 6 f  w* i; N! m9 Y3 ]
  67.   if(w25qxx_qpi_mode)  A: R: w5 b1 y8 H4 _" @
  68.   {9 d2 B. V4 i. }; m
  69.     QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_4_LINES);  //QPI,写command指令,地址为0,4线传数据_8位地址_无地址_4线传输指令,无空周期,1个字节数据% P, i7 O8 x7 P3 h
  70.   }5 K5 ?& d  [) @$ p
  71.   else
    1 D1 p: {0 O- W! c
  72.   {
    ; l! @+ y0 v* K% u) |& o
  73.     QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_1_LINE);  //SPI,写command指令,地址为0,单线传数据_8位地址_无地址_单线传输指令,无空周期,1个字节数据0 U2 b, t. J# p3 P2 k" M
  74.   }
    " [! P3 L3 c! S! f5 p( R8 U0 G1 q0 P* z* T

  75. . C5 N6 x# h$ n  j
  76.   QSPIReceive(&value, 1);
    3 Y1 S/ `9 k" k2 n+ i2 i* X5 s% f

  77. 4 h0 |4 P8 s' B9 w
  78.   return value;
    / y0 Y8 x  Q4 I$ w! @
  79. }5 _" c- f# i5 ]7 Y$ f$ c9 C5 E: t
  80. 4 x* M1 D8 O; S) J" G4 ^& g, y
  81. // 写状态寄存器
    4 \& b' Z, R' _/ s! L
  82. static void w25qxx_write_status(uint8_t reg, uint8_t status)- l: G; z: T, o  _/ v$ d4 S1 x! I8 x
  83. {
    , c5 Z4 G1 Z5 }5 j9 \" G3 C
  84.   uint8_t command = 0;( M! n' t, @3 b. G
  85. $ U) D/ G0 Z+ H, W
  86.   switch(reg)
    5 x5 d! h3 m6 P
  87.   {
    ! m+ A  P! p" c: J0 i! @
  88.   case 1:
    " g' T& a( w. Y+ f0 o( I
  89.     command = W25QXX_WRITE_STATUS_REG1;  //写状态寄存器1指令; X  X' u+ d" R) ]* P5 u, |
  90.     break;9 V; y3 K1 w/ k- Y0 ?: x5 R
  91.   case 2:
    ' f7 ?9 k! ~$ c* D" b: P" D
  92.     command = W25QXX_WRITE_STATUS_REG2;  //写状态寄存器2指令
    " l1 G6 R' {+ I5 i% ~( h" o
  93.     break;
    3 ?4 F: E7 T- h! }+ n, G- t
  94.   case 3:3 _* J$ n( F4 k0 r
  95.     command = W25QXX_WRITE_STATUS_REG3;  //写状态寄存器3指令
      Q1 b1 h2 f( |/ B# X$ X
  96.     break;
    2 Y  U- P7 v" F+ K
  97.   default:5 W8 v- ~: W* |0 s& j
  98.     command = W25QXX_WRITE_STATUS_REG1;
    / H$ P9 L6 [8 |
  99.     break;$ A, K" @$ ^5 }
  100.   }
    ! B, C2 X1 v; M4 \1 v, f
  101.   if(w25qxx_qpi_mode)9 {! u) S+ }" U: [' \) E' E" c2 \- K; L
  102.   {
    1 k) Y  |  l/ }. \$ |4 h8 h
  103.     QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_4_LINES); // QPI,写command指令,地址为0,4线传数据_8位地址_无地址_4线传输指令,无空周期,1个字节数据
    4 S& E/ C, n5 v1 U5 A
  104.   }1 r* }) ~9 @9 d
  105.   else1 d/ U2 d: l* i9 t$ X
  106.   {- P- N9 B' ]2 T# [3 ~
  107.     QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_1_LINE); // SPI,写command指令,地址为0,单线传数据_8位地址_无地址_单线传输指令,无空周期,1个字节数据
    8 e3 g9 K" W  U) P+ {  U
  108.   }
    ' X) ]8 A$ {7 n- }8 ?
  109.   QSPITransmit(&status, sizeof(status));# B. z5 O" z- S5 ]
  110. }% u* `  ]: _1 O: k$ I& }
  111.   y$ d$ L% e& i- c; J" |9 ], Q5 x3 _
  112. // 写使能  将S1寄存器的WEL置位9 M8 q' K# d+ g
  113. static void w25qxx_write_enable(void)9 u% C# N. s( S0 @# |/ a8 Q$ X
  114. {5 s1 R% x0 w" }' _: [
  115.   if(w25qxx_qpi_mode)
    7 R) m4 r) G) @; @
  116.   {7 L# \# t7 @3 R0 n& T! G# Y- o- z4 X* @
  117.     QSPISendCMD(W25QXX_WRITE_ENABLE, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // QPI,写使能指令,地址为0,无数据_8位地址_无地址_4线传输指令,无空周期,0个字节数据
    6 ]" c6 s  k" D; M4 p( x$ l4 b
  118.   }  c& v* z* D) i& P6 X0 [
  119.   else
    ) h1 p! b- O" i! `6 ~
  120.   {
      \3 T6 ~$ Y+ e
  121.     QSPISendCMD(W25QXX_WRITE_ENABLE, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // SPI,写使能指令,地址为0,无数据_8位地址_无地址_单线传输指令,无空周期,0个字节数据; x, D( ^3 @9 |1 K2 C/ i
  122.   }
    % n/ D4 p2 j! Q. ~* O; @
  123. }  j5 l4 o, h+ d( ]$ j" `- g6 ?1 Y) c

  124. 0 j0 I" L: F6 [# ]
  125. // 写失能  将WEL清零1 `$ B7 |; d$ S& P( W4 W9 L2 m5 i$ S
  126. void W25QXX_Write_Disable(void)$ d3 g( K1 x8 u& y1 Y3 K
  127. {
    * E% e5 B" d4 L
  128.   if(w25qxx_qpi_mode)9 y( F" y! h$ Y! g1 t! P6 B2 j
  129.   {
    ( S) l; o" ^/ u- m& M, x
  130.     QSPISendCMD(W25QXX_WRITE_DISABLE, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // QPI,写禁止指令,地址为0,无数据_8位地址_无地址_4线传输指令,无空周期,0个字节数据
    . }' n# S. w  m  X/ C
  131.   }6 W0 Q4 v7 r( t0 i: @
  132.   else
    ; _$ }5 x# a7 x
  133.   {
      X7 `4 K6 L. F) a; Q, W* ]
  134.     QSPISendCMD(W25QXX_WRITE_DISABLE, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // SPI,写禁止指令,地址为0,无数据_8位地址_无地址_单线传输指令,无空周期,0个字节数据# A$ c% j4 V7 c* z
  135.   }
    / |* l- _, j0 N! p
  136. }8 [, ]' H  \0 _1 I: _. s' u; ~
  137. . E  }9 i/ o  G. {6 d
  138. // 等待空闲
    ( r/ o: K, K0 A! t) x
  139. void w25qxx_wait_busy(void)
    7 r  P/ A: l, ]% z( h
  140. {' ?  B. t( \+ [% L
  141.   while((w25qxx_read_status(1) & 0x01) == 0x01); // 等待BUSY位清空3 g$ v4 R& _6 A4 n& `5 ~3 J7 T9 t& D
  142. }/ l( x; G5 U0 G
  143. % f6 b6 N' r" f8 N; E1 r: a0 \, [
  144. // W25QXX进入QSPI模式. j2 t. N  u/ \0 Q4 j4 i% q8 K* a. j
  145. static void w25qxx_qspi_init(void)
    , e5 l. w3 s9 V, \
  146. {- L0 i0 {4 m  }5 d# T# I
  147.   uint8_t reg2;1 u; }2 ^2 c1 m# T7 T
  148.   reg2 = w25qxx_read_status(2); // 先读出状态寄存器2的原始值
    ' D1 @3 J% ^9 p# i) N6 }3 X
  149.   if((reg2 & 0X02) == 0)  // QE位未使能: ]3 @- O# N8 w0 p; K( ?6 @
  150.   {
    7 Q1 Z0 c( r8 W1 e; k7 w+ s6 p
  151.     w25qxx_write_enable(); // 写使能
    : g" ^( }% ^2 p9 o4 o
  152.     reg2 |= 1 << 1; // 使能QE位
    2 k# W# Z: `+ y7 ~: |
  153.     w25qxx_write_status(2, reg2); // 写状态寄存器2
    7 J* d4 m; F+ D8 U. J0 D0 u
  154.   }! c, A7 v! O# i
  155. / p4 v9 t+ t+ M( E4 c
  156.   QSPISendCMD(W25QXX_ENTER_QPIMODE, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // 写command指令,地址为0,无数据_8位地址_无地址_单线传输指令,无空周期,0个字节数据
    & n9 k8 `' M9 L9 \2 J

  157. ; Z9 n  N: M& z1 z' i
  158.   w25qxx_qpi_mode = 1; // 标记QSPI模式/ T; c( `! [  W; w
  159. }
      U' {3 e" j# N

  160. , T7 s: v6 r. f/ C6 Y7 R; E  [
  161. // 0XEF13,表示芯片型号为W25Q80" c1 _! P, [6 a$ F" ]
  162. // 0XEF14,表示芯片型号为W25Q161 @8 T) c; O% F: _# t5 ~% Z
  163. // 0XEF15,表示芯片型号为W25Q32) y* c+ G3 |5 R1 b' k
  164. // 0XEF16,表示芯片型号为W25Q64" u9 ]+ T* |8 b/ {& [
  165. // 0XEF17,表示芯片型号为W25Q128
    - ]- n' |5 b% u3 b
  166. // 0XEF18,表示芯片型号为W25Q256; k5 N' U, y0 X3 g* _
  167. static void w25qxx_id_get(void)
    3 A6 }* @$ v. Y9 N; J6 e
  168. {
    9 `% a- [4 `# A+ c
  169.   uint8_t temp[2];
    5 n7 l0 J' m" `3 S- P
  170.   uint16_t device_id;
      z  ?& U, @* P: T6 k

  171. / i$ T7 }6 z: ^1 i
  172.   if(w25qxx_qpi_mode)  l: [5 \3 d' I+ c* o' h
  173.   {
    9 K& ~; a$ B$ G* m
  174.     QSPISendCMD(W25QXX_MANUFACT_DEVICEID, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_24_BITS, QSPI_DATA_4_LINES); // QPI,读id,地址为0,4线传输数据_24位地址_4线传输地址_4线传输指令,无空周期,2个字节数据4 t4 y3 i8 r0 @/ o6 b
  175.   }  D; L- v" n3 r' W
  176.   else
    5 j: B" O* s) A4 Q8 f
  177.   {& ]/ }# K0 u+ v
  178.     QSPISendCMD(W25QXX_MANUFACT_DEVICEID, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_1_LINE, QSPI_ADDRESS_24_BITS, QSPI_DATA_1_LINE); // SPI,读id,地址为0,单线传输数据_24位地址_单线传输地址_单线传输指令,无空周期,2个字节数据) i8 w' g2 C2 h$ ~' T" u9 u
  179.   }
    - c; e7 R5 Y# ^9 E% ?
  180. 3 }% q( ~7 N2 y( Q2 X3 u& l3 l
  181.   QSPIReceive(temp, 2);# [, }% K  h) W8 J' w; [, X
  182. : [# ^1 x& p. X. f! M. u3 H! c5 X
  183.   device_id = (temp[0] << 8) | temp[1];% u" g' T$ k# L. |# h* M& `8 b: E
  184. ! L1 X1 l* t( e/ \
  185.   printf("QSPI Flash Device ID: %X\r\n", device_id);
    / b" Q3 U6 s: A; m* v  p3 c" f" w
  186. }- ^0 q# a) G+ D( n
  187. . [/ ?- b5 d; a8 T
  188. void W25QXXInit(void); ^/ [0 j" u; z5 m: Z8 R
  189. {
    ) Y3 r- g* j. @1 a* K
  190.   uint8_t temp;) w7 w& t9 L$ V- B7 F0 n- |
  191. " O, w" V# r- n6 f: b0 z. J
  192.   QSPIInit();
    / x  c% x' t% i  t
  193.   w25qxx_qspi_init(); // 使能QSPI模式
    4 w7 d7 y; u* c& O+ S
  194.   w25qxx_id_get();    // 读取FLASH ID.
    2 ^* N3 i- Q# J, J  T# K
  195. ! O/ c0 ?# s; p- y  B8 D1 ?% ~" e
  196.   temp = w25qxx_read_status(3); // 读取状态寄存器3,判断地址模式
    % m9 x9 L6 V' N7 B( y
  197.   if((temp & 0X01) == 0) // 如果不是4字节地址模式,则进入4字节地址模式, p1 J) C' }1 ~/ r/ B: _) i
  198.   {
    2 h0 V- a" N4 s; M
  199.     w25qxx_write_enable(); // 写使能% ~+ G2 l6 [2 `) r$ a; z
  200.     QSPISendCMD(W25QXX_EABLE_4BYTE_ADDR, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // QPI,使能4字节地址指令,地址为0,无数据_8位地址_无地址_4线传输指令,无空周期,0个字节数据7 }4 e  e  l4 ]* B& M1 K8 d
  201.   }  z. d) e3 X2 y. }# h
  202.   w25qxx_write_enable(); // 写使能' B6 j5 J8 i$ d+ f6 x0 Z
  203.   QSPISendCMD(W25QXX_SET_READ_PARAM, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_4_LINES); // QPI,设置读参数指令,地址为0,4线传数据_8位地址_无地址_4线传输指令,无空周期,1个字节数据
    0 Z) L& X6 Q! J9 K1 M2 H! `/ _% S7 t
  204.   temp = 3 << 4;      // 设置P4&P5=11,8个dummy clocks,104M
    . W* N1 D& l: B6 x0 Q
  205.   QSPITransmit(&temp, 1);
    % a$ M  i( {# }1 J( \" w
  206. }
    # J: q1 m4 m" u% S- Q2 d
  207. * D8 v# N1 c- @2 M2 t, I, v1 Z
  208. // 擦除一块4096字节 最少需要150ms. x7 h$ o/ D( f- v6 K$ V$ Z; o
  209. void W25QXXSectorErase(uint32_t addr)
    : O+ `) b' U# p- T# A4 W
  210. {
    3 x! h1 O; t2 l/ q
  211.   //addr /= 4096;  M( F) P) ?( v0 u' r! e& O% ^
  212.   addr *= 4096;
    % d! s$ o0 x' M7 g' E- E( @
  213. 4 _  {! q8 {6 u( k. L+ ^- T
  214.   w25qxx_write_enable();
      D% n0 u: }, a# u: k8 N& I
  215.   w25qxx_wait_busy();
    0 Y' r/ ~) z5 [
  216.   QSPISendCMD(W25QXX_SECTOR_ERASE, addr, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_32_BITS, QSPI_DATA_NONE); // QPI,写扇区擦除指令,地址为0,无数据_32位地址_4线传输地址_4线传输指令,无空周期,0个字节数据. i7 k1 E; q; f8 }' x
  217.   w25qxx_wait_busy();! F& X6 Z4 T- Z8 B: I) Y0 ]. ?
  218. }, C3 ]& x1 q! `" X$ K: H0 [
  219. - m1 q- f+ P) t8 Q! k- v3 C
  220. ; l4 w& @6 ?( |4 p
  221. // 擦除整个芯片
    + H% F  W) {! H
  222. void W25QXXChipErase(void)
    & x$ u6 y. t% I4 E. z# K* i4 n
  223. {8 k( s3 I7 c+ N4 \4 |8 d2 V" @
  224.   w25qxx_write_enable();          //SET WEL
    : @2 v5 d& d5 X
  225.   w25qxx_wait_busy();
    . a3 g) a) Q" P/ _0 b# _  Q! t9 @6 x
  226.   QSPISendCMD(W25QXX_CHIP_ERASE, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // QPI,写全片擦除指令,地址为0,无数据_8位地址_无地址_4线传输指令,无空周期,0个字节数据1 m3 D  V9 ~' U  |" N! S
  227.   w25qxx_wait_busy();           //等待芯片擦除结束
    7 q3 Z+ }6 {# H
  228. }3 P4 C8 k, t: X: j9 b4 m5 y
  229. ) Q! a6 X& {" @. j$ ^
  230. // 写最多256字节. Z, h/ Z" t+ x* U+ f
  231. void W25QXXWritePage(uint32_t addr, uint8_t *buffer, uint16_t length). Z; P- a( _8 e, A( j8 |4 }7 ^
  232. {
    ( U0 k( }. Q) z; w1 c
  233.   w25qxx_write_enable();          //写使能; w9 i  o0 Y; a
  234.   QSPISendCMD(W25QXX_PAGE_PROGRAM, addr, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_32_BITS, QSPI_DATA_4_LINES); // QPI,页写指令,地址为WriteAddr,4线传输数据_32位地址_4线传输地址_4线传输指令,无空周期,NumByteToWrite个数据
    & [: h  X9 S8 o  T* ?' f. K$ f
  235.   QSPITransmit(buffer, length);
    + [+ S, d7 p8 S' O" B6 A* j) Q+ a" _
  236.   w25qxx_wait_busy();            //等待写入结束8 J2 C5 J$ a& N/ t  u
  237. }
    / [! X" e2 E2 q0 D
  238. # p! r! V) d' J1 b5 R' y
  239. // 写最多65536字节 无擦除动作! b! f5 A7 M. b+ V! l1 T: J
  240. void W25QXXWriteExt(uint32_t addr, uint8_t *buffer, uint16_t length)
    ) p, a6 G4 {# g  o) U7 h4 L. ^. k' u+ x
  241. {+ f0 ~0 I  A/ w% `6 ]6 v8 n3 c' V* W( x
  242.   uint16_t pageremain = 256 - addr % 256; // 单页剩余的字节数* X- S7 g. g) x+ K! q3 F5 V

  243. 8 @" y$ z0 z& n4 D9 n" v: V( O
  244.   if(addr + length - 1 > (32 * 1024 * 1024))& \* X0 S' p$ {$ b/ _; o
  245.   {
    ' ]0 Z* N" n5 u6 r6 I) r& ?( q
  246.     return;
    . k8 o, m4 }  [7 o" R
  247.   }2 S. W: z$ F0 j5 a' m

  248. 7 P% ?' w! l& F6 n1 i
  249.   if(length <= pageremain)/ F! x- {0 G( N7 F
  250.   {
    * n. \: ]7 W- K$ V' E
  251.     pageremain = length;  // 不大于256个字节, G) B2 R) h6 k% r3 e, _; z
  252.   }
    " w) N* V& T8 ~* p+ V. s
  253.   while(1)" z& D; }% p7 T) e
  254.   {
    0 b* {! M# N/ t; @3 f
  255.     // 分页写入) N) Q1 c% i5 |3 s: a4 _& ?
  256.     W25QXXWritePage(addr, buffer, pageremain);
    ) d# g9 i. v  @! G
  257. ! a$ |" `; m) W& U/ k; L% s$ t
  258.     if(length == pageremain)
    1 o/ W6 \: @- M0 D3 e) a3 e! t
  259.     {; h, C1 U$ x1 A, B2 k  P
  260.       break;  //写入结束了
    8 `. Z$ H* i' R
  261.     }& f# D7 {8 w' r! l
  262.     else) p, |  T+ w3 d0 J( S
  263.     {
    $ k. \; k/ C% _& ]- C: D7 A7 \7 |
  264.       buffer += pageremain;  h0 L0 P$ z5 S" i7 z# {  s3 d
  265.       addr += pageremain;4 b) c2 p9 p; O
  266. ) ?- a5 B" y6 d8 r
  267.       length -= pageremain; // 减去已经写入了的字节数
    3 Y, a* X* c% `4 r
  268.       if(length > 256)
    & `" P* U$ F& B- z+ @
  269.       {" }! G  y. T/ T6 N- U9 G% C. N
  270.         pageremain = 256;  // 一次可以写入256个字节- P# d7 H# P  M: Q1 W" x( J, p
  271.       }5 a! \# E4 q2 x1 T% v( B. T
  272.       else
    6 T8 h" g% v0 O- C2 [
  273.       {; V6 j" B, y, |* Y+ F0 P- b1 v% B
  274.         pageremain = length;  // 不够256个字节了
    / \' l0 z- v/ q% j" I
  275.       }" ~7 e/ F" ?' |+ Z- v" J
  276.     }
    * R$ x, Y* k6 X; Q
  277.   }* V3 h- D9 w9 E6 e
  278. }: S1 O( z# |$ B: z! h0 p
  279. % R. @5 ^/ i& x% O1 q( D) y' k; L
  280. // 写最多65536字节 带擦除动作 / Z- h6 s! v# r- E" j
  281. static uint8_t W25QXX_BUFFER[4096];
      J" X8 l, p- A
  282. void W25QXXWrite(uint32_t addr, uint8_t* buffer, uint16_t length)
    3 k- K! `1 J, s
  283. {& ?: f. c" V8 D; p5 s9 Z
  284.   uint32_t secpos;: Y% m4 v% e- `" M2 r0 L" S# X
  285.   uint16_t secoff, secremain, i;9 Y/ |- O# Z  f" r) k8 y, P' S
  286.   uint8_t * w25q_buf;
    6 H8 ]# k7 e" @
  287. $ j) U. r* h( n- I: R4 G; M
  288.   w25q_buf = W25QXX_BUFFER;7 D# d+ G2 C- i. p2 v% r7 ]9 _
  289. 2 e2 S+ t8 u$ }; C
  290.   secpos = addr / 4096; // 扇区地址
    9 b4 A6 L6 E6 W9 v) r1 i& s1 Y3 E; l
  291.   secoff = addr % 4096; // 在扇区内的偏移: V: I5 I: y. A$ D; l5 u6 q
  292.   secremain = 4096 - secoff; // 扇区剩余空间大小
    6 y/ Q: V5 }: w1 I, R

  293. , }6 ]1 j; X* a" x( @. R. v
  294.   if(length <= secremain)& [! f9 a; F. t/ I1 M
  295.   {/ H8 o& z0 {9 f0 `1 u0 N1 u7 ~
  296.     secremain = length;  // 不大于4096个字节
    + _( p: ~1 H* R" x9 x" W
  297.   }
    4 {/ k5 X- H6 W6 F
  298.   while(1)+ ~; r9 w$ ~) X: r0 _. H( L! ]- _( ~
  299.   {$ e- @* X- J4 K9 ?7 ], Z
  300.           WatchdogFeed();! V$ Z/ K+ o9 L! f  d4 @+ J* A
  301.     W25QXXRead(secpos * 4096, w25q_buf, 4096); // 读出整个扇区的内容
    * E) s8 W  Y; ~  {* \3 [  ~
  302.     for(i = 0; i < secremain; ++i)- m& P4 g$ O# }; @- r) P5 V
  303.     {
    . T6 B& u& v# ?0 e' K3 }. c
  304.       if(w25q_buf[secoff + i] != 0XFF)
    6 p9 M6 k0 [" T+ @
  305.       {1 _. s( |" L+ h# p- u0 J' |/ ]0 h
  306.         break;  // 需要擦除
    ; V9 V& U% O# U) @4 a
  307.       }
    % u4 ?1 |& j& V
  308.     }
    $ c4 |, [% [: a7 @, b" B
  309.     if(i < secremain) // 需要擦除; l4 ^6 R8 c8 |4 @% S" k) ]% l
  310.     {3 F) f; E# o9 _3 y; P
  311.       W25QXXSectorErase(secpos); // 擦除这个扇区
    4 g6 X' J+ {4 t" q. l& m4 F
  312.       for(i = 0; i < secremain; ++i)) |5 |5 P/ p# z5 Z6 ~
  313.       {, Y, C2 u  a& j( _) @: ~# n# K- S
  314.         w25q_buf[i + secoff] = buffer<i>;</i>
    5 t# Y: _( t+ B6 b8 k& F
  315.       }
    ) q- Y6 Y0 i, n) x3 M4 o
  316.       W25QXXWriteExt(secpos * 4096, w25q_buf, 4096); // 写入整个扇区
    - M/ |  u& U6 R6 w! I+ ~: ?2 B. ^1 S
  317. 3 |) N6 q+ N. @+ K( }
  318.     }: |" v( G; ~. N  k
  319.     else" g6 M" V2 X7 X! x1 B* Q1 D
  320.     {
    ( `+ [  K* P4 F) G3 Y" p) |- }
  321.       W25QXXWriteExt(addr, buffer, secremain);  // 写已经擦除了的,直接写入扇区剩余区间.
    # T! z  S; q- _( K& s
  322.     }/ V- o; y% T* s
  323.     if(length == secremain)
    9 R. y& B; Z/ r' O2 r
  324.     {. g5 f" ~5 i8 }5 f, A' q6 u
  325.       break;
    6 e8 `* V. P; [/ @- S+ T
  326.     }
    & V. x) Y$ U/ ~$ J
  327.     else
    * L8 `, z( |4 ]  {
  328.     {
    7 L& |# K# S0 q. ?
  329.       secpos++;
    ! V; G' X% r. ~. s, `
  330.       secoff = 0;
    7 a7 g* N" |# y

  331. & T" ^- e) o) u- X! e
  332.       buffer += secremain;
    # H5 ~7 G3 X6 r# C) e. K9 ]- @' t# H
  333.       addr += secremain;: i  i# G% b9 m
  334.       length -= secremain;, i+ Y  V, L1 G& W% u
  335.       if(length > 4096)2 A7 @$ g& o6 T' s" C" L2 l
  336.       {% V7 c( m& \1 M+ g7 N! m: G
  337.         secremain = 4096;
    8 @6 C/ t+ E4 |+ {  }! d' o5 l
  338.       }) q* k7 w3 u9 j3 n* z
  339.       else/ r+ ]) \# P$ [* V$ H& R
  340.       {: j2 M. b0 K; F, J
  341.         secremain = length;3 ]# g, E( ~+ B, H/ D1 p" d
  342.       }
    ( v* M1 `; C, s" T
  343.     }
    ! M+ E. U( U0 {
  344.   };
    ( N( Z+ ]& m# U  x; t7 m
  345. }/ s0 \, ~3 R& E& p5 M

  346. , B  @" U$ V; D: B# n

  347. . l1 T5 T" h" ?) D5 e' N! j
  348. // 读取SPI FLASH,仅支持QPI模式  在指定地址开始读取指定长度的数据 65536
    6 M: f3 q4 c# x
  349. void W25QXXRead(uint32_t addr, uint8_t *buffer, uint16_t length)3 B% k* z- _. c4 O" ?
  350. {
    0 Q+ u4 r+ O% `$ O% y1 @6 h
  351.   QSPISendCMD(W25QXX_FAST_READ_DATA, addr, 8, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_32_BITS, QSPI_DATA_4_LINES); // QPI,快速读数据,地址为ReadAddr,4线传输数据_32位地址_4线传输地址_4线传输指令,8空周期,length个数据  ]$ V2 R" I8 V! c+ {, ^+ A9 n
  352.   QSPIReceive(buffer, length);
    : q+ O/ h0 B; N: y/ x2 o
  353. }
复制代码

* ~9 T) h/ k  e* o也可以使用SPI操作W25QXX。
/ q" E4 X' `( t' J% W3 Q# P: Y/ i% v/ J: K
( K9 |% g7 b5 g, q

: v5 c: p0 A# K
收藏 评论0 发布时间:2021-12-11 12:00

举报

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