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

【经验分享】STM32F7xx —— QSPI

[复制链接]
STMCU小助手 发布时间:2021-12-11 12:00
一、QSPI
- }6 `+ U, O3 b% h, A8 Y" v3 B        SPI 是 Queued SPI 的简写,是 Motorola公司推出的 SPI 接口的扩展,比 SPI 应用更加广泛。在 SPI 协议的基础上,Motorola 公司对其功能进行了增强,增加了队列传输机制,推出了队列串行外围接口协议(即 QSPI 协议),使用该接口,用户可以一次性传输包含多达16个8位或16位数据的传输队列。一旦传输启动,直到传输结束,都不需要CPU干预,极大的提高了传输效率。该协议在ColdFire系列MCU得到广泛应用。与SPI相比,QSPI的最大结构特点是以80字节的RAM代替了SPI的发送和接收寄存器。QSPI 是一种专用的通信接口,连接单、双或四(条数据线) SPI Flash 存储介质。
8 V4 g5 r! k/ b( R$ D; K+ |+ J% K7 l. p/ s' f: r
该接口可以在以下三种模式下工作:# B# F+ M* w6 m9 X
① 间接模式:使用 QSPI 寄存器执行全部操作
! H  v, \( S( ?0 r2 E3 t② 状态轮询模式:周期性读取外部 Flash 状态寄存器,而且标志位置 1 时会产生中断(如擦除或烧写完成,会产生中断)" @$ E, ]; g$ [& l: D) h( t1 T* I
③ 内存映射模式:外部 Flash 映射到微控制器地址空间,从而系统将其视作内部存储器。8 w, m! u2 R9 J8 k

; a) |) K8 S3 D: UQSPI通过6根线与SPI芯片通信,下图是内部框图:
! S2 ]( T, F' i% O- {! @2 x7 V$ z9 V9 K6 V" T# ]  t
20190123173205949.png
% z% }' ]! S$ F
) f+ h0 A$ I4 }0 c" v- z
QSPI每条命令,必须包含一个或多个阶段:指令、地址、交替字节、空指令和数据。3 u+ N1 f  J. B5 I  |% c, n- S5 p
# [- H) o' Z* L+ D9 K9 Z" A
QSPI发送命令:等待QSPI空闲;设置命令参数。
* r3 _7 B, {+ D* g
. p4 K2 L' s4 G$ P$ _QSPI读数据:设置数据传输长度;设置QSPI工作模式并设置地址;读取数据。% w8 x& I) Y) H! [; v0 r2 Z

: F5 X- \( J& t0 KQSPI写数据:设置数据传输长度;设置QSPI工作模式并设置地址;写数据。
2 n9 n4 k3 d* E8 Q1 o
" K9 B7 x8 j# x; N# o
& M6 `7 p2 H- j二、几个重要的函数. n; B( w- F; A: @
  1. HAL_StatusTypeDef     HAL_QSPI_Init     (QSPI_HandleTypeDef *hqspi); // 初始化
    8 `0 t+ {- a' i1 p) x  c
  2. % K/ G" g1 \2 M2 [
  3. HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout); // 发送命令$ A1 _5 f, m. a* U" O- ~, L+ h

  4. / J  k; h2 M$ W+ C% r) g
  5. HAL_StatusTypeDef     HAL_QSPI_Transmit     (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 发送数据! o9 D! i7 f* P( A6 i3 M# r

  6. 4 w+ v2 {% n5 X: q* p5 _( @7 D
  7. HAL_StatusTypeDef     HAL_QSPI_Receive      (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 接收数据
复制代码

6 d. X2 E2 ?, Z# ^3 d三、几个重要的结构) y- C; L9 o  F
  1. // QSPI操作句柄
    , S  N; G0 G; M! |8 Z' M* ?
  2. typedef struct, w* Z( j" c2 W
  3. {
    . {: r6 O- N3 a9 t8 q
  4.   QUADSPI_TypeDef            *Instance;        /* QSPI registers base address        */3 o1 x  B) l4 M# q$ Y0 a
  5.   QSPI_InitTypeDef           Init;             /* QSPI communication parameters      */  u; m. H' x) p9 T( S+ z7 W
  6.   uint8_t                    *pTxBuffPtr;      /* Pointer to QSPI Tx transfer Buffer */
    7 s/ |' t8 d3 y
  7.   __IO uint16_t              TxXferSize;       /* QSPI Tx Transfer size              */7 B3 Y7 m3 T/ @1 W6 U$ i9 }
  8.   __IO uint16_t              TxXferCount;      /* QSPI Tx Transfer Counter           */, f$ }* `6 _9 Q! I
  9.   uint8_t                    *pRxBuffPtr;      /* Pointer to QSPI Rx transfer Buffer */# x" w- [, H1 o& s2 W
  10.   __IO uint16_t              RxXferSize;       /* QSPI Rx Transfer size              */
    9 `, Q; `+ T) J- C, a* B9 V; V* g& v
  11.   __IO uint16_t              RxXferCount;      /* QSPI Rx Transfer Counter           */4 j; ?7 S/ ^2 I' e5 E
  12.   DMA_HandleTypeDef          *hdma;            /* QSPI Rx/Tx DMA Handle parameters   */4 t  H' T( O6 I5 P4 k, b
  13.   __IO HAL_LockTypeDef       Lock;             /* Locking object                     */; s  B! }. l3 M' t1 S# J
  14.   __IO HAL_QSPI_StateTypeDef State;            /* QSPI communication state           */
    8 j, H- Y- k* F4 U
  15.   __IO uint32_t              ErrorCode;        /* QSPI Error code                    */
    $ y5 Y1 D% r- ^9 ]* }9 S/ w$ y
  16.   uint32_t                   Timeout;          /* Timeout for the QSPI memory access */
    5 Q  {+ Y* P7 _+ ?
  17. }QSPI_HandleTypeDef;& N' ?/ {1 ~3 V. j- Y7 b
  18. ! O, R% h' y" }& L( }
  19. // Instance:QSPI基地址  ---  QUADSPI5 ]" Q( c  b1 b# V" M- E
  20. // Init:设置QSPI参数
    4 `) P0 f1 n8 r: ^) [
  21. // pTxBuffPtr,TxXferSize,TxXferCount:发送缓存指针 发送数据量 剩余数据量/ Y$ N4 b! V: @: [8 \$ ]
  22. // pRxBuffPtr,RxXferSize,RxXferCount:接收缓存指针 发送数据量 剩余数据量6 L5 Z6 e% h1 H' A( m7 g: a
  23. // hdma与DMA相关, 其他为过程变量不需要关心
复制代码
  1. // 参数配置 时钟分频系数  FIFO阈值  采样移位  FLASH大小  片选高电平时间  时钟模式  闪存ID  双闪存模式设置5 d& Z; q4 k, Z: H
  2. typedef struct% p0 h0 S2 o4 t3 r% w
  3. {9 C  I5 \& j' m4 t; [4 I
  4.   uint32_t ClockPrescaler;     /* Specifies the prescaler factor for generating clock based on the AHB clock.
    ( k  o) @5 W% r1 T% Y
  5.                                   This parameter can be a number between 0 and 255 */ / Z8 E" j$ `6 i+ O% _+ G. N" R
  6. ! p8 t& l" o+ P
  7.   uint32_t FifoThreshold;      /* Specifies the threshold number of bytes in the FIFO (used only in indirect mode)# v  K) ^! S. T. @, d& x# ~# O' `
  8.                                   This parameter can be a value between 1 and 32 */
    % w7 _5 i+ Z, ^
  9. . G, j" ^9 h; h. e0 N
  10.   uint32_t SampleShifting;     /* Specifies the Sample Shift. The data is sampled 1/2 clock cycle delay later to
    ' W6 j! s* f' [1 o& I# B' U, H
  11.                                   take in account external signal delays. (It should be QSPI_SAMPLE_SHIFTING_NONE in DDR mode): K/ v. H5 R5 e' U9 B2 N
  12.                                   This parameter can be a value of @ref QSPI_SampleShifting */7 k# w3 h( M  r, c% C
  13. & N. u1 |" o! k' f3 U
  14.   uint32_t FlashSize;          /* Specifies the Flash Size. FlashSize+1 is effectively the number of address bits
    1 I; [, `2 A9 j9 }3 s) g1 v
  15.                                   required to address the flash memory. The flash capacity can be up to 4GB
    5 ~" N, X+ i- a1 J- X" A
  16.                                   (addressed using 32 bits) in indirect mode, but the addressable space in % ?$ b6 P5 F& S
  17.                                   memory-mapped mode is limited to 256MB
    1 U7 o8 @& G# u4 {2 B( f# d) \; ]
  18.                                   This parameter can be a number between 0 and 31 */" a% b# x$ d( |( `
  19. 8 Y7 S7 R( C/ Z# _+ y6 r' J' \
  20.   uint32_t ChipSelectHighTime; /* Specifies the Chip Select High Time. ChipSelectHighTime+1 defines the minimum number & x! G7 Y" q) ^
  21.                                   of clock cycles which the chip select must remain high between commands./ l1 m) N5 A! q- `8 H* C
  22.                                   This parameter can be a value of @ref QSPI_ChipSelectHighTime */
    , Y$ ^+ e( p/ M6 D. a
  23. & L( B! q! |0 P+ @7 Z7 n
  24.   uint32_t ClockMode;          /* Specifies the Clock Mode. It indicates the level that clock takes between commands.' w9 T& N2 ^, @
  25.                                   This parameter can be a value of @ref QSPI_ClockMode */
      @( x3 m& w) i
  26.   ~4 ?. r5 a9 p) M$ W3 k
  27.   uint32_t FlashID;            /* Specifies the Flash which will be used,
    . b# W' t: Q6 Z
  28.                                   This parameter can be a value of @ref QSPI_Flash_Select */- L5 j+ h, b% |' w0 J: Z! t% g

  29. - I! F) z1 l4 _
  30.   uint32_t DualFlash;          /* Specifies the Dual Flash Mode State3 n; D' f. w% u& ^+ N5 O3 O. u
  31.                                   This parameter can be a value of @ref QSPI_DualFlash_Mode */                                               
    $ k' V+ z2 j/ M' r0 r" ^
  32. }QSPI_InitTypeDef;
复制代码
  1. // 采样移位  P) W) v" l2 w( i/ B
  2. #define QSPI_SAMPLE_SHIFTING_NONE           ((uint32_t)0x00000000U)        /*!<No clock cycle shift to sample data*/! b( B3 Y% b3 A& |* R/ m
  3. #define QSPI_SAMPLE_SHIFTING_HALFCYCLE      ((uint32_t)QUADSPI_CR_SSHIFT) /*!<1/2 clock cycle shift to sample data*/
复制代码

' {9 b- V" e& N8 a8 `+ O1 \四、QSPI接口设计(仅供参考)
2 h5 D! h! u! P1 c
  1. #define QSPI_CLK_ENABLE()         __HAL_RCC_QSPI_CLK_ENABLE();
    4 h+ y. H! L6 f3 F1 l: [. h) a
  2. 5 \+ H. s1 A$ {
  3. #define QSPI_BK1_NCS_PORT         GPIOB
    , \& J9 {0 K+ ]' p& M% X( V2 K/ y
  4. #define QSPI_BK1_NCS_PIN          GPIO_PIN_6
    9 [& i* }# k& s3 g. R5 h. h
  5. #define QSPI_BK1_NCS_AF           GPIO_AF10_QUADSPI
      V/ n$ t0 A# Q2 M
  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). I7 J+ }# |2 P; N
  7. 5 v# F4 o/ o& n8 {1 G
  8. #define QSPI_BK1_CLK_PORT         GPIOB, u, h1 j- t" _" a) M
  9. #define QSPI_BK1_CLK_PIN          GPIO_PIN_2$ j' k9 @/ ?0 x* r
  10. #define QSPI_BK1_CLK_AF           GPIO_AF9_QUADSPI
    / I3 o% f" U2 _$ S8 `0 d
  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). ]1 @) Q. j/ R+ R! n$ q, G9 F$ Y

  12. ( R2 E7 b3 N: O- C2 Q+ Z, }
  13. #define QSPI_BK1_IO0_PORT         GPIOF, U+ d% f7 T& O# _
  14. #define QSPI_BK1_IO0_PIN          GPIO_PIN_8
      r: u; F4 M" X1 B' o/ j8 Z
  15. #define QSPI_BK1_IO0_AF           GPIO_AF10_QUADSPI/ j. b( M5 C( ~/ {2 E
  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)& R2 {+ A% G7 J

  17. ' r1 J, [/ [" j4 G4 b  }' ^; y
  18. #define QSPI_BK1_IO1_PORT         GPIOF& [5 L$ ^1 ]3 I/ F5 v" i
  19. #define QSPI_BK1_IO1_PIN          GPIO_PIN_9+ ]# ^+ I( j9 j) S0 u4 A
  20. #define QSPI_BK1_IO1_AF           GPIO_AF10_QUADSPI% v% |/ w# y- {4 l. F
  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)
    $ c/ S( Y9 P! H5 j% D# i2 W
  22. % i; E0 p3 i) |# l2 l
  23. #define QSPI_BK1_IO2_PORT         GPIOF
    ! t  ~/ w+ p* Y  T& n' a
  24. #define QSPI_BK1_IO2_PIN          GPIO_PIN_74 }: t# R2 f0 z+ b* M1 |8 s
  25. #define QSPI_BK1_IO2_AF           GPIO_AF9_QUADSPI
      p. a1 w7 J+ j  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)7 i8 ]% {* B. a  Q) g& X* G
  27. 8 U' L- v+ Q# ~7 L$ j1 Q
  28. #define QSPI_BK1_IO3_PORT         GPIOF, X& e9 L6 p, ?7 K' c+ e
  29. #define QSPI_BK1_IO3_PIN          GPIO_PIN_6
    ( r2 d; a; u4 T' r8 u1 @  K
  30. #define QSPI_BK1_IO3_AF           GPIO_AF9_QUADSPI* V' z: w3 f& N: x
  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. // 封装几个必要的接口
    2 ]) D5 e  B6 {  T- t
  2. static QSPI_HandleTypeDef qspi_handle;
    4 ^" C2 \5 o  F6 n+ x  M

  3. ( ^$ }, E0 a0 p# M; v2 r# u% R' Q7 o
  4. static void qspi_gpio_init(void)
    0 j& w1 r) o" l# m% `  g
  5. {! o9 y: [( ?) g6 Z; X& G! X$ f
  6.   QSPI_CLK_ENABLE();
    6 j3 E3 u! U8 b! R1 o3 |/ f; p
  7. ( u4 V, C! N1 z3 N( l
  8.   QSPI_BK1_NCS_CONFIG();* ?2 \7 q5 ~5 |; O7 J
  9.   QSPI_BK1_CLK_CONFIG();
    9 g: i6 T( P4 i, @
  10.   QSPI_BK1_IO0_CONFIG();
    8 A, j7 L) m/ i$ u$ C8 X& e4 g
  11.   QSPI_BK1_IO1_CONFIG();
    ) O. Z1 s  L5 |" N- Y
  12.   QSPI_BK1_IO2_CONFIG();. d( A3 k: @$ }
  13.   QSPI_BK1_IO3_CONFIG();! D8 N) D2 t2 M0 Y1 m$ b# C- z
  14. }
    8 ^& ?5 |6 C, K" s. b% s. s

  15. : o0 s! v1 R: s) r4 I
  16. static void qspi_mode_init(void)
    " S( W' Z0 S7 ^0 H$ k% a+ e
  17. {( A' L9 H% u' x8 p5 L5 A$ T5 R
  18.   qspi_handle.Instance = QUADSPI;                        // QSPI
      s6 K# T& B5 }" i0 k( I" K
  19.   qspi_handle.Init.ClockPrescaler = 2;                   // QPSI分频比,W25Q256最大频率为104M 所以此处应该为2,QSPI频率就为216/(2+1)=72MHZ9 _' ]2 B4 _. E& G" v; @3 r. `- o
  20.   qspi_handle.Init.FifoThreshold = 4;                    // FIFO阈值为4个字节
      \: J7 A" a9 m; N+ O
  21.   qspi_handle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; // 采样移位半个周期(DDR模式下,必须设置为0)
    3 M" U/ K# m  x% b" r
  22.   qspi_handle.Init.FlashSize = POSITION_VAL(0X2000000) - 1; // SPI FLASH大小,W25Q256大小为32M字节+ c# L* B7 d  G8 Q6 V8 ^2 l4 p
  23.   qspi_handle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_4_CYCLE; // 片选高电平时间为4个时钟(13.8*4=55.2ns),即手册里面的tSHSL参数
    ! V, P/ \4 `' G
  24.   qspi_handle.Init.ClockMode = QSPI_CLOCK_MODE_0;        // 模式0
    # V, T, m1 x1 ?$ _; u9 I8 r3 ~5 C
  25.   qspi_handle.Init.FlashID = QSPI_FLASH_ID_1;            // 第一片flash& v2 T9 |' @  Z: R# ~
  26.   qspi_handle.Init.DualFlash = QSPI_DUALFLASH_DISABLE;   // 禁止双闪存模式
    # q6 l+ K+ `( p% v
  27.   HAL_QSPI_Init(&qspi_handle);      //QSPI初始化
    * `. P( N1 R7 H4 Q# G
  28. }
    4 w1 d9 i0 s4 g5 U( \) \& f

  29. 6 o7 N! w5 v' W) ?: |( V) g. K; C$ j+ R
  30. void QSPIInit(void)
    ) M. J! }1 F5 ?3 X! \
  31. {
    5 r5 H( y# h) K9 C
  32.   qspi_gpio_init();2 \  S; O' l) W- Z( ?; G
  33.   qspi_mode_init();
    7 [* N. L) h6 y" n2 n
  34. }8 I' J+ l8 E0 s6 i, j
  35. $ m0 k' n& l3 J# Y
  36. // QSPI发送命令) a! |7 \$ G% d& H3 E! o  `4 g
  37. // instruction:要发送的指令, V" `8 a+ w& P3 T
  38. // address:发送到的目的地址7 \6 q% e) G% j* b8 e& \3 g* {- |
  39. // dummyCycles:空指令周期数
    ) W! ?4 D1 `$ s+ j4 U
  40. // instructionMode:指令模式;QSPI_INSTRUCTION_NONE,QSPI_INSTRUCTION_1_LINE,QSPI_INSTRUCTION_2_LINE,QSPI_INSTRUCTION_4_LINE% \4 x% w  p$ P5 F3 q. |5 C% D% H
  41. // addressMode:地址模式; QSPI_ADDRESS_NONE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_2_LINE,QSPI_ADDRESS_4_LINE
    . D, J  I. N: u! s& S
  42. // addressSize:地址长度;QSPI_ADDRESS_8_BITS,QSPI_ADDRESS_16_BITS,QSPI_ADDRESS_24_BITS,QSPI_ADDRESS_32_BITS
    . f. {. x! X! s* A! f
  43. // dataMode:数据模式; QSPI_DATA_NONE,QSPI_DATA_1_LINE,QSPI_DATA_2_LINE,QSPI_DATA_4_LINE
    ; u0 l- H( y6 }7 `( V
  44. void QSPISendCMD(uint32_t instruction, uint32_t address, uint32_t dummyCycles, uint32_t instructionMode, uint32_t addressMode, uint32_t addressSize, uint32_t dataMode)
    & m5 H8 i( [8 W2 O) R; v0 C" y& a& a
  45. {
    ; g9 o: ]# H9 C8 M8 L& h9 J
  46.   QSPI_CommandTypeDef Cmdhandler;
    6 v* [0 w9 Y0 K3 B. c' i# q$ R8 U

  47. . {, [! u- N! R5 Q. s) _4 C+ }1 K
  48.   Cmdhandler.Instruction = instruction;           // 指令
    ) c% S7 w& b- }; ?- d& d
  49.   Cmdhandler.Address = address;                   // 地址
    6 t1 ~$ y  R4 C  [4 I. @& j
  50.   Cmdhandler.DummyCycles = dummyCycles;           // 设置空指令周期数6 J& U& {$ f% u9 Z) M4 T
  51.   Cmdhandler.InstructionMode = instructionMode;   // 指令模式0 @$ _* {+ P% d( h8 D3 h
  52.   Cmdhandler.AddressMode = addressMode;           // 地址模式
    ; Y/ P7 c* a; b+ }0 m  T/ }
  53.   Cmdhandler.AddressSize = addressSize;           // 地址长度1 Y% x# F0 Z& V& i- ^9 H! H0 t
  54.   Cmdhandler.DataMode = dataMode;                 // 数据模式. c. r7 \, f. l; o9 H0 a9 G  J
  55.   Cmdhandler.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 每次都发送指令, z! d: B1 ?4 Q4 J  q
  56.   Cmdhandler.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 无交替字节
      W& Q/ T/ p# N1 T! n
  57.   Cmdhandler.DdrMode = QSPI_DDR_MODE_DISABLE;           // 关闭DDR模式% L# ~- n5 u0 r
  58.   Cmdhandler.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    8 G/ G2 p3 r$ k$ J2 a! T
  59. + ]$ U3 q4 Z- G$ M% b* i4 K
  60.   HAL_QSPI_Command(&qspi_handle, &Cmdhandler, 5000);
    1 d' k* [' F$ s2 A  {, m; d- Q9 u
  61. }$ l! U& _/ @+ U( E- Y& b
  62. 5 E& R4 x' K) h: Y" L3 t  ]/ f. T; C
  63. // 接收指定长度数据
    4 o1 {  a( h& G4 R; g+ ?9 l
  64. uint8_t QSPIReceive(uint8_t *buffer, uint32_t length)
    4 f5 D0 N" s" S& F5 r9 Q
  65. {
    , N- P* u: |' K6 s& O
  66.   qspi_handle.Instance->DLR = length - 1;
    0 F6 u, f' Y4 [/ `& D& B* D
  67.         
    . B& k1 P& R! j2 ?2 k+ t* S
  68.   if(HAL_QSPI_Receive(&qspi_handle, buffer, 5000) == HAL_OK)
    " |, A  R6 x5 z  [8 T1 g- Z
  69.   {% a# s7 E! k: a+ f, a: A8 Z
  70.     return 0;  //接收数据! v9 r0 {. G+ B
  71.   }
    7 I9 Z4 f, |. ?
  72.   else
    : g! n: A# R, F2 g# f
  73.   {
    ' V& [2 ]9 [$ ^9 n1 o9 a
  74.     return 1;
    # F% ], T  o" k7 w
  75.   }
    5 ~  u5 P9 z/ n" D" [4 O) L' Q
  76. }
    , o8 A* _" h4 g( n

  77. 9 Q( P4 z, u5 h) p8 n; m  p% h
  78. // 发送指定长度数据
    / t: R8 Y; i' i1 V, U
  79. uint8_t QSPITransmit(uint8_t *buffer, uint32_t length)6 n+ R/ P5 a4 H8 p2 k, n2 H: l* A
  80. {
    ) F. B- [: ~& w  Q) ^. i3 I+ j
  81.   qspi_handle.Instance->DLR = length - 1;, w4 X+ g9 T2 W' ]7 c
  82.   if(HAL_QSPI_Transmit(&qspi_handle, buffer, 5000) == HAL_OK)7 \, {+ `0 w: S8 J5 z- k2 s
  83.   {
    2 ]! o) ^! J/ t5 \  {
  84.     return 0;  //发送数据, a) e# v# X" }0 i6 m" f( \! t0 f
  85.   }
    ' m* P7 @! {: Q. W6 R+ c
  86.   else
    # R1 ]8 d. N' K  f& o
  87.   {. d- j* ^; w9 B0 l8 S- k) w
  88.     return 1;
    ! X7 z9 |, W$ M( Q2 x3 }
  89.   }
    ; b% i9 g8 }* z0 A# ]) T* b
  90. }
复制代码
' l( [! J' T2 D- [9 g% [
五、QSPI驱动W25Q256
5 U8 ~- t/ }$ l5 Q" cW25Q256:32M  512块,每块16个扇区,每个扇区4K。具体指令看手册。封装了如下接口(初始化读写擦除等接口是以后移植文件系统的基础):$ V6 O; Z; q$ T0 _0 q8 i; }
' ?9 `2 g2 x# H0 M
  1. #define W25QXX_WRITE_ENABLE      0x06
    1 Q  s+ h# F9 }
  2. #define W25QXX_WRITE_DISABLE     0x04: {$ {' K3 v# m6 y! G! _  f
  3. #define W25QXX_READ_STATUS_REG1  0x05
    * q6 t7 G3 [0 n1 f; B/ A9 {
  4. #define W25QXX_READ_STATUS_REG2  0x35* a4 l- q/ r. \# i" \; E  _
  5. #define W25QXX_READ_STATUS_REG3  0x15
    ) S2 k7 a* }0 L* F  P6 U
  6. #define W25QXX_WRITE_STATUS_REG1 0x01
    & a8 i$ i5 C+ M8 [) z/ [! W9 y
  7. #define W25QXX_WRITE_STATUS_REG2 0x31
    $ x( U0 t( i- b7 i" ?
  8. #define W25QXX_WRITE_STATUS_REG3 0x11
    % G6 k- y9 }' e
  9. #define W25QXX_READ_DATA         0x03; V; D: }: O; O$ c6 s
  10. #define W25QXX_FAST_READ_DATA    0x0B; Y/ F" ~8 X( `+ m3 N
  11. #define W25QXX_FAST_READ_DUAL    0x3B+ L3 [& D, b* G; A5 C+ F
  12. #define W25QXX_PAGE_PROGRAM      0x02
    . V8 ], D5 N6 U8 ^% W
  13. #define W25QXX_BLOCK_ERASE       0xD8
    + l7 W4 V% d+ s5 `
  14. #define W25QXX_SECTOR_ERASE      0x20, v5 m! ^  H4 }3 M; y% B0 c
  15. #define W25QXX_CHIP_ERASE        0xC7
    ' W- x+ O- I% O% s/ W
  16. #define W25QXX_DEVICEID          0xAB
    ' C) H4 d% i; ]
  17. #define W25QXX_MANUFACT_DEVICEID 0x908 D. I& w- k: s2 r3 c* _/ N
  18. #define W25QXX_JEDEC_DEVICEID    0x9F' ]: N  U3 Q* p$ `7 o: m# ^$ P
  19. #define W25QXX_EABLE_4BYTE_ADDR  0xB7
    / T1 D$ R, w$ ?( Q
  20. #define W25QXX_EXIT_4BYTE_ADDR   0xE9
    % F4 ]+ b* _4 u5 A: D2 B: b
  21. #define W25QXX_SET_READ_PARAM    0xC0) ]" |* H3 J5 ], W' r2 T
  22. #define W25QXX_ENTER_QPIMODE     0x387 Z& }; G4 |" j9 {. y0 D+ z- n/ s
  23. #define W25QXX_EXIT_QPIMODE      0xFF
    % T4 ?1 e$ B% V3 y, ^, Y4 q' `/ Y" ?
  24. ' L' X; @' N. C
  25. uint8_t w25qxx_qpi_mode = 0; // QSPI模式标志:0,SPI模式;1,QPI模式.
    / o" n2 ?# Z5 V

  26. 3 N( j  E; S$ C9 o+ ]! C
  27. // 读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器0 W  `& b0 Z: {' K
  28. // 状态寄存器1:+ S0 S, a' H( v! o
  29. // BIT7  6   5   4   3   2   1   0" b7 \! ~, {1 z7 @  V
  30. // SPR   RV  TB BP2 BP1 BP0 WEL BUSY
    & w+ E+ Y3 _/ L/ d) n$ J, Y
  31. // SPR:默认0,状态寄存器保护位,配合WP使用. q9 t6 Y# ~: B& b$ Q1 |2 d
  32. // TB,BP2,BP1,BP0:FLASH区域写保护设置
    ' q; u! W& P! Z( I! r
  33. // WEL:写使能锁定& {+ O: z7 r7 ~) U; [& ]
  34. // BUSY:忙标记位(1,忙;0,空闲)
    : h6 l+ {2 ?; o( \* w7 s* a
  35. // 默认:0x007 a# \* X# O/ y& U. ^6 u( x) m( H
  36. // 状态寄存器2:
    " G  N$ v' a# Z5 j2 P; O
  37. // BIT7  6   5   4   3   2   1   0
    3 c, d% V4 x3 C; f7 ]8 u
  38. // SUS   CMP LB3 LB2 LB1 (R) QE  SRP1$ A/ Y3 t8 f) k9 m7 c, B; P
  39. // 状态寄存器3:- \8 s) g, v* |5 ^! r' h2 N* t; X
  40. // BIT7      6    5    4   3   2   1   03 E0 y) T6 ^" }1 h' f' m
  41. // HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
    ! T$ a" D( z5 W% h& [6 C
  42. // reg:状态寄存器号,范:1~36 z) x$ E2 c$ Y  w
  43. // 返回值:状态寄存器值* O  Z+ J4 K2 r( Y0 D9 e+ U
  44. static uint8_t w25qxx_read_status(uint8_t reg)
    4 c3 d* ~* `7 a+ h  Y& m1 ]
  45. {
    ; \  |, w9 G+ c3 ?% W  d, r
  46.   uint8_t value = 0, command = 0;; @. w1 b8 k& L2 M! {
  47. 7 A5 f4 k, D; w8 J: H2 O
  48.   switch(reg)
    % f( j- }# N* \9 T
  49.   {
    2 A; u. A; a- b' R5 Q8 E
  50.   case 1:
    . u7 ]8 k" g5 ^) v# d; M5 f
  51.     command = W25QXX_READ_STATUS_REG1;  // 读状态寄存器1指令
    5 Q- J- C4 u$ }' c" `% W
  52.     break;" n  G( M  {) M1 h
  53. ( h9 v( I! H: ]5 D& [7 V- g
  54.   case 2:+ a. E/ X" o9 z9 w/ z, c0 }- Q2 f
  55.     command = W25QXX_READ_STATUS_REG2;  // 读状态寄存器2指令
    # h$ }# e5 e3 c7 B2 ~
  56.     break;
    ) l% i* z! J4 W0 [3 s. H, g- f
  57. 6 ^6 B% ]8 ?/ o6 G9 {$ ^1 B
  58.   case 3:2 ^2 ~3 }/ e; x
  59.     command = W25QXX_READ_STATUS_REG3;  // 读状态寄存器3指令, [' L! T) G% w, E* o2 w( j9 ^
  60.     break;# d$ V( W% H) R6 Q1 z& ^
  61. ' w, n$ k! r2 J
  62.   default:* ]) i1 @0 \' d  y% M; e
  63.     command = W25QXX_READ_STATUS_REG1;2 }: r( @( R5 V8 Z3 B
  64.     break;
    : Y5 f) Z2 B! \! S
  65.   }
    9 ?- B- ^  D. V, a  z( |
  66. ; A; G1 A/ n; c7 X% y4 x
  67.   if(w25qxx_qpi_mode)
    3 A7 P% |- Z& n' v$ `6 B
  68.   {  A- y: ^7 @5 u& L; H
  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; \9 q. z' l& j: Y0 o2 F
  70.   }
    + O' o# A1 ^% M8 ^7 Y. y; N8 x
  71.   else
    . ~* ]+ c& m, }7 ^- s; m
  72.   {
    5 j% g3 O7 k7 J; L. h
  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个字节数据. }& I1 \& q$ Z7 ]" _
  74.   }
      W% h9 h" L0 T( J: S8 I- P& V

  75. 6 v3 O% b1 h/ ]4 v7 x
  76.   QSPIReceive(&value, 1);
      A* v0 W! o6 `

  77. 7 D( y% ?) t8 q' N9 V
  78.   return value;' m- t+ W7 I) {4 q: T/ s
  79. }! Y! {8 G! s2 Z( j$ s8 ]
  80. # d+ b  R5 Y& V( K2 p; f
  81. // 写状态寄存器1 C5 q9 i! ?0 s5 V& z% D
  82. static void w25qxx_write_status(uint8_t reg, uint8_t status)" z, [( P  u0 v  b9 }1 }# V
  83. {
    6 {  H- {. e! W: [3 C' ^6 r
  84.   uint8_t command = 0;1 m/ l1 g. }# w/ z2 d1 `9 t
  85. # [2 q/ }2 D2 Y8 i
  86.   switch(reg)  Z6 a8 M, ~0 r' o4 f( P) j0 U
  87.   {
    & p" H$ X; m. }6 a
  88.   case 1:
    3 X, h# Y% |1 G
  89.     command = W25QXX_WRITE_STATUS_REG1;  //写状态寄存器1指令, {$ M; [! x" V& \5 F
  90.     break;- P0 c% l2 U* ]
  91.   case 2:6 C: h$ x( o$ L; F7 j
  92.     command = W25QXX_WRITE_STATUS_REG2;  //写状态寄存器2指令, q. V! B. K+ F8 g$ o
  93.     break;+ X8 |6 o6 b2 q* b" ^0 I
  94.   case 3:
    ! y; Q; X* ^/ L: u" {, ]$ Y* }
  95.     command = W25QXX_WRITE_STATUS_REG3;  //写状态寄存器3指令
    ( o6 L: a6 \( R8 W2 U
  96.     break;# y# r3 X0 S6 K* c: F" h: G& n
  97.   default:0 O8 m! C: M6 N" m: o7 J' f
  98.     command = W25QXX_WRITE_STATUS_REG1;) @6 `1 f6 U4 T; _& F; V
  99.     break;
    $ {7 h, \8 m% h+ l+ v8 h0 X/ q
  100.   }
    & N' H) I3 n5 {3 V& N/ O
  101.   if(w25qxx_qpi_mode)4 V) x% J  ]* H  e  o4 `
  102.   {
    + p+ Z8 K/ Y. K. Z$ X# c
  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个字节数据
    5 ]7 T: @# W% ?' S
  104.   }3 B- m  o' t  e- ?1 B3 m
  105.   else
    ! r8 l' |! @" c8 R; e+ V
  106.   {
    8 m1 h4 _( _- Q! r+ B# s
  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个字节数据
    5 X' w( u$ f( X
  108.   }
    * V+ ~, V( k+ j8 z* o3 Z
  109.   QSPITransmit(&status, sizeof(status));
    2 y( G6 y3 o# o
  110. }
    * r) V; o: _; D# x  H5 v3 V
  111. ; {; h/ j( @  h. E
  112. // 写使能  将S1寄存器的WEL置位
    ) {6 H1 N! R( M, b3 k
  113. static void w25qxx_write_enable(void)
    * U( b& G: i4 k: \- |
  114. {3 x/ Q( F1 T  t. j+ p) V# [
  115.   if(w25qxx_qpi_mode)
    ; x4 K* l4 o6 J
  116.   {+ @% r1 J' K) s. K+ }, ], ?0 M
  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个字节数据
    ! J; I- ?0 _0 v7 }7 j" d* T& N
  118.   }5 z8 G. U1 E% u& S3 t
  119.   else
    9 r0 U$ h  B! X- Q- x1 q
  120.   {
    3 n- F. o5 F  t; M
  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个字节数据
    % u2 T8 Q& j' t. A- w5 f! \
  122.   }2 \% {% F3 O* X* y7 p
  123. }1 H3 A$ [1 i! Y* F1 Z

  124. 3 n: b, U5 R3 N1 O8 ]& l# j5 Y" l+ q
  125. // 写失能  将WEL清零
    & P) ^5 }+ d  s! x' k( N) d2 z) s0 h0 A
  126. void W25QXX_Write_Disable(void)
    0 ?8 M+ Q# J5 B+ c2 q8 F
  127. {7 e. s1 m* i3 F8 v1 \6 F+ g
  128.   if(w25qxx_qpi_mode)" L& @* ]" p. A( a: v" \6 s
  129.   {! f, ?& g# P% O6 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个字节数据
    & k& S' X& B' S* ?( l% R. }
  131.   }; ]" ?7 Q+ }2 A' D/ p* H1 _/ `9 Z
  132.   else' j  m2 Q# x( J
  133.   {
    1 p+ R' M; R5 |' O; T1 u9 F
  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 ?5 x. t5 k2 D8 P
  135.   }
      O2 \/ D2 ^1 O7 `, w
  136. }
    4 z9 Q4 U7 |! }' i% {% M) N. ?
  137. 3 J2 F9 R2 [! U7 ^& g
  138. // 等待空闲$ U. V3 {- y0 K" s) {
  139. void w25qxx_wait_busy(void)
    ! C1 q- F$ Q5 r
  140. {- B/ W3 K& r: a' u
  141.   while((w25qxx_read_status(1) & 0x01) == 0x01); // 等待BUSY位清空
      {1 i* |" M2 v& U1 @! P
  142. }
    1 l3 N% [4 k* [/ u/ v

  143. 2 [4 ~$ }# R, F1 F
  144. // W25QXX进入QSPI模式8 i! D6 z5 s5 W
  145. static void w25qxx_qspi_init(void)
      H3 `) {9 C- h; P9 [4 ^$ k
  146. {
    - q: H3 n( e' _$ l
  147.   uint8_t reg2;
    2 ~4 }% [. }! ]8 }
  148.   reg2 = w25qxx_read_status(2); // 先读出状态寄存器2的原始值
    % B) G5 W; p/ p. z# t6 Q
  149.   if((reg2 & 0X02) == 0)  // QE位未使能
    " l1 ^, E( v( t* G( O# ]; K
  150.   {3 O' a5 |/ q6 ]) A2 z% n
  151.     w25qxx_write_enable(); // 写使能
    , m) W' r( y; B8 I" C$ z) V
  152.     reg2 |= 1 << 1; // 使能QE位
    ' C% z$ b+ X  [+ `
  153.     w25qxx_write_status(2, reg2); // 写状态寄存器2  T! v" x- `  @+ S" o
  154.   }
    - e1 a8 v5 h+ _3 `$ \' `
  155. 0 R& I& A; Z$ V9 H* U9 U: e
  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个字节数据, S% H$ ?  I% v' }

  157. 9 J& x3 j, k) K. l! R
  158.   w25qxx_qpi_mode = 1; // 标记QSPI模式  c! J  i/ s# l8 }
  159. }
    # d4 g* }: w% V# b3 e8 C

  160. 0 B. n1 q: x7 G6 v% y% O) ]
  161. // 0XEF13,表示芯片型号为W25Q80& t0 k- v9 }; p
  162. // 0XEF14,表示芯片型号为W25Q164 l% G/ N( L* j8 x+ K6 O& O; h
  163. // 0XEF15,表示芯片型号为W25Q32+ g2 N/ Z# `. B4 Y0 r+ x' w1 i& a
  164. // 0XEF16,表示芯片型号为W25Q64
    3 i( y% i& o+ y
  165. // 0XEF17,表示芯片型号为W25Q128
    , g; S) j* y$ G8 w
  166. // 0XEF18,表示芯片型号为W25Q256
    ( l' e( t) b* s: Y+ T' x- L4 n
  167. static void w25qxx_id_get(void)
    * G/ C9 N) X9 @% z
  168. {4 [5 q# }9 d7 s' ^- [
  169.   uint8_t temp[2];
    ; V9 G0 A# o% T
  170.   uint16_t device_id;; a" `& O& h0 @+ R3 y) e

  171. * \% l9 @; `% C# p: S* R
  172.   if(w25qxx_qpi_mode)
    ; `/ J1 f+ o) z' j7 g* X% ~
  173.   {
    . \% B  U5 D4 c
  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个字节数据
    " u0 i- Y; L  R
  175.   }
    9 x+ o' E8 A  B
  176.   else4 r5 t( K. S  j! e9 t8 b- \8 i
  177.   {
    ! y' G. x5 b, F& g5 j
  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个字节数据
    & E6 N+ v1 I" w, p
  179.   }
    ' G9 o: z+ i7 \5 h+ Q0 u7 @$ ]
  180. - h' a6 W/ ^8 \- {1 G$ t
  181.   QSPIReceive(temp, 2);
    7 [- l8 M. u: U) P

  182. - h  n. @" F- @; j! |: S: s# k
  183.   device_id = (temp[0] << 8) | temp[1];
    $ u% ]( E0 }- \7 A: A7 K- R
  184. % l0 T+ K7 ]' _: ^
  185.   printf("QSPI Flash Device ID: %X\r\n", device_id);
    4 K" P$ b8 E4 V4 U% X
  186. }. d. I7 I: y" w- X  O4 D

  187. % a( y" b! Z; S! \' x$ m
  188. void W25QXXInit(void)
    , }% [7 r$ o" B, Y+ \. ~
  189. {
    7 q9 Z. k5 t2 J, @& D
  190.   uint8_t temp;& z) x8 S( [- [7 c
  191. ( N) n: D5 q" w) O. T
  192.   QSPIInit();
    2 O/ ~+ C# X2 s, z' W; [
  193.   w25qxx_qspi_init(); // 使能QSPI模式
    2 C9 P+ `/ w- e4 C4 P) E+ j
  194.   w25qxx_id_get();    // 读取FLASH ID.
    * e& p+ P: W( Z+ ?4 S

  195. " O+ b- y% v, [
  196.   temp = w25qxx_read_status(3); // 读取状态寄存器3,判断地址模式
    . Z/ h1 N$ v! h/ U
  197.   if((temp & 0X01) == 0) // 如果不是4字节地址模式,则进入4字节地址模式
    * Q2 K6 }- A; Q/ u. a1 K- a
  198.   {
    ) D- F4 [8 E+ |( I& K
  199.     w25qxx_write_enable(); // 写使能" {% ~8 Q8 O4 h0 ^
  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个字节数据
    $ |6 m5 N3 K- V
  201.   }! p9 _. _. b$ v7 _* r
  202.   w25qxx_write_enable(); // 写使能# y6 ~; Y4 s. U+ t# T
  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个字节数据
    8 K& b$ t7 o' n- W! M  n
  204.   temp = 3 << 4;      // 设置P4&P5=11,8个dummy clocks,104M* {" u1 a5 ^- l5 i. G# p7 x- d% @
  205.   QSPITransmit(&temp, 1);; \2 R6 V& w& E: }* s
  206. }
    + q+ x( f4 j8 \3 m8 h
  207. : T( h, S+ C& K: W6 x
  208. // 擦除一块4096字节 最少需要150ms% c* \+ K1 H6 t( G, w
  209. void W25QXXSectorErase(uint32_t addr)
    6 W/ p  _; w$ B5 f. E
  210. {
    7 V. j% O# W8 q, e
  211.   //addr /= 4096;' Q" i/ f9 M+ j/ h1 ^6 G+ U$ R- n
  212.   addr *= 4096;
    ) g" U3 ^  v9 ~( R$ [: X9 E  U" C
  213. 1 A* Q/ H) d. g( n/ P' D+ |, \  A
  214.   w25qxx_write_enable();1 T- }. o& o, I# |  l* I0 N/ M+ M
  215.   w25qxx_wait_busy();
    6 C6 M8 R( f3 `* g
  216.   QSPISendCMD(W25QXX_SECTOR_ERASE, addr, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_32_BITS, QSPI_DATA_NONE); // QPI,写扇区擦除指令,地址为0,无数据_32位地址_4线传输地址_4线传输指令,无空周期,0个字节数据$ E( S9 O* D  {/ }- g2 d: `/ S& u
  217.   w25qxx_wait_busy();
    3 p3 N, _  H4 I1 F: l
  218. }  J& [, u$ s2 ^; v# W7 z# }
  219. " a5 F+ z: m/ l+ f
  220. ! f. e! U2 [& p2 R+ b/ y* T1 \
  221. // 擦除整个芯片! r6 q- b% R$ M% z% w) [+ u3 d
  222. void W25QXXChipErase(void)2 P1 A' T; E& |6 H0 A
  223. {
    * w9 c7 ?: {; T) l% A$ P! q9 ]
  224.   w25qxx_write_enable();          //SET WEL8 K$ b7 A2 a" s+ f' t3 E
  225.   w25qxx_wait_busy();% Z& ~8 _4 Y3 a+ M- F' m
  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个字节数据0 L. U- O( x" v$ ]+ b
  227.   w25qxx_wait_busy();           //等待芯片擦除结束! _8 u  g5 t& H8 m
  228. }
    5 X* n, G9 }+ j/ F8 u' G- U
  229. 5 b) h4 T5 I  `* |
  230. // 写最多256字节9 T: }/ r( }8 {
  231. void W25QXXWritePage(uint32_t addr, uint8_t *buffer, uint16_t length)
    4 ]5 ?8 s: c" w0 I
  232. {! y. `% q. x$ a* c- A
  233.   w25qxx_write_enable();          //写使能
    + w% i  z9 y: q% {. C3 ~
  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个数据0 x7 A. v. {& l& D5 H7 J& y& X
  235.   QSPITransmit(buffer, length);+ c% a+ X2 G1 ^7 B7 N' i* L
  236.   w25qxx_wait_busy();            //等待写入结束
    8 @+ n4 e% a( D* ~5 y
  237. }
    ) N0 e3 ]4 h' ]) s( m9 _

  238. ; r, R2 n' I4 Y  {1 }0 ]; P0 j
  239. // 写最多65536字节 无擦除动作+ l' k5 h6 q+ D/ l) U, ]
  240. void W25QXXWriteExt(uint32_t addr, uint8_t *buffer, uint16_t length)# O+ X  n9 m6 X- ~  N: C! n
  241. {
    ! o2 o5 g5 R3 @
  242.   uint16_t pageremain = 256 - addr % 256; // 单页剩余的字节数/ D  y7 x8 n7 ^8 ?
  243. 4 y9 ?# y2 a& e$ Q
  244.   if(addr + length - 1 > (32 * 1024 * 1024))1 ~3 {* x8 }3 _6 }& j9 y3 R0 Z/ z
  245.   {
    3 G) M. s& p& A7 ?* w
  246.     return;
    ! g2 j/ y& R$ d, n7 p9 E0 O; X
  247.   }" c3 E8 a! v$ d# `
  248. # Z( `! r3 j% L& G
  249.   if(length <= pageremain)4 k& k/ J* {  i
  250.   {
    + l/ i  F% I: Z8 \0 P
  251.     pageremain = length;  // 不大于256个字节8 t! ^! U( a; {5 Y; ^6 h& D5 b
  252.   }% u# ^) c6 [% a4 K" D$ C
  253.   while(1)2 v3 N4 U: T0 K. N2 X9 t' V5 N
  254.   {" W/ _. k& v3 X7 s
  255.     // 分页写入0 |, y$ s$ E9 d% T2 Q+ y
  256.     W25QXXWritePage(addr, buffer, pageremain);
    4 G2 q4 a* W; m
  257. + _! A- o" M( g" o2 U5 x9 r! ?
  258.     if(length == pageremain)
    - i4 b! Z7 b$ j3 E+ N+ X+ X; d
  259.     {
    : h# b) k- O* c0 z2 C% g/ b
  260.       break;  //写入结束了* K# \# P& Q7 p! a8 P( M
  261.     }
    ; D* ?+ r! {+ `8 x
  262.     else* v$ c% i; {" |, T7 b  Y7 U4 C2 u( @
  263.     {
    & X* M, q/ a4 F9 d3 ]+ u3 O. w
  264.       buffer += pageremain;! r8 g7 T% Y6 m
  265.       addr += pageremain;
    * k, b" ]8 |% [
  266. 4 E' Z7 l# b3 E- o# P! c5 W
  267.       length -= pageremain; // 减去已经写入了的字节数. I; M& ^5 T, v% |
  268.       if(length > 256)
    ; n, Y& [+ |! y5 ]5 c4 `
  269.       {
    - B  g! Q$ I" B2 f. R3 e( Y
  270.         pageremain = 256;  // 一次可以写入256个字节& z* n/ {2 W2 u# U
  271.       }
    * h1 m% a5 ]" S4 A" z/ A5 E; D
  272.       else2 P3 J' h4 u, I4 L+ M; Y! ^3 K
  273.       {: m. `! H2 k$ D0 D  {, @: i
  274.         pageremain = length;  // 不够256个字节了
    & C2 F) T' [# U& y" P
  275.       }
    " K: @+ ^' P) \% k3 e8 _, W
  276.     }
      L9 I$ r4 k5 {3 F: g* g4 {: {  W
  277.   }
    0 S* }4 d( A; K6 n9 i5 F
  278. }
    " l: d- l5 l/ m" d' E$ N

  279. 6 s) c& w5 g) v: o
  280. // 写最多65536字节 带擦除动作
    7 p! L/ d1 q  I: G, s6 t
  281. static uint8_t W25QXX_BUFFER[4096];* K1 }% A' L6 q; J" E( I" ?
  282. void W25QXXWrite(uint32_t addr, uint8_t* buffer, uint16_t length)9 x% e' Y: h' H0 E0 {6 `
  283. {0 D% R% z$ z1 P8 W, I3 q
  284.   uint32_t secpos;
    : `' W' s8 J( o& k' P* V1 ?
  285.   uint16_t secoff, secremain, i;3 ^; j9 M4 e. f! L& j
  286.   uint8_t * w25q_buf;
    8 J7 A: D' c- w8 k. ?# q9 V4 o5 g
  287. : X% T& j2 S" r' s2 U% x
  288.   w25q_buf = W25QXX_BUFFER;/ j( y. [( ]4 s1 z( i. D
  289. ! |: T7 Y( W7 m/ U% ^) n# j
  290.   secpos = addr / 4096; // 扇区地址  @$ ~. f+ y! O; h2 W, [
  291.   secoff = addr % 4096; // 在扇区内的偏移( K$ A( m. Z4 L- O
  292.   secremain = 4096 - secoff; // 扇区剩余空间大小( ?  a( ^5 j# X( k
  293. " v. p) j2 M' F. t' ^4 M
  294.   if(length <= secremain)! h: b7 z% @& h5 ~; F0 y2 I# g
  295.   {6 O, j6 q+ a& j, x, U9 d3 P" k' e7 p
  296.     secremain = length;  // 不大于4096个字节" S, t$ s2 v  W$ y$ v
  297.   }
    # i6 p5 P9 O0 d/ x& q
  298.   while(1)5 Z( \; X6 c! Y* e) v# f/ S; o
  299.   {7 u1 @4 \. q7 ^2 w
  300.           WatchdogFeed();& T5 c" g. [5 K: X
  301.     W25QXXRead(secpos * 4096, w25q_buf, 4096); // 读出整个扇区的内容
    - w# _& `, X  q# u, C
  302.     for(i = 0; i < secremain; ++i)- g4 d7 M5 e5 h7 q) ^8 ~6 _
  303.     {+ H. |$ G% Y& b2 ^: E) ^; U
  304.       if(w25q_buf[secoff + i] != 0XFF)
    % F- y0 A9 v" @
  305.       {
    4 }" |! @, G$ ^- X7 t7 s
  306.         break;  // 需要擦除1 S. W8 T$ G" ^( e* G
  307.       }* }  y- T9 V" }' H
  308.     }) J' P; x( ^2 z% J9 @
  309.     if(i < secremain) // 需要擦除
    2 A! n+ }- S' n3 A# w* `$ n# G
  310.     {; s) P4 C3 e; w6 j
  311.       W25QXXSectorErase(secpos); // 擦除这个扇区6 U' r" K# a0 |
  312.       for(i = 0; i < secremain; ++i)7 X4 A* V8 N8 I1 @, T& [
  313.       {
    # B9 f& F6 `8 v. o
  314.         w25q_buf[i + secoff] = buffer<i>;</i>1 W! S4 l3 p* c$ ]1 ?& X
  315.       }1 D! R' G+ X, j% T
  316.       W25QXXWriteExt(secpos * 4096, w25q_buf, 4096); // 写入整个扇区/ S$ L8 a. c4 e: n. p
  317. 9 S+ K; H$ Z- q  g% V" o4 i$ N
  318.     }
    $ A, D  y$ O6 i7 B1 w5 V/ b; Y
  319.     else
    0 a! e- `, V+ L2 H- ^% s7 ], d) r- V3 j
  320.     {
    5 m' i* }. g6 I3 x( ^
  321.       W25QXXWriteExt(addr, buffer, secremain);  // 写已经擦除了的,直接写入扇区剩余区间.4 c% E% P/ P6 |; j" Y$ g( v) I  u. I  P
  322.     }" J6 e, H- K7 p/ Z/ [+ W
  323.     if(length == secremain)
    9 B; ]* G1 S/ s, |+ k7 o( C
  324.     {2 F! J2 b9 n4 _4 j( W
  325.       break;
    ( P% D" |8 e. w6 i4 o
  326.     }7 ~: ]" q) D. Z5 h6 |4 o4 K
  327.     else
    8 S3 |/ \: p' i& M
  328.     {
    & }' @6 [& D* _( z' L3 {
  329.       secpos++;% b5 M! ?) M& P3 g( ^
  330.       secoff = 0;
    3 i" d+ _, k  G! ?

  331. $ h1 e! l0 x  `# G$ o
  332.       buffer += secremain;
    ; k; C' G9 y4 h: T8 ~% t, K, i( c* l: C
  333.       addr += secremain;
    ; P1 f5 M0 v/ @" X. F  |
  334.       length -= secremain;; e* J. U9 e; [" R$ K- r
  335.       if(length > 4096)
    9 X( I2 P! X: \; M
  336.       {
    # A2 X3 w/ W( I; i; m
  337.         secremain = 4096;+ H/ ~6 H" a7 Q7 E- h" V
  338.       }
    0 i8 h# S8 n( v5 h$ ^( t7 D
  339.       else+ [; ^7 f/ r% I/ [9 ?
  340.       {
    , X; U# K- p+ b! i: U. Q
  341.         secremain = length;: h' u5 J3 V$ ~5 Y
  342.       }
    , A  I! |6 V3 [9 b9 u
  343.     }
    + U) j5 w" Y7 b' B( q% _9 T
  344.   };
    8 n9 H" g: W* F' F! m* m
  345. }
    & l$ K; t' ?( c) C4 y

  346. + B: g# Y0 H1 L5 i8 D
  347. ; k' P* u2 j/ v: F5 Y* z
  348. // 读取SPI FLASH,仅支持QPI模式  在指定地址开始读取指定长度的数据 65536
    $ w- @$ P$ J- Y# V; P  X9 B
  349. void W25QXXRead(uint32_t addr, uint8_t *buffer, uint16_t length)8 C0 x/ _$ ]& M/ F/ N
  350. {
    5 Q& @, ]( x+ g; }5 r3 O
  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个数据
    7 g; W% e% J7 {5 {& w
  352.   QSPIReceive(buffer, length);7 {5 _; w$ G& t! ~( f& }1 ?
  353. }
复制代码

! Y% @: k' a( @, D% l也可以使用SPI操作W25QXX。( C& a1 x, b- l
0 x$ P* X. }. i

- ~; i2 k. R* V" Z& k2 u2 _3 a9 E/ }
收藏 评论0 发布时间:2021-12-11 12:00

举报

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