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

【经验分享】STM32F7xx —— QSPI

[复制链接]
STMCU小助手 发布时间:2021-12-11 12:00
一、QSPI" z/ b4 W1 ~) Q
        SPI 是 Queued SPI 的简写,是 Motorola公司推出的 SPI 接口的扩展,比 SPI 应用更加广泛。在 SPI 协议的基础上,Motorola 公司对其功能进行了增强,增加了队列传输机制,推出了队列串行外围接口协议(即 QSPI 协议),使用该接口,用户可以一次性传输包含多达16个8位或16位数据的传输队列。一旦传输启动,直到传输结束,都不需要CPU干预,极大的提高了传输效率。该协议在ColdFire系列MCU得到广泛应用。与SPI相比,QSPI的最大结构特点是以80字节的RAM代替了SPI的发送和接收寄存器。QSPI 是一种专用的通信接口,连接单、双或四(条数据线) SPI Flash 存储介质。% T& [/ O* O% f4 v. ]  p
  f- v7 [' s: y; ^7 p4 z9 h
该接口可以在以下三种模式下工作:( P: s" k  n7 p5 F
① 间接模式:使用 QSPI 寄存器执行全部操作
/ q) K4 }) ^) J5 H" w& T# O) G② 状态轮询模式:周期性读取外部 Flash 状态寄存器,而且标志位置 1 时会产生中断(如擦除或烧写完成,会产生中断)4 Z7 D! |. K; t& ?3 d. k7 X1 O
③ 内存映射模式:外部 Flash 映射到微控制器地址空间,从而系统将其视作内部存储器。) X8 {! \& f% u7 \9 P0 L/ @
% `& {6 I% z- F" z" c% E! h1 u$ ^9 f1 e
QSPI通过6根线与SPI芯片通信,下图是内部框图:
% I4 `& A: K! d7 |. F6 I3 Z- t
1 }1 u$ m- B4 g7 ~7 Y: E) G; V& H0 [
20190123173205949.png

0 }1 a- `. @3 C% C/ B: V  S
9 b/ V; k8 q6 m( J* lQSPI每条命令,必须包含一个或多个阶段:指令、地址、交替字节、空指令和数据。
! P7 n/ d$ K" @; Q% K- G: r: L7 ?. c  l
QSPI发送命令:等待QSPI空闲;设置命令参数。- l3 }7 Q6 e$ c3 r

. s  `- [; o5 ]/ m( ~) w7 [3 ~QSPI读数据:设置数据传输长度;设置QSPI工作模式并设置地址;读取数据。
3 D* l- O1 \* d6 z5 y/ r7 L! e2 a
$ G5 W! V7 B4 W- y6 g3 Q- XQSPI写数据:设置数据传输长度;设置QSPI工作模式并设置地址;写数据。
1 a  F$ Q. b; r' k! D$ Y  o+ y6 y" @( K/ _2 t. _
' o1 f5 ?, l0 ~3 Q
二、几个重要的函数1 }- @: I/ k- v; J; {
  1. HAL_StatusTypeDef     HAL_QSPI_Init     (QSPI_HandleTypeDef *hqspi); // 初始化9 c1 c; K4 }+ E  c, h
  2. / }0 D  N6 Q4 Y5 [* E5 [
  3. HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout); // 发送命令7 M8 q4 A: [0 R. b) |* v$ M9 b
  4. 7 O" D* ^3 h# Z
  5. HAL_StatusTypeDef     HAL_QSPI_Transmit     (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 发送数据3 H& o! {! u1 A7 R7 {" N
  6. ! E" z) s# m# U# i7 n! g, T
  7. HAL_StatusTypeDef     HAL_QSPI_Receive      (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 接收数据
复制代码

6 N$ E  g, ?+ v) S& ]3 ~三、几个重要的结构
) }& k6 p; x! A( Z8 \  t# H1 K
  1. // QSPI操作句柄
    ( d6 O7 x3 n+ \$ Q: O9 A0 E
  2. typedef struct# L* H. M8 E; s, [1 ~+ M0 O
  3. {
    0 X- T6 }; o' q( C+ ]1 ~
  4.   QUADSPI_TypeDef            *Instance;        /* QSPI registers base address        */0 u$ _+ m; }/ r9 s+ ~
  5.   QSPI_InitTypeDef           Init;             /* QSPI communication parameters      */
    9 E% s9 x9 k5 k
  6.   uint8_t                    *pTxBuffPtr;      /* Pointer to QSPI Tx transfer Buffer */
    $ |2 w8 Z& y6 ~" [  g4 p
  7.   __IO uint16_t              TxXferSize;       /* QSPI Tx Transfer size              */
    1 W) g8 \  D" W5 b+ X# M0 m
  8.   __IO uint16_t              TxXferCount;      /* QSPI Tx Transfer Counter           */. H9 [, \% k0 E& O* t' q
  9.   uint8_t                    *pRxBuffPtr;      /* Pointer to QSPI Rx transfer Buffer */. ^, h! X. f  L  M, k
  10.   __IO uint16_t              RxXferSize;       /* QSPI Rx Transfer size              */
    : r" \" E/ c% T4 S( O2 b8 Y" D* A
  11.   __IO uint16_t              RxXferCount;      /* QSPI Rx Transfer Counter           */+ P  _6 |; s' A  w3 b0 n; J
  12.   DMA_HandleTypeDef          *hdma;            /* QSPI Rx/Tx DMA Handle parameters   */7 b8 n- @2 r* O+ }( M
  13.   __IO HAL_LockTypeDef       Lock;             /* Locking object                     */# M3 P" l, F' n: X5 ~% n
  14.   __IO HAL_QSPI_StateTypeDef State;            /* QSPI communication state           */
    . F  G& f  A0 M. b5 w6 }. |: f' E
  15.   __IO uint32_t              ErrorCode;        /* QSPI Error code                    */
    " j! J# z6 h6 z
  16.   uint32_t                   Timeout;          /* Timeout for the QSPI memory access */
    ) H2 Z3 Y( U5 M& B
  17. }QSPI_HandleTypeDef;
    9 x" O2 Z% Q# U) ^' u

  18. ' X4 g3 j, z+ {# |/ h- T
  19. // Instance:QSPI基地址  ---  QUADSPI
    # W' P7 q4 n9 P) }
  20. // Init:设置QSPI参数- l4 T+ M$ `/ |) e& _! a$ I
  21. // pTxBuffPtr,TxXferSize,TxXferCount:发送缓存指针 发送数据量 剩余数据量
    - {" B; G5 Y; A2 T* N4 U+ p
  22. // pRxBuffPtr,RxXferSize,RxXferCount:接收缓存指针 发送数据量 剩余数据量2 z* O, k0 s5 V0 X+ @
  23. // hdma与DMA相关, 其他为过程变量不需要关心
复制代码
  1. // 参数配置 时钟分频系数  FIFO阈值  采样移位  FLASH大小  片选高电平时间  时钟模式  闪存ID  双闪存模式设置* x" c, b# B( P! f/ a$ n( q0 B
  2. typedef struct& T4 M) C8 m+ ?+ H% @' v" s8 c% O. G* X
  3. {
    # n* K, ~0 \, m; K: \1 ]# J
  4.   uint32_t ClockPrescaler;     /* Specifies the prescaler factor for generating clock based on the AHB clock.
    ) N  j8 t+ L3 B4 m
  5.                                   This parameter can be a number between 0 and 255 */
    ' E* C* l' }, P

  6. / v7 L+ k3 x. m* o
  7.   uint32_t FifoThreshold;      /* Specifies the threshold number of bytes in the FIFO (used only in indirect mode)
    6 E( R0 t: u3 ?7 y
  8.                                   This parameter can be a value between 1 and 32 */
    ) k  q- x$ j& G; H6 G
  9. 1 u7 u) Y2 O! M5 L
  10.   uint32_t SampleShifting;     /* Specifies the Sample Shift. The data is sampled 1/2 clock cycle delay later to
    % m+ N$ I/ Y' x. I8 _4 e
  11.                                   take in account external signal delays. (It should be QSPI_SAMPLE_SHIFTING_NONE in DDR mode)9 }( G, b+ ^' V+ O2 q* k
  12.                                   This parameter can be a value of @ref QSPI_SampleShifting */1 g* }9 x& N" [# [
  13. ! \3 w7 p3 |  W/ f! N5 {
  14.   uint32_t FlashSize;          /* Specifies the Flash Size. FlashSize+1 is effectively the number of address bits ( K( X# I; h  w
  15.                                   required to address the flash memory. The flash capacity can be up to 4GB
    * }  I; K2 I0 r: v
  16.                                   (addressed using 32 bits) in indirect mode, but the addressable space in : a  T# E" e4 w; i8 Y
  17.                                   memory-mapped mode is limited to 256MB7 ?! ~$ P9 ^" P7 N: N: N# [
  18.                                   This parameter can be a number between 0 and 31 */
    8 I+ h" g7 Q+ l) e& H

  19.   P% i0 z! I( f! c$ J% f
  20.   uint32_t ChipSelectHighTime; /* Specifies the Chip Select High Time. ChipSelectHighTime+1 defines the minimum number ' m2 X% ?+ T# A, o4 q
  21.                                   of clock cycles which the chip select must remain high between commands.
    7 s& U' ^0 t9 X2 }- n2 S. M
  22.                                   This parameter can be a value of @ref QSPI_ChipSelectHighTime */ 9 G1 |0 u" k' c* L* \% f

  23. $ H4 w# F! M, S. S
  24.   uint32_t ClockMode;          /* Specifies the Clock Mode. It indicates the level that clock takes between commands.
    / Z- m( F8 c+ o+ W) y  r) H8 u4 J
  25.                                   This parameter can be a value of @ref QSPI_ClockMode */; Y* {/ u, H; c% q7 x, L
  26. ; \0 i5 Q5 ~( e
  27.   uint32_t FlashID;            /* Specifies the Flash which will be used,3 h0 b0 }- I4 p9 y8 J& p+ |3 t
  28.                                   This parameter can be a value of @ref QSPI_Flash_Select */
    4 [3 D: I' a" t+ @* w
  29. * C2 X) f' y2 {1 H9 O$ E, ^0 f  v
  30.   uint32_t DualFlash;          /* Specifies the Dual Flash Mode State
    - l* b- w1 ~& y- t  W& A
  31.                                   This parameter can be a value of @ref QSPI_DualFlash_Mode */                                               
    , _0 N7 r8 v  ^! K
  32. }QSPI_InitTypeDef;
复制代码
  1. // 采样移位
    . v/ f) w; h0 {
  2. #define QSPI_SAMPLE_SHIFTING_NONE           ((uint32_t)0x00000000U)        /*!<No clock cycle shift to sample data*/
    . J6 e' V: j; w1 S
  3. #define QSPI_SAMPLE_SHIFTING_HALFCYCLE      ((uint32_t)QUADSPI_CR_SSHIFT) /*!<1/2 clock cycle shift to sample data*/
复制代码

; s4 @& U( t* L: x/ H3 W四、QSPI接口设计(仅供参考)

  J! S0 T! e/ w0 P2 B6 H/ \5 F
  1. #define QSPI_CLK_ENABLE()         __HAL_RCC_QSPI_CLK_ENABLE();
    " A5 u  c* r4 N1 P( _% H

  2. ( }# a+ E/ x4 H) r; _
  3. #define QSPI_BK1_NCS_PORT         GPIOB  o8 @( l& Z% u
  4. #define QSPI_BK1_NCS_PIN          GPIO_PIN_6# J4 I6 M- g- u8 P* I6 y
  5. #define QSPI_BK1_NCS_AF           GPIO_AF10_QUADSPI: o0 Z, g/ C- Y' m' g; C0 b
  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); }, n) Z" d9 W9 ?% n* k
  7. 5 T6 g7 Y& C6 @. @% v+ V6 m0 d
  8. #define QSPI_BK1_CLK_PORT         GPIOB
    # H( v/ P3 m# j0 h9 y& X
  9. #define QSPI_BK1_CLK_PIN          GPIO_PIN_2
    & F/ B8 k4 x( q% l8 Q
  10. #define QSPI_BK1_CLK_AF           GPIO_AF9_QUADSPI  a0 A, i; J: n. M0 E
  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)
    2 P+ z! Q7 r$ w- e1 E" B
  12. , w) n7 A/ e9 v0 V- r$ A
  13. #define QSPI_BK1_IO0_PORT         GPIOF
    ( j2 }6 X5 l$ c; {/ {9 I' _
  14. #define QSPI_BK1_IO0_PIN          GPIO_PIN_8; Z2 I; a& G+ M( V
  15. #define QSPI_BK1_IO0_AF           GPIO_AF10_QUADSPI% @9 \" Y6 V7 c2 Z8 A
  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)
    - c: k! |! D. x. K' U5 W& n& `. p

  17.   {% x# a4 ~' n% V4 T; l
  18. #define QSPI_BK1_IO1_PORT         GPIOF
    8 b  H% M' T' {- p
  19. #define QSPI_BK1_IO1_PIN          GPIO_PIN_9
    ( j) z2 F5 h$ n  s' B) a9 q
  20. #define QSPI_BK1_IO1_AF           GPIO_AF10_QUADSPI& K! X. `  _$ S9 u0 {0 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)1 ?, C$ J7 \% t) F
  22. ) K7 `8 G6 m. ]. \2 s. r1 K
  23. #define QSPI_BK1_IO2_PORT         GPIOF7 Q# c: i. R: F
  24. #define QSPI_BK1_IO2_PIN          GPIO_PIN_7
    / s! Z, g& V: B% P! V8 e9 j9 A, M- T
  25. #define QSPI_BK1_IO2_AF           GPIO_AF9_QUADSPI  t% b' `1 O& z' ~
  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)& C- }$ |  E* [1 K$ m$ Y, \; w/ O
  27. 5 u  j0 D/ k0 \$ x
  28. #define QSPI_BK1_IO3_PORT         GPIOF
    7 I! i# o9 X$ V: o4 Z% o
  29. #define QSPI_BK1_IO3_PIN          GPIO_PIN_63 {# H$ t/ u5 Y( o% |( X5 P$ [7 Z
  30. #define QSPI_BK1_IO3_AF           GPIO_AF9_QUADSPI" D2 p- [0 y3 H& C$ B
  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. // 封装几个必要的接口
    , n, R/ w% y0 p8 V* [
  2. static QSPI_HandleTypeDef qspi_handle;* B; s* W* z% L5 i; E7 \
  3. 8 I  o+ \# u! ]0 g* l: Q3 J% r
  4. static void qspi_gpio_init(void): f7 S9 u4 Y& [5 W) h5 {2 ~
  5. {# _/ g# l4 D1 _. Z
  6.   QSPI_CLK_ENABLE();
    : a( e, X" L% L. r& H' d2 T0 i
  7. 7 S6 `1 Z9 e& v9 o  O. b
  8.   QSPI_BK1_NCS_CONFIG();
    5 E5 ?+ P9 I1 Y- |* \5 Q. A+ r
  9.   QSPI_BK1_CLK_CONFIG();6 n9 d5 q' f3 I+ ]
  10.   QSPI_BK1_IO0_CONFIG();/ D- [# C3 M; G$ Q7 j& v! d1 r
  11.   QSPI_BK1_IO1_CONFIG();
    0 t2 T) f- Y8 ^
  12.   QSPI_BK1_IO2_CONFIG();
    ( Q/ I5 ~* i: H0 n6 Q
  13.   QSPI_BK1_IO3_CONFIG();( J' r# n: g4 q
  14. }
    ; A- E6 D; ~' E
  15. / v+ ^& \, [1 ^4 Y. P; p
  16. static void qspi_mode_init(void)
    % @8 V: Y- u: Z. w5 @
  17. {$ v3 h5 z. B4 Q# u8 ?
  18.   qspi_handle.Instance = QUADSPI;                        // QSPI* |, H  S( C' [0 e- E
  19.   qspi_handle.Init.ClockPrescaler = 2;                   // QPSI分频比,W25Q256最大频率为104M 所以此处应该为2,QSPI频率就为216/(2+1)=72MHZ4 R' g8 a9 r5 v* _- c/ e6 x) Q
  20.   qspi_handle.Init.FifoThreshold = 4;                    // FIFO阈值为4个字节
    3 g: P+ Q* W' }0 n% [7 O8 J0 k8 W
  21.   qspi_handle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; // 采样移位半个周期(DDR模式下,必须设置为0)6 C7 }8 g6 G9 c, v, R  \2 S" W) G
  22.   qspi_handle.Init.FlashSize = POSITION_VAL(0X2000000) - 1; // SPI FLASH大小,W25Q256大小为32M字节* ~+ v% x. w# `, T2 M6 D2 e
  23.   qspi_handle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_4_CYCLE; // 片选高电平时间为4个时钟(13.8*4=55.2ns),即手册里面的tSHSL参数8 n: y* x1 Z( Z' d
  24.   qspi_handle.Init.ClockMode = QSPI_CLOCK_MODE_0;        // 模式06 |7 G8 g; C( y8 k0 N" ]6 I3 f8 N
  25.   qspi_handle.Init.FlashID = QSPI_FLASH_ID_1;            // 第一片flash
    , o; y5 h- W" r( m% {1 k2 ]
  26.   qspi_handle.Init.DualFlash = QSPI_DUALFLASH_DISABLE;   // 禁止双闪存模式
    : [! h/ l9 @1 k( E  n
  27.   HAL_QSPI_Init(&qspi_handle);      //QSPI初始化
    ; d( s: o3 T8 M+ o  o( n: L4 j& M
  28. }+ f' p/ o0 K* c1 |1 K' I
  29. ( I, B  T6 P( h
  30. void QSPIInit(void); H! ]# C  _& h6 e$ @
  31. {8 u3 ^: e5 F! x& ?, @$ t2 }* J) i  s
  32.   qspi_gpio_init();
      O4 e) j+ O  \5 M( S/ w- ]! _: _
  33.   qspi_mode_init();
    ) ]+ U- s+ s# N
  34. }
    : t) J6 Y, I! Z: M0 F  a) h
  35. 5 e1 S, X# q, L9 f; L
  36. // QSPI发送命令
    + R2 Z/ V" W0 J& w( a: Z
  37. // instruction:要发送的指令
    ' \9 U! F% z& _
  38. // address:发送到的目的地址
    " J2 C4 w/ M) y- q2 t4 z
  39. // dummyCycles:空指令周期数9 K. A3 n1 ]& b; ~$ [* Y
  40. // instructionMode:指令模式;QSPI_INSTRUCTION_NONE,QSPI_INSTRUCTION_1_LINE,QSPI_INSTRUCTION_2_LINE,QSPI_INSTRUCTION_4_LINE$ B- N, p& K# T5 [- S( T$ j
  41. // addressMode:地址模式; QSPI_ADDRESS_NONE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_2_LINE,QSPI_ADDRESS_4_LINE
    " C% P+ T" Q' M
  42. // addressSize:地址长度;QSPI_ADDRESS_8_BITS,QSPI_ADDRESS_16_BITS,QSPI_ADDRESS_24_BITS,QSPI_ADDRESS_32_BITS
    6 L0 s* p/ X2 U8 ]* g1 I
  43. // dataMode:数据模式; QSPI_DATA_NONE,QSPI_DATA_1_LINE,QSPI_DATA_2_LINE,QSPI_DATA_4_LINE
    : G$ l# B/ |  g
  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- }1 [; D3 R* s5 r4 ?
  45. {
    4 ~4 h; o! p( V( E
  46.   QSPI_CommandTypeDef Cmdhandler;9 k$ u+ a1 A+ p7 J5 U' o
  47. ) T* T, ]5 h9 }$ \% X3 A' r2 v
  48.   Cmdhandler.Instruction = instruction;           // 指令
    5 m, L1 ?0 I1 l
  49.   Cmdhandler.Address = address;                   // 地址
    0 P5 y7 A( W4 k  ], B
  50.   Cmdhandler.DummyCycles = dummyCycles;           // 设置空指令周期数# A( E' d! M" ?( i
  51.   Cmdhandler.InstructionMode = instructionMode;   // 指令模式
    * \+ D6 F* V( D! i: |7 E2 q
  52.   Cmdhandler.AddressMode = addressMode;           // 地址模式9 l  ?5 G  C9 l! `3 u
  53.   Cmdhandler.AddressSize = addressSize;           // 地址长度
    8 s" W2 Y. r/ l; Q" ^6 C
  54.   Cmdhandler.DataMode = dataMode;                 // 数据模式) N2 g* x7 ~" r' q: M/ K3 a
  55.   Cmdhandler.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 每次都发送指令! B5 z) y. U7 v- E5 b8 s
  56.   Cmdhandler.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 无交替字节
    5 }4 p: G6 H( Z  E9 l
  57.   Cmdhandler.DdrMode = QSPI_DDR_MODE_DISABLE;           // 关闭DDR模式' j# V0 R& M+ I( X: f4 [; l
  58.   Cmdhandler.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    / z' F9 m$ j' x
  59. 2 @) Z" B( z: F4 y$ Y2 a
  60.   HAL_QSPI_Command(&qspi_handle, &Cmdhandler, 5000);
    6 N# ~$ s$ o9 h% n
  61. }
    4 r1 J6 E3 F( g. ^; K. ?

  62. 6 ^1 f; j: [% ^
  63. // 接收指定长度数据
    7 H* H2 L) S7 ^( J
  64. uint8_t QSPIReceive(uint8_t *buffer, uint32_t length). {8 y6 G$ `2 Q% y/ D8 s7 M. |) `: d! m
  65. {7 ^8 i" N% k1 c# Z
  66.   qspi_handle.Instance->DLR = length - 1;& Q! f# ?. d2 l, b5 G
  67.         
    ; n, g4 H) u3 w& K! i
  68.   if(HAL_QSPI_Receive(&qspi_handle, buffer, 5000) == HAL_OK)+ _5 h) V. |* O- H( D
  69.   {
    9 Z- {6 b6 I8 P# b( f* f1 R+ p4 {
  70.     return 0;  //接收数据
    8 g( D+ n# H" j0 r& I/ H: n
  71.   }
    & I/ h  ]1 D2 E
  72.   else5 X& O1 N+ N$ j
  73.   {
    , t7 k5 b- y2 P& _: r5 _) n% n9 N
  74.     return 1;
      [' f4 Q2 {2 G' I; v8 u4 w
  75.   }
    + T# T7 \0 g7 z# u4 T2 z
  76. }3 V6 _2 |8 Y. P6 ]4 _! A
  77. 9 B6 h/ R4 C' w- k1 x( f* ~  z
  78. // 发送指定长度数据% R, ~6 S9 r0 s. N  }  I+ m
  79. uint8_t QSPITransmit(uint8_t *buffer, uint32_t length)
    : b- l1 {, ]7 _- ^( y
  80. {
    # U2 j% w6 g1 _# j; U
  81.   qspi_handle.Instance->DLR = length - 1;
    . V! z( \0 v0 q7 j$ U, K4 i5 ?2 K% C
  82.   if(HAL_QSPI_Transmit(&qspi_handle, buffer, 5000) == HAL_OK)
    3 I/ j" x) B; f9 g1 G# p
  83.   {
    2 G3 ]" Y' E: ]+ \7 b  ]3 `: q
  84.     return 0;  //发送数据8 s; s( h8 S3 e$ j3 y5 b0 Z
  85.   }
    3 T) Z* F9 j) {; m, |* r! e, i
  86.   else
      d& H1 v- \% \4 ?, s
  87.   {* a/ M  V/ F4 Z
  88.     return 1;
    4 h' X9 `& x, l
  89.   }/ w0 F( _- a! n8 B/ g8 L
  90. }
复制代码
+ |$ \& `& l  J1 Y
五、QSPI驱动W25Q2569 G1 s: R& U0 e
W25Q256:32M  512块,每块16个扇区,每个扇区4K。具体指令看手册。封装了如下接口(初始化读写擦除等接口是以后移植文件系统的基础):
6 r! C6 @, m9 N
+ |& I$ T. V2 E; Q6 g1 y
  1. #define W25QXX_WRITE_ENABLE      0x06; q$ ]: G, D4 A- {! R* O0 X$ Q
  2. #define W25QXX_WRITE_DISABLE     0x04
    ! d- K5 h; s- D! Z2 C  c+ R5 E! i
  3. #define W25QXX_READ_STATUS_REG1  0x051 E6 G4 y* M8 N
  4. #define W25QXX_READ_STATUS_REG2  0x35
    * n& W: v4 F. }( I
  5. #define W25QXX_READ_STATUS_REG3  0x15
    : T- h) M  [& h, \
  6. #define W25QXX_WRITE_STATUS_REG1 0x01, e0 ]: H7 L4 \9 @4 Q
  7. #define W25QXX_WRITE_STATUS_REG2 0x31
    / G+ S' @8 Y9 H. B
  8. #define W25QXX_WRITE_STATUS_REG3 0x11
    7 P1 N* T3 ]8 W* i  k6 a
  9. #define W25QXX_READ_DATA         0x03: x1 [# U9 n$ ^9 j1 T
  10. #define W25QXX_FAST_READ_DATA    0x0B1 j) D: r. \/ W; f- `+ a4 L1 ^
  11. #define W25QXX_FAST_READ_DUAL    0x3B) G  P# ]2 b3 M7 I* i" G% M+ z
  12. #define W25QXX_PAGE_PROGRAM      0x02
    ' k8 H( t1 r5 \7 L0 G9 ^% O
  13. #define W25QXX_BLOCK_ERASE       0xD8
    9 o$ V. x4 J; l+ x4 c' W
  14. #define W25QXX_SECTOR_ERASE      0x201 F& {3 D9 e  \! f7 d# r
  15. #define W25QXX_CHIP_ERASE        0xC70 n7 m" g& ]: @  ~1 K0 r
  16. #define W25QXX_DEVICEID          0xAB
    . N/ C  H0 F! x% q0 k! w8 ^
  17. #define W25QXX_MANUFACT_DEVICEID 0x90( v, F( H2 A+ ^( S6 Y( T
  18. #define W25QXX_JEDEC_DEVICEID    0x9F5 V  V6 q& U, }+ o3 A% S' n# {
  19. #define W25QXX_EABLE_4BYTE_ADDR  0xB7* ?( c2 h1 N1 S8 d$ q  K
  20. #define W25QXX_EXIT_4BYTE_ADDR   0xE9
    ! R8 @; t" \# p6 L, S
  21. #define W25QXX_SET_READ_PARAM    0xC0  ?( Q4 T( }+ j5 ]! t7 C
  22. #define W25QXX_ENTER_QPIMODE     0x38
    5 e; Q' N9 K* {  R7 T
  23. #define W25QXX_EXIT_QPIMODE      0xFF$ d9 L* |( k! g. }7 @

  24. * \4 n* Z' N3 f  ?
  25. uint8_t w25qxx_qpi_mode = 0; // QSPI模式标志:0,SPI模式;1,QPI模式.
    9 m1 e. Y5 n0 t. g& ~0 Y1 r) K
  26. 1 c' k$ F8 i/ i- U; n, q
  27. // 读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器: H. @3 E2 I/ _2 n2 ^2 x) G5 ~
  28. // 状态寄存器1:
    0 j* `( w! {) @5 P
  29. // BIT7  6   5   4   3   2   1   03 |+ x9 r$ j: \% `$ L- ?# \
  30. // SPR   RV  TB BP2 BP1 BP0 WEL BUSY
    ( A5 B# v! h8 z0 O4 I
  31. // SPR:默认0,状态寄存器保护位,配合WP使用
      Z; U- ?6 W9 N2 h, s, r
  32. // TB,BP2,BP1,BP0:FLASH区域写保护设置
      ?0 x! Q& i% ^+ w+ Z
  33. // WEL:写使能锁定
    % h  s/ T7 S) z% N
  34. // BUSY:忙标记位(1,忙;0,空闲)
    9 R+ W1 e+ U; C$ y, p$ q
  35. // 默认:0x00
    & Y: I" k1 q. ~. D* R) r
  36. // 状态寄存器2:
    5 E6 o( c2 Q$ c) d5 w
  37. // BIT7  6   5   4   3   2   1   0
      T( I6 P4 R; J' x) S( B8 y
  38. // SUS   CMP LB3 LB2 LB1 (R) QE  SRP12 o9 J! `( H, P5 V
  39. // 状态寄存器3:/ r/ L' X$ w1 b. X' `
  40. // BIT7      6    5    4   3   2   1   0, M* V" q4 K, S
  41. // HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS" y, y$ V4 t8 b! i1 c
  42. // reg:状态寄存器号,范:1~3
    ' o! {& C* ?& n: |7 n+ K) w
  43. // 返回值:状态寄存器值
    8 f) U* [( p- F6 c8 }1 \6 L
  44. static uint8_t w25qxx_read_status(uint8_t reg)2 l( c( K& ^, \3 Q
  45. {! u% U3 z' f# b$ @( f" w
  46.   uint8_t value = 0, command = 0;
    / M4 w; D' X3 R# [- T$ R2 A

  47. 7 B7 r, P/ c; M4 {! k$ C
  48.   switch(reg)3 b3 L6 \- O  f% [! [6 t8 X+ G
  49.   {( o& v2 ?" a  P1 v* X! Y2 v4 o
  50.   case 1:% q5 V8 K) O) f, S1 \
  51.     command = W25QXX_READ_STATUS_REG1;  // 读状态寄存器1指令  o- E2 u1 S  B+ h" G$ P& k
  52.     break;
    ; b% ?" ]; f% @9 L# Z$ d, D

  53. , o1 a; W2 }! z% z1 R4 Y2 z  e
  54.   case 2:. K3 P  z: K* I9 {" I" T
  55.     command = W25QXX_READ_STATUS_REG2;  // 读状态寄存器2指令, g9 h' s  s/ C6 E$ C. }" l
  56.     break;6 E! e7 H5 I. M7 L5 H" \
  57. 4 H! U) Y( h2 g1 \: ^
  58.   case 3:2 a( Z5 q0 k' L' }
  59.     command = W25QXX_READ_STATUS_REG3;  // 读状态寄存器3指令1 g3 V5 O4 O: T/ }" ^
  60.     break;
    6 |$ |" b9 m$ C8 H5 J" W" V) l

  61. % D! g' z8 X; x- _
  62.   default:
    5 J# m1 U% s, z8 {: A6 \" f9 w
  63.     command = W25QXX_READ_STATUS_REG1;  u" b/ U: Q  Q+ ^" i% v& B6 n
  64.     break;
    ; A( a, T' L! m7 S: r
  65.   }! e! n: [- M" o; q8 c$ L8 I, M
  66. % Q6 C- d9 F* Y( H3 D* B' z& I7 M
  67.   if(w25qxx_qpi_mode)! ?6 f- X& _' Q6 D2 u
  68.   {2 G7 w. Y: Q1 ]0 K1 R
  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个字节数据& a; J" g4 t% e) F! n4 W  b- @
  70.   }
    1 x( M6 O6 o: {/ C; ~5 _3 |, s
  71.   else! S7 m: e% o4 b
  72.   {8 D/ Z! b8 V8 w
  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% @0 i# ~4 R4 u+ F9 q
  74.   }
    3 g; e+ {6 C% d# S! T+ |. m4 m

  75. , K7 S" r+ M$ @- x0 U& L2 F! q
  76.   QSPIReceive(&value, 1);; ~: o7 X8 a! G" p1 m. j

  77. - \7 R; W6 _# p) d% D
  78.   return value;6 W- H# T8 \7 M  h6 p. [
  79. }
    4 ~6 @6 P8 V7 E' D3 y; E5 _& w

  80. 3 q9 T+ E1 Q9 s( N* E3 j/ g
  81. // 写状态寄存器
    1 f' r( i2 L& `# n8 i
  82. static void w25qxx_write_status(uint8_t reg, uint8_t status)& e6 ^. i3 O( g1 z* X7 {8 @
  83. {
    ) L; i: k9 V3 M1 `1 a! D+ c5 G
  84.   uint8_t command = 0;5 K0 P4 O; z7 `% W, g

  85. % n% c3 i2 \) t6 H
  86.   switch(reg)# ?$ p* o5 B7 B3 \2 u2 I/ H5 q. }( p
  87.   {
    7 S/ T- {: Q" U9 m- k
  88.   case 1:
    & {5 x& s* i& j7 j
  89.     command = W25QXX_WRITE_STATUS_REG1;  //写状态寄存器1指令
    - n! v. N" a& R6 [
  90.     break;  ~) F! s6 E, z0 L. Z: Z
  91.   case 2:
      ?( U5 O3 L% B0 {9 D8 {. `  Y
  92.     command = W25QXX_WRITE_STATUS_REG2;  //写状态寄存器2指令, c" T) F0 q: n  o- S1 `
  93.     break;/ a. d: m( F+ Q1 p8 t1 E
  94.   case 3:4 N$ o, x6 F4 Q" P% l4 g
  95.     command = W25QXX_WRITE_STATUS_REG3;  //写状态寄存器3指令2 B3 p' W# q% c2 V+ t  {% z
  96.     break;
    ) \3 v2 E5 F* H# l7 h
  97.   default:. H5 Q0 u* q: x4 |( |  `
  98.     command = W25QXX_WRITE_STATUS_REG1;
    * T" _' l! G) p! h* M
  99.     break;1 J& S& d. m9 w% ^6 `# ]
  100.   }
    8 A& R7 ~& K1 H2 z+ x7 Z& W
  101.   if(w25qxx_qpi_mode)
    ( j$ M7 Y& O9 U0 ~6 z
  102.   {
    ( N$ s+ f8 F, B1 X8 L; Z$ P4 z$ a
  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个字节数据
    8 |! ~! v* u- Z% M2 k6 [8 H3 T
  104.   }
    " j. D% R( m; m- T  e
  105.   else
    ! _6 T0 o% N' ?0 T
  106.   {+ k9 `! |; f3 @& D
  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个字节数据
    - f9 {7 ]- }9 {6 S
  108.   }7 u+ u+ A7 F2 }$ _& V
  109.   QSPITransmit(&status, sizeof(status));
    2 g: s% s& C- u
  110. }
    # }+ J2 L# a9 j6 ]3 q/ P& p/ U
  111. 5 \! m" |6 }. M0 o3 G) ?- S
  112. // 写使能  将S1寄存器的WEL置位
    + W+ \# y" g9 U/ T' u
  113. static void w25qxx_write_enable(void)* m; T& {5 t2 ~2 T& j7 n
  114. {
    2 N& f2 K4 h+ D( v* c! r! T
  115.   if(w25qxx_qpi_mode)
    / \7 N+ [. P% t1 U
  116.   {4 _. m! I! e# G* m6 d# ^* c
  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个字节数据
    & W# F+ R: W% p
  118.   }
    3 |: r* e" @) X
  119.   else
    ; T- ~. q8 p* O7 E
  120.   {: b( C) J0 C% x4 Z* B. Z" ?
  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个字节数据) o  Q, g3 F, E! |* S2 y
  122.   }
      N: r2 s; H, s
  123. }; N* W4 l, s, I' q8 \: w

  124.   S* O- t: F9 ?2 ]% G3 N6 X- v
  125. // 写失能  将WEL清零
    ) t3 g. n: p6 Q
  126. void W25QXX_Write_Disable(void)* I$ J1 Q: I1 L7 Z! \9 K* s
  127. {! b3 U: |0 {1 }0 ~
  128.   if(w25qxx_qpi_mode); F2 q' j( ^, }9 B# C
  129.   {
    / @% v0 t! _' s! r$ I
  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个字节数据. g3 T8 O4 `9 O& h% g+ a. b
  131.   }
    , V1 F1 c- l6 j1 d, o+ I
  132.   else
    ) |: [/ b% V, Z9 z- K7 G
  133.   {
    ; a6 l6 c) r* g
  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个字节数据
    - l2 e* R4 S# J. W0 U
  135.   }" _$ l5 V% i$ P- K" L/ T$ E
  136. }- Q6 X5 n1 n& {6 e7 g
  137. 0 _) [  q1 t2 @9 n2 b3 l: @2 B
  138. // 等待空闲
    * ]7 g; E: }! t" U! _4 z
  139. void w25qxx_wait_busy(void)
    8 T8 r, W2 N9 _$ {1 ~
  140. {
    ' w; c1 Z8 B3 ]! p% z+ d
  141.   while((w25qxx_read_status(1) & 0x01) == 0x01); // 等待BUSY位清空+ V6 e! r5 Z6 \" x8 |+ M. A5 B
  142. }
    & F+ w" F" J8 F- ~  P
  143. 1 N/ Y, b0 Q  }" M
  144. // W25QXX进入QSPI模式
    " |# q: ~  o$ P
  145. static void w25qxx_qspi_init(void)
    ) z% q) S; w0 s. ?7 @; p
  146. {' W. ]5 S3 `* ^& M; z1 q
  147.   uint8_t reg2;, e& u* p* z5 K+ y& E
  148.   reg2 = w25qxx_read_status(2); // 先读出状态寄存器2的原始值# ?" q# t+ A/ U) t* S
  149.   if((reg2 & 0X02) == 0)  // QE位未使能
    $ y9 E- y; r8 G8 a
  150.   {
    * C/ Q0 u5 B+ n' f1 I; V
  151.     w25qxx_write_enable(); // 写使能- b  v, ?% P0 o- r+ q# [
  152.     reg2 |= 1 << 1; // 使能QE位$ \! i. G2 R+ l. S" m. c
  153.     w25qxx_write_status(2, reg2); // 写状态寄存器2
    ! i$ H' k9 [6 R% ^, h
  154.   }
    3 T$ |& ]0 k; B& \, E$ [' z
  155. ) D6 g2 [- b% s
  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个字节数据
    , P! O8 u( @" a; R, {- p3 J0 C

  157. : y, ^9 k; V6 h% {" ~, D, y
  158.   w25qxx_qpi_mode = 1; // 标记QSPI模式
    0 L* r- x% ~  N' Q; s% m; c9 V& W
  159. }' n, `* e" u9 P1 _* _

  160. 0 L3 W0 u4 ?$ ^/ c7 O& \
  161. // 0XEF13,表示芯片型号为W25Q80
    1 h/ N1 V' w. H+ {( B6 \" e
  162. // 0XEF14,表示芯片型号为W25Q16
    0 s# D1 N+ f  A( s6 i# d- Q
  163. // 0XEF15,表示芯片型号为W25Q32
    - }- X) T. [, X! k/ J
  164. // 0XEF16,表示芯片型号为W25Q64
    ! d& H6 A8 D7 F" h% [2 ]& x
  165. // 0XEF17,表示芯片型号为W25Q128
    , x% g8 o3 x0 L, d: {5 |. {8 S) n8 i
  166. // 0XEF18,表示芯片型号为W25Q256
    8 a, c+ m% D; U: F
  167. static void w25qxx_id_get(void)
    - Q' E% z& G4 E' w) M
  168. {  X& B; P. z1 J5 _: F
  169.   uint8_t temp[2];
      t6 Q0 ~5 P/ |0 I6 e) }
  170.   uint16_t device_id;
    5 {& A' I; }# U

  171. ( A5 F% Z, A$ {- T4 H" ?
  172.   if(w25qxx_qpi_mode)
    9 c" J" w0 A* `1 Z
  173.   {
    8 i6 P# d7 ^! R: _: D$ \+ 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个字节数据
    % U. m! h1 R8 O' n3 D1 b8 W6 W% l8 y
  175.   }
    - i+ h; b' `) y2 x, {7 _% A
  176.   else
    1 q0 Q+ o% `: |" D+ V
  177.   {( I2 v  {6 E  ~' i' w$ N- \6 H& K
  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个字节数据2 A/ z2 h& D# y% I* r3 ]
  179.   }
    ! j: \9 f- K3 R2 _
  180. " T  }0 ~/ K: @1 D/ I
  181.   QSPIReceive(temp, 2);
    ! P$ B4 b8 t$ }& `! x2 G. t/ v

  182. ( }+ `/ M7 z+ e# A! G) _
  183.   device_id = (temp[0] << 8) | temp[1];
    ; Y- {5 ]: l! |# g' z) d
  184. ) ^5 H( y1 p% T8 i
  185.   printf("QSPI Flash Device ID: %X\r\n", device_id);( s! X: E: P+ ?* s) W
  186. }
    # B6 B( a$ q2 N6 ]! p! E$ f$ K  Z

  187. # x. ?7 |/ E' {4 d7 W6 a* _% q5 x, e
  188. void W25QXXInit(void)8 t4 G( J- J1 y8 W% x& g
  189. {3 t, w8 G1 d% E1 R% g3 p# r
  190.   uint8_t temp;
    8 R5 y6 Q2 M$ E' J  H) {2 ?

  191. 5 R) ?& h# ^2 E. n
  192.   QSPIInit();8 ]! ?+ t* s! ], J! s9 f* K$ _' n6 @
  193.   w25qxx_qspi_init(); // 使能QSPI模式
    4 B# H, m( |) b3 R8 {  K
  194.   w25qxx_id_get();    // 读取FLASH ID.0 [  E/ Z# O7 s9 D2 X# V
  195. ' z9 I3 T4 K3 ~4 H5 _  }
  196.   temp = w25qxx_read_status(3); // 读取状态寄存器3,判断地址模式8 o2 C# e9 A# O5 g3 p
  197.   if((temp & 0X01) == 0) // 如果不是4字节地址模式,则进入4字节地址模式
    $ J7 R$ ^, G+ x" u3 t* D
  198.   {1 S1 [5 g) _3 T3 D% d9 p+ B
  199.     w25qxx_write_enable(); // 写使能
    $ L+ x. J) X% D/ i
  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个字节数据
    0 M# b9 J: s4 b# J9 i7 u4 l4 T( m
  201.   }
    + C  T( n, r; A9 T5 q, }
  202.   w25qxx_write_enable(); // 写使能
    3 B8 g! M$ Y  g9 P/ A* `1 Y
  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个字节数据9 d' g! ?$ t2 m( o
  204.   temp = 3 << 4;      // 设置P4&P5=11,8个dummy clocks,104M
      m0 C# j0 N" M0 g% E+ [4 v
  205.   QSPITransmit(&temp, 1);2 i/ _1 F6 t0 z9 N  q
  206. }1 c% P' m) ]4 w1 ~. v; K7 T

  207.   a' F8 g" L- [/ ]
  208. // 擦除一块4096字节 最少需要150ms
    ' V' u- B4 q5 X) f) E3 _" ~+ c
  209. void W25QXXSectorErase(uint32_t addr)5 D  R/ q* W# ?* Q' P: W+ k" p
  210. {
    ( h0 w( d0 N" W6 ~& A! g0 I
  211.   //addr /= 4096;* r& |, n) ]0 o+ J
  212.   addr *= 4096;
    ' e; F2 W5 l, W* M( I
  213. 4 {# f7 s% ]; t5 S6 o
  214.   w25qxx_write_enable();
    3 Y4 o0 c' T+ L$ A4 h! _  r" }
  215.   w25qxx_wait_busy();
    ) i  p2 F8 `$ [; f0 ~& B, r
  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个字节数据
    " \6 P1 Z' A) j
  217.   w25qxx_wait_busy();
    7 N0 m; t' g  Y& F; I9 R9 J! ?( I8 `
  218. }, t# k2 x! \1 K7 ^( B5 S" L) t" [

  219. 0 b' b3 r/ C0 R/ C

  220. 7 v& e3 Q( R& k9 E" M
  221. // 擦除整个芯片
    / E( [2 K* I9 I4 d- s" S6 Q' o
  222. void W25QXXChipErase(void)# H5 ~, B: \  O3 E& R1 z, \- R$ `
  223. {
    3 P8 H6 S( B5 ^# O: h) V5 h! P( Z2 d6 x
  224.   w25qxx_write_enable();          //SET WEL5 e  z& h- ?1 R+ o
  225.   w25qxx_wait_busy();
    2 `' n' o+ q8 C0 t5 q  g, m& i
  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个字节数据3 ~* ?4 r, K/ N( _: l
  227.   w25qxx_wait_busy();           //等待芯片擦除结束
    / M6 g1 A+ b) J; L0 o& f
  228. }
    - b' ?( r/ O# K! ]- W1 n! G% s
  229.   X$ x# P2 b, J9 l
  230. // 写最多256字节2 M& H; [% s0 u/ v4 b% l
  231. void W25QXXWritePage(uint32_t addr, uint8_t *buffer, uint16_t length)" I' Q. y5 }# Z8 ]2 K
  232. {$ E$ s9 B$ c' Y8 k7 f
  233.   w25qxx_write_enable();          //写使能. y" _7 _6 ^% }' m( x1 ]
  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个数据4 e* C) J% T. h1 ], z+ r
  235.   QSPITransmit(buffer, length);
    ' k3 K/ d' {; L" \+ {' Z& P
  236.   w25qxx_wait_busy();            //等待写入结束
    ! {1 T- E" ], `; V2 T; V1 |
  237. }
    ( \. c/ U, q5 o- V) v

  238. : C* f. ^1 R. v- v! p" H3 @
  239. // 写最多65536字节 无擦除动作8 Q$ {# M& G6 W: W! F
  240. void W25QXXWriteExt(uint32_t addr, uint8_t *buffer, uint16_t length)
    ! M4 |( J8 F+ o$ X' D
  241. {
    : v0 z# X, J/ f+ U
  242.   uint16_t pageremain = 256 - addr % 256; // 单页剩余的字节数
    % Q' R8 G1 R1 L0 ~' G

  243. # i5 r0 n# w  n( S2 W7 T) d
  244.   if(addr + length - 1 > (32 * 1024 * 1024))4 k% x- }$ Q& r4 ?( B8 L2 m
  245.   {; {7 D' [# G/ v, U) M
  246.     return;
    $ B0 c; d: V" ^# G0 i8 y9 ~
  247.   }9 _! r1 X1 w* [- D0 u! F
  248. 2 \* C2 q: x- M  }- J
  249.   if(length <= pageremain)
    + q0 c1 `. `; f1 i+ o! N5 |8 k
  250.   {) F- T* W3 X9 Z7 S
  251.     pageremain = length;  // 不大于256个字节6 X9 C7 e5 I5 j, u, M0 L+ l
  252.   }# T1 n( ~! a8 l6 X
  253.   while(1)
    # x+ b* |  X) x4 p6 \- B& m/ o# F3 z* B
  254.   {
    & ?$ z0 c$ n' B; P$ ]8 ?
  255.     // 分页写入* J# f: t* o3 m
  256.     W25QXXWritePage(addr, buffer, pageremain);- N& C% C4 K) P6 d# l
  257. , N5 c  `' r3 S! P
  258.     if(length == pageremain)
      ]$ O& ]0 W! H+ I' a
  259.     {
    2 a+ w  P1 S0 O3 r0 f
  260.       break;  //写入结束了
    7 W' x" s, |+ l- f  s3 i
  261.     }
    ' j2 I1 E8 ~* K) ]9 H  P
  262.     else
    * ~4 B$ r, U% y: }1 j1 h6 Y
  263.     {: D" D; L  v* h7 S3 A4 N: F: V
  264.       buffer += pageremain;
    0 k1 |# A) O; C9 H
  265.       addr += pageremain;/ g& L  g4 \! U7 `3 k. ?4 R

  266. 7 @5 K  `/ o' L0 g% R
  267.       length -= pageremain; // 减去已经写入了的字节数
    2 _8 |: y5 d0 Y" \/ G
  268.       if(length > 256)
    ! F) v' Z: i6 Q9 _0 Y/ P! X, T
  269.       {
    " T% j1 U" L# |# R2 Q6 k; b0 C
  270.         pageremain = 256;  // 一次可以写入256个字节
    - B5 `& g& H& Y  w- f, X) w0 z
  271.       }
    % e& @: X/ L$ ~
  272.       else
    4 w7 M6 ?5 c! l1 `$ N
  273.       {
    . s" J3 o' I9 P. \/ G% E) s/ `
  274.         pageremain = length;  // 不够256个字节了! n% G! q+ K7 _
  275.       }; v# b: p6 R: y6 P4 c
  276.     }" w/ `+ n( F) |. D* E# }9 {
  277.   }
    % _, Y* i$ S+ x( L( _1 g
  278. }( y# u8 c5 Y3 w9 ?
  279. 8 i* Z5 B$ Y- f0 \: M5 y9 c
  280. // 写最多65536字节 带擦除动作 1 A0 o& Y4 ]# R; M
  281. static uint8_t W25QXX_BUFFER[4096];4 g" ^6 H# o' E8 h
  282. void W25QXXWrite(uint32_t addr, uint8_t* buffer, uint16_t length)
    ( f& e2 C. L0 x3 J3 S
  283. {
    9 A  p6 I# g! O( U! d; F
  284.   uint32_t secpos;: v& n* z: W  k& b8 L
  285.   uint16_t secoff, secremain, i;
    0 ?6 N/ U( J. O" G
  286.   uint8_t * w25q_buf;
    4 x% G) {+ s! T6 {0 q, [# w4 G
  287. ( \4 k, I) `6 \* Q1 ~
  288.   w25q_buf = W25QXX_BUFFER;
    . c% o5 M$ e$ ^# Y+ S: W

  289. 4 S) l  `; S* g7 e" I( v
  290.   secpos = addr / 4096; // 扇区地址
    . Q6 T6 L* _$ w
  291.   secoff = addr % 4096; // 在扇区内的偏移( V! C, Q* i, y: i6 e* N6 `: z: K
  292.   secremain = 4096 - secoff; // 扇区剩余空间大小
    # z( w8 a+ P5 Z$ C9 S' r! R% O$ ?- g% O
  293. & g( \' \  j- ]9 G, Z$ G4 v* U0 R
  294.   if(length <= secremain)* v$ e! Y) z4 [2 M" i
  295.   {+ I/ Q0 e0 c5 Y2 Q! \' g
  296.     secremain = length;  // 不大于4096个字节' Z1 x0 E7 H1 ^* ~( c8 t
  297.   }9 a+ n* }# p/ F/ ]0 ~/ P# p7 G% k
  298.   while(1)
    ' H' u7 b+ B" I# Q! [( h/ w: U
  299.   {
    * `* t4 f1 b( e! {) J
  300.           WatchdogFeed();6 X0 z- N3 A+ u# ^7 w+ V; z
  301.     W25QXXRead(secpos * 4096, w25q_buf, 4096); // 读出整个扇区的内容$ p) e8 ~* ]: O" [# X. k7 ?2 v
  302.     for(i = 0; i < secremain; ++i)& a( i9 H! B! [5 m
  303.     {. j8 ]2 m& V7 d# G$ \# ]7 \& g
  304.       if(w25q_buf[secoff + i] != 0XFF)
      q: S# Y5 f2 i# ]. R
  305.       {
    $ n5 C6 d# A, S; v4 i! S
  306.         break;  // 需要擦除* q# f1 m  N" q( }- r6 I
  307.       }6 T! C! l. p- a, Z9 }7 T9 J0 T
  308.     }4 r& u/ R& i! P5 R* w' y
  309.     if(i < secremain) // 需要擦除
    6 D4 _9 |) ]% G5 r
  310.     {, [& U/ {  H9 g3 n7 F
  311.       W25QXXSectorErase(secpos); // 擦除这个扇区
    2 P, p' ?: E) k" q; E
  312.       for(i = 0; i < secremain; ++i)3 c" t4 V- u8 D" k+ K
  313.       {
    ' s& F) k- B* N0 a0 F
  314.         w25q_buf[i + secoff] = buffer<i>;</i>
    3 W- K: Y  {: N4 C0 L. N
  315.       }* x! I; t# |) O, O% {5 c+ c. [
  316.       W25QXXWriteExt(secpos * 4096, w25q_buf, 4096); // 写入整个扇区8 b- ~7 F- w: V

  317. . H- v- E9 ^/ M7 c8 ^: h9 e8 h7 p
  318.     }; y% q( t7 |2 U  F3 E( }- T
  319.     else7 [  [9 E+ B0 {: U4 R
  320.     {! p7 U4 T6 |0 a7 @
  321.       W25QXXWriteExt(addr, buffer, secremain);  // 写已经擦除了的,直接写入扇区剩余区间.
    . ~( Z* a; o- y7 K( O. ^7 I& B
  322.     }4 |& }2 w! t9 S
  323.     if(length == secremain)% e( _( D% e# Y7 F$ I; }$ W% m3 n
  324.     {
    + j3 u, V# o) {7 }. `
  325.       break;
    1 i8 M+ O2 S" Q, C5 F
  326.     }
    . A/ V6 @7 V, K/ y" x6 z. o9 X
  327.     else# M; G6 w: I. ?& ^, Q, n: t
  328.     {
    : C& J5 M( F# f+ L: C
  329.       secpos++;6 r% y: w8 a- c7 C& \( Z
  330.       secoff = 0;, G' [8 m* a! r, Z( S

  331. / _% P3 O' I1 f6 u9 \. f( k
  332.       buffer += secremain;
    6 _0 o( r8 h+ n
  333.       addr += secremain;
    1 ^8 z4 h7 \9 m& B7 G
  334.       length -= secremain;) t7 k( F7 R3 S5 r* r
  335.       if(length > 4096)" X; q4 D" z$ Z( a3 [1 \4 Z% U
  336.       {. I) O" U- v$ Q/ }$ b  F
  337.         secremain = 4096;0 ?' e" I( @9 J9 r8 V0 g
  338.       }
    # G) d8 O+ ~0 ]- H
  339.       else
    , d/ Y: A3 V( g0 c
  340.       {
    & S. J/ J9 P  D, v# |
  341.         secremain = length;
    # o3 h1 {8 P) X. ~6 P
  342.       }
    2 g4 `' P4 }. a3 Y9 g& @  d
  343.     }4 P0 h$ ~- X+ h$ V
  344.   };) H6 M/ q+ C; {7 p- I6 [
  345. }
    ; f. _! K* J) j  X" k" A- Z1 p

  346. 4 _5 y9 c6 g3 W6 l0 T' Q

  347. 4 h. T0 I2 e; f2 d3 F
  348. // 读取SPI FLASH,仅支持QPI模式  在指定地址开始读取指定长度的数据 65536
    ' A9 s& b& R, s8 B: h" n
  349. void W25QXXRead(uint32_t addr, uint8_t *buffer, uint16_t length)
    : d" Y. A  z* |, }) I0 E
  350. {9 v8 q& Z; ?8 v3 t) S; f7 i
  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个数据
    0 U% P) P- y( E' r; d2 ?' B
  352.   QSPIReceive(buffer, length);
    4 s" _8 `, H$ B7 r: E" l2 d. X
  353. }
复制代码

' w9 l0 U4 e& i* X也可以使用SPI操作W25QXX。( G' l) S& F3 n0 u) @$ o( g

  f3 h# z+ ^7 {. j4 X3 t
( x6 x" x% z6 G; a+ H2 m' Y7 @% w
7 I7 ?" P2 K: K4 \0 j$ A
收藏 评论0 发布时间:2021-12-11 12:00

举报

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