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

【经验分享】STM32F7xx —— QSPI

[复制链接]
STMCU小助手 发布时间:2021-12-11 12:00
一、QSPI
% Z8 Q! r; e0 c  g' O        SPI 是 Queued SPI 的简写,是 Motorola公司推出的 SPI 接口的扩展,比 SPI 应用更加广泛。在 SPI 协议的基础上,Motorola 公司对其功能进行了增强,增加了队列传输机制,推出了队列串行外围接口协议(即 QSPI 协议),使用该接口,用户可以一次性传输包含多达16个8位或16位数据的传输队列。一旦传输启动,直到传输结束,都不需要CPU干预,极大的提高了传输效率。该协议在ColdFire系列MCU得到广泛应用。与SPI相比,QSPI的最大结构特点是以80字节的RAM代替了SPI的发送和接收寄存器。QSPI 是一种专用的通信接口,连接单、双或四(条数据线) SPI Flash 存储介质。
6 t7 {9 A" S. m5 ?0 W1 V" n& r* a, ~! D0 S4 \9 {
该接口可以在以下三种模式下工作:
; _) {8 s+ q" f8 _7 M4 @8 ^4 B1 H- n① 间接模式:使用 QSPI 寄存器执行全部操作* Y, L- H9 B. a% R5 N8 v
② 状态轮询模式:周期性读取外部 Flash 状态寄存器,而且标志位置 1 时会产生中断(如擦除或烧写完成,会产生中断)
% X/ c( D8 L8 R; k: k③ 内存映射模式:外部 Flash 映射到微控制器地址空间,从而系统将其视作内部存储器。. G; }3 ~, z+ X) g9 a) ~+ \9 `
1 R7 H. V: N+ W. i6 L9 }& U5 W: ^
QSPI通过6根线与SPI芯片通信,下图是内部框图:
  j- z0 T4 @6 g: M
5 L% c3 g3 U; D" K+ m- W
20190123173205949.png

: E# N6 e9 O2 ?0 `! E4 y. z, y% z& t* S# ~& Q* _& K
QSPI每条命令,必须包含一个或多个阶段:指令、地址、交替字节、空指令和数据。6 K/ c: w3 p# N7 |) k, A4 c6 \0 f

" |' s0 [% n7 [- F" h- kQSPI发送命令:等待QSPI空闲;设置命令参数。
; F1 b9 W3 a- p6 d6 f6 `
* I1 i; P* }9 L% p5 L  i  tQSPI读数据:设置数据传输长度;设置QSPI工作模式并设置地址;读取数据。
# P# k) b( q& M! l# o; S! d+ A- W+ E: q+ k' v8 U8 f
QSPI写数据:设置数据传输长度;设置QSPI工作模式并设置地址;写数据。% A3 b/ T" n$ w2 q7 C

: u) v" f5 X! U  r5 I0 Q3 B  b6 b( K8 b* q0 Y, I, O) K+ d* D) x1 W
二、几个重要的函数
" t+ m$ I6 x! H2 N9 {- f- R
  1. HAL_StatusTypeDef     HAL_QSPI_Init     (QSPI_HandleTypeDef *hqspi); // 初始化
    3 X7 ?' X9 o' l
  2. 4 z  K5 S; _. n: S% }( Y
  3. HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout); // 发送命令
    , y% R1 [8 u. P3 f

  4. 2 y6 _; {4 u/ ]
  5. HAL_StatusTypeDef     HAL_QSPI_Transmit     (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 发送数据
    * S+ M1 O6 t0 k' x( N4 q
  6. 8 `! N- W' j& U
  7. HAL_StatusTypeDef     HAL_QSPI_Receive      (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 接收数据
复制代码

, Q* Y, R4 v9 `  [9 X8 L三、几个重要的结构' r, X9 K. N1 R% K1 x; J3 _
  1. // QSPI操作句柄) [' U* L* F+ b# r, r  x* Z
  2. typedef struct
    ) t( P. v) D" N/ a9 Y, E
  3. {
    6 T2 N9 r4 j5 {# s5 i) u
  4.   QUADSPI_TypeDef            *Instance;        /* QSPI registers base address        */
    : C# H+ G2 l( I+ q
  5.   QSPI_InitTypeDef           Init;             /* QSPI communication parameters      */" @) q; ]8 F/ U) p# o1 s! a* g
  6.   uint8_t                    *pTxBuffPtr;      /* Pointer to QSPI Tx transfer Buffer */- B7 V7 Y, w: J0 b* L
  7.   __IO uint16_t              TxXferSize;       /* QSPI Tx Transfer size              */
    ' i9 c# z) ]8 L& w  t- L
  8.   __IO uint16_t              TxXferCount;      /* QSPI Tx Transfer Counter           */
    . @6 `) j2 q( z/ H
  9.   uint8_t                    *pRxBuffPtr;      /* Pointer to QSPI Rx transfer Buffer */  ]7 S+ ]9 [; c9 ]. R9 ]
  10.   __IO uint16_t              RxXferSize;       /* QSPI Rx Transfer size              */9 j; W$ s" Y3 O* k8 F9 O
  11.   __IO uint16_t              RxXferCount;      /* QSPI Rx Transfer Counter           */; |9 U, M! W+ q2 K7 J: ^' L
  12.   DMA_HandleTypeDef          *hdma;            /* QSPI Rx/Tx DMA Handle parameters   */
    " W/ S/ P. ]) q  I3 ]) Q) V8 |
  13.   __IO HAL_LockTypeDef       Lock;             /* Locking object                     */# v5 l' K7 C9 B; Z# c3 t
  14.   __IO HAL_QSPI_StateTypeDef State;            /* QSPI communication state           */- J" `2 }. B+ [. K, A
  15.   __IO uint32_t              ErrorCode;        /* QSPI Error code                    */( c. \3 B' E% Q$ u. K0 \( O7 r
  16.   uint32_t                   Timeout;          /* Timeout for the QSPI memory access */ 9 }! Q5 k2 T: u+ X( \( ^! a
  17. }QSPI_HandleTypeDef;
    % d8 g" q4 I) Z# P" L" u
  18. ; D- O, T: ]5 |5 L- x5 G
  19. // Instance:QSPI基地址  ---  QUADSPI) ^) j- L1 V- l* F" G
  20. // Init:设置QSPI参数3 N0 [! ^2 x. e$ l
  21. // pTxBuffPtr,TxXferSize,TxXferCount:发送缓存指针 发送数据量 剩余数据量0 F' x  l8 |9 s) b* }3 {
  22. // pRxBuffPtr,RxXferSize,RxXferCount:接收缓存指针 发送数据量 剩余数据量0 G; [! m2 o! Q/ m
  23. // hdma与DMA相关, 其他为过程变量不需要关心
复制代码
  1. // 参数配置 时钟分频系数  FIFO阈值  采样移位  FLASH大小  片选高电平时间  时钟模式  闪存ID  双闪存模式设置
    . w' {9 S: |+ E0 q6 U" [8 g
  2. typedef struct8 ?$ w; S9 _( Z( ^
  3. {0 i# q) N5 R; V5 l- X9 h2 P/ h. p
  4.   uint32_t ClockPrescaler;     /* Specifies the prescaler factor for generating clock based on the AHB clock.1 k1 ]1 g; }$ d6 E2 i* F
  5.                                   This parameter can be a number between 0 and 255 */
    9 `: P( l  c2 J
  6. 0 }% M% L/ O/ G. ?# U( S
  7.   uint32_t FifoThreshold;      /* Specifies the threshold number of bytes in the FIFO (used only in indirect mode)
    4 e" S2 ~9 P& I# h- a; _! E) w5 j
  8.                                   This parameter can be a value between 1 and 32 */& P2 x+ t2 P1 M& N' a- @) s! v7 R6 W

  9. - P+ _! d- u& R* E( C9 i
  10.   uint32_t SampleShifting;     /* Specifies the Sample Shift. The data is sampled 1/2 clock cycle delay later to
    ! {. s$ ]  a, v, C3 b: Y' i7 t
  11.                                   take in account external signal delays. (It should be QSPI_SAMPLE_SHIFTING_NONE in DDR mode)
    % S& M( ]2 ~7 d& h: q8 d/ k! [9 N4 N+ K0 p
  12.                                   This parameter can be a value of @ref QSPI_SampleShifting */7 A  T' x' T* H8 R4 Y, k7 Q9 j
  13. . }* a3 G9 D& |. z
  14.   uint32_t FlashSize;          /* Specifies the Flash Size. FlashSize+1 is effectively the number of address bits 3 p4 J; i" a/ X' x' a8 W; v
  15.                                   required to address the flash memory. The flash capacity can be up to 4GB
    2 H+ ?. m) v2 i. g
  16.                                   (addressed using 32 bits) in indirect mode, but the addressable space in
    6 {0 \% s/ D. ?3 @
  17.                                   memory-mapped mode is limited to 256MB6 @3 \, ]/ y2 l. A
  18.                                   This parameter can be a number between 0 and 31 */$ O5 n5 e: |4 U' y& @! [. \
  19. 7 |# l3 k8 u% B$ K
  20.   uint32_t ChipSelectHighTime; /* Specifies the Chip Select High Time. ChipSelectHighTime+1 defines the minimum number
    ) \- q# g2 ]  l- f' _
  21.                                   of clock cycles which the chip select must remain high between commands." V9 y1 |$ e' S! |6 n4 i6 E
  22.                                   This parameter can be a value of @ref QSPI_ChipSelectHighTime */ & Z. m& [# Q" m
  23. # T3 a3 `. r9 j( D% u
  24.   uint32_t ClockMode;          /* Specifies the Clock Mode. It indicates the level that clock takes between commands.( \( G$ L( I: L+ P
  25.                                   This parameter can be a value of @ref QSPI_ClockMode */0 [% }4 {/ [. @$ R- u+ v

  26. & W& C$ H9 Q" N5 }$ X1 ^4 ]% ^
  27.   uint32_t FlashID;            /* Specifies the Flash which will be used,
    $ [8 }% |1 i. c  K5 }% `* h4 h
  28.                                   This parameter can be a value of @ref QSPI_Flash_Select */0 v- ~8 q! K. p  N, F1 `
  29. : ?6 o7 @4 e4 S  i- H3 Y
  30.   uint32_t DualFlash;          /* Specifies the Dual Flash Mode State
    $ V8 q9 o, [% U( m& u8 Z
  31.                                   This parameter can be a value of @ref QSPI_DualFlash_Mode */                                               , W, U6 o" z% l% Y
  32. }QSPI_InitTypeDef;
复制代码
  1. // 采样移位
    , ^9 N- U0 z' \0 t3 \: H! J0 g
  2. #define QSPI_SAMPLE_SHIFTING_NONE           ((uint32_t)0x00000000U)        /*!<No clock cycle shift to sample data*/
    - b8 L, [6 M" B' ~% b
  3. #define QSPI_SAMPLE_SHIFTING_HALFCYCLE      ((uint32_t)QUADSPI_CR_SSHIFT) /*!<1/2 clock cycle shift to sample data*/
复制代码
1 [- _  x! Z, p% B
四、QSPI接口设计(仅供参考)

0 n# @& M2 [+ q4 _
  1. #define QSPI_CLK_ENABLE()         __HAL_RCC_QSPI_CLK_ENABLE();  ?: G0 \3 Z% ^7 R

  2. ; Y2 S" |" r" t- _0 J8 m6 y7 @& X
  3. #define QSPI_BK1_NCS_PORT         GPIOB. C7 y# Q! |# Q! m
  4. #define QSPI_BK1_NCS_PIN          GPIO_PIN_64 P6 L/ t. s& y' J$ ~
  5. #define QSPI_BK1_NCS_AF           GPIO_AF10_QUADSPI
    / b( q$ M+ R0 D$ [
  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$ A8 M# l3 @# X2 J( X: V* k
  7. % `7 K+ W2 q1 v! B: G, l, G. B
  8. #define QSPI_BK1_CLK_PORT         GPIOB* n4 n7 c1 {* G& I3 e8 ?
  9. #define QSPI_BK1_CLK_PIN          GPIO_PIN_2
    9 u  ^/ j/ |8 J; \- X
  10. #define QSPI_BK1_CLK_AF           GPIO_AF9_QUADSPI
    3 D' }4 S: j, G* F( v* @6 C
  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)
    ! ~* f  L6 I+ g) c6 N

  12. 9 v+ S; U& W( g" F
  13. #define QSPI_BK1_IO0_PORT         GPIOF7 K& Z* D9 u, D
  14. #define QSPI_BK1_IO0_PIN          GPIO_PIN_8
    / b% j+ s: ^1 c
  15. #define QSPI_BK1_IO0_AF           GPIO_AF10_QUADSPI
    . k7 E+ n2 W, W; v: I
  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)
    1 J: |& t) {8 Z+ O- t1 C: k1 U+ t

  17. * f( C& y" D/ x
  18. #define QSPI_BK1_IO1_PORT         GPIOF
    . k* u9 A  O$ n  t/ k
  19. #define QSPI_BK1_IO1_PIN          GPIO_PIN_9
    ' ^4 V- u# z% p/ |
  20. #define QSPI_BK1_IO1_AF           GPIO_AF10_QUADSPI- E; U. B6 H) |8 x7 a, N
  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)
    3 s* D; O- i/ L! i! V
  22. 1 ?7 @; B" v; m" F  q4 t
  23. #define QSPI_BK1_IO2_PORT         GPIOF  q( z  L7 l$ m2 R$ A" [1 c6 x: o, ?
  24. #define QSPI_BK1_IO2_PIN          GPIO_PIN_7
    ) Q; P0 X; ]/ x2 |4 b; C
  25. #define QSPI_BK1_IO2_AF           GPIO_AF9_QUADSPI
    ; I2 S3 l' w- B9 v( ^: t
  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)4 `9 D/ A- v& H2 \* i$ }% W2 `
  27. ' v' z1 C2 V. U  }, z$ V& q. b5 h
  28. #define QSPI_BK1_IO3_PORT         GPIOF" F" ?6 g( d2 W& p- o% i4 |4 ]
  29. #define QSPI_BK1_IO3_PIN          GPIO_PIN_6
    # b5 ~" o$ R7 t3 O! p( n* V* Z; n
  30. #define QSPI_BK1_IO3_AF           GPIO_AF9_QUADSPI
    2 ^( G1 }: ?  S2 Q, N
  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. // 封装几个必要的接口
    3 c) t: n% _+ n! I' j
  2. static QSPI_HandleTypeDef qspi_handle;
    - ^9 Q* P' k+ Z( D

  3. * F0 s! x8 ?- ^. g. p4 D; D
  4. static void qspi_gpio_init(void)1 ^  L9 N5 c) @: A# x( s, ?
  5. {# I5 t7 \) h& [/ v' _
  6.   QSPI_CLK_ENABLE();9 P7 F, R$ E- p* O% A5 c) w

  7. & w6 o4 R* U2 k' a9 q0 I
  8.   QSPI_BK1_NCS_CONFIG();
      c; l1 C% R  [$ l& S8 G5 x
  9.   QSPI_BK1_CLK_CONFIG();( ~" m1 B- I) U. i$ }/ c: F# x
  10.   QSPI_BK1_IO0_CONFIG();
    & [7 Q5 h  x, V0 T+ W0 f
  11.   QSPI_BK1_IO1_CONFIG();
    $ ?7 i2 Y# ]! O
  12.   QSPI_BK1_IO2_CONFIG();& W5 d; [+ n/ \+ c4 p7 d. {- f
  13.   QSPI_BK1_IO3_CONFIG();6 X+ F5 ~" j" K7 g; E% C# p
  14. }
    * K8 ^- S; w5 |; C% r

  15.   H6 ]  t* W. @6 y$ [
  16. static void qspi_mode_init(void)( u- M) e( L( k7 W
  17. {
    - ?2 O8 r8 z. \) @' W3 I
  18.   qspi_handle.Instance = QUADSPI;                        // QSPI6 D0 Q1 Z# ~8 g1 k& R) \# U( r0 V
  19.   qspi_handle.Init.ClockPrescaler = 2;                   // QPSI分频比,W25Q256最大频率为104M 所以此处应该为2,QSPI频率就为216/(2+1)=72MHZ& ~4 y/ }7 k: Z9 t  ?( j" J  E2 T
  20.   qspi_handle.Init.FifoThreshold = 4;                    // FIFO阈值为4个字节3 o# {/ G' q3 r. Y: `6 r
  21.   qspi_handle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; // 采样移位半个周期(DDR模式下,必须设置为0), L, [! ?, l2 x
  22.   qspi_handle.Init.FlashSize = POSITION_VAL(0X2000000) - 1; // SPI FLASH大小,W25Q256大小为32M字节+ [+ q1 L( A8 [6 d% \
  23.   qspi_handle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_4_CYCLE; // 片选高电平时间为4个时钟(13.8*4=55.2ns),即手册里面的tSHSL参数: O3 w. ]8 P& \- _- x/ S
  24.   qspi_handle.Init.ClockMode = QSPI_CLOCK_MODE_0;        // 模式0
    . d2 W! u2 P! f& _
  25.   qspi_handle.Init.FlashID = QSPI_FLASH_ID_1;            // 第一片flash
    % p; L8 M) a! c. [3 h
  26.   qspi_handle.Init.DualFlash = QSPI_DUALFLASH_DISABLE;   // 禁止双闪存模式* m7 r8 w% M: O1 T
  27.   HAL_QSPI_Init(&qspi_handle);      //QSPI初始化
    : y! w) Z  w7 Q: D* X0 W
  28. }0 ~9 ^+ I" Y/ K5 ]7 _& ?
  29. 2 |9 T5 N: v7 v1 I2 w' f
  30. void QSPIInit(void)' p7 [7 X: r; R% [+ V2 a, X. m  a/ e
  31. {
    8 d% c2 q8 q8 d0 Y$ U: _
  32.   qspi_gpio_init();
    & H0 o  s" M" V0 O, I
  33.   qspi_mode_init();+ {2 K! G" b  ?/ O
  34. }
    $ @  H1 S8 Y: }: y- T: Y( t8 C8 ^+ p

  35. 6 R4 ^- I  R) f; h. n9 r; p
  36. // QSPI发送命令
    % O/ t  T  v! s% d
  37. // instruction:要发送的指令* T' }8 Y* w3 b: Q1 O7 k' ]
  38. // address:发送到的目的地址
    3 i$ [' a, Q3 S
  39. // dummyCycles:空指令周期数: m. o0 n8 H, D5 a* ~9 i
  40. // instructionMode:指令模式;QSPI_INSTRUCTION_NONE,QSPI_INSTRUCTION_1_LINE,QSPI_INSTRUCTION_2_LINE,QSPI_INSTRUCTION_4_LINE( I/ Y9 G+ S- _7 {5 d; K/ G
  41. // addressMode:地址模式; QSPI_ADDRESS_NONE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_2_LINE,QSPI_ADDRESS_4_LINE
    ! ]! A9 t$ I) f  h6 }- U2 o
  42. // addressSize:地址长度;QSPI_ADDRESS_8_BITS,QSPI_ADDRESS_16_BITS,QSPI_ADDRESS_24_BITS,QSPI_ADDRESS_32_BITS( @7 g& l9 j' a3 f# z
  43. // dataMode:数据模式; QSPI_DATA_NONE,QSPI_DATA_1_LINE,QSPI_DATA_2_LINE,QSPI_DATA_4_LINE
    + E: g" w/ [% m% ]+ K
  44. void QSPISendCMD(uint32_t instruction, uint32_t address, uint32_t dummyCycles, uint32_t instructionMode, uint32_t addressMode, uint32_t addressSize, uint32_t dataMode)' w# ]3 m( F5 ]5 g4 k
  45. {
    ! T  N8 \- w+ X" K' B) ^
  46.   QSPI_CommandTypeDef Cmdhandler;
    # z! Y2 A& M4 t. H5 q/ k
  47. * k& O: p' M0 t6 w
  48.   Cmdhandler.Instruction = instruction;           // 指令
    1 n- k/ ?# p8 ~2 Y9 G1 d+ R9 j5 Z
  49.   Cmdhandler.Address = address;                   // 地址& T: [: _  v4 V( ?( d2 o- Q3 o
  50.   Cmdhandler.DummyCycles = dummyCycles;           // 设置空指令周期数
    , z6 Z0 @# b8 k% d
  51.   Cmdhandler.InstructionMode = instructionMode;   // 指令模式
    * B/ ^' |* C( H& Q: y% B
  52.   Cmdhandler.AddressMode = addressMode;           // 地址模式3 @( l0 e0 G* y5 G% g
  53.   Cmdhandler.AddressSize = addressSize;           // 地址长度# G0 T% b7 e: ]
  54.   Cmdhandler.DataMode = dataMode;                 // 数据模式
    . p; N- c# t' j- L, r" p
  55.   Cmdhandler.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 每次都发送指令2 I( y  x& o/ r; I3 X/ t
  56.   Cmdhandler.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 无交替字节+ k6 \: l8 N$ G/ B* N: c
  57.   Cmdhandler.DdrMode = QSPI_DDR_MODE_DISABLE;           // 关闭DDR模式
    ) G2 I  [( r! s
  58.   Cmdhandler.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;/ X8 x0 g6 t( Z, E1 P

  59. 6 Q/ J$ N5 |4 g7 V. B2 r
  60.   HAL_QSPI_Command(&qspi_handle, &Cmdhandler, 5000);3 Z, \: c. X5 o, V
  61. }, {/ c! K7 ?/ ^' E

  62. ( D, k7 f/ x8 E* j1 N. x
  63. // 接收指定长度数据
    + i- |+ e2 e5 i7 o4 U
  64. uint8_t QSPIReceive(uint8_t *buffer, uint32_t length)
    . U1 |9 d4 N& a" c; {' C
  65. {
    $ ?* b3 g& h) A! c2 Y( g
  66.   qspi_handle.Instance->DLR = length - 1;
    + v9 p& y7 j. S5 V: Z& M
  67.         5 v% o: \# `3 W) [# `; m2 N! u! Z
  68.   if(HAL_QSPI_Receive(&qspi_handle, buffer, 5000) == HAL_OK)
    3 S/ j+ k$ @; B; R+ q0 Y! |
  69.   {
    ( y, J- D/ H6 P/ K
  70.     return 0;  //接收数据
    3 j, ?9 y+ {. Z% n
  71.   }
    # g; j; u+ i1 @, q4 l
  72.   else
    0 a" m% [; f' `6 p+ w+ h
  73.   {, I) Y0 \  e, J: l2 g* X
  74.     return 1;& `( Q/ n" U, h1 R, i6 f3 ^, h
  75.   }% l- k% \8 E  `
  76. }! ^0 K& L# I5 |% t, v, m/ q) E1 v
  77. ! Y! K, ]& F+ @. H9 e2 r8 W
  78. // 发送指定长度数据& R$ I5 F% X5 E# D$ b: W7 K
  79. uint8_t QSPITransmit(uint8_t *buffer, uint32_t length)& Y* ?; H/ |' s: `
  80. {
    ( }, `! R* o* O" F: e
  81.   qspi_handle.Instance->DLR = length - 1;
    , @0 T, Z% V8 r# J4 w
  82.   if(HAL_QSPI_Transmit(&qspi_handle, buffer, 5000) == HAL_OK): W" u( ^& k/ J9 d! F3 M- \2 s: U/ V
  83.   {
    9 i% G1 V, s5 {7 I
  84.     return 0;  //发送数据  ~  ?6 B$ D: A  F8 m  A
  85.   }
    7 t: z8 `3 ^$ X- h1 C" F% v
  86.   else9 l4 w  t% Y4 G7 ?9 K7 C- h* u, @
  87.   {' j! I! g( s1 z2 {9 I+ T  Q! n9 R
  88.     return 1;
    , p9 p1 p! g+ @* s" ^! }
  89.   }: X) b3 X& ^) p% D, R6 I5 b
  90. }
复制代码

+ c; H1 K. A# ]+ d2 \# x五、QSPI驱动W25Q256
: b( e9 [$ x( G& O' j* L9 I' o6 ?W25Q256:32M  512块,每块16个扇区,每个扇区4K。具体指令看手册。封装了如下接口(初始化读写擦除等接口是以后移植文件系统的基础):5 k3 t: _' X  v. g9 R: Y, s
( B3 [3 ?( d' s8 M( c: v% B
  1. #define W25QXX_WRITE_ENABLE      0x06
    : }: {7 F8 k; L  `
  2. #define W25QXX_WRITE_DISABLE     0x04
    , b+ j2 Q4 }) \7 ?( Z
  3. #define W25QXX_READ_STATUS_REG1  0x058 o2 s& m* k6 Q1 \. |2 d
  4. #define W25QXX_READ_STATUS_REG2  0x35  }3 `& F6 G2 O- ~+ n
  5. #define W25QXX_READ_STATUS_REG3  0x15
    : X+ R% s; D( R; B
  6. #define W25QXX_WRITE_STATUS_REG1 0x01
    ) x: {" @4 A$ j8 V* _% ^
  7. #define W25QXX_WRITE_STATUS_REG2 0x31) k0 L- \$ g: t" S0 ~8 V' ]
  8. #define W25QXX_WRITE_STATUS_REG3 0x11# f( E" C2 m% n/ w
  9. #define W25QXX_READ_DATA         0x03$ {' I" \1 t# ?4 s, M+ i- `3 G
  10. #define W25QXX_FAST_READ_DATA    0x0B7 d) x. Q& q2 x: k% e- H
  11. #define W25QXX_FAST_READ_DUAL    0x3B4 o, p( B/ R6 D- C3 Z
  12. #define W25QXX_PAGE_PROGRAM      0x02
    0 M. |0 r5 g3 @: p1 l1 Q# r
  13. #define W25QXX_BLOCK_ERASE       0xD8
    1 w5 w$ h( x. z! T! U6 K# t
  14. #define W25QXX_SECTOR_ERASE      0x20
    ( e: A, k+ r: s
  15. #define W25QXX_CHIP_ERASE        0xC7
    3 c9 v& T* l5 Q8 ~3 @4 B
  16. #define W25QXX_DEVICEID          0xAB% i& O3 D$ c1 r! l! U4 J
  17. #define W25QXX_MANUFACT_DEVICEID 0x90/ U0 W5 ], z2 P7 i
  18. #define W25QXX_JEDEC_DEVICEID    0x9F
    1 g# m1 [9 c  K$ ^! |0 P7 g$ q4 N
  19. #define W25QXX_EABLE_4BYTE_ADDR  0xB7
    6 i" u2 W! g( |2 L" t8 r8 Q
  20. #define W25QXX_EXIT_4BYTE_ADDR   0xE91 S3 t. ?3 c! G$ m+ D
  21. #define W25QXX_SET_READ_PARAM    0xC0
    + C# J6 F& Y, K9 I2 t+ V( K; b
  22. #define W25QXX_ENTER_QPIMODE     0x38% S. Y4 S6 }! D& p3 m* R# ]
  23. #define W25QXX_EXIT_QPIMODE      0xFF
    ; _4 K2 D; @/ g, P0 R
  24. $ b+ T9 y) x0 M, ?' L  z. X
  25. uint8_t w25qxx_qpi_mode = 0; // QSPI模式标志:0,SPI模式;1,QPI模式.
    0 B: h. G5 X5 ^) f; y6 I
  26. 8 A4 d" a8 `% O( P) F4 c; E
  27. // 读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器; W! J6 m, ]" T0 f
  28. // 状态寄存器1:9 w$ |% W. h& m* W  n! X
  29. // BIT7  6   5   4   3   2   1   05 f. I4 b7 ]0 W. d, ^0 s; O3 r6 ~
  30. // SPR   RV  TB BP2 BP1 BP0 WEL BUSY
      x6 S+ y+ U+ w# v1 {" O
  31. // SPR:默认0,状态寄存器保护位,配合WP使用
      N# _/ x! A  r- ~/ c+ V- V( M
  32. // TB,BP2,BP1,BP0:FLASH区域写保护设置
    * Y- W: e$ I( ]3 c( f3 t- a! ^8 s
  33. // WEL:写使能锁定
    ! N, i: A4 \+ o* M( ~
  34. // BUSY:忙标记位(1,忙;0,空闲)
    2 i) l  j1 ?+ ?3 y
  35. // 默认:0x00: @' e+ P3 N( d0 ]
  36. // 状态寄存器2:% w$ s* y: ?& G/ L; t4 [$ G
  37. // BIT7  6   5   4   3   2   1   0
    9 x8 M7 m* n0 q9 |+ d7 D
  38. // SUS   CMP LB3 LB2 LB1 (R) QE  SRP1( S' S/ {% z! H
  39. // 状态寄存器3:. p5 G- T) z- ?/ X
  40. // BIT7      6    5    4   3   2   1   0/ }" n5 a1 _9 o" |/ W5 @
  41. // HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
    " @3 n  f/ k& z5 Y
  42. // reg:状态寄存器号,范:1~3
    & u) S; X6 p, L5 t% w' N
  43. // 返回值:状态寄存器值
    + O, K7 w$ F! ]* z6 U
  44. static uint8_t w25qxx_read_status(uint8_t reg)' M3 q. a! y1 c/ H" u
  45. {; w0 K: {' O: L* V$ u' \; D
  46.   uint8_t value = 0, command = 0;9 z9 |1 Y9 `) W$ [6 U$ m

  47. ( u! V6 F; _, L  N5 X# [5 W
  48.   switch(reg)
    + {) h! O1 b" j# B% v
  49.   {
    % `) s2 |( p, u) \- _$ g
  50.   case 1:
    : }# b4 E/ h, t% l
  51.     command = W25QXX_READ_STATUS_REG1;  // 读状态寄存器1指令
    ( O0 I% o: D$ k  C2 g: l- ^: K
  52.     break;# D6 u' I" [' U- ]8 h" [

  53. 0 P* K* M  e0 o/ z! c5 y$ H
  54.   case 2:# C' `' C0 u, N9 ~5 O
  55.     command = W25QXX_READ_STATUS_REG2;  // 读状态寄存器2指令5 T2 P- ~& o* k7 R& Z
  56.     break;
    - O5 u+ Y- i+ C7 Y0 g) H& J

  57. ( B7 t# K6 ^) |+ }
  58.   case 3:
    % c% w* j& ?' N5 S0 z
  59.     command = W25QXX_READ_STATUS_REG3;  // 读状态寄存器3指令
    4 z6 Q/ d2 P3 d% ^5 l& {; r
  60.     break;
    - J0 X# B2 r$ E) q
  61. 9 p7 E% \( V5 l. v9 ^1 r8 O
  62.   default:0 i1 E) u+ B6 s8 _, y3 Y
  63.     command = W25QXX_READ_STATUS_REG1;% T3 [3 m: e/ W" e
  64.     break;) V6 r0 W& _: A; A
  65.   }" C2 E$ [2 n' g, S! N7 v) n
  66. 3 L, o2 s0 x4 z. V1 ^
  67.   if(w25qxx_qpi_mode), P: e8 f. }5 f9 m: |3 @
  68.   {
    0 G+ w' q9 T  j2 K9 N
  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 ?% a( P0 k% K& E
  70.   }
    * n) r1 q3 t& \1 G
  71.   else
    1 N/ O8 v: N- V. V8 Z* A; |5 s
  72.   {5 Z+ [$ o% o7 e0 `) h# S6 c* 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个字节数据" [7 t! s0 G/ @3 F+ y4 N+ t
  74.   }
    8 T6 A$ ~. J; |3 Q* p

  75. : ~0 D( m& ?( M8 A, B
  76.   QSPIReceive(&value, 1);& L/ Q7 H9 [+ E& C; Y4 o4 l
  77. + C, k% s1 o* ?3 m
  78.   return value;
    ' }$ O4 ^! I( u# ?
  79. }
    : O2 h& O( R4 B3 X

  80. 4 o, G. T7 e0 m+ E# D% n
  81. // 写状态寄存器4 `8 w) e1 a( O1 w, C( b9 u2 U3 K
  82. static void w25qxx_write_status(uint8_t reg, uint8_t status)/ N! s. D$ r' X6 k: {6 S
  83. {; \0 ^9 P- U, u+ G* n
  84.   uint8_t command = 0;8 R; P/ ]/ l3 N' T1 b& ]2 N

  85. * S7 C+ y' o' R$ n) p
  86.   switch(reg)( F9 L2 Q+ ^, Y0 X: m  n2 n
  87.   {
    ) v  f, L8 X. l- w/ y9 v1 M+ q8 |
  88.   case 1:( j# S) I. @9 Y) L. h
  89.     command = W25QXX_WRITE_STATUS_REG1;  //写状态寄存器1指令% t2 z1 ^5 J" P+ B/ @4 m$ q6 a( [
  90.     break;
    + W: c6 ]# w$ A; T
  91.   case 2:
    # C  o2 R+ a% f
  92.     command = W25QXX_WRITE_STATUS_REG2;  //写状态寄存器2指令" w: Z& J; Z  P: q! Z" q
  93.     break;
    6 R! B5 q4 s1 M/ T! k- v* p
  94.   case 3:# Z4 c$ Z$ g6 j! d- G
  95.     command = W25QXX_WRITE_STATUS_REG3;  //写状态寄存器3指令
    - v+ u& Z7 C! h7 }  b- i" K
  96.     break;7 g- H% X; m. E- R' x1 X# r
  97.   default:+ V! p2 {' V* ~& D% s2 k
  98.     command = W25QXX_WRITE_STATUS_REG1;
    : a3 r$ Z9 E. T
  99.     break;
    # a+ @& z5 t# }* P- M( d! `
  100.   }
    8 z  l+ m2 @1 K$ S
  101.   if(w25qxx_qpi_mode)( K$ h: I9 h7 Q( t* q5 a& U& ]
  102.   {8 A6 _! ?9 Y# X# w7 A1 z, k7 k1 X
  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个字节数据
    & }. ?; K" Z8 ?4 r7 d! D
  104.   }" m+ l, k. R0 w' I' \
  105.   else
    , ^  g2 S2 J1 b+ T* V* Z, v- }
  106.   {
    5 O- X- a* s1 N1 K  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个字节数据
    7 ^7 J- [" T% m' o6 j3 u" W
  108.   }- S1 w+ m5 S! m) z
  109.   QSPITransmit(&status, sizeof(status));7 Q5 o7 b  X# k+ L" t3 e
  110. }3 |$ g. v# B' F
  111. ; M# |0 G, @6 b. [" |# a: b
  112. // 写使能  将S1寄存器的WEL置位6 c8 U+ \8 M9 X. E2 m' y
  113. static void w25qxx_write_enable(void)
    % L; [  f" e9 d2 E" r
  114. {
    * {. A2 P3 F8 X
  115.   if(w25qxx_qpi_mode)3 G1 G, z/ ^8 \6 w" F
  116.   {8 A2 J8 t8 s% m: a- G- k$ w
  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个字节数据
    & ?4 I: @0 Q  w. q4 J
  118.   }
    3 [8 ^' k0 E2 ?3 {* P4 \
  119.   else
    2 g. X) q6 J  ^# K' [' p# [4 ]2 F5 M
  120.   {
    % p5 D+ u3 e& k9 m0 D8 I  d" B: K
  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个字节数据& }8 o! f" T9 Z" X; S7 t
  122.   }
    6 Q" H+ Y4 }7 P6 V2 q) m
  123. }: S) x( a# r1 Q8 ?7 G

  124. ' l' g$ j+ s. z& S; X+ e
  125. // 写失能  将WEL清零
    - ^: \" U6 m2 ^' l8 F& Q9 N1 w* h
  126. void W25QXX_Write_Disable(void)% Q- Z( j3 E+ }, f8 n, K, [' r6 I& H
  127. {
    % v0 ?, `" r' o- h
  128.   if(w25qxx_qpi_mode)( B$ ^0 K, n1 u9 E- X
  129.   {) P1 h! f9 h. H+ B9 x! @3 W3 k
  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个字节数据
    $ Z% ~' l0 J" {6 `+ A8 e6 f( P2 O6 t
  131.   }# g9 T1 s7 \4 @4 [  x! [
  132.   else
    7 t% ]$ F  P6 |! L2 x
  133.   {
    ! d8 R' E- I0 D
  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个字节数据
    6 ^1 a5 R! F  b
  135.   }6 p) Q* _) p$ r  Q6 }) L# A5 C
  136. }
    " |" n* ?5 K9 D/ s
  137. 4 k  \. p, s4 S" ?9 _' D; c1 m
  138. // 等待空闲
    0 f* p$ ?9 {) c  \* {: y1 r
  139. void w25qxx_wait_busy(void)
    " z" N" ]! e0 j! F. Z
  140. {9 y4 f6 V7 M3 m! E1 D  y
  141.   while((w25qxx_read_status(1) & 0x01) == 0x01); // 等待BUSY位清空; b/ c* x4 Z4 N/ I3 x3 f
  142. }) b/ ]/ W6 ]/ q: K' z8 F' w
  143. ( c( z: e" O" R* j* i6 F
  144. // W25QXX进入QSPI模式! p, Z1 A$ g! E" S
  145. static void w25qxx_qspi_init(void)
      W* H+ _& V7 Y* U$ G7 {
  146. {
    9 u, W2 k" x! D& X* G& d
  147.   uint8_t reg2;
    9 j+ _% Q5 I$ V* \
  148.   reg2 = w25qxx_read_status(2); // 先读出状态寄存器2的原始值" C0 y7 c+ t3 S2 N5 n
  149.   if((reg2 & 0X02) == 0)  // QE位未使能
    " H3 C+ Z5 {4 X  r
  150.   {
    6 w* N$ O# y6 w* m5 t3 n
  151.     w25qxx_write_enable(); // 写使能
    + p) U. a' A& \
  152.     reg2 |= 1 << 1; // 使能QE位
    3 c! ~: k: u* Q, M
  153.     w25qxx_write_status(2, reg2); // 写状态寄存器2
    ' j5 U3 I8 R+ V9 J) e
  154.   }, F. q. b% V$ x, x; p* t
  155. 7 y0 z6 G" g* I! p& \4 T1 {/ @
  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个字节数据
    ; n+ X/ B! p0 q& n; K6 i$ [

  157. 9 h, Z" n; Y$ M
  158.   w25qxx_qpi_mode = 1; // 标记QSPI模式) |/ k" r% ^/ w: k
  159. }$ }. n: T6 c: h: a" v
  160. , j( l) [! @/ N- F
  161. // 0XEF13,表示芯片型号为W25Q80: N- B  U# c0 G
  162. // 0XEF14,表示芯片型号为W25Q16
    ( D6 Y# S, ?& r. u* ?  x8 w
  163. // 0XEF15,表示芯片型号为W25Q32
    0 C. P7 E4 q* a9 G0 Q4 }
  164. // 0XEF16,表示芯片型号为W25Q64
    * Z& W5 n7 i$ r' x' S" N
  165. // 0XEF17,表示芯片型号为W25Q128
    4 C8 Q: b5 y, ]/ ?
  166. // 0XEF18,表示芯片型号为W25Q256
    8 s$ X2 P5 R6 X' K
  167. static void w25qxx_id_get(void)
    + L2 {7 [( V6 ?
  168. {
    / m1 j4 t/ g5 H8 v
  169.   uint8_t temp[2];' ^- F8 U0 e  K8 j
  170.   uint16_t device_id;
    : J. E# E+ \; B0 o6 T

  171. ! c6 q( \% [5 n# N5 u
  172.   if(w25qxx_qpi_mode)5 A) M* V5 g  G5 t' r4 d2 i
  173.   {8 f! K2 c: t7 l( G0 N
  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个字节数据
      [. r( q; |! G. b  ]5 L; @) i
  175.   }. S5 k5 ^, P- u6 g
  176.   else
    % |1 N- i( p$ D  S
  177.   {$ u$ e  D/ g: X, G, o
  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个字节数据' d, z; D, \3 s! l2 f! h
  179.   }; E2 W4 q% n$ c: T7 ^+ `1 s
  180. 7 ?" r2 z; K: ^& }- r3 B' X
  181.   QSPIReceive(temp, 2);" ?6 x5 m1 {! R  ?" v

  182. 9 z9 N: @* G5 |+ G1 |
  183.   device_id = (temp[0] << 8) | temp[1];
    % l( i7 U$ g. P$ U/ z+ U! b" N
  184. 3 a; y% Z) T7 X1 F6 Y  U
  185.   printf("QSPI Flash Device ID: %X\r\n", device_id);
    % e5 c9 |$ V! ^, M: }4 f- m' j% g+ j
  186. }
    " F3 A5 P2 K. Q# J

  187. 4 }) [+ N$ H$ n) E6 v% F6 j( i
  188. void W25QXXInit(void)- @& j; S+ J) @6 H: g$ t
  189. {4 _/ ]5 c1 A$ q. B: D8 [
  190.   uint8_t temp;
    7 U* _0 k- j& y5 D' s9 Y, V
  191. 6 A1 @% T' o; {+ W- E+ ~
  192.   QSPIInit();
    : e- M6 N; a# z; h0 m
  193.   w25qxx_qspi_init(); // 使能QSPI模式/ n. G& `7 @/ P* M6 ?( d8 F3 E
  194.   w25qxx_id_get();    // 读取FLASH ID./ m8 a; ]& Q; k) w

  195. 9 \' @$ I4 z3 G: ?9 R
  196.   temp = w25qxx_read_status(3); // 读取状态寄存器3,判断地址模式3 W3 {) e  E9 [" V3 p0 \# q" e
  197.   if((temp & 0X01) == 0) // 如果不是4字节地址模式,则进入4字节地址模式! q$ ^* R* v% i4 }
  198.   {4 ~8 \2 Q$ o2 v* G( ]( G
  199.     w25qxx_write_enable(); // 写使能
    ( k  r- U5 @, S7 z* N
  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个字节数据- v5 f" h; h0 K6 G7 A: ~' e
  201.   }
    # E# b% [( U+ G' c' O" y) C& V
  202.   w25qxx_write_enable(); // 写使能
    ' L2 N- P! V4 O" K
  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个字节数据
    - u2 ^! d$ N! h, g7 c& [
  204.   temp = 3 << 4;      // 设置P4&P5=11,8个dummy clocks,104M
    * n! S" A: ?- |& Z4 m
  205.   QSPITransmit(&temp, 1);
    ( D( y8 g( N, N* J/ c1 i" r8 ~
  206. }8 z+ Q4 x- K8 E! A( b' @& x
  207. . q3 R: @) `* M2 t1 w8 J5 r
  208. // 擦除一块4096字节 最少需要150ms# |( w9 I3 O9 C* F
  209. void W25QXXSectorErase(uint32_t addr)
    4 f# Y. Z7 O! y
  210. {
    9 s% l# E* }" l8 q3 x% O
  211.   //addr /= 4096;1 E* y0 o- ~0 Q! L5 |
  212.   addr *= 4096;
    ( A1 D3 }3 a) p2 d4 ~7 c$ B
  213. 1 R- N1 U/ ], l$ w' X2 F& u/ Q
  214.   w25qxx_write_enable();( x2 p$ p3 j$ E; ?$ ?
  215.   w25qxx_wait_busy();% u" j  c3 A2 O$ K8 B5 ]
  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个字节数据* m2 e+ D+ R9 k# m# K* ?% j% j
  217.   w25qxx_wait_busy();
    2 s! f) ~; d) q) Y  a
  218. }0 x  @, W: o5 U0 \+ V# R6 L

  219. - P1 W* N$ N" N* J0 v) V  v( l
  220. $ F. P+ t; |, M. C7 |  o; H
  221. // 擦除整个芯片2 l1 N0 g5 q  I
  222. void W25QXXChipErase(void)
    - i; F$ Q+ Z$ a3 I9 c/ e
  223. {/ L# v% {" C" i' B8 s, a7 U
  224.   w25qxx_write_enable();          //SET WEL
    ; E% _1 [7 o& O; e5 X; ~0 H2 U7 l  h
  225.   w25qxx_wait_busy();
    ! r( P& |. y6 y) J( z. Q# |
  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个字节数据$ t  t! I; @3 X. {" |
  227.   w25qxx_wait_busy();           //等待芯片擦除结束
    - \0 N; {! V( s' e
  228. }  W* i. q4 b4 {: g2 f- V2 `
  229. ( T. [  b% \8 N) [+ z6 ~
  230. // 写最多256字节9 |: I/ m( E3 C9 I! p5 i0 t9 U" n# @
  231. void W25QXXWritePage(uint32_t addr, uint8_t *buffer, uint16_t length)/ E. `% V0 U8 C9 N. g% c
  232. {
    , ]  R- e4 |% y" x) O5 w
  233.   w25qxx_write_enable();          //写使能
    . {1 Z. Q% F0 w3 c% R/ ]% s+ e8 h
  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个数据
    , C  w) b( J+ G
  235.   QSPITransmit(buffer, length);) d+ b5 Q" ]$ Y$ S7 C( R' s
  236.   w25qxx_wait_busy();            //等待写入结束
    6 L! Z7 G$ X3 L! I, [/ l, J* D" V
  237. }7 V# l6 O; j6 h) i( x- ^
  238. + }+ M* M$ x! V" K8 z
  239. // 写最多65536字节 无擦除动作, F/ [' Z, }: J4 k, I; U
  240. void W25QXXWriteExt(uint32_t addr, uint8_t *buffer, uint16_t length)
    - `; N' Y. B$ Q/ T
  241. {
    6 v& }* O- N5 _, d6 Y/ Z
  242.   uint16_t pageremain = 256 - addr % 256; // 单页剩余的字节数; _# R8 [& f8 @  n3 Z# R
  243. 4 v2 ?! m* X. ?. i- S: X& h4 m
  244.   if(addr + length - 1 > (32 * 1024 * 1024))6 n1 |, ?" a" S3 g1 b. d& `
  245.   {
    6 a3 `% o0 j" P; h
  246.     return;) F; o# S* o% ]4 d
  247.   }
    ! F& c7 t# S: ?' w, n6 S

  248. : _* \" |/ B- |, G" j
  249.   if(length <= pageremain)
    1 P5 a& f: U( `/ S0 k
  250.   {
    & `* a( z% I, b" j
  251.     pageremain = length;  // 不大于256个字节( }5 V2 @9 w" V. p# Y4 I
  252.   }! X& q/ F+ H1 y+ U  e& D
  253.   while(1), O: Q) n4 E  [: H1 r" U6 ]% ]
  254.   {
    3 m. ~" q  q! r3 A2 s& h2 _( W# x
  255.     // 分页写入
    # K6 {  R5 Y+ I
  256.     W25QXXWritePage(addr, buffer, pageremain);
      w7 k8 f' G) Z: `- O; {3 W
  257. 6 {7 L' [/ p2 F: z
  258.     if(length == pageremain)+ r, v3 E2 b5 e" \* s* ^: A1 W
  259.     {
    , w' h* D. c$ o
  260.       break;  //写入结束了! T1 R; r! r/ u. \$ ?
  261.     }
    5 [( n+ C# {  u& A% w* F8 ]
  262.     else$ _! _( r3 _% ~% p
  263.     {
    7 A  L; ^: ~' H3 `" I) p
  264.       buffer += pageremain;9 k+ ?+ s3 J, B$ K% z. O
  265.       addr += pageremain;
    1 }- B# j" I9 ?2 `1 v
  266. : N$ g! E  A! h- O
  267.       length -= pageremain; // 减去已经写入了的字节数
    ; s9 t3 @  C4 \" [
  268.       if(length > 256)
    + U$ r& L, ?% c9 \) W" |' i
  269.       {
    8 J. o. h- D/ |! ~
  270.         pageremain = 256;  // 一次可以写入256个字节
    0 U1 e" _9 b- g
  271.       }
    . h9 u3 c& c2 e3 K
  272.       else
    ; p9 Q2 ^9 f( e, e$ U8 t4 _
  273.       {3 ^9 J4 L6 I. x0 X
  274.         pageremain = length;  // 不够256个字节了
    - t5 W" {2 p! A, Y/ }2 T" i& a
  275.       }
    , l: K1 b+ _% g1 h  y3 F. e4 \8 `$ B0 H
  276.     }7 \: z8 T: [2 C5 c  W' `
  277.   }" n$ ]" Y0 |+ q& P! ?  x% w
  278. }5 z4 V9 U( C- U& e

  279. ! z1 N0 R0 M+ ]/ r
  280. // 写最多65536字节 带擦除动作
    : i! V6 n% D) s1 Z- R% R) Z
  281. static uint8_t W25QXX_BUFFER[4096];
    9 j- d% e& s  q+ k' k
  282. void W25QXXWrite(uint32_t addr, uint8_t* buffer, uint16_t length)# ^5 Z) f: s5 y- W+ w
  283. {
    ! o* f5 C, J0 j7 M. J
  284.   uint32_t secpos;
    , v4 O. K0 e- U
  285.   uint16_t secoff, secremain, i;
    + g! t+ j& v' k) O, ~
  286.   uint8_t * w25q_buf;  m9 b# N: ^2 q9 v7 A
  287. ' `$ Q+ i0 ?! [! i; T0 R0 x. {
  288.   w25q_buf = W25QXX_BUFFER;
    7 K! i8 A; D1 l8 p# L
  289. $ ]" w2 x# T! A+ y2 t4 \
  290.   secpos = addr / 4096; // 扇区地址
    / S# y0 y8 W) o8 n( t
  291.   secoff = addr % 4096; // 在扇区内的偏移' D8 l! z; b. C4 S" z
  292.   secremain = 4096 - secoff; // 扇区剩余空间大小- F* ~4 @6 T; m
  293. 0 J1 K7 R8 }' p
  294.   if(length <= secremain)  _3 K2 V5 e4 s$ p
  295.   {
    * c# P( T+ ^1 p- ^2 A
  296.     secremain = length;  // 不大于4096个字节) \5 C# G2 U* ^3 \  s
  297.   }
    ! C0 S  v+ f3 ^% L: J0 \3 |
  298.   while(1)8 m) t1 z' H; [
  299.   {
    0 @& n& b( M6 Y. H$ c
  300.           WatchdogFeed();
    $ ^  U) |  j3 x$ m: u& q0 G
  301.     W25QXXRead(secpos * 4096, w25q_buf, 4096); // 读出整个扇区的内容! J- F1 `- O- g/ [2 k
  302.     for(i = 0; i < secremain; ++i)5 A0 j2 k9 p9 p* Y9 P
  303.     {
    2 F  ]9 a0 f! g
  304.       if(w25q_buf[secoff + i] != 0XFF)1 q, }  K- n1 G8 D
  305.       {; u$ {4 y- R0 f+ {1 n
  306.         break;  // 需要擦除, {3 k3 o6 }* J, R
  307.       }
    + W5 }/ h4 y  M0 ^8 [  G
  308.     }6 N0 e% W1 f$ s% m' R/ L
  309.     if(i < secremain) // 需要擦除
    # W; k# b8 V: n3 H4 {& G8 a
  310.     {
    " [. }3 L1 x2 y2 {. _; x5 n
  311.       W25QXXSectorErase(secpos); // 擦除这个扇区
    9 [% |+ u$ q7 L$ J
  312.       for(i = 0; i < secremain; ++i)3 q5 J; ]* v6 p) `+ _+ @
  313.       {
    ! v1 z% Z. R- I  t* y
  314.         w25q_buf[i + secoff] = buffer<i>;</i>  e$ N1 V4 }0 J: f8 L2 t( c/ y# L" r0 U
  315.       }/ K; d' l) N2 Y
  316.       W25QXXWriteExt(secpos * 4096, w25q_buf, 4096); // 写入整个扇区
    / a& ~/ I6 x+ i/ Y+ U: s
  317. ; V/ b8 N1 @! x% E9 N
  318.     }# j6 Q- H+ r: H  [1 C5 q3 s2 P  K! R
  319.     else- a/ p1 o' P' K$ P# I
  320.     {1 B/ \& y* z1 j! ^
  321.       W25QXXWriteExt(addr, buffer, secremain);  // 写已经擦除了的,直接写入扇区剩余区间.
    . u  r- O1 o; O6 c
  322.     }
    $ e) y9 e+ M3 v9 Q. o
  323.     if(length == secremain)) u: s7 [8 s8 p! F6 l/ L* c
  324.     {
    $ _6 O6 l% n# Z4 r. c
  325.       break;
    $ b+ v' U9 M2 z! J$ K
  326.     }% H1 _* ?2 j4 @1 N
  327.     else
    9 I& i2 u9 O4 e7 j; H! B
  328.     {  N. k9 e! f! |: w8 ^# k% O: V9 t. ~
  329.       secpos++;
    * o8 Z7 T; i6 I' ]. V
  330.       secoff = 0;/ W/ M8 o# W8 z+ P/ Y9 a5 d

  331. $ f: o( @. H% V5 v# a. m
  332.       buffer += secremain;+ ?4 Q. f. D/ X; |
  333.       addr += secremain;* y  q3 R# S9 f) m3 D$ G
  334.       length -= secremain;: e& C( c1 O. c* V# B: v
  335.       if(length > 4096)  s9 t7 e8 S6 ?
  336.       {
    ' s5 p4 n; ?" s+ h! {
  337.         secremain = 4096;
    # u. I" u0 b9 Y$ ~1 R
  338.       }
    * f. o, \( B$ _9 n8 ^
  339.       else
    , A6 @& u- N. J8 Q5 O
  340.       {  I8 V+ p7 t8 P( H9 Q5 o* x9 p
  341.         secremain = length;
    # C1 E" b7 S2 {6 `$ k1 G7 G0 G) t9 e6 R
  342.       }& z+ B9 [$ o% m; u. w/ A6 m& N
  343.     }
    . t, p7 S' }! R7 l
  344.   };
    9 B) a/ w8 l$ y5 e
  345. }
    4 Z2 S  O  O' T" C! c

  346. & R& N3 \/ B0 E

  347.   l" @8 m/ _5 p
  348. // 读取SPI FLASH,仅支持QPI模式  在指定地址开始读取指定长度的数据 65536
    7 C  P( k  @' a5 M; R, B" x
  349. void W25QXXRead(uint32_t addr, uint8_t *buffer, uint16_t length)4 G' r, a9 k- c" C2 F0 Y
  350. {$ B5 \6 s* o# u% c5 e
  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个数据
    ; b" h1 D4 C1 [6 @( u# X5 |
  352.   QSPIReceive(buffer, length);$ V+ Q: g8 S2 n
  353. }
复制代码

* R" V9 m: z7 `& s0 K( t7 ]* X$ }! [+ v也可以使用SPI操作W25QXX。
7 R+ z3 A/ v7 G, W2 H0 ]% ^. B/ i% B6 `+ T+ `& [! |9 t5 F3 B
- ]0 n- ^! ?2 M/ x

6 [. O* e9 v: A2 b; T2 D& T
赞 收藏 评论0 发布时间:2021-12-11 12:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版