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

【经验分享】STM32F7xx —— QSPI

[复制链接]
STMCU小助手 发布时间:2021-12-11 12:00
一、QSPI& @6 `9 g0 A5 @( v/ B! b: v
        SPI 是 Queued SPI 的简写,是 Motorola公司推出的 SPI 接口的扩展,比 SPI 应用更加广泛。在 SPI 协议的基础上,Motorola 公司对其功能进行了增强,增加了队列传输机制,推出了队列串行外围接口协议(即 QSPI 协议),使用该接口,用户可以一次性传输包含多达16个8位或16位数据的传输队列。一旦传输启动,直到传输结束,都不需要CPU干预,极大的提高了传输效率。该协议在ColdFire系列MCU得到广泛应用。与SPI相比,QSPI的最大结构特点是以80字节的RAM代替了SPI的发送和接收寄存器。QSPI 是一种专用的通信接口,连接单、双或四(条数据线) SPI Flash 存储介质。
* B- Z0 q7 i- d. y, ?4 A3 b# T1 V
该接口可以在以下三种模式下工作:
. ?# O8 U+ j+ @& L① 间接模式:使用 QSPI 寄存器执行全部操作
9 H1 l* m# }. c. w② 状态轮询模式:周期性读取外部 Flash 状态寄存器,而且标志位置 1 时会产生中断(如擦除或烧写完成,会产生中断)
( _9 t) _7 g) m9 M- ?  [4 i- W9 l③ 内存映射模式:外部 Flash 映射到微控制器地址空间,从而系统将其视作内部存储器。
/ k) I1 x# @; O! |" E! @: H7 Z$ Y( l4 k& k- I
QSPI通过6根线与SPI芯片通信,下图是内部框图:. e% |$ ^: f# _2 U5 u8 j

0 _7 d0 W% ^( }6 G+ `3 m. O
20190123173205949.png

+ M* E# O9 B; u9 }: A- n
; a' h% L6 y) ~: n4 sQSPI每条命令,必须包含一个或多个阶段:指令、地址、交替字节、空指令和数据。
! W0 p3 c% b5 t- M7 }# D; T1 j
& _5 m- S) ~3 M. V% H+ U: VQSPI发送命令:等待QSPI空闲;设置命令参数。
- ]7 S$ E9 \4 i/ S- o
' A; J+ T) I% Q: C$ N" Z) rQSPI读数据:设置数据传输长度;设置QSPI工作模式并设置地址;读取数据。3 n: I" P' I% x; \# i# \8 J
  ?1 F$ K  Z( U  f" y
QSPI写数据:设置数据传输长度;设置QSPI工作模式并设置地址;写数据。
9 j9 e! u9 \2 B) |5 Z1 @& ^: d1 D- D  q

# H* S+ A3 ]6 ]) s( O' ^二、几个重要的函数$ X. s2 K0 F; }7 s5 n( `
  1. HAL_StatusTypeDef     HAL_QSPI_Init     (QSPI_HandleTypeDef *hqspi); // 初始化/ N: S" Z& z8 \: D/ {" Y

  2. 3 J: h- N7 l' w
  3. HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout); // 发送命令9 o5 M% ~6 S3 ~3 m6 T1 C. n* q1 L2 ]
  4. ' ]) Z% _$ V! a# V* M
  5. HAL_StatusTypeDef     HAL_QSPI_Transmit     (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 发送数据
    6 Z( \8 F& X; a- J& V$ j9 t+ G

  6. $ o: _% }) Q0 ?
  7. HAL_StatusTypeDef     HAL_QSPI_Receive      (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 接收数据
复制代码

0 }! V' _7 I$ H$ W7 I* c: g% m三、几个重要的结构* `2 G' [% w8 ^3 W
  1. // QSPI操作句柄
    ) G5 M5 G! v+ H5 m2 b6 i. t
  2. typedef struct
      X9 j9 y* v8 n& C
  3. {7 c/ ^/ b& s' |& D6 @7 k# e
  4.   QUADSPI_TypeDef            *Instance;        /* QSPI registers base address        */
    - `# f! k; a) n* ~1 @9 C9 O# P
  5.   QSPI_InitTypeDef           Init;             /* QSPI communication parameters      */6 D6 p5 N: e$ m; z$ W8 B
  6.   uint8_t                    *pTxBuffPtr;      /* Pointer to QSPI Tx transfer Buffer */
    , a* X3 t* r6 r& t: l
  7.   __IO uint16_t              TxXferSize;       /* QSPI Tx Transfer size              */: s% d; {3 F3 L2 \$ L
  8.   __IO uint16_t              TxXferCount;      /* QSPI Tx Transfer Counter           */' B" l$ D, {) }5 C, Z! h
  9.   uint8_t                    *pRxBuffPtr;      /* Pointer to QSPI Rx transfer Buffer */
      ^1 B  E3 w+ M7 D4 |
  10.   __IO uint16_t              RxXferSize;       /* QSPI Rx Transfer size              */
    0 s; x0 @8 W" j. g
  11.   __IO uint16_t              RxXferCount;      /* QSPI Rx Transfer Counter           */
    ' ~  x& o5 i: m8 {/ h% \9 h
  12.   DMA_HandleTypeDef          *hdma;            /* QSPI Rx/Tx DMA Handle parameters   */
    " }3 I) J9 }: F
  13.   __IO HAL_LockTypeDef       Lock;             /* Locking object                     */2 T+ t4 j6 n6 ?9 c8 C
  14.   __IO HAL_QSPI_StateTypeDef State;            /* QSPI communication state           */2 u& t( y2 S' p; P5 A0 B
  15.   __IO uint32_t              ErrorCode;        /* QSPI Error code                    */1 i# i, m2 h) R, l
  16.   uint32_t                   Timeout;          /* Timeout for the QSPI memory access */
    2 X7 j+ }% N' N
  17. }QSPI_HandleTypeDef;6 J7 H. d) z4 [9 X$ {

  18. ; L2 |$ m- T5 _5 `7 ~1 W
  19. // Instance:QSPI基地址  ---  QUADSPI8 G! \8 W$ e3 a4 I: l8 \6 C
  20. // Init:设置QSPI参数3 P  P; B1 a6 H9 \3 ^0 j: y; _
  21. // pTxBuffPtr,TxXferSize,TxXferCount:发送缓存指针 发送数据量 剩余数据量
    0 C" j1 P) ~% i' Q0 t
  22. // pRxBuffPtr,RxXferSize,RxXferCount:接收缓存指针 发送数据量 剩余数据量
    * A0 A, d' K8 ]1 y' \8 w
  23. // hdma与DMA相关, 其他为过程变量不需要关心
复制代码
  1. // 参数配置 时钟分频系数  FIFO阈值  采样移位  FLASH大小  片选高电平时间  时钟模式  闪存ID  双闪存模式设置( f( J0 S" u/ c2 y7 S
  2. typedef struct
    9 W; a1 w4 }$ O  Q2 k  N
  3. {, l$ a- O1 H; M* V5 D" S9 i
  4.   uint32_t ClockPrescaler;     /* Specifies the prescaler factor for generating clock based on the AHB clock.
    & O. ~# l4 C$ X/ F
  5.                                   This parameter can be a number between 0 and 255 */
    6 F% U4 `7 `% [6 t0 I% G2 Q/ L% S, k
  6. # K+ |! m5 D9 L% \
  7.   uint32_t FifoThreshold;      /* Specifies the threshold number of bytes in the FIFO (used only in indirect mode)
    5 e- [6 d# o1 o' ~. k
  8.                                   This parameter can be a value between 1 and 32 */9 `, p! k* [4 y

  9.   H8 T0 n8 W* Z6 W5 `
  10.   uint32_t SampleShifting;     /* Specifies the Sample Shift. The data is sampled 1/2 clock cycle delay later to
    ' u! H/ d0 \7 u4 Q& Q
  11.                                   take in account external signal delays. (It should be QSPI_SAMPLE_SHIFTING_NONE in DDR mode)
    . {! n4 q3 o- v/ `3 \  q5 }, H0 D" x
  12.                                   This parameter can be a value of @ref QSPI_SampleShifting */6 f  R9 }1 z/ ?  o7 N
  13. , V) e- ^7 s$ L# M) O5 J
  14.   uint32_t FlashSize;          /* Specifies the Flash Size. FlashSize+1 is effectively the number of address bits 7 c. ]: S+ S' W) _# r4 l
  15.                                   required to address the flash memory. The flash capacity can be up to 4GB
    ) f' D; ^5 Z: x1 A- V4 F
  16.                                   (addressed using 32 bits) in indirect mode, but the addressable space in , ^. {; \! ^! ?6 I: [
  17.                                   memory-mapped mode is limited to 256MB
    ! C. u7 H. k2 {1 S
  18.                                   This parameter can be a number between 0 and 31 */1 m3 J3 ~. ]7 j  G

  19. + r* u# v% U) l4 I7 o
  20.   uint32_t ChipSelectHighTime; /* Specifies the Chip Select High Time. ChipSelectHighTime+1 defines the minimum number / C" n& p% m' \3 x6 q" s+ ]0 `4 s
  21.                                   of clock cycles which the chip select must remain high between commands.
    + e: _/ v6 f7 P: R( j2 o
  22.                                   This parameter can be a value of @ref QSPI_ChipSelectHighTime */ - z, Q, f" S- Q; s9 d
  23. ! b% {- P$ {& Z/ ~6 R$ \6 U
  24.   uint32_t ClockMode;          /* Specifies the Clock Mode. It indicates the level that clock takes between commands.
    ' p! m# a! `& y: m2 q
  25.                                   This parameter can be a value of @ref QSPI_ClockMode */# n$ B7 B" ^5 a' ^

  26. ' F, b% y2 y* ~4 k( g
  27.   uint32_t FlashID;            /* Specifies the Flash which will be used,% o0 D, _% m% F$ J4 E. o6 M
  28.                                   This parameter can be a value of @ref QSPI_Flash_Select */
    4 H8 Y4 V; K7 }5 u! h8 L6 u

  29. 3 S: s  a( ], m* \9 n3 w8 g- s2 m
  30.   uint32_t DualFlash;          /* Specifies the Dual Flash Mode State
    . k% z. I3 c5 A$ I  i1 I
  31.                                   This parameter can be a value of @ref QSPI_DualFlash_Mode */                                               
    - R# b( o: j' \
  32. }QSPI_InitTypeDef;
复制代码
  1. // 采样移位
    : p; g; d' e) r" A9 G# U
  2. #define QSPI_SAMPLE_SHIFTING_NONE           ((uint32_t)0x00000000U)        /*!<No clock cycle shift to sample data*/
    3 c8 |$ m' v! I5 w
  3. #define QSPI_SAMPLE_SHIFTING_HALFCYCLE      ((uint32_t)QUADSPI_CR_SSHIFT) /*!<1/2 clock cycle shift to sample data*/
复制代码
/ Q  U9 n; s9 v+ ~  v7 P# ]* @7 o
四、QSPI接口设计(仅供参考)

+ S% l1 a' v& W$ |  R8 U3 F
  1. #define QSPI_CLK_ENABLE()         __HAL_RCC_QSPI_CLK_ENABLE();
    , v6 A/ _; V+ z
  2. ! N6 R9 t' M1 M. ]
  3. #define QSPI_BK1_NCS_PORT         GPIOB
    6 N/ K/ k6 X# E# c( p$ ^
  4. #define QSPI_BK1_NCS_PIN          GPIO_PIN_6
    : }1 c$ O) s- ~. V; ~$ `
  5. #define QSPI_BK1_NCS_AF           GPIO_AF10_QUADSPI
    . }1 W2 c8 g1 Q8 V7 h4 v1 X% i1 I
  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)
    * l- E, ]4 N9 O% J! w% j7 o

  7. 0 Y2 I7 k) P6 z
  8. #define QSPI_BK1_CLK_PORT         GPIOB
    9 u  B* k5 G, W  y: T  v! g
  9. #define QSPI_BK1_CLK_PIN          GPIO_PIN_2
    - k. E- D9 K. _" {
  10. #define QSPI_BK1_CLK_AF           GPIO_AF9_QUADSPI# t' g3 _( S! ]  q0 t
  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)+ {* J6 h; t( x7 c
  12. % F' o/ d4 k8 b" Z) w. @5 y& f
  13. #define QSPI_BK1_IO0_PORT         GPIOF: ^; _* }) d7 m1 Y' ]
  14. #define QSPI_BK1_IO0_PIN          GPIO_PIN_8
    + ~& k$ ]7 [$ ^# D8 K( \/ z
  15. #define QSPI_BK1_IO0_AF           GPIO_AF10_QUADSPI- `' V! P+ \, s5 D( |4 k
  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)
    % x; L) m9 Y) w2 ~: W( O
  17. 8 A2 m5 m9 g6 e; ?6 P" @+ w
  18. #define QSPI_BK1_IO1_PORT         GPIOF7 u" }. ^$ o3 }% v0 {! t3 Y) I
  19. #define QSPI_BK1_IO1_PIN          GPIO_PIN_9
    / u# |/ _- ^. C: ?  ^+ _7 F# u7 t
  20. #define QSPI_BK1_IO1_AF           GPIO_AF10_QUADSPI! X- p) W0 [, c# q& M9 G
  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)
    2 ~1 w2 e( C( n
  22. ' @: C8 \- N' n1 H
  23. #define QSPI_BK1_IO2_PORT         GPIOF
    5 s6 `7 Q, T9 e5 [5 o- r1 E* s" Z
  24. #define QSPI_BK1_IO2_PIN          GPIO_PIN_7; r0 ]6 `% y; e- m2 Y- c
  25. #define QSPI_BK1_IO2_AF           GPIO_AF9_QUADSPI
    : k/ o. {( S: V' G
  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)
    0 J$ z8 d. o. T4 W! r, b/ C

  27.   c* x: x% B. X: a9 L, Y; U4 H4 F
  28. #define QSPI_BK1_IO3_PORT         GPIOF
    ; M& ?: R5 A9 \( Q( l6 D
  29. #define QSPI_BK1_IO3_PIN          GPIO_PIN_6
    7 W& i. `& E1 e5 W+ B0 `$ ?9 U0 |
  30. #define QSPI_BK1_IO3_AF           GPIO_AF9_QUADSPI
    9 L5 m: z7 Y0 S. 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. // 封装几个必要的接口
    # e% p5 K( @4 J. {2 l- X- E
  2. static QSPI_HandleTypeDef qspi_handle;
      d; o# M' Z/ @( n6 m
  3. ( B* f6 k8 L( @. x/ B" F' m
  4. static void qspi_gpio_init(void)
      p7 v: ^$ B; ~/ @. q  ]2 e
  5. {
    1 _/ z' J- o! i
  6.   QSPI_CLK_ENABLE();9 _3 e: Z3 _6 l# N* t& F2 g) i. n- ~
  7. * @( _  [; N8 N
  8.   QSPI_BK1_NCS_CONFIG();
    ; x" Q$ l9 b: o# N
  9.   QSPI_BK1_CLK_CONFIG();
    9 Z( e# t. j- A- e" i# Y' M5 e
  10.   QSPI_BK1_IO0_CONFIG();
    ; D: F! d: E+ v) S+ D: z5 j
  11.   QSPI_BK1_IO1_CONFIG();( k8 J; `% l9 q# Y
  12.   QSPI_BK1_IO2_CONFIG();% M( M& \7 w$ M3 p) S' R  O
  13.   QSPI_BK1_IO3_CONFIG();
    5 y0 g* Q' b8 L8 J% a
  14. }+ M0 O% C, |! i% T4 }- _2 c

  15. $ ?1 F6 v5 |3 A
  16. static void qspi_mode_init(void)
    0 r" I" R. }9 n4 d* {
  17. {
    ; }$ N# k# c3 K2 L* V0 Z
  18.   qspi_handle.Instance = QUADSPI;                        // QSPI4 t* [3 l) G5 x7 Q. l' Q" |0 u" R8 o8 e
  19.   qspi_handle.Init.ClockPrescaler = 2;                   // QPSI分频比,W25Q256最大频率为104M 所以此处应该为2,QSPI频率就为216/(2+1)=72MHZ
    8 T1 D! `: O+ ^5 e! [4 s$ q
  20.   qspi_handle.Init.FifoThreshold = 4;                    // FIFO阈值为4个字节7 h' ~% B! p7 m
  21.   qspi_handle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; // 采样移位半个周期(DDR模式下,必须设置为0)
    2 y) X! b4 J. W# f4 M) ~
  22.   qspi_handle.Init.FlashSize = POSITION_VAL(0X2000000) - 1; // SPI FLASH大小,W25Q256大小为32M字节$ m4 j, g8 [) W$ f* C5 j
  23.   qspi_handle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_4_CYCLE; // 片选高电平时间为4个时钟(13.8*4=55.2ns),即手册里面的tSHSL参数1 |6 n+ O, v/ u/ G- f0 d4 Q% \5 s  M$ S
  24.   qspi_handle.Init.ClockMode = QSPI_CLOCK_MODE_0;        // 模式0
    ' E, T7 X( w! a# B
  25.   qspi_handle.Init.FlashID = QSPI_FLASH_ID_1;            // 第一片flash
    + H3 P9 r7 \4 I% @
  26.   qspi_handle.Init.DualFlash = QSPI_DUALFLASH_DISABLE;   // 禁止双闪存模式
    5 o# r4 \. v* M1 f) d9 n4 M/ j
  27.   HAL_QSPI_Init(&qspi_handle);      //QSPI初始化
    * Y8 a9 T5 E- J0 M  V- F
  28. }  R$ y! L4 t; u

  29. 5 c4 W$ {( t( F5 p. _
  30. void QSPIInit(void)( q6 _! p2 w- k# [
  31. {5 S1 u- O& [' l  ^" _( P
  32.   qspi_gpio_init();
    7 w/ \' R4 l6 I. f
  33.   qspi_mode_init();
    / n! P9 _) b- c3 Z
  34. }
    ( G0 N+ U, y* P- Q, k& E3 |

  35. + ~  a" _7 w+ a' w$ |5 F* z
  36. // QSPI发送命令7 p. A% q7 n8 H/ h
  37. // instruction:要发送的指令  Y9 T6 _/ W& n* ^( }" P. z
  38. // address:发送到的目的地址
    2 u, V3 N8 C# `( ~9 y' q$ T
  39. // dummyCycles:空指令周期数2 y8 l1 ~+ |3 `# z) |4 w& T9 Y
  40. // instructionMode:指令模式;QSPI_INSTRUCTION_NONE,QSPI_INSTRUCTION_1_LINE,QSPI_INSTRUCTION_2_LINE,QSPI_INSTRUCTION_4_LINE. `# E8 u* ~0 o( J4 J
  41. // addressMode:地址模式; QSPI_ADDRESS_NONE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_2_LINE,QSPI_ADDRESS_4_LINE9 ^6 `9 P+ T% T  D# U, Y
  42. // addressSize:地址长度;QSPI_ADDRESS_8_BITS,QSPI_ADDRESS_16_BITS,QSPI_ADDRESS_24_BITS,QSPI_ADDRESS_32_BITS* C( T$ r! k  J
  43. // dataMode:数据模式; QSPI_DATA_NONE,QSPI_DATA_1_LINE,QSPI_DATA_2_LINE,QSPI_DATA_4_LINE
    3 L( M  ?) W/ |
  44. void QSPISendCMD(uint32_t instruction, uint32_t address, uint32_t dummyCycles, uint32_t instructionMode, uint32_t addressMode, uint32_t addressSize, uint32_t dataMode)( G% {' r; P8 `, `$ T/ g
  45. {' o  t6 O; Z  o! |5 q' Z
  46.   QSPI_CommandTypeDef Cmdhandler;
    / Z1 ~) R7 v4 l  o/ ~

  47. 2 R! S; C2 P0 c2 c# O' [2 {
  48.   Cmdhandler.Instruction = instruction;           // 指令
    $ y/ G$ \* @( T$ S# T" Q& d
  49.   Cmdhandler.Address = address;                   // 地址7 _5 `& i3 ?. ?, X
  50.   Cmdhandler.DummyCycles = dummyCycles;           // 设置空指令周期数4 G, ?% `" d0 M3 z
  51.   Cmdhandler.InstructionMode = instructionMode;   // 指令模式
    6 `4 y# L7 Z( m: n+ ?7 ?
  52.   Cmdhandler.AddressMode = addressMode;           // 地址模式" m7 L5 V% a: k* |
  53.   Cmdhandler.AddressSize = addressSize;           // 地址长度
    . J% J6 U: ]! p- Q) c: @& V0 V& n
  54.   Cmdhandler.DataMode = dataMode;                 // 数据模式; \; p/ N9 a! A" O5 q3 Q
  55.   Cmdhandler.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 每次都发送指令
    9 ?' ]8 Z( b6 {
  56.   Cmdhandler.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 无交替字节" ]5 @( B6 c5 j' k. B5 e
  57.   Cmdhandler.DdrMode = QSPI_DDR_MODE_DISABLE;           // 关闭DDR模式# |( g$ Z1 V8 U
  58.   Cmdhandler.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;* \1 g8 y5 i0 b: s% u9 u

  59. ) K2 s$ M% ~  M, N. ~
  60.   HAL_QSPI_Command(&qspi_handle, &Cmdhandler, 5000);
    $ n/ g' X, _4 A$ N" U% b
  61. }! l4 O2 X/ Y- A

  62. 7 R! u/ o! L; g9 N
  63. // 接收指定长度数据$ _8 _1 i* k8 _$ W
  64. uint8_t QSPIReceive(uint8_t *buffer, uint32_t length)
    ( s) M3 m' O3 _9 ~* y7 i
  65. {% r( T8 d% R- z( K# b9 \
  66.   qspi_handle.Instance->DLR = length - 1;
    3 R/ o- K  L, s; }1 }5 W1 [- T
  67.         
    % G5 ?4 b& `. `7 k$ i
  68.   if(HAL_QSPI_Receive(&qspi_handle, buffer, 5000) == HAL_OK)! A* l2 G  I- _
  69.   {  X; h3 i  G% B7 n
  70.     return 0;  //接收数据
    6 O' q+ o, V3 U1 d
  71.   }
    5 e: I- ?! `: Z2 u8 [
  72.   else7 y+ T1 e! t  x1 U- H. U- |
  73.   {& ~, S( b1 r) X2 A( [; J' l
  74.     return 1;
    ! x: u' O: `0 i( S$ L, b1 F
  75.   }8 E3 N+ T4 [, j5 r) A9 P1 s; N- i% E
  76. }
    3 t3 n* N: l- ?* ]5 p

  77. ( _; X" ~2 n* J, W1 @( F3 a: W' }
  78. // 发送指定长度数据
    - N1 _6 v6 `8 }8 @6 Z. |0 T
  79. uint8_t QSPITransmit(uint8_t *buffer, uint32_t length)
    3 J/ D8 z3 s& P
  80. {& {7 m& m/ D8 P) J. Q9 z* U
  81.   qspi_handle.Instance->DLR = length - 1;6 P6 N( Q% H7 Z, D
  82.   if(HAL_QSPI_Transmit(&qspi_handle, buffer, 5000) == HAL_OK). V: j2 u3 {7 u* ?1 }" M
  83.   {: G9 q- |4 z4 b/ i! [& N
  84.     return 0;  //发送数据* ?9 \$ |7 T& |7 |* e3 A/ u( E2 |
  85.   }3 I( N8 k3 d# R  ]7 E
  86.   else
    " {6 {7 ~& P' J( _( e/ I9 X, s
  87.   {1 l* |0 D* X) l- A3 s3 D0 M
  88.     return 1;
    * a6 l. m! @# x6 e+ t9 Z
  89.   }
    ! q* m% ]) B+ U& b8 D& g( J9 O
  90. }
复制代码
* S5 H6 W8 j8 R! P6 C; s
五、QSPI驱动W25Q256
9 A( e9 ~7 }9 \$ e8 QW25Q256:32M  512块,每块16个扇区,每个扇区4K。具体指令看手册。封装了如下接口(初始化读写擦除等接口是以后移植文件系统的基础):1 H) M6 W9 R1 E. e
6 {' m& X/ L; O+ m  |+ j
  1. #define W25QXX_WRITE_ENABLE      0x06
    " |/ z$ I3 |; R- c* H
  2. #define W25QXX_WRITE_DISABLE     0x04
    " F& Y4 G6 D& ?' C
  3. #define W25QXX_READ_STATUS_REG1  0x05* i4 E% T, j# |
  4. #define W25QXX_READ_STATUS_REG2  0x35/ e" O" @$ H, d
  5. #define W25QXX_READ_STATUS_REG3  0x155 r# x" V4 M) i, [% A* a2 e2 r
  6. #define W25QXX_WRITE_STATUS_REG1 0x01
    + _5 e$ B' N3 j
  7. #define W25QXX_WRITE_STATUS_REG2 0x318 i$ W# [/ t' R  G+ Y
  8. #define W25QXX_WRITE_STATUS_REG3 0x11
    / K) I/ b' U3 T$ t' N
  9. #define W25QXX_READ_DATA         0x03
    ' [; ^2 H; f. M% _7 f0 ^, F+ l% X
  10. #define W25QXX_FAST_READ_DATA    0x0B
    2 V. D0 C0 R7 o9 t5 u; z; V
  11. #define W25QXX_FAST_READ_DUAL    0x3B
    # C' x" g  a" Z# h) p7 r$ ?4 b
  12. #define W25QXX_PAGE_PROGRAM      0x02
    8 G# l4 y( E  ?2 U7 l* Q
  13. #define W25QXX_BLOCK_ERASE       0xD8
    & G4 o& @1 s9 P3 c3 P
  14. #define W25QXX_SECTOR_ERASE      0x20
    " P  K( ^( U8 B0 c5 S
  15. #define W25QXX_CHIP_ERASE        0xC7
    % o* f* y5 h" U9 w6 A' n
  16. #define W25QXX_DEVICEID          0xAB
    2 Y* |0 ?1 ?3 D& Y/ s6 W; `
  17. #define W25QXX_MANUFACT_DEVICEID 0x90, ?: L8 G# v4 X
  18. #define W25QXX_JEDEC_DEVICEID    0x9F1 I, ^6 C% k# [7 g1 G
  19. #define W25QXX_EABLE_4BYTE_ADDR  0xB7
    8 ?9 J# @- L9 n/ }
  20. #define W25QXX_EXIT_4BYTE_ADDR   0xE9, w4 t0 \! T! Q9 S4 p
  21. #define W25QXX_SET_READ_PARAM    0xC0
    ; {8 n/ y2 K" x+ M; e/ ?% n3 {
  22. #define W25QXX_ENTER_QPIMODE     0x38
    : D3 {+ S0 k) e/ \) ~. D& {8 r
  23. #define W25QXX_EXIT_QPIMODE      0xFF# y. M, d# S2 w
  24. , H/ O2 y7 T6 v* i+ Y2 S% O6 a! F
  25. uint8_t w25qxx_qpi_mode = 0; // QSPI模式标志:0,SPI模式;1,QPI模式.! H" N: Y7 z) L( J& i' Q; Y) m+ A* c
  26. 6 H+ I# s4 \2 o) M' `
  27. // 读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器
    ( g4 a6 l& N* l& k
  28. // 状态寄存器1:' V3 E* p+ f1 R3 ~
  29. // BIT7  6   5   4   3   2   1   03 f$ m4 z# f% k
  30. // SPR   RV  TB BP2 BP1 BP0 WEL BUSY
    ; J7 `+ \# G8 M+ U6 |. I! b# E& O
  31. // SPR:默认0,状态寄存器保护位,配合WP使用0 e9 j, k  z5 u& H. G
  32. // TB,BP2,BP1,BP0:FLASH区域写保护设置
    : t# }' r4 e6 Q  D; n: J
  33. // WEL:写使能锁定' N1 p- i8 F$ I- e; Q0 R
  34. // BUSY:忙标记位(1,忙;0,空闲). A% p# ]4 y$ U( q
  35. // 默认:0x00
    - E5 j% T& e: j: N3 }1 z
  36. // 状态寄存器2:6 L# z& ?5 V1 c6 w% n
  37. // BIT7  6   5   4   3   2   1   0
    $ q* _, R1 {2 P: ^  M$ F9 A: E& l
  38. // SUS   CMP LB3 LB2 LB1 (R) QE  SRP14 A( l+ F/ d+ t  w
  39. // 状态寄存器3:2 D9 d+ z! }0 W
  40. // BIT7      6    5    4   3   2   1   00 C: z- Q8 F8 u. a6 V9 o5 R' S
  41. // HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS+ R: i' F6 F' ]# Y
  42. // reg:状态寄存器号,范:1~3* o. P' Q  T8 e1 G; v
  43. // 返回值:状态寄存器值" z, E/ r1 \* E& j2 i0 V9 S
  44. static uint8_t w25qxx_read_status(uint8_t reg)
    0 p; ^/ e3 y/ j  y
  45. {! e4 l% ?* I) @0 @1 Q! S
  46.   uint8_t value = 0, command = 0;
    8 V! u3 y7 b+ m3 j

  47. 2 j/ F) C7 E& ^. C/ ]
  48.   switch(reg)5 r9 w: R# `8 c' i8 p
  49.   {
    ( v0 j: o8 G1 T3 D
  50.   case 1:
    1 s5 _, J* Z  {2 O
  51.     command = W25QXX_READ_STATUS_REG1;  // 读状态寄存器1指令% Q3 w( J. [, |/ Y+ H5 N. K
  52.     break;' s* O5 i$ Y8 x" k( U
  53. * d6 X3 q- F4 B3 q+ i' ^
  54.   case 2:1 J* Q  @, X. E* Z8 X; w. W' q5 ]: N
  55.     command = W25QXX_READ_STATUS_REG2;  // 读状态寄存器2指令' B8 i5 r. `( O2 E3 }
  56.     break;
    " E! P$ ?% k0 i- H

  57. 2 C. R* c! c/ I  I0 v, n/ h
  58.   case 3:
    7 y( j+ }/ u  g9 g; d8 A3 I: S3 x; L8 l( z
  59.     command = W25QXX_READ_STATUS_REG3;  // 读状态寄存器3指令
    # n% X! T3 u% d: \/ Q; B0 e' i* p  B
  60.     break;* g2 s+ f' @" @5 x" y0 p
  61. - N$ T! t5 A) q- r  U
  62.   default:
    + n1 j5 G$ U+ h! c
  63.     command = W25QXX_READ_STATUS_REG1;( {+ Z9 `. i" v& _
  64.     break;
    ( B8 L* W! ]+ z
  65.   }' v: [3 X) q9 I) q. k3 j* c# `

  66. 7 i! m) w# _( Q$ U9 b6 i0 l
  67.   if(w25qxx_qpi_mode)
    0 g+ M' R$ Z6 z1 |
  68.   {; F: D' T. J  I
  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个字节数据
    & C+ S- T1 G+ n% {% J& @5 Z' C7 Q
  70.   }
    8 s# S, a  c' n* o4 N; U- t) V5 W
  71.   else
    0 Y6 Z1 K. W+ C6 m( z
  72.   {& D* \( t1 f1 q, ?
  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个字节数据
    $ I$ g( U$ V8 X5 c, _
  74.   }
    - w$ h* r# V+ x8 i: ?& e
  75. 9 r( J8 y1 Q9 e& T6 R6 U- ^
  76.   QSPIReceive(&value, 1);
    & |. v8 T7 M) R; J4 E2 P
  77. + w) g0 H/ h' M7 C5 W
  78.   return value;
    4 a# l- P; @" |0 j, \; p3 e
  79. }
    ) ~+ c  I0 @6 A, x2 M( }

  80. $ e5 ~. C3 |% Q- d8 N% u/ ^2 f0 {5 U
  81. // 写状态寄存器" D/ G" D2 Y  d6 I
  82. static void w25qxx_write_status(uint8_t reg, uint8_t status)
    ) R& H2 v- K5 t. Y- m* S# }
  83. {
    ' p4 R4 x+ ]$ Z7 n  [. I3 y
  84.   uint8_t command = 0;
    , S+ T- C' w, M

  85. 1 N5 E  O0 H5 g
  86.   switch(reg)
    ' n' n; y0 S* q9 w+ o# o$ E/ r
  87.   {
    0 q7 `9 \, B$ ^
  88.   case 1:- D& G9 x! `1 s( u9 W' P
  89.     command = W25QXX_WRITE_STATUS_REG1;  //写状态寄存器1指令
    " {5 ]+ M, j' a# M' Y7 V
  90.     break;3 d- u; j, R0 x, S
  91.   case 2:7 u; E7 C, T% o3 x/ [* Z
  92.     command = W25QXX_WRITE_STATUS_REG2;  //写状态寄存器2指令- _! D+ W$ `# W/ Q
  93.     break;8 t3 R& x( ^/ Y; x# h$ `
  94.   case 3:
    $ M1 B6 R' ]" d* i! M) S
  95.     command = W25QXX_WRITE_STATUS_REG3;  //写状态寄存器3指令: I4 ^3 h3 n; M
  96.     break;
    & V) ^5 Y- ?+ a% J  [2 J1 n  D/ R
  97.   default:1 n  L" q) U) h  i0 I8 z& F
  98.     command = W25QXX_WRITE_STATUS_REG1;
    - w! R( [3 k4 p+ B2 \+ j
  99.     break;( B, Q/ {. D1 R' t' ]  P
  100.   }! |6 l+ \7 B" t( F
  101.   if(w25qxx_qpi_mode), g8 a! Y: w& [0 a
  102.   {) E$ X9 ]/ Q) W9 I$ A. R
  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个字节数据0 c/ z  ]- c4 a& [
  104.   }3 A& A$ `0 |! [5 y
  105.   else
    3 |3 {0 b& Y( q4 U  a7 B
  106.   {
    1 D+ C# `+ g1 F+ Q
  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个字节数据
    2 ?4 K: m+ q2 G
  108.   }
    2 V% S, R$ L3 g  w- U. d, @/ P* B
  109.   QSPITransmit(&status, sizeof(status));
    : ?2 p1 d6 W! V
  110. }# K: a# O) Z& O
  111. 9 J8 z0 ~- P! |# j( O. m1 g9 z, F
  112. // 写使能  将S1寄存器的WEL置位7 c2 r) w+ A2 y- h# p6 d
  113. static void w25qxx_write_enable(void)
    3 W3 x: `- b  Y' E
  114. {
    / F8 }  k8 C  P2 z3 M8 }" {: P
  115.   if(w25qxx_qpi_mode)' Q9 p0 [5 {+ \% I
  116.   {2 \$ c4 k" }: Q4 J8 S: Q
  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个字节数据- S, Q& v; [, C
  118.   }
      I# Q2 n  A. H6 s3 L1 b, k8 S. \
  119.   else
      A2 i) A8 b+ ^8 A
  120.   {3 F, \4 l3 Y$ j; O+ [( D- j2 P
  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个字节数据
    ; U& M# m# k$ b! |% q/ I/ i
  122.   }
    , l. F& I4 `: p, M, x
  123. }% j3 t4 Q4 K8 I/ R3 P; |: n7 \
  124. 0 Z. Y; k! X  s- I7 v
  125. // 写失能  将WEL清零
    : A' o' q8 {6 h, P2 ~
  126. void W25QXX_Write_Disable(void)
    * x( C3 ?: F' p
  127. {/ S/ ^! z: d3 K( j4 \7 b
  128.   if(w25qxx_qpi_mode)
    $ C- ?: T& b- W+ n
  129.   {
    # c, o0 h5 o  n2 F5 f2 ]* n
  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个字节数据1 c6 Y( t9 u' g8 o) ?, W
  131.   }2 `9 U' {& S2 m6 i, Q
  132.   else( z+ n  V$ w8 Q# q: x- n: d7 W
  133.   {
    + t7 E5 q. @1 e; X- m# u) x
  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个字节数据' Q$ G: H1 O) C9 ~& R( m9 e2 M9 G
  135.   }" C( q* L, I2 G4 b7 e5 q
  136. }6 t$ S* ~1 ?# ?& N( T8 B- Y9 f
  137. 3 t$ _+ y/ I' T, P* l) u' D
  138. // 等待空闲
    & \, `0 M* {9 P7 X( m1 d
  139. void w25qxx_wait_busy(void)& q: U( X/ [  W7 [9 v
  140. {0 `7 a1 e8 u$ o9 k' o8 S  {
  141.   while((w25qxx_read_status(1) & 0x01) == 0x01); // 等待BUSY位清空9 Z: B4 N+ @" ?8 m4 o6 f
  142. }3 t% }! e) A4 u) {, G
  143. , {* j, j$ _3 Y. v
  144. // W25QXX进入QSPI模式. V3 _. o* e6 K
  145. static void w25qxx_qspi_init(void)0 ^6 n( A4 Y  G2 d
  146. {
    6 E# C5 y) f) h  q: {: M
  147.   uint8_t reg2;3 ^/ M- o# @! B9 g
  148.   reg2 = w25qxx_read_status(2); // 先读出状态寄存器2的原始值
    1 c0 d' Q5 V( E
  149.   if((reg2 & 0X02) == 0)  // QE位未使能
    0 c7 s8 }' [, T6 D# Y# N& r
  150.   {
    % P, _  N: N. Q) I* p$ e1 p  [
  151.     w25qxx_write_enable(); // 写使能
    7 V. O5 b% I! h! O$ l) v
  152.     reg2 |= 1 << 1; // 使能QE位* c. T1 V8 T% l9 I- g  j0 \- C8 B
  153.     w25qxx_write_status(2, reg2); // 写状态寄存器2
    + w4 D; L5 f4 h$ w# }
  154.   }6 H) U+ n. e( r& M

  155. 4 T6 j) |! [7 T! U$ D( Z3 b6 L7 \0 }( Y
  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个字节数据
    * q8 E: K+ w8 _0 [! H, e
  157. , V! V& ?/ l0 o$ B4 `5 X& V( N6 u, a
  158.   w25qxx_qpi_mode = 1; // 标记QSPI模式% V. `8 z) v# R6 O9 M
  159. }
    ' }4 R5 t) Y3 R) p

  160. 9 X& O4 }8 t1 Q+ A
  161. // 0XEF13,表示芯片型号为W25Q80
    ( Q& H2 M+ E: r+ b% W
  162. // 0XEF14,表示芯片型号为W25Q16. q( H" R8 |2 s. f' n+ ]
  163. // 0XEF15,表示芯片型号为W25Q32
    1 @9 N/ G( ?" k5 p0 ^" ~
  164. // 0XEF16,表示芯片型号为W25Q64) v/ l& J5 @& x: J7 E: b
  165. // 0XEF17,表示芯片型号为W25Q128( n/ E# d0 G8 ]7 L$ y
  166. // 0XEF18,表示芯片型号为W25Q256# b$ D8 I8 i: A" V3 L
  167. static void w25qxx_id_get(void)
    9 ]4 w' j8 y: F
  168. {
    % b' o8 {, z4 p3 r
  169.   uint8_t temp[2];7 K( G- j/ W. C. h& D
  170.   uint16_t device_id;
    * @+ C' v: }( X1 ?0 s  i

  171. $ K+ G6 B$ X2 L) q$ z8 i# `
  172.   if(w25qxx_qpi_mode)
    ! m$ W) s+ e8 K8 r7 i+ R/ S
  173.   {
    $ ]7 B1 y: \+ S7 |9 t
  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个字节数据
    6 x; X/ a0 I6 E& ~
  175.   }0 Q( r3 m8 T3 A6 G5 N! ^' Z& {' [
  176.   else
    1 F% j  R0 R) I8 S6 m# f
  177.   {
    ' n% y0 y) n2 |
  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个字节数据
    ; r3 h. o% s- u! [& I
  179.   }0 x' z3 Z! r# u* D
  180. / z7 h% c  ?$ v1 `4 I
  181.   QSPIReceive(temp, 2);
    / b' @* |$ |( O# C3 d# s" l6 R

  182. : x, q7 r9 G8 ?$ _8 H
  183.   device_id = (temp[0] << 8) | temp[1];: F. {1 C* ]8 M, `* k) _; i
  184. 4 P3 H; T' ?! Z% q* e: B% F
  185.   printf("QSPI Flash Device ID: %X\r\n", device_id);0 o0 ~0 c+ c7 N
  186. }, K  o. Y' j8 Z8 @* s- B$ V- y6 G
  187. ' d1 ]5 m1 n& f: }2 T" S0 L
  188. void W25QXXInit(void)
    + {6 F0 H# Q6 X3 g/ U1 s4 A
  189. {. D3 E: h6 z* B& ^( q
  190.   uint8_t temp;* n9 K! `! m! F1 R  p

  191. . j2 F) ^# z+ L' X3 M3 x. v1 e
  192.   QSPIInit();
    ) ^- C# k; A' n9 X0 z2 a$ s
  193.   w25qxx_qspi_init(); // 使能QSPI模式( O9 i3 J. S' Q# V
  194.   w25qxx_id_get();    // 读取FLASH ID.# v! u8 A' t5 a8 l. |

  195. ! x( d/ x9 a/ k6 l
  196.   temp = w25qxx_read_status(3); // 读取状态寄存器3,判断地址模式2 z4 ~/ q; t& `  L$ S4 h
  197.   if((temp & 0X01) == 0) // 如果不是4字节地址模式,则进入4字节地址模式- U) o0 `1 [0 @  j" e
  198.   {* d$ \) |- v- K; `: ?3 G/ m
  199.     w25qxx_write_enable(); // 写使能4 `- ]2 ^3 k% v4 N  u2 H/ _
  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个字节数据' x1 M/ G& w3 Z/ w4 {: i
  201.   }  j0 A2 x. P+ k2 K, g7 W1 c
  202.   w25qxx_write_enable(); // 写使能0 ~8 @2 L5 c% J1 u9 I8 J4 x
  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个字节数据
    - p: {! P- Y9 L; r6 R7 |
  204.   temp = 3 << 4;      // 设置P4&P5=11,8个dummy clocks,104M1 Y) R; r$ I/ b! c# l
  205.   QSPITransmit(&temp, 1);
    % A! t) h% z/ F3 Z0 m. J
  206. }* o& [. o3 Z- M
  207. + |( L; t( ?6 R' E! S
  208. // 擦除一块4096字节 最少需要150ms0 f# D/ j; W& W  Q
  209. void W25QXXSectorErase(uint32_t addr)& a( D& ~7 u3 ~0 n. ~( s/ Q
  210. {: V+ B3 {6 l( |: Y) {+ R( K
  211.   //addr /= 4096;5 p9 A% C% g3 ^8 T
  212.   addr *= 4096;
    " q5 A1 M- U$ w* ^

  213. 1 Q1 C( ^$ m+ ?- c
  214.   w25qxx_write_enable();  P3 D- E& F" Y
  215.   w25qxx_wait_busy();
    ; ]- f( w: ?* ~: q$ g
  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个字节数据+ |$ V3 m3 m0 o0 k7 w$ m+ i
  217.   w25qxx_wait_busy();
    & [; i0 G! v2 ~" ?4 ?& m
  218. }
    ! f8 S/ O! m/ y5 ~  I# w2 ^8 e6 h
  219. # p5 M3 |/ K$ h, l3 c- G8 C# x

  220. 6 |6 |+ c+ q. ~* x+ ^% Q
  221. // 擦除整个芯片7 v! l! a" \3 O9 L$ V, ~
  222. void W25QXXChipErase(void)
    & `: S' z* h& q
  223. {
    ' I+ `7 l+ R" S: z
  224.   w25qxx_write_enable();          //SET WEL+ u4 @: g0 }5 [. u9 \! B
  225.   w25qxx_wait_busy();" N$ A- F; g! ^, _' ?6 E
  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个字节数据
    ' j9 _2 g( E# [7 D( S$ O( t: Q
  227.   w25qxx_wait_busy();           //等待芯片擦除结束% s, D, [- ]; Q3 g: w4 x# A  S! `& y
  228. }* w: Z9 n* i# S& p
  229. , z% x0 g2 Q9 i+ Q6 j& H4 \
  230. // 写最多256字节$ B# h; |3 d! V
  231. void W25QXXWritePage(uint32_t addr, uint8_t *buffer, uint16_t length)
    1 [' P7 V2 z) K
  232. {
    4 }" f7 {  c  ]1 ^7 X/ q
  233.   w25qxx_write_enable();          //写使能% o/ I& u6 \+ [0 D- e
  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个数据
    . ]3 Y% B; y4 L1 j" F2 g6 V
  235.   QSPITransmit(buffer, length);
    3 J# q# i& o6 q- i
  236.   w25qxx_wait_busy();            //等待写入结束
    ! E! L: F; V* M& H2 I) J# L( \& c. N
  237. }
    6 b" r  ^, F5 c$ }# @
  238. ( C2 Q& R. f" g) G$ z/ ?' ^
  239. // 写最多65536字节 无擦除动作
    % r: r5 z8 @2 W9 {; Y0 c6 Z
  240. void W25QXXWriteExt(uint32_t addr, uint8_t *buffer, uint16_t length)
    9 `  O; v4 B. q! Q
  241. {; ~" t2 t+ c8 v5 Q7 t
  242.   uint16_t pageremain = 256 - addr % 256; // 单页剩余的字节数
    - K6 k# x$ e* J' V$ K

  243. . G5 N# O+ g/ b
  244.   if(addr + length - 1 > (32 * 1024 * 1024))
    ! ^5 t9 u8 Y. ~1 u- }
  245.   {& M4 p8 k. \- F& a3 G: {! P) t
  246.     return;9 F1 R8 n; f, r) c) b3 i* M( W
  247.   }1 Q" @% C6 v, X% R1 {3 i" s2 S! N7 D
  248. $ r+ e9 [2 r$ t5 m7 A/ o
  249.   if(length <= pageremain)2 d/ q. E7 A8 A2 r/ h4 r  S
  250.   {
    9 A7 C$ J8 [3 q" q; Y9 X
  251.     pageremain = length;  // 不大于256个字节; V- {( \0 |) r) E
  252.   }
    8 K: |+ L6 S4 Z
  253.   while(1)
    * Y' l$ T, N* Y. F& |
  254.   {
    $ e* P0 ^: f, a8 O
  255.     // 分页写入" l! u6 z. R" S& v' h, `8 c
  256.     W25QXXWritePage(addr, buffer, pageremain);) A7 v8 ^( v: m  O2 K  z$ X4 n

  257. 1 ~$ _/ b# J. G% j& ]
  258.     if(length == pageremain), y/ |/ O3 m% N/ u0 M) \# `
  259.     {
    / X" D, U* `) p2 B9 `- O
  260.       break;  //写入结束了# ?0 O$ b8 ], Y5 ~; H3 I; S( X
  261.     }
    5 U( z0 H, ?5 R" W  U
  262.     else
    / }! b" _9 V# K) J& L& c
  263.     {& v% w4 c5 f" D- E0 h0 G1 A
  264.       buffer += pageremain;+ d8 x! z* A, H  i/ K" r
  265.       addr += pageremain;' k( M4 o2 C1 B+ ~" U: u2 o) p9 ^

  266. 0 h, m9 p! Z6 u
  267.       length -= pageremain; // 减去已经写入了的字节数$ h. J" |! E* L. y. t/ n
  268.       if(length > 256): x6 s- p+ z% ]& F( e- b' s
  269.       {: k0 c' q9 k+ f4 ?$ V+ ]9 \' ~
  270.         pageremain = 256;  // 一次可以写入256个字节7 g0 V. ]! Z5 f* k
  271.       }1 f' Z9 C: a7 k/ }
  272.       else
      q* T7 S$ Y4 V  n0 K4 ]# {. I
  273.       {* M5 a0 L5 @$ y8 c) h
  274.         pageremain = length;  // 不够256个字节了$ J& ^* ?' i/ U
  275.       }- j1 O  X( `) e7 U9 w; }) r
  276.     }* {+ w$ o9 L; r
  277.   }
    9 Z' X6 K* [- t$ b) |& ]
  278. }2 I5 l% u$ S! E- ^2 O  T8 s
  279. + t- R% S* p. k% _* X
  280. // 写最多65536字节 带擦除动作
    , H+ Y5 a* O3 t: M' j
  281. static uint8_t W25QXX_BUFFER[4096];4 }& S7 K* B; f( A" Z
  282. void W25QXXWrite(uint32_t addr, uint8_t* buffer, uint16_t length)
    & e' h& @+ G3 u& L
  283. {4 B6 x- c4 C. `" L5 ?* F
  284.   uint32_t secpos;) S2 n& t! B  N8 D3 D
  285.   uint16_t secoff, secremain, i;
    8 r3 b4 E- _/ T
  286.   uint8_t * w25q_buf;
    + U8 a. H8 r! B& X

  287. 4 J, u& u9 B5 m6 {- K9 g
  288.   w25q_buf = W25QXX_BUFFER;
    9 h! M! Z2 Y* B1 b0 v

  289. # ~/ m% D) C9 A& e! E
  290.   secpos = addr / 4096; // 扇区地址/ u! d2 o/ R3 t# c7 F/ }& q3 i+ W
  291.   secoff = addr % 4096; // 在扇区内的偏移# W. I8 H& z" x5 o$ Y# ?$ u
  292.   secremain = 4096 - secoff; // 扇区剩余空间大小  v& Y! Z8 w6 {! l& S9 t3 w& [

  293. 8 O7 c; h  U- Q, k6 o" y+ b0 J
  294.   if(length <= secremain)
    ' U) `5 D8 R- F
  295.   {1 ~4 T8 L4 u' J; d  j3 t
  296.     secremain = length;  // 不大于4096个字节/ j! x9 t+ g$ X2 a2 ^
  297.   }
    , z& S9 p* T0 b7 Y! M
  298.   while(1)$ u0 X1 H2 |1 A. B- n( e( J  ]
  299.   {
    1 S2 g. t& o: G# z! I5 j/ I* t
  300.           WatchdogFeed();1 ~3 X* k, E. V
  301.     W25QXXRead(secpos * 4096, w25q_buf, 4096); // 读出整个扇区的内容# b" J: R; p* L" h
  302.     for(i = 0; i < secremain; ++i)7 B/ G. P3 |4 O+ N' S3 u
  303.     {& J( U9 }) _  q  Y8 O
  304.       if(w25q_buf[secoff + i] != 0XFF)
    ' Z2 w# |2 A6 r  |
  305.       {* j! f* W0 y( P% B4 V( L$ Z! k, f1 j
  306.         break;  // 需要擦除' S$ X, r. H  m& r
  307.       }
    / T  s& R6 n+ \7 r9 u4 [
  308.     }, x! M1 ?5 j8 ^
  309.     if(i < secremain) // 需要擦除
    2 g) P; n9 a- t5 V% f! o" c1 t. t
  310.     {3 m9 v: s5 `  F$ N% u9 J$ ?; U
  311.       W25QXXSectorErase(secpos); // 擦除这个扇区
    4 ^. }5 j  ~* N) @- a
  312.       for(i = 0; i < secremain; ++i)
    0 ^& N+ F& o- [( F6 {
  313.       {
    " [) ^0 G9 ^+ Y
  314.         w25q_buf[i + secoff] = buffer<i>;</i>
    ) b" M; g+ y8 W. M
  315.       }
    % s! U! H- N; m: z2 k
  316.       W25QXXWriteExt(secpos * 4096, w25q_buf, 4096); // 写入整个扇区
    : D' a% F, A) r  Y! F) C6 Z1 S3 _  o
  317. ) K( G7 ^% }5 C' c( n  Y2 _' _3 w% g
  318.     }6 N" o; {  F9 I
  319.     else
    % ]- s: {' X! c# b5 E1 V
  320.     {
    * S+ c5 ?! ?# S: I
  321.       W25QXXWriteExt(addr, buffer, secremain);  // 写已经擦除了的,直接写入扇区剩余区间.; U2 ^" Q! X6 N& \8 M/ x' ~
  322.     }
    - C6 t9 _# T$ _) ^4 a7 S" `
  323.     if(length == secremain)
    / X' f) ]0 D: o9 u
  324.     {1 a% ?. f# i% r. V
  325.       break;4 J- @, _3 y. H5 v) c& w( y* _% Q
  326.     }3 C# q" Y3 Q- E
  327.     else1 D9 Z8 C, o- f2 B/ X
  328.     {
    ' z$ _9 r; ^6 i. n2 x9 d) P, U
  329.       secpos++;' f, x8 {6 L9 z# b8 m; R& Y
  330.       secoff = 0;+ V' B- u% R3 F0 {9 i- M2 q! w) e
  331. , ~: [8 d/ t* ]) k- \! M
  332.       buffer += secremain;
    . I- f& V& D/ o! J1 F" r1 |! a9 i
  333.       addr += secremain;
      @. T; j- q3 H( V
  334.       length -= secremain;
    7 f" \9 D* h4 N7 I, G
  335.       if(length > 4096)0 g) K+ J+ }3 B% ~$ e
  336.       {
    7 D+ L* x, J8 b( W9 n
  337.         secremain = 4096;
    ' P  r0 A& o9 d
  338.       }
    9 U8 P' G& ?2 Q% y/ h# U$ g5 E) \" p
  339.       else$ J4 W/ G* G3 M2 z9 T9 a" @
  340.       {3 x% v' u# k$ w2 K
  341.         secremain = length;
    & T2 ~2 {" o2 o3 h6 O* `0 p
  342.       }: U# N, {0 `: i
  343.     }1 u5 j4 C' ^: ~
  344.   };4 G6 a1 J* u3 _% A: o' b+ H9 K9 b2 A
  345. }
    ! Y4 R- e4 X1 C& W+ z& }

  346. - W0 J9 X) f+ {" Y5 m7 H0 R6 I

  347. 8 G* u$ e6 `. ?
  348. // 读取SPI FLASH,仅支持QPI模式  在指定地址开始读取指定长度的数据 65536% i# Y$ x' s! d2 K, Z( `
  349. void W25QXXRead(uint32_t addr, uint8_t *buffer, uint16_t length)
      A- H- f+ o7 Y9 r0 p
  350. {5 R+ {! n8 _" u0 O! `' M- ~
  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个数据
    / Q; @( j( Q; b; U2 S
  352.   QSPIReceive(buffer, length);- M8 q- Q* |. R
  353. }
复制代码
/ ^" m% @0 H/ L  F& o2 D$ z9 {
也可以使用SPI操作W25QXX。
) C# m& r6 o$ {
! o: x8 v6 `' s6 G! j$ ?' Y9 Z, X/ ]8 S  R, T

5 X, Z. {, @0 e% H" Q% f
收藏 评论0 发布时间:2021-12-11 12:00

举报

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