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

【经验分享】STM32F7xx —— QSPI

[复制链接]
STMCU小助手 发布时间:2021-12-11 12:00
一、QSPI
0 w8 H8 A3 i$ C* a        SPI 是 Queued SPI 的简写,是 Motorola公司推出的 SPI 接口的扩展,比 SPI 应用更加广泛。在 SPI 协议的基础上,Motorola 公司对其功能进行了增强,增加了队列传输机制,推出了队列串行外围接口协议(即 QSPI 协议),使用该接口,用户可以一次性传输包含多达16个8位或16位数据的传输队列。一旦传输启动,直到传输结束,都不需要CPU干预,极大的提高了传输效率。该协议在ColdFire系列MCU得到广泛应用。与SPI相比,QSPI的最大结构特点是以80字节的RAM代替了SPI的发送和接收寄存器。QSPI 是一种专用的通信接口,连接单、双或四(条数据线) SPI Flash 存储介质。# w, e+ W* I1 t& A9 H
- z% L5 i4 N9 m5 m, }
该接口可以在以下三种模式下工作:
) O# [1 J! c$ J0 R) z① 间接模式:使用 QSPI 寄存器执行全部操作; j* b: j, H$ }0 k7 X, g# {8 H0 p' u
② 状态轮询模式:周期性读取外部 Flash 状态寄存器,而且标志位置 1 时会产生中断(如擦除或烧写完成,会产生中断)
; D& h, k7 N/ f; c/ N/ @  I③ 内存映射模式:外部 Flash 映射到微控制器地址空间,从而系统将其视作内部存储器。
% L: K7 V+ `% C6 L) a" ?
% u$ n* ]" B" ^QSPI通过6根线与SPI芯片通信,下图是内部框图:0 T8 K, w8 M* t
( s$ v6 Y& U: S! `
20190123173205949.png

& I9 q( I/ U8 ~2 }+ h# a8 F2 f5 Q& P2 ^$ J
QSPI每条命令,必须包含一个或多个阶段:指令、地址、交替字节、空指令和数据。
, d; y2 L6 f  z. {& n
% ^( g' A6 k# E! [9 NQSPI发送命令:等待QSPI空闲;设置命令参数。, q3 x8 u! x/ N

/ c2 O' h5 c' ?+ [9 V* e- oQSPI读数据:设置数据传输长度;设置QSPI工作模式并设置地址;读取数据。
( ~( G% Y0 H: _; g+ C* d+ g. c
& W* u9 l& |. a1 V% ?  w! d, E$ a6 X. [QSPI写数据:设置数据传输长度;设置QSPI工作模式并设置地址;写数据。/ L, ~# \0 e5 i* j3 P1 I" H

, D/ `' P! \* j& h6 P9 m2 _' y7 C, s( P
二、几个重要的函数
% r+ P' r, l, T- \6 e
  1. HAL_StatusTypeDef     HAL_QSPI_Init     (QSPI_HandleTypeDef *hqspi); // 初始化
    6 o- P* t. H) q, L. A7 P
  2. + u+ n# {% E4 p4 [; L
  3. HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout); // 发送命令
    , u8 K6 t! C' N
  4. ! \: z7 M" ~& D4 |0 k! W
  5. HAL_StatusTypeDef     HAL_QSPI_Transmit     (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 发送数据$ {. u' U$ B) {4 `; r1 l! F
  6. 4 j6 ~6 Z) w# F+ D/ H" ~
  7. HAL_StatusTypeDef     HAL_QSPI_Receive      (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 接收数据
复制代码
& R( S+ A/ n  R7 ~0 k4 C2 x
三、几个重要的结构
! [/ V) b! U5 R7 r
  1. // QSPI操作句柄( f1 L/ M% y2 Q% e! D, y. L
  2. typedef struct2 U" X, ]: Q8 l& f& ?
  3. {
    + R0 [+ H1 l4 t2 ~. L# H# ^1 V
  4.   QUADSPI_TypeDef            *Instance;        /* QSPI registers base address        */: Y2 M1 K; I& c. U& b
  5.   QSPI_InitTypeDef           Init;             /* QSPI communication parameters      */
    " T* T' t# R: F$ [% `! p
  6.   uint8_t                    *pTxBuffPtr;      /* Pointer to QSPI Tx transfer Buffer */. q1 j6 W! l) e, h' t1 C! _2 l! G; \
  7.   __IO uint16_t              TxXferSize;       /* QSPI Tx Transfer size              */
    / ]. t* U' _/ y/ _0 F( d+ s& M
  8.   __IO uint16_t              TxXferCount;      /* QSPI Tx Transfer Counter           */6 W, p( ?) R2 }. l/ U, L
  9.   uint8_t                    *pRxBuffPtr;      /* Pointer to QSPI Rx transfer Buffer */8 ]! Z* `* X- I3 i* ~
  10.   __IO uint16_t              RxXferSize;       /* QSPI Rx Transfer size              */- z  h& U, Z) P" `1 _. h1 r: p- \
  11.   __IO uint16_t              RxXferCount;      /* QSPI Rx Transfer Counter           */
    , k! z: L! {# L" w7 _# A
  12.   DMA_HandleTypeDef          *hdma;            /* QSPI Rx/Tx DMA Handle parameters   */
    , N7 r" z) x: T! Z+ j/ p
  13.   __IO HAL_LockTypeDef       Lock;             /* Locking object                     *// m: j0 n7 j+ f
  14.   __IO HAL_QSPI_StateTypeDef State;            /* QSPI communication state           */  E. W0 z4 u( g* y
  15.   __IO uint32_t              ErrorCode;        /* QSPI Error code                    */
    6 |8 f6 a9 n% Q
  16.   uint32_t                   Timeout;          /* Timeout for the QSPI memory access */
    % U& y- M/ g% ^/ A+ Z* Z6 v  r( s
  17. }QSPI_HandleTypeDef;
      Z* U. h' n% n  G+ G

  18. % S3 P( ?3 z& T& P$ `1 l( U+ V9 a
  19. // Instance:QSPI基地址  ---  QUADSPI
    3 d! {+ H- K4 V$ H
  20. // Init:设置QSPI参数
    * o; b/ c% D, v! x5 f7 u
  21. // pTxBuffPtr,TxXferSize,TxXferCount:发送缓存指针 发送数据量 剩余数据量7 D, R% \  U6 R$ P
  22. // pRxBuffPtr,RxXferSize,RxXferCount:接收缓存指针 发送数据量 剩余数据量5 X4 X2 j- V4 ]) w6 V5 y7 X$ d
  23. // hdma与DMA相关, 其他为过程变量不需要关心
复制代码
  1. // 参数配置 时钟分频系数  FIFO阈值  采样移位  FLASH大小  片选高电平时间  时钟模式  闪存ID  双闪存模式设置& a$ A, ^: v7 [% L/ Y
  2. typedef struct; `3 d& m' P* Y% h- p/ q; @
  3. {6 Y8 r% D; S! J' `/ O1 C/ {0 f
  4.   uint32_t ClockPrescaler;     /* Specifies the prescaler factor for generating clock based on the AHB clock.( q" m( ]0 U! B& g$ |+ s3 d
  5.                                   This parameter can be a number between 0 and 255 */
    ) x" |' i' S; g/ R! y
  6. % R3 B8 r$ X8 K* B2 J
  7.   uint32_t FifoThreshold;      /* Specifies the threshold number of bytes in the FIFO (used only in indirect mode)$ P' y. k9 e. I' T
  8.                                   This parameter can be a value between 1 and 32 */
    ! k: v) A+ X, K( `7 I
  9. ' _( U; @( b6 x6 p; u; t
  10.   uint32_t SampleShifting;     /* Specifies the Sample Shift. The data is sampled 1/2 clock cycle delay later to * ]4 \! c* r* q) D9 ?
  11.                                   take in account external signal delays. (It should be QSPI_SAMPLE_SHIFTING_NONE in DDR mode)3 d; p" Q+ i, O# p1 U. O
  12.                                   This parameter can be a value of @ref QSPI_SampleShifting */  p. f% l" V! m6 [1 X5 I1 r8 K
  13. 3 ]+ h; F7 G. D
  14.   uint32_t FlashSize;          /* Specifies the Flash Size. FlashSize+1 is effectively the number of address bits
    " j% W+ _4 [! i7 J$ s8 Z$ d3 Q
  15.                                   required to address the flash memory. The flash capacity can be up to 4GB
    ' ]3 k2 Q4 ?. n* E6 ]; o; m) B
  16.                                   (addressed using 32 bits) in indirect mode, but the addressable space in , j# ]3 z. w9 G  [; j
  17.                                   memory-mapped mode is limited to 256MB
    , M) e- e. w+ p/ J$ R
  18.                                   This parameter can be a number between 0 and 31 */
    % c1 D) ^/ T5 d2 b$ f! R6 b7 i
  19. 4 |9 u7 w$ p+ q/ h
  20.   uint32_t ChipSelectHighTime; /* Specifies the Chip Select High Time. ChipSelectHighTime+1 defines the minimum number
    0 N3 s- N  t* r  i* {; k: p
  21.                                   of clock cycles which the chip select must remain high between commands.
    0 A5 V2 [' P0 E- P
  22.                                   This parameter can be a value of @ref QSPI_ChipSelectHighTime */
    " \; a  w/ H( C

  23. + w1 H4 u! k% B1 Q$ A& ]
  24.   uint32_t ClockMode;          /* Specifies the Clock Mode. It indicates the level that clock takes between commands.
    3 o) S  T3 L* s4 d
  25.                                   This parameter can be a value of @ref QSPI_ClockMode */6 ]  g. @5 q" P% }: _

  26. ) f0 I2 M9 d. j( F9 Y) S$ g
  27.   uint32_t FlashID;            /* Specifies the Flash which will be used,
    ! i! K4 D. s& t3 [
  28.                                   This parameter can be a value of @ref QSPI_Flash_Select */
    & }5 Y' w! y& V1 t8 n: e1 u8 A$ h6 A
  29. * Z1 j; ]7 d: L* u9 A8 _7 N3 O7 J
  30.   uint32_t DualFlash;          /* Specifies the Dual Flash Mode State' A- O( o, y  B% Q
  31.                                   This parameter can be a value of @ref QSPI_DualFlash_Mode */                                               
    2 T8 {7 J/ W4 {
  32. }QSPI_InitTypeDef;
复制代码
  1. // 采样移位$ r) W& Y8 h8 a3 Q! e
  2. #define QSPI_SAMPLE_SHIFTING_NONE           ((uint32_t)0x00000000U)        /*!<No clock cycle shift to sample data*/
    , d5 ^* `2 r% a# |+ n
  3. #define QSPI_SAMPLE_SHIFTING_HALFCYCLE      ((uint32_t)QUADSPI_CR_SSHIFT) /*!<1/2 clock cycle shift to sample data*/
复制代码
3 `: v7 r9 H2 F6 F  e& e1 g( t
四、QSPI接口设计(仅供参考)
4 ]8 ]' J3 {: P/ V- L" g& t* l
  1. #define QSPI_CLK_ENABLE()         __HAL_RCC_QSPI_CLK_ENABLE();
    8 }+ r4 A5 O2 Z* L; R1 U  h

  2. 4 D$ z9 b( w: s1 Q
  3. #define QSPI_BK1_NCS_PORT         GPIOB
    + o5 h4 `7 T; n: {; F" [4 _$ P$ n
  4. #define QSPI_BK1_NCS_PIN          GPIO_PIN_6
    7 B6 T7 R, b- w+ `
  5. #define QSPI_BK1_NCS_AF           GPIO_AF10_QUADSPI
    + J# ]0 ]2 }7 g7 G. n% H+ r
  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)
    9 S! V, b5 Q# C( Z0 W
  7. . x* t1 P) [( a: {+ H. g
  8. #define QSPI_BK1_CLK_PORT         GPIOB
    ! m5 B* m6 B# K) Q$ Q  l* g
  9. #define QSPI_BK1_CLK_PIN          GPIO_PIN_2; v5 K6 Z+ E7 K& M8 p
  10. #define QSPI_BK1_CLK_AF           GPIO_AF9_QUADSPI
      L: l! P" D5 ?1 T4 @
  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; d7 f" j3 v! L# l
  12. 2 F5 |/ K) ~6 Y; g; G* F% W
  13. #define QSPI_BK1_IO0_PORT         GPIOF
    - F. N" q6 Z  h# e$ o
  14. #define QSPI_BK1_IO0_PIN          GPIO_PIN_81 e* c# r& q6 L6 X2 ]3 b& n0 B) J
  15. #define QSPI_BK1_IO0_AF           GPIO_AF10_QUADSPI& I/ ~3 c) b% a3 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)5 P; M0 l" H' _* P  A
  17. ) d% K- x) `* \7 ]) W! @5 n4 m# f
  18. #define QSPI_BK1_IO1_PORT         GPIOF* H0 U+ {+ z- X# ]& V
  19. #define QSPI_BK1_IO1_PIN          GPIO_PIN_9( a) k3 k7 k, o. d% M! }. J
  20. #define QSPI_BK1_IO1_AF           GPIO_AF10_QUADSPI
    ' I  _8 I0 f. E: Z
  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)
    ! L. C9 I8 C; c/ S" a

  22. 2 `4 ^2 A/ S2 I5 c
  23. #define QSPI_BK1_IO2_PORT         GPIOF
    0 I' `; Y2 \. O& I, V% d. ]
  24. #define QSPI_BK1_IO2_PIN          GPIO_PIN_7
    9 A5 h) k: d4 m. q# R- ]: E- p( m
  25. #define QSPI_BK1_IO2_AF           GPIO_AF9_QUADSPI
    * }; c. W) @+ i; b4 V: h% ~9 E
  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)
    ; R' |" U2 F' Q
  27. % i) P* h' V/ E( u
  28. #define QSPI_BK1_IO3_PORT         GPIOF
    ! F2 I% ]( o1 Q+ c* s+ k8 J: J9 U" h
  29. #define QSPI_BK1_IO3_PIN          GPIO_PIN_6
    7 q2 ~2 p0 T) U$ x7 R* \7 Z8 D& O
  30. #define QSPI_BK1_IO3_AF           GPIO_AF9_QUADSPI
    / i) H! P) H7 i, ~1 h  m
  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. // 封装几个必要的接口4 K( r+ {* Y  t' l& w: x+ L( f0 K
  2. static QSPI_HandleTypeDef qspi_handle;& E, y7 q( G) u2 V9 d% u; O7 \
  3. 0 r& z8 c; `6 O# ^
  4. static void qspi_gpio_init(void). w: @& E6 g0 R' N2 Q) R
  5. {
    5 _* J0 M6 e! A# c
  6.   QSPI_CLK_ENABLE();
    * s- j7 n* T" d/ m

  7. 2 ^* Q9 s) ]; t! z3 _. n/ K* Z1 ~
  8.   QSPI_BK1_NCS_CONFIG();# M8 E- s  _9 B, s2 z
  9.   QSPI_BK1_CLK_CONFIG();' N* s  Y$ l0 N9 y
  10.   QSPI_BK1_IO0_CONFIG();
    ; f$ R2 h4 `2 P
  11.   QSPI_BK1_IO1_CONFIG();
    7 X2 n1 _4 j; C8 D1 [1 b2 E
  12.   QSPI_BK1_IO2_CONFIG();
    # r4 a" p' q, ^' J
  13.   QSPI_BK1_IO3_CONFIG();
    $ \% B) R+ W% u( K7 w
  14. }! ^- T8 ^) Q4 ~4 l8 k
  15. 6 D4 l9 ^; C* i0 ~& t8 N
  16. static void qspi_mode_init(void)
    - s1 c/ g4 @# i/ b& |
  17. {
    # u  j" q  ^" s$ M, R2 K3 a+ }5 X
  18.   qspi_handle.Instance = QUADSPI;                        // QSPI2 ~* U3 ^* i$ V) |9 J5 f8 d+ c6 w2 a
  19.   qspi_handle.Init.ClockPrescaler = 2;                   // QPSI分频比,W25Q256最大频率为104M 所以此处应该为2,QSPI频率就为216/(2+1)=72MHZ6 Y  E1 t8 k" C+ U7 g
  20.   qspi_handle.Init.FifoThreshold = 4;                    // FIFO阈值为4个字节
    : r- G# }- O. U
  21.   qspi_handle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; // 采样移位半个周期(DDR模式下,必须设置为0)& `2 s0 T& V( T2 c
  22.   qspi_handle.Init.FlashSize = POSITION_VAL(0X2000000) - 1; // SPI FLASH大小,W25Q256大小为32M字节9 y4 {! \: ^, x
  23.   qspi_handle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_4_CYCLE; // 片选高电平时间为4个时钟(13.8*4=55.2ns),即手册里面的tSHSL参数) X( d( ~0 N+ T! j1 o! o
  24.   qspi_handle.Init.ClockMode = QSPI_CLOCK_MODE_0;        // 模式0
    7 U# w5 t2 h& Q
  25.   qspi_handle.Init.FlashID = QSPI_FLASH_ID_1;            // 第一片flash# f! A2 W. y4 B* f2 J7 p
  26.   qspi_handle.Init.DualFlash = QSPI_DUALFLASH_DISABLE;   // 禁止双闪存模式/ p  n6 a" r6 O2 f+ R6 n$ S% D8 q
  27.   HAL_QSPI_Init(&qspi_handle);      //QSPI初始化
    5 Z. u$ e+ x9 o
  28. }
    3 L4 `; W5 L. {% ?4 F

  29. # u  C" |# B) m7 f, A) V; x" a
  30. void QSPIInit(void)
    $ j0 V) g8 q- L9 o0 I9 j  [
  31. {
    * I5 A- F; Q9 \8 V
  32.   qspi_gpio_init();
    5 a( s+ F# X" g9 h5 S" }
  33.   qspi_mode_init();) O: j. T4 ~6 d  d
  34. }/ T6 g/ v/ n+ K$ ~

  35. " l. c( o3 O" ~; f' f
  36. // QSPI发送命令
    1 N% @& {% s9 `
  37. // instruction:要发送的指令% ^! U! v2 i6 M4 R5 s
  38. // address:发送到的目的地址
    % t/ I& o" R: z1 M) Q/ L  [
  39. // dummyCycles:空指令周期数' I' h$ j) V4 ?" G3 M$ c* `7 m) U
  40. // instructionMode:指令模式;QSPI_INSTRUCTION_NONE,QSPI_INSTRUCTION_1_LINE,QSPI_INSTRUCTION_2_LINE,QSPI_INSTRUCTION_4_LINE) G+ S* c0 n1 b$ ]( h+ Q
  41. // addressMode:地址模式; QSPI_ADDRESS_NONE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_2_LINE,QSPI_ADDRESS_4_LINE1 a# ]9 a" Z8 O7 C
  42. // addressSize:地址长度;QSPI_ADDRESS_8_BITS,QSPI_ADDRESS_16_BITS,QSPI_ADDRESS_24_BITS,QSPI_ADDRESS_32_BITS+ L! w  {( O$ k. e, u
  43. // dataMode:数据模式; QSPI_DATA_NONE,QSPI_DATA_1_LINE,QSPI_DATA_2_LINE,QSPI_DATA_4_LINE
    9 Q! n: q  I7 S- M+ A  ?" {" B
  44. void QSPISendCMD(uint32_t instruction, uint32_t address, uint32_t dummyCycles, uint32_t instructionMode, uint32_t addressMode, uint32_t addressSize, uint32_t dataMode)
    " T: U8 M4 l- t: E3 M
  45. {) G" L) Z7 J" v( w6 _8 a
  46.   QSPI_CommandTypeDef Cmdhandler;% {# y" i6 ~) u6 _: y2 b

  47. 6 Y; |% `- G9 p0 e. S$ z' E
  48.   Cmdhandler.Instruction = instruction;           // 指令( F* J& }0 q+ X0 X3 i
  49.   Cmdhandler.Address = address;                   // 地址
    , h8 a3 _. v( O% r' i( |
  50.   Cmdhandler.DummyCycles = dummyCycles;           // 设置空指令周期数
    , [1 ?7 v" m1 B7 g5 ?4 T  Q" F& m
  51.   Cmdhandler.InstructionMode = instructionMode;   // 指令模式
    ! @4 ^& K- D: t: {
  52.   Cmdhandler.AddressMode = addressMode;           // 地址模式3 B, c* @( h$ Z& Y( J; w
  53.   Cmdhandler.AddressSize = addressSize;           // 地址长度7 L8 M( s  w% F  h+ X: |7 A( G; D
  54.   Cmdhandler.DataMode = dataMode;                 // 数据模式3 y8 \) h6 L1 {% ?) w$ ~3 e1 n7 }
  55.   Cmdhandler.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 每次都发送指令
    , _& Y+ |2 p2 t$ j, u$ `
  56.   Cmdhandler.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 无交替字节
    ' L, w* |+ T5 q6 C( g$ T1 @
  57.   Cmdhandler.DdrMode = QSPI_DDR_MODE_DISABLE;           // 关闭DDR模式
    / f4 Z6 ~, Y8 h  `
  58.   Cmdhandler.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    # c6 Z- f/ T& \3 S+ M
  59. $ k4 s0 I7 X2 j; K8 z" b+ h
  60.   HAL_QSPI_Command(&qspi_handle, &Cmdhandler, 5000);
    # L* D- t. ], \( u2 n0 W) \
  61. }* t3 q, f3 k+ b! v/ m  I

  62. 3 W" ?0 }: m7 x7 X: D, f9 a' R
  63. // 接收指定长度数据
    . i' v' [/ x6 G- S$ l
  64. uint8_t QSPIReceive(uint8_t *buffer, uint32_t length)
    & x. ^: r' u. k4 z- f  `8 _! A
  65. {
    2 r" ^- d! v* o5 {* `+ }) u  K
  66.   qspi_handle.Instance->DLR = length - 1;6 m; ?& N, m* |. N' O
  67.         3 {! K/ l- k% i' i3 g" p% v' l0 p# G
  68.   if(HAL_QSPI_Receive(&qspi_handle, buffer, 5000) == HAL_OK)' o5 F$ \- h- M3 c* ^
  69.   {5 u/ `- Y7 Y+ A- v
  70.     return 0;  //接收数据
    . h' I) w( p+ H  z
  71.   }  J" i. E! F, G* m8 s
  72.   else# H4 Q8 N1 q3 ^: O' H+ h$ }8 A9 ]. P
  73.   {% M6 V+ X$ }. V! `; @
  74.     return 1;+ l1 }$ g7 R& o: E  T+ l9 p
  75.   }
    : P- X* H) H& o/ u( j6 p8 C2 V
  76. }/ C9 z2 s4 g( E
  77. 5 g: D: T# X$ A( M
  78. // 发送指定长度数据
    ' B% j8 j0 K' \) K* \: x2 ?7 t4 p
  79. uint8_t QSPITransmit(uint8_t *buffer, uint32_t length)
    " @: D2 Y" P1 `) ]  Q
  80. {
    * [5 W: l7 D' |7 S
  81.   qspi_handle.Instance->DLR = length - 1;
    7 G& C' R* o2 P- {/ d, r
  82.   if(HAL_QSPI_Transmit(&qspi_handle, buffer, 5000) == HAL_OK)* h" S8 D- }4 |2 H, i3 V" \  F8 }
  83.   {
    # S( U2 U$ S7 G5 {
  84.     return 0;  //发送数据
    & x. _! l' E5 F  o) x1 T1 J
  85.   }5 w2 U$ O) e# j# v# B+ j. p8 b
  86.   else: J# r5 `' ^+ F! @: Q/ g. o: p
  87.   {" J3 a# ~" |" c5 [7 {( i7 r
  88.     return 1;
    7 ~& ^/ ^$ X  P* _, v
  89.   }8 L) o. Y2 ~& O* C$ c! J
  90. }
复制代码
" Y- s- r4 v4 w+ w$ w8 d5 f; L
五、QSPI驱动W25Q256
- u2 |# A+ q3 S; d  S& `W25Q256:32M  512块,每块16个扇区,每个扇区4K。具体指令看手册。封装了如下接口(初始化读写擦除等接口是以后移植文件系统的基础):" g1 R2 d& Q# ]9 c* T3 ^

: M" Y. i: W3 Z$ B: F  o
  1. #define W25QXX_WRITE_ENABLE      0x06
    5 _, h0 a* J% x' `% ^+ ]
  2. #define W25QXX_WRITE_DISABLE     0x04
      }0 z: |0 G- H/ S. F( ?- q* O
  3. #define W25QXX_READ_STATUS_REG1  0x05& G9 a+ L( }& @# I& K, V: a4 Z
  4. #define W25QXX_READ_STATUS_REG2  0x35) L( M/ p5 S. `6 _7 x& m& |
  5. #define W25QXX_READ_STATUS_REG3  0x153 d; @# A; H4 c5 r8 _
  6. #define W25QXX_WRITE_STATUS_REG1 0x01
    ; y6 e# u8 ^: x) {7 C1 p  q
  7. #define W25QXX_WRITE_STATUS_REG2 0x316 V- m& F8 Y0 Y8 Q, M' v
  8. #define W25QXX_WRITE_STATUS_REG3 0x11
    $ o% |. z$ x' W( a
  9. #define W25QXX_READ_DATA         0x03
    + k' l; L" `; t  U0 |. ?! N
  10. #define W25QXX_FAST_READ_DATA    0x0B# O! f" E0 W3 m9 F- R' i" k8 V
  11. #define W25QXX_FAST_READ_DUAL    0x3B* V' i, V1 `% Z8 K3 w
  12. #define W25QXX_PAGE_PROGRAM      0x02. g9 T' J2 m$ z9 y' O
  13. #define W25QXX_BLOCK_ERASE       0xD80 R$ w4 f, {% J, y  @
  14. #define W25QXX_SECTOR_ERASE      0x20
    7 z+ L5 c! l. A* B/ s+ E
  15. #define W25QXX_CHIP_ERASE        0xC7
    % D5 H5 ^6 \" q5 o7 \3 I$ V; p
  16. #define W25QXX_DEVICEID          0xAB
    $ w/ K5 v' e, \6 R* L
  17. #define W25QXX_MANUFACT_DEVICEID 0x904 G3 l* C( s  ~6 z( m" D$ W  E
  18. #define W25QXX_JEDEC_DEVICEID    0x9F
    * N# Q7 M# i  r) d3 D) [
  19. #define W25QXX_EABLE_4BYTE_ADDR  0xB7
    8 w. t4 M9 ]* U" l" f
  20. #define W25QXX_EXIT_4BYTE_ADDR   0xE9
    9 K- D* J; ]1 M% Y5 }* B
  21. #define W25QXX_SET_READ_PARAM    0xC0
    ; L* n4 g# T3 M1 F
  22. #define W25QXX_ENTER_QPIMODE     0x38
    + B8 x, j! t) Z# l* M  z
  23. #define W25QXX_EXIT_QPIMODE      0xFF
    ) e" Y. }5 Z5 j" d. |: U, I

  24. 6 n+ [/ r% _9 C8 k
  25. uint8_t w25qxx_qpi_mode = 0; // QSPI模式标志:0,SPI模式;1,QPI模式.
    ) Y: A2 o: X' Z0 s2 d1 q
  26. * i0 s0 Z3 {% D
  27. // 读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器* Q& V' {1 D9 _4 N# r
  28. // 状态寄存器1:
    ! _. D; w9 ^. \. r: j3 L8 D
  29. // BIT7  6   5   4   3   2   1   0
    7 |% I4 g% n  p  a& F
  30. // SPR   RV  TB BP2 BP1 BP0 WEL BUSY  y# j% T/ I0 _8 a0 F% {
  31. // SPR:默认0,状态寄存器保护位,配合WP使用
    . B' V5 l1 _# v# \, r$ K+ }
  32. // TB,BP2,BP1,BP0:FLASH区域写保护设置6 z/ ~! g  G" h: Z! K8 F6 k3 D
  33. // WEL:写使能锁定
    : f( p3 f7 s6 ]6 n# |
  34. // BUSY:忙标记位(1,忙;0,空闲)
    7 W: H' F: r) k2 I8 w
  35. // 默认:0x00
    # ~. _1 b! m% W# R8 u
  36. // 状态寄存器2:1 w1 ], t3 u! X3 X- h$ N/ L
  37. // BIT7  6   5   4   3   2   1   0
    % }# F5 P8 q4 f& Z
  38. // SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
    / _) L& z3 r. i1 q! I1 @, m$ z
  39. // 状态寄存器3:: Q" j. X) j/ |$ \( r0 W, A5 S
  40. // BIT7      6    5    4   3   2   1   08 Z% {1 Z, u; P+ Q  b4 [# M" i
  41. // HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS& R6 D! B+ }0 c$ W
  42. // reg:状态寄存器号,范:1~3
    ' N: T) T1 Y9 g& ^8 W
  43. // 返回值:状态寄存器值
    7 z; K, F, t  x* D" D
  44. static uint8_t w25qxx_read_status(uint8_t reg)
    5 j/ S' J8 b$ P% b8 F
  45. {1 t1 A% E0 G# p
  46.   uint8_t value = 0, command = 0;
    , Z! p& w" H/ L6 g9 D
  47. * n0 b3 p0 O$ `! p/ F
  48.   switch(reg). A1 k: X! u/ F: \  q+ Q
  49.   {
    7 T, z0 \# n* H  l1 V$ d# j
  50.   case 1:
    9 M% b9 p; l2 U) T' c' X$ N! I
  51.     command = W25QXX_READ_STATUS_REG1;  // 读状态寄存器1指令
    & d5 E$ y7 D( e
  52.     break;3 p6 c$ c5 `* |4 Q6 u1 A) _7 \( A
  53. ! D  D9 Q, a" I4 u* y
  54.   case 2:$ D8 s8 L1 a- y9 z
  55.     command = W25QXX_READ_STATUS_REG2;  // 读状态寄存器2指令+ ]2 q% |7 f. E, U4 Q
  56.     break;
    : u( Q2 y) T( j7 S5 Y9 {4 M

  57. 7 R$ x8 H" p. U: M- B" n4 t
  58.   case 3:
    ) }( S0 \0 U2 n
  59.     command = W25QXX_READ_STATUS_REG3;  // 读状态寄存器3指令
    + x$ i' Y' W) b+ ~# Q" H
  60.     break;2 _8 u. U& D' t) K0 e6 w
  61. ' n* s+ ^1 u9 C
  62.   default:( ]! T9 |1 f' X& H9 y) E3 y+ C8 U% h8 ]
  63.     command = W25QXX_READ_STATUS_REG1;
    9 {6 h8 R  ^6 e. w: T. U7 f
  64.     break;
    9 X& `5 ^0 @8 u, r8 [! R& J
  65.   }
    7 _: {% j2 s' ?9 f
  66. ( }7 d  W8 h  \, g7 N  l& j
  67.   if(w25qxx_qpi_mode)
    4 P4 R  H4 P7 h" b
  68.   {
    ! p3 o  i3 u8 t8 l2 @2 D8 g
  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个字节数据1 T. w  S: y  N0 c6 m, y7 _% {
  70.   }1 T2 j% ^! y4 w, S" I/ Q7 _
  71.   else
    # w  q. \  o6 ?+ Z& _- A
  72.   {% s- k& s" B# J. i; Z; _  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个字节数据+ g" z, c) _/ u% R) T
  74.   }5 p4 X/ \1 {! f
  75. # X/ n0 s& R4 F5 _
  76.   QSPIReceive(&value, 1);, k7 a2 Y% _" ]- o4 b9 J5 o
  77. # @! i7 U& C% b  O
  78.   return value;
    . R6 t" B/ A# P3 |8 u% y4 d4 G
  79. }/ \- J1 n3 Z* [% y6 D7 F( K+ `
  80. $ N2 t+ y: A8 b* \$ X! ]$ B
  81. // 写状态寄存器
    0 K3 {! l4 ]; A( A
  82. static void w25qxx_write_status(uint8_t reg, uint8_t status)+ H- g2 i9 F  g. h: n
  83. {% {+ _0 ~) u4 J3 ^. Q  |
  84.   uint8_t command = 0;+ n3 b+ m% p+ E' M5 Z2 e& O

  85. / o; o- e0 A5 a% `  h/ t$ |
  86.   switch(reg)) Y1 g$ V$ a  ~! b
  87.   {. t, _& i# t+ h8 L" d+ b" C8 V/ w: C
  88.   case 1:% `  m1 t$ ~! m! X
  89.     command = W25QXX_WRITE_STATUS_REG1;  //写状态寄存器1指令
    7 m5 H6 P$ \1 L2 @
  90.     break;0 m: _1 d6 h7 Z
  91.   case 2:
    5 \. Z5 S% d, z  B4 ?! E
  92.     command = W25QXX_WRITE_STATUS_REG2;  //写状态寄存器2指令5 ]' Z8 v7 m1 M# y$ G" b- v
  93.     break;0 N5 u3 ^8 U5 ^; n1 `6 i
  94.   case 3:1 R, j4 H9 |4 c
  95.     command = W25QXX_WRITE_STATUS_REG3;  //写状态寄存器3指令
    , R$ w' o2 t* a6 n' l2 \* C
  96.     break;
    8 n2 a0 @  N6 T& a
  97.   default:; e6 U6 r+ h8 ?- {0 D
  98.     command = W25QXX_WRITE_STATUS_REG1;8 Q2 q0 Z/ K" }4 p9 M
  99.     break;! B: {, _7 n5 g  ^3 q# B
  100.   }
    7 L5 Z3 S$ H# Y# b5 A
  101.   if(w25qxx_qpi_mode)# m. h4 W8 z- q9 y
  102.   {, b) n3 i9 y+ ]2 o  n
  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个字节数据
    & |( p" ]* F8 e! w
  104.   }" @$ Q* L! h7 R. {0 C
  105.   else
    - g2 H- H6 b) H; m3 {, g( O! [
  106.   {, a$ \4 M" l# z  \' o
  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个字节数据3 J, Q' U9 e. @7 I( F
  108.   }
    - G. s& o0 R8 S4 F. Y" F
  109.   QSPITransmit(&status, sizeof(status));
    9 w3 r0 m5 f) ]4 x- a' F: w
  110. }1 R9 w2 @/ r, @2 H

  111. 8 `) N8 H1 u- v* m9 L0 b; ~2 P
  112. // 写使能  将S1寄存器的WEL置位& \/ J& L7 T! V9 I* a1 D
  113. static void w25qxx_write_enable(void)# _# g' p6 @: F) C
  114. {
    ' h& A5 G+ x- j$ p. H. R2 n% w6 C
  115.   if(w25qxx_qpi_mode)- x" o, n1 q, i
  116.   {: c% e: D& n) D4 h! h" E& R
  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个字节数据+ i/ Z/ s- K; G- b
  118.   }
    ( z% J8 Z8 Q6 l! |+ |1 B5 R9 _
  119.   else+ ~* L5 R  K$ T* T) o0 X! y
  120.   {
    9 n- t9 a6 u# W, |8 J
  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个字节数据# H+ Z- A6 x! D2 l7 T' v- |
  122.   }
    + v) j5 E; X% v+ H% t! K
  123. }
    1 O  Y) i: M3 t8 m# j4 j

  124. + y  n! [5 V2 d
  125. // 写失能  将WEL清零" W" C+ G3 i" F- V9 N$ p
  126. void W25QXX_Write_Disable(void)& C% e% P$ s! a0 V+ a
  127. {# X  s: \$ r9 V/ [4 f
  128.   if(w25qxx_qpi_mode)' \8 a/ C7 M* _& M  g7 w0 u
  129.   {: i4 s7 D5 X2 ]# j$ n" y& x% z' b
  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 C3 p$ z- Y; m" Y- [. P
  131.   }
    . A' U0 {8 ]) c  ~! K  ~; Z
  132.   else
    & F# p! c6 g& Z% m) J/ ]  D
  133.   {
    ; K. _7 |7 H6 `% @2 G& a3 @
  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个字节数据# ^) B$ Z" f" C( h$ U
  135.   }
    : [7 I; C3 q7 i1 l; h& o3 t
  136. }+ T. W/ u  {* i
  137. ) z2 _% M6 z6 r3 V% I; ?; @
  138. // 等待空闲& T9 E: _  L, a7 B0 U8 W$ m
  139. void w25qxx_wait_busy(void)
    " f8 N. q4 ?/ a# T% Q2 c
  140. {
    7 g/ i2 f- ^* K6 G7 W( D1 `3 \" x! d
  141.   while((w25qxx_read_status(1) & 0x01) == 0x01); // 等待BUSY位清空
    1 R: c) E, o9 F
  142. }
    3 U+ Q  g) m7 n
  143. $ l' I7 V" I% K- d' l" f
  144. // W25QXX进入QSPI模式
    % H" d6 `2 r1 j! E1 \# t5 S& _
  145. static void w25qxx_qspi_init(void)
    ( B: f& k: Q+ ]# \' r
  146. {
    9 A  r9 ^8 t5 H! {
  147.   uint8_t reg2;& k' k3 W0 T  `9 I
  148.   reg2 = w25qxx_read_status(2); // 先读出状态寄存器2的原始值
    ) y4 r; d) w; |2 w) D
  149.   if((reg2 & 0X02) == 0)  // QE位未使能  X% E& L' g8 d& }/ V+ R: M
  150.   {
    . K  {; N6 [. v
  151.     w25qxx_write_enable(); // 写使能" x+ V' B& \1 y# _  n* Z, J
  152.     reg2 |= 1 << 1; // 使能QE位" f5 T' D* ?4 l! V/ [, e  [
  153.     w25qxx_write_status(2, reg2); // 写状态寄存器2  j! |0 M5 S  `9 v* p9 @
  154.   }( Y# h1 Y- A- X

  155. 6 R4 H& ], R3 I1 A
  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个字节数据
    6 Y% r1 i6 |3 @  g! u

  157. ; y( X' p* Q8 \5 z8 I* }
  158.   w25qxx_qpi_mode = 1; // 标记QSPI模式- t4 G; I' K1 F$ W
  159. }+ D; `0 j  Z5 d! G: m

  160. - O7 }3 [# D! v8 F* G; D
  161. // 0XEF13,表示芯片型号为W25Q805 e9 I( Z! i( _
  162. // 0XEF14,表示芯片型号为W25Q16; Z( u/ M' l' Y
  163. // 0XEF15,表示芯片型号为W25Q327 \$ C- p- z6 B: `9 E: Q
  164. // 0XEF16,表示芯片型号为W25Q64. p' N$ x" ^; o0 e/ ~  T3 ~/ u; B
  165. // 0XEF17,表示芯片型号为W25Q128: n8 E- i/ F8 @
  166. // 0XEF18,表示芯片型号为W25Q256" K* i. o, |; ]0 k( \& u
  167. static void w25qxx_id_get(void)
    / ^; Q: c$ n! [
  168. {
    % N1 m- g: K. g6 a1 `3 u
  169.   uint8_t temp[2];& C2 Q, V' B; u9 n# V
  170.   uint16_t device_id;
    4 l$ Y2 H% S% z3 F' |

  171. 9 u3 R/ B) v; }. [/ g% j
  172.   if(w25qxx_qpi_mode)
    * V8 q: z4 @2 E& [8 O# d. F
  173.   {* A4 t. k" Y0 q/ V
  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 L) }2 f; P* j; z
  175.   }) J; }* C' [+ S
  176.   else
    , G. D# U0 C+ p0 O1 [  V5 w4 q
  177.   {" N- P9 o2 W3 f3 {2 c  }" b
  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个字节数据
    % L, J! W" v/ F  Y" J. |6 ]
  179.   }
    3 y5 Z9 [3 o  U  z& s0 u
  180. 3 U* F+ g0 F( e
  181.   QSPIReceive(temp, 2);. G$ G% |: s" y0 s% a

  182. + M  b8 l8 G( O2 `4 n# D  Y  E
  183.   device_id = (temp[0] << 8) | temp[1];
    ( F1 G9 Y2 n" u  t/ l
  184. 1 Q; I0 `+ l3 ^, D9 i3 t* z
  185.   printf("QSPI Flash Device ID: %X\r\n", device_id);% i- O$ S; l; q: ]; N
  186. }' s( \8 {% J' a' r8 @

  187. ! Q) l( O; `* ]( {( C% Y& _2 [7 G7 m
  188. void W25QXXInit(void)$ k$ s. x: s3 ^* I8 X
  189. {) Q1 v; L2 k5 }( f0 Q  h
  190.   uint8_t temp;' k. ]* S+ X$ o  U

  191. ; N, z/ ]. ]- n& F$ z( U7 l2 d
  192.   QSPIInit();
    + z. x# N0 b4 Z) ^
  193.   w25qxx_qspi_init(); // 使能QSPI模式
    2 T4 N- e+ m; X
  194.   w25qxx_id_get();    // 读取FLASH ID.
    # P( Y2 }; o3 p/ C' B( q5 T

  195. % P  X9 a4 A- p& k% e9 \, }% q
  196.   temp = w25qxx_read_status(3); // 读取状态寄存器3,判断地址模式
      A- R" o6 e: m, H# ?2 ]: ~
  197.   if((temp & 0X01) == 0) // 如果不是4字节地址模式,则进入4字节地址模式) b0 B, Y7 c! d+ N0 ?) {( j6 L
  198.   {. i6 i) V' a4 u( `1 x
  199.     w25qxx_write_enable(); // 写使能
    + A! U  ~. E  T( 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个字节数据
    " v: Q8 \0 |: P; S/ H) Z
  201.   }
    / v3 n7 c5 Q/ G+ @
  202.   w25qxx_write_enable(); // 写使能
    ! t' a9 L. e9 R1 a/ y* q% ~: a
  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个字节数据+ u) Z7 g: P$ h- O
  204.   temp = 3 << 4;      // 设置P4&P5=11,8个dummy clocks,104M: v) k+ o5 J0 M* H
  205.   QSPITransmit(&temp, 1);
    + {/ h- B) j' N% p
  206. }
    % o: w2 x" |; n/ L/ z- P% I' ?: ]

  207. 0 F, b9 @+ u6 B& Z) q$ q. o
  208. // 擦除一块4096字节 最少需要150ms
    / B* M! |8 f' p' }6 A8 R# j/ {
  209. void W25QXXSectorErase(uint32_t addr)# U- n/ \0 \% d/ ~: t
  210. {$ U: Q4 [) [9 |+ K
  211.   //addr /= 4096;
    * t$ J0 A% \% D7 x. E) w. }' H) l
  212.   addr *= 4096;
    ( ]' o5 W) V# t6 x
  213. * C, S! C; b/ y- w$ ^
  214.   w25qxx_write_enable();
    4 P. F# O/ [: U: y$ q8 P
  215.   w25qxx_wait_busy();# ]  M4 n# @+ e
  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个字节数据. r5 |0 D" ]- n- t( [/ c
  217.   w25qxx_wait_busy();$ p' ^) i: o. m( j% h
  218. }
    ' M: x$ b9 f( `1 ?# p$ V
  219. % k$ I( s" G+ i! \, u6 s

  220. + b( W3 ~% q. f# Z
  221. // 擦除整个芯片0 f8 `) D2 S' H
  222. void W25QXXChipErase(void)
    9 ^* u- B8 v; |5 b5 B1 m" e
  223. {
    ) U/ i) n8 K; L% m- D, K3 H$ w
  224.   w25qxx_write_enable();          //SET WEL/ v1 ~( m' G$ q; [8 K  q: ^
  225.   w25qxx_wait_busy();
    $ U2 H* C% U& O9 ^% l
  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 v! \# H. y6 r6 k6 b# Y" O" Q
  227.   w25qxx_wait_busy();           //等待芯片擦除结束
    ; }1 ?+ }) R$ Q1 m# E
  228. }
    # O- M/ T& ]1 M" j4 f2 e1 V

  229. 1 w& b3 T2 H) ^5 L6 T% Q/ n
  230. // 写最多256字节
    3 o* l$ m" W: H/ y% Y- {+ L
  231. void W25QXXWritePage(uint32_t addr, uint8_t *buffer, uint16_t length)( j' b7 B7 B. c1 j
  232. {
    , u. g! H! c* R. y: t' z
  233.   w25qxx_write_enable();          //写使能
    : Z7 Y, @* ]+ C3 [7 H6 [
  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个数据
    ) q) e4 i: x" m' R- p
  235.   QSPITransmit(buffer, length);
    " e0 J& o2 h: j; A
  236.   w25qxx_wait_busy();            //等待写入结束
    $ j1 s# h& h; _% r" c: x  R
  237. }
    - |$ k% V7 m) v5 I1 ?9 M0 X6 M
  238. 3 t* g  _9 D; C7 ]8 Z. H$ L+ E0 U+ _
  239. // 写最多65536字节 无擦除动作
    2 i% b; A0 X: U' u" p  w/ G
  240. void W25QXXWriteExt(uint32_t addr, uint8_t *buffer, uint16_t length)
    9 f$ |. V- J+ J; T7 n; |/ L3 B
  241. {1 A" q, o/ n1 F* r5 G
  242.   uint16_t pageremain = 256 - addr % 256; // 单页剩余的字节数
    2 E! I) S) Y- j- {6 s" ]+ Z

  243. . h. R/ L0 f; l  M0 ?3 w7 b" G/ `
  244.   if(addr + length - 1 > (32 * 1024 * 1024))
    % f1 g% J/ a8 y
  245.   {
    1 }( {+ b+ d9 S
  246.     return;1 u4 Z3 Z; e' D) B( ]% I" P
  247.   }) y4 y& p. g4 h! v7 P) D! X% Q

  248. - m0 E$ ]; _8 u( U
  249.   if(length <= pageremain)3 u; J3 V; Y* i2 z7 D% ?6 M, }* {
  250.   {
    % m, O8 u6 K! c' A( e
  251.     pageremain = length;  // 不大于256个字节
    ; ^; i! v2 p1 B- I# v- C7 M5 l
  252.   }
    * m* v4 ], D) \) M
  253.   while(1)
    3 O4 k4 Y% H( ]4 V3 V& W
  254.   {
    4 H1 V! _" q5 p  A2 ^
  255.     // 分页写入0 s- ?  X' A! V! |
  256.     W25QXXWritePage(addr, buffer, pageremain);! H3 D( j4 N# G
  257. % o8 j$ @- W# b% N
  258.     if(length == pageremain)
    ) m0 m$ a5 |8 B: O2 w; e% J
  259.     {
    ' m0 K  _6 d: E( Q2 }
  260.       break;  //写入结束了
    # R  `6 F# g" y  s
  261.     }
    ; u, f4 Y7 s3 A& V$ i* D
  262.     else
    * C5 w$ r$ d* p: q5 q9 Y8 Z, T
  263.     {
    5 i& i# K/ Z. G/ \! ?  y
  264.       buffer += pageremain;
    ! c: @1 U0 E3 F3 z7 u1 Q
  265.       addr += pageremain;
    9 R* _9 `' J7 P; z, {- P) `

  266. * C4 ]* U& g8 ~* D
  267.       length -= pageremain; // 减去已经写入了的字节数
    ' n" c: m# [3 X& H" W8 A$ z' O
  268.       if(length > 256)
    . j5 A" ?3 t, C3 {' b- K3 ]% t' {" w
  269.       {0 |; W1 \! e( f7 Z% d5 _
  270.         pageremain = 256;  // 一次可以写入256个字节5 A- G& O4 z* E* \% ^
  271.       }
    / M3 k8 V1 G; r. {/ Z* J
  272.       else# l3 |( ?; J% j) O
  273.       {4 H4 T5 F2 E- }  f" q; q2 D" p
  274.         pageremain = length;  // 不够256个字节了) N% M! [. }* r* D' v1 {3 }: F
  275.       }7 {5 r$ `% J9 l4 B' N( E+ ^
  276.     }/ @  K: C/ j- q
  277.   }; ]7 y% S2 D4 f. `% M* U
  278. }. k' D- r$ |8 b
  279. 4 A  Q" O' n5 I0 D$ w
  280. // 写最多65536字节 带擦除动作 ; }6 W) l; [+ n7 K
  281. static uint8_t W25QXX_BUFFER[4096];
    8 `2 x1 o% |! _( Z# Z
  282. void W25QXXWrite(uint32_t addr, uint8_t* buffer, uint16_t length)
    2 M: z/ J3 ~/ c6 X
  283. {, k1 w' d8 W/ T9 C5 a8 b
  284.   uint32_t secpos;1 m# p3 u) Y& T* K
  285.   uint16_t secoff, secremain, i;- n+ z  [0 T7 L2 y+ X0 q
  286.   uint8_t * w25q_buf;
    . P/ L6 r% V+ M

  287. " u6 s% z7 O  r7 j
  288.   w25q_buf = W25QXX_BUFFER;, V9 B( p4 Y( F3 c

  289. 0 }9 ^8 r# C$ e! a
  290.   secpos = addr / 4096; // 扇区地址! @2 j" z4 @# s
  291.   secoff = addr % 4096; // 在扇区内的偏移' Z* s, p, {1 P0 O4 ]# J
  292.   secremain = 4096 - secoff; // 扇区剩余空间大小; C' W5 m7 n+ f2 k
  293. ( K! W: c( W! e6 H, h
  294.   if(length <= secremain)8 _3 a& \+ W8 R8 J$ P0 _9 u- ]% ^
  295.   {
    ( s4 @" W$ O) b& q. F
  296.     secremain = length;  // 不大于4096个字节
    . n0 f4 p* |  o  j1 b- l  W6 X
  297.   }2 U1 `& g8 e' g9 B( ^
  298.   while(1)7 R( Z7 S; r) r& V& h& Y
  299.   {
    4 ^: [; Y9 R/ }, ?- o
  300.           WatchdogFeed();5 E9 B! ?1 Q( ]
  301.     W25QXXRead(secpos * 4096, w25q_buf, 4096); // 读出整个扇区的内容
    $ R: K; W+ d8 X, z4 m
  302.     for(i = 0; i < secremain; ++i)
    % ]! w% c9 ~7 |0 n8 z6 E7 \$ f
  303.     {
    * z; G  H3 P  B2 s% E7 D
  304.       if(w25q_buf[secoff + i] != 0XFF)/ g! u$ D' O6 A! q3 T7 Y& J) m
  305.       {
    ! P3 {- T% q4 O- U
  306.         break;  // 需要擦除  X3 c8 S: U4 T# U- K0 ]$ O7 Q$ j
  307.       }3 s9 V# n( i1 I. k. N4 Y' k" m1 Z
  308.     }
    0 V$ Z8 H7 u. |. s
  309.     if(i < secremain) // 需要擦除
    : T3 ?' {: g' }1 D! y
  310.     {" S# x; F7 C4 c# U, e. H
  311.       W25QXXSectorErase(secpos); // 擦除这个扇区
    + ?- w2 K$ O2 `0 h5 v$ I
  312.       for(i = 0; i < secremain; ++i)
    1 N, K" z1 C' [( ~7 s
  313.       {( {9 X1 O: q0 X* B* z( \
  314.         w25q_buf[i + secoff] = buffer<i>;</i>
    9 ?8 |0 H. G! k: b
  315.       }
    2 @+ w& D( ?; V$ y% ?* K, a5 j
  316.       W25QXXWriteExt(secpos * 4096, w25q_buf, 4096); // 写入整个扇区( }! m  E- d& j# V/ M' E1 O- e

  317. , K8 ^0 y  Y- D# G7 L( c! q
  318.     }7 `8 o8 Q, j8 [9 U$ y( \- M
  319.     else2 }# h9 o# I# P. o) Y
  320.     {
    ! j; P. Y" [9 S5 g1 l& K7 z  k
  321.       W25QXXWriteExt(addr, buffer, secremain);  // 写已经擦除了的,直接写入扇区剩余区间.
    ! P  y" P( k" x; u% ?  F
  322.     }
    / J4 h' w6 e' i  X' b# T: B
  323.     if(length == secremain)4 x8 Z( O/ f& g! C% X5 f/ J2 @
  324.     {) k5 t0 t, i; k% H: l
  325.       break;; v& u3 K6 H2 o( x9 x8 d
  326.     }
    7 P% T- V: B  |6 `# X) t
  327.     else! f9 O; W  |" G; u0 C
  328.     {
    ; p8 v1 }" H3 i' X+ X. t( `
  329.       secpos++;3 O! p4 K, M/ C
  330.       secoff = 0;
    * X" `6 T. P# Z" U6 b
  331. 8 F0 t3 C. W! T- m( Y5 n
  332.       buffer += secremain;
    * s4 D0 A+ s' H' s
  333.       addr += secremain;3 Z+ U6 F3 t0 p& i# L. {1 d) \
  334.       length -= secremain;  h- M/ e- Y& _( K# j3 o! {) q( g0 D/ h) _( F
  335.       if(length > 4096)
    ! ~6 |* J3 {. M) Q' j
  336.       {
    7 B' S% K  o# y
  337.         secremain = 4096;: O3 V3 P$ I( j8 J0 ?; M9 |
  338.       }
      q9 U9 l$ C7 ~& z
  339.       else7 |: B: M) [7 O8 k6 m# O
  340.       {0 U. P3 h6 K; r1 }$ A2 J) j
  341.         secremain = length;5 G( k' T. p# r0 f" ^1 M5 ?! f
  342.       }
    % \, }& p1 N. Q
  343.     }
    , K3 {# [. y# H- o
  344.   };
    * k4 ^' t( y% m# T" N' W
  345. }% @) V# X& h2 L9 _

  346. 8 m+ q+ A0 p( m
  347. 3 M. d' G: u6 j4 |/ {
  348. // 读取SPI FLASH,仅支持QPI模式  在指定地址开始读取指定长度的数据 65536( m) ~! H1 g+ l7 {+ ]/ W
  349. void W25QXXRead(uint32_t addr, uint8_t *buffer, uint16_t length)
    * c; M0 U' M5 O- c
  350. {$ H2 m! r8 e7 Y% Y- ~. f! b9 Z
  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个数据& u& u, A( R% {3 ~; a6 s& S
  352.   QSPIReceive(buffer, length);
    0 Y( P) V2 `4 A2 I4 p
  353. }
复制代码

0 I4 s& H% P7 |/ p也可以使用SPI操作W25QXX。+ w: f# ^9 _3 k0 Z* \  n% e
" L. d2 _' r9 k+ D+ S7 o2 y

7 f3 p' \# ?- S0 H
: [7 o# W0 L) X
收藏 评论0 发布时间:2021-12-11 12:00

举报

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