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

【经验分享】STM32F7xx —— QSPI

[复制链接]
STMCU小助手 发布时间:2021-12-11 12:00
一、QSPI
0 |4 Z: u  N1 F4 p9 t        SPI 是 Queued SPI 的简写,是 Motorola公司推出的 SPI 接口的扩展,比 SPI 应用更加广泛。在 SPI 协议的基础上,Motorola 公司对其功能进行了增强,增加了队列传输机制,推出了队列串行外围接口协议(即 QSPI 协议),使用该接口,用户可以一次性传输包含多达16个8位或16位数据的传输队列。一旦传输启动,直到传输结束,都不需要CPU干预,极大的提高了传输效率。该协议在ColdFire系列MCU得到广泛应用。与SPI相比,QSPI的最大结构特点是以80字节的RAM代替了SPI的发送和接收寄存器。QSPI 是一种专用的通信接口,连接单、双或四(条数据线) SPI Flash 存储介质。
% \; ?" Z& a3 L4 U$ c
8 T$ g. j; c* Z+ v$ O 该接口可以在以下三种模式下工作:
- ~" D1 R1 k. |* X! V① 间接模式:使用 QSPI 寄存器执行全部操作
& ?. F0 ]* D+ F# R* H8 R: p② 状态轮询模式:周期性读取外部 Flash 状态寄存器,而且标志位置 1 时会产生中断(如擦除或烧写完成,会产生中断)
* _. K* p  s) k" ]9 {③ 内存映射模式:外部 Flash 映射到微控制器地址空间,从而系统将其视作内部存储器。/ M& S2 b" ]8 O9 _+ {
6 B  Y' I0 G9 @
QSPI通过6根线与SPI芯片通信,下图是内部框图:
6 Y# i% m9 L. B& n) E
5 k/ }0 i; p/ _
20190123173205949.png
) d/ y/ I9 Z9 z: K5 d" T9 K

0 _, W- }1 Z; ~: IQSPI每条命令,必须包含一个或多个阶段:指令、地址、交替字节、空指令和数据。% I- [! C1 B$ d. i) R3 n

" [& I3 r, U1 N6 {% gQSPI发送命令:等待QSPI空闲;设置命令参数。
5 p6 H3 y2 b* G) x5 {5 M+ ~8 w7 x  F7 y1 q# P- ~5 ^- W( @
QSPI读数据:设置数据传输长度;设置QSPI工作模式并设置地址;读取数据。6 ~" b8 L/ ]" L( I3 ]

  G1 E1 c( N" C4 [+ F  x9 s. vQSPI写数据:设置数据传输长度;设置QSPI工作模式并设置地址;写数据。  V6 s5 V# P( ?. y) P  b. P

! p4 x( \0 e) L8 s" A" [: W
$ E+ T1 b, v& t0 ]0 z: ~  T1 z二、几个重要的函数9 I9 ~2 l5 y1 B. N
  1. HAL_StatusTypeDef     HAL_QSPI_Init     (QSPI_HandleTypeDef *hqspi); // 初始化
    . c& [' m; O9 I( P

  2. ! @! q% N' r- m
  3. HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout); // 发送命令* ?2 z8 J7 s. v; Y

  4. 8 t( O8 S9 p, @6 E! ]
  5. HAL_StatusTypeDef     HAL_QSPI_Transmit     (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 发送数据
    6 `, e) ^2 S9 ?. P
  6. ( B+ _  X) {& G+ j; q/ s
  7. HAL_StatusTypeDef     HAL_QSPI_Receive      (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 接收数据
复制代码
/ M6 z2 [* B) @  ^( k! T& x* ?' ~
三、几个重要的结构/ {4 g5 L4 o0 h
  1. // QSPI操作句柄& ]; H- F, ^2 U) B1 r
  2. typedef struct
    2 T; c# E" G0 N9 q
  3. {" a8 I, j4 a; w- `1 K0 ?8 V
  4.   QUADSPI_TypeDef            *Instance;        /* QSPI registers base address        */
      r% s; e/ B# G5 O8 _
  5.   QSPI_InitTypeDef           Init;             /* QSPI communication parameters      */: H( P3 t$ [: t  \$ y& O
  6.   uint8_t                    *pTxBuffPtr;      /* Pointer to QSPI Tx transfer Buffer */
    * h- h$ D4 M) n, A* J2 j
  7.   __IO uint16_t              TxXferSize;       /* QSPI Tx Transfer size              */1 }9 o' s' O5 v" ~, Q
  8.   __IO uint16_t              TxXferCount;      /* QSPI Tx Transfer Counter           */" d3 B5 _+ F# p
  9.   uint8_t                    *pRxBuffPtr;      /* Pointer to QSPI Rx transfer Buffer */3 S. H' R- j: |- }( s. l3 a
  10.   __IO uint16_t              RxXferSize;       /* QSPI Rx Transfer size              */
    ( h( o/ a2 Z; B, j! k" e) w- C, E
  11.   __IO uint16_t              RxXferCount;      /* QSPI Rx Transfer Counter           */
    - b+ B( a8 m  M% g$ g! n, h/ Y4 }
  12.   DMA_HandleTypeDef          *hdma;            /* QSPI Rx/Tx DMA Handle parameters   */+ X! n5 R4 G8 O0 c/ {1 s9 C
  13.   __IO HAL_LockTypeDef       Lock;             /* Locking object                     */  ]$ a' I; ]% a- a( ^" `
  14.   __IO HAL_QSPI_StateTypeDef State;            /* QSPI communication state           */
    . |' v" e9 A5 c
  15.   __IO uint32_t              ErrorCode;        /* QSPI Error code                    */2 d+ j: d( j/ P4 V7 G2 v
  16.   uint32_t                   Timeout;          /* Timeout for the QSPI memory access */
    , i! I$ u4 N6 a. B" T0 y" w1 n& W) R( i
  17. }QSPI_HandleTypeDef;
    : x3 s  U( D+ M/ T/ D, h* L2 x+ ?( t  j
  18. * R6 G( e' f- m1 o. ?
  19. // Instance:QSPI基地址  ---  QUADSPI# g& |  w/ w  V6 w2 E! b
  20. // Init:设置QSPI参数
    2 T  s9 N- A# A
  21. // pTxBuffPtr,TxXferSize,TxXferCount:发送缓存指针 发送数据量 剩余数据量
    % |* V3 `# D1 I6 r. Y: J
  22. // pRxBuffPtr,RxXferSize,RxXferCount:接收缓存指针 发送数据量 剩余数据量
    8 e) `6 ?0 Q) O8 }2 v8 o! k; {
  23. // hdma与DMA相关, 其他为过程变量不需要关心
复制代码
  1. // 参数配置 时钟分频系数  FIFO阈值  采样移位  FLASH大小  片选高电平时间  时钟模式  闪存ID  双闪存模式设置
    & b1 Q/ X; ?& r7 Y+ S
  2. typedef struct( R: T2 k8 }4 Y8 L4 q
  3. {) U! H' @' l$ ~2 U- D
  4.   uint32_t ClockPrescaler;     /* Specifies the prescaler factor for generating clock based on the AHB clock.1 B0 \' a) _  ?1 w" I* w" J
  5.                                   This parameter can be a number between 0 and 255 */
    ' z3 Y/ a( n8 F- i6 N+ H9 t2 y) ?: D

  6. % I+ t" x' u7 B" E5 u8 m
  7.   uint32_t FifoThreshold;      /* Specifies the threshold number of bytes in the FIFO (used only in indirect mode)+ @6 `' M7 i+ ~2 b
  8.                                   This parameter can be a value between 1 and 32 */' P4 C6 h3 _2 }' C& p& F& V  e! i

  9. ) P( t8 H& ?- L7 Q7 |
  10.   uint32_t SampleShifting;     /* Specifies the Sample Shift. The data is sampled 1/2 clock cycle delay later to   u0 R$ R! H! o, W6 p4 H' `
  11.                                   take in account external signal delays. (It should be QSPI_SAMPLE_SHIFTING_NONE in DDR mode)
    9 {9 K1 ^) l" a. M
  12.                                   This parameter can be a value of @ref QSPI_SampleShifting */( g: F7 |9 I: e5 h) S/ j
  13. . B- g! T6 e, o& p$ N1 c( U
  14.   uint32_t FlashSize;          /* Specifies the Flash Size. FlashSize+1 is effectively the number of address bits ( ?( @) ~" j$ x# f; s
  15.                                   required to address the flash memory. The flash capacity can be up to 4GB # l" p( w6 o  G
  16.                                   (addressed using 32 bits) in indirect mode, but the addressable space in
    6 B# s  b$ P# z" {9 A' t4 d/ R0 X" i
  17.                                   memory-mapped mode is limited to 256MB( i; e) C: a2 h7 E' r9 X1 v5 Z
  18.                                   This parameter can be a number between 0 and 31 */; @' [5 d/ W4 t' H  j: F$ O( R6 ]
  19. 3 `1 h  U! n& v% k# [! q) n" K
  20.   uint32_t ChipSelectHighTime; /* Specifies the Chip Select High Time. ChipSelectHighTime+1 defines the minimum number * z; y% X$ w; v! j1 r! A7 y
  21.                                   of clock cycles which the chip select must remain high between commands.
    1 ~6 X3 F) L4 ^" g+ N9 Y- F
  22.                                   This parameter can be a value of @ref QSPI_ChipSelectHighTime */ - \$ D- F8 F  y; p8 G5 s

  23. ; A8 O# w" f3 h) r' s# P
  24.   uint32_t ClockMode;          /* Specifies the Clock Mode. It indicates the level that clock takes between commands.; W9 a1 ?$ m" ~" {. g+ |* w0 |
  25.                                   This parameter can be a value of @ref QSPI_ClockMode */
    " ]& Y* q+ ^# l6 N4 ^
  26.   r- O8 G' i% P5 O1 _+ P
  27.   uint32_t FlashID;            /* Specifies the Flash which will be used,* Z0 k( {: O! a, \
  28.                                   This parameter can be a value of @ref QSPI_Flash_Select */
    2 n: P" b3 Z; a( C' u

  29. 3 X$ n0 ], [1 m
  30.   uint32_t DualFlash;          /* Specifies the Dual Flash Mode State4 {7 s" J( X# F4 f" C7 j$ b3 ~: q
  31.                                   This parameter can be a value of @ref QSPI_DualFlash_Mode */                                               $ w5 \, n/ h' ~/ @7 S7 ~
  32. }QSPI_InitTypeDef;
复制代码
  1. // 采样移位9 ^& ~; N4 L, ?8 V* x  v: T% q5 d
  2. #define QSPI_SAMPLE_SHIFTING_NONE           ((uint32_t)0x00000000U)        /*!<No clock cycle shift to sample data*/
    4 V) s, ~* D% `6 F' @% B
  3. #define QSPI_SAMPLE_SHIFTING_HALFCYCLE      ((uint32_t)QUADSPI_CR_SSHIFT) /*!<1/2 clock cycle shift to sample data*/
复制代码
) [( T2 A! }; F, h4 _* r
四、QSPI接口设计(仅供参考)

" V4 H/ `4 d; T5 m! w3 Z
  1. #define QSPI_CLK_ENABLE()         __HAL_RCC_QSPI_CLK_ENABLE();( F% Z7 }7 B2 t4 F: D
  2. 8 ^: [# v/ p) L; I
  3. #define QSPI_BK1_NCS_PORT         GPIOB% E: C  H+ [, R" j' b( g
  4. #define QSPI_BK1_NCS_PIN          GPIO_PIN_6
    1 y- r1 s' V0 x, C& C
  5. #define QSPI_BK1_NCS_AF           GPIO_AF10_QUADSPI
    4 M: C0 i( F6 I" _' r
  6. #define QSPI_BK1_NCS_CONFIG()     GPIOConfigExt(QSPI_BK1_NCS_PORT, QSPI_BK1_NCS_PIN, GPIO_MODE_AF_PP, GPIO_PULLUP, QSPI_BK1_NCS_AF)
    + A5 r3 e4 Q- V  y# U
  7. 3 C/ |( q. i/ k5 \, a, d* q
  8. #define QSPI_BK1_CLK_PORT         GPIOB! z- K5 M6 [5 \5 g1 Z) k4 C
  9. #define QSPI_BK1_CLK_PIN          GPIO_PIN_2
    5 b# Z. ~; B' b- |0 i9 q
  10. #define QSPI_BK1_CLK_AF           GPIO_AF9_QUADSPI
    ( W1 Q  n3 \1 `- W9 v9 \) @) 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)
    # X" n! ^7 y4 q$ A( l
  12. ( F3 J; {6 H* ~! _  z+ v
  13. #define QSPI_BK1_IO0_PORT         GPIOF
    % a' C  e+ Z5 y- R+ `; M
  14. #define QSPI_BK1_IO0_PIN          GPIO_PIN_8
    # R6 K, R* U7 }, Y
  15. #define QSPI_BK1_IO0_AF           GPIO_AF10_QUADSPI+ m& C5 s' h) [! u3 ?; ]' T
  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)
    " q$ y4 ~+ L  T

  17. ; m3 Q) Z1 U: D5 t9 X2 m2 H
  18. #define QSPI_BK1_IO1_PORT         GPIOF
    - \% R$ @' ^/ e0 A2 r" F! F  s
  19. #define QSPI_BK1_IO1_PIN          GPIO_PIN_9
    : R4 z3 k: V& f& I
  20. #define QSPI_BK1_IO1_AF           GPIO_AF10_QUADSPI
    : t2 K! R- Q$ J$ 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)
    + x+ ^" ~5 W5 x, w/ I

  22. 8 U8 V! s; Y2 X
  23. #define QSPI_BK1_IO2_PORT         GPIOF, H( j% K# Z5 ^/ |) l+ N
  24. #define QSPI_BK1_IO2_PIN          GPIO_PIN_7" {& E0 M( [- }% j9 J& W
  25. #define QSPI_BK1_IO2_AF           GPIO_AF9_QUADSPI
    7 W* `7 F1 D  j/ A. y1 d$ A
  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)
    # u; }+ k& k$ L6 W+ f0 D

  27. $ i0 E9 j9 i' Z/ z4 Z
  28. #define QSPI_BK1_IO3_PORT         GPIOF* u9 K% m8 U8 y
  29. #define QSPI_BK1_IO3_PIN          GPIO_PIN_6! k) I" b+ k$ n; \! J' l* k
  30. #define QSPI_BK1_IO3_AF           GPIO_AF9_QUADSPI
    . x% C9 s! H% @- w: ?: Y9 w! ?
  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. // 封装几个必要的接口
    $ O+ _2 c: B& `: p- R  `9 Z$ n" k
  2. static QSPI_HandleTypeDef qspi_handle;
    $ i9 E5 T- A; O% N( _4 d- N4 J
  3. 0 n/ d+ f: {8 o+ `/ f1 o* |
  4. static void qspi_gpio_init(void)0 ]' U8 M. g+ l6 ~
  5. {! }" Z1 i& y/ L! q0 |8 \* L
  6.   QSPI_CLK_ENABLE();
    $ ]% s2 G; ?) \5 U$ D7 C

  7. - ~% ~; x: ]' C$ R5 n1 \
  8.   QSPI_BK1_NCS_CONFIG();3 M1 r  g8 O8 K5 ?  Y( p
  9.   QSPI_BK1_CLK_CONFIG();
    . x$ E  K4 Y+ M# r9 T" n
  10.   QSPI_BK1_IO0_CONFIG();
    4 I7 D/ P% J8 \
  11.   QSPI_BK1_IO1_CONFIG();, Z+ f7 q1 t: w
  12.   QSPI_BK1_IO2_CONFIG();3 K3 f$ j. B# p# Z: Z/ y. \
  13.   QSPI_BK1_IO3_CONFIG();# [: C* i# R" i. @' v5 b5 i
  14. }
    0 }' d5 R' z6 _; ]

  15. , L, e* W& u( Q9 `  S. t1 k
  16. static void qspi_mode_init(void)
    ; Y; o, ~* b+ f9 \, e$ [- `8 Z
  17. {
    8 m5 V- q2 z! Q* L& v
  18.   qspi_handle.Instance = QUADSPI;                        // QSPI1 y3 K5 F5 r, T4 W4 d. R% X6 ]9 Y
  19.   qspi_handle.Init.ClockPrescaler = 2;                   // QPSI分频比,W25Q256最大频率为104M 所以此处应该为2,QSPI频率就为216/(2+1)=72MHZ
    ' g) F. V% _8 l& ^( x
  20.   qspi_handle.Init.FifoThreshold = 4;                    // FIFO阈值为4个字节
    2 R3 }, x' `) Q" a! t3 c
  21.   qspi_handle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; // 采样移位半个周期(DDR模式下,必须设置为0)
    9 [' w1 e8 M8 [+ j% V
  22.   qspi_handle.Init.FlashSize = POSITION_VAL(0X2000000) - 1; // SPI FLASH大小,W25Q256大小为32M字节# Z4 v% {, o+ m& ^; D
  23.   qspi_handle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_4_CYCLE; // 片选高电平时间为4个时钟(13.8*4=55.2ns),即手册里面的tSHSL参数
    ! [0 h1 _) h) R( r: f! ?
  24.   qspi_handle.Init.ClockMode = QSPI_CLOCK_MODE_0;        // 模式0  v: \6 w0 g' t; E" R7 L. b5 a
  25.   qspi_handle.Init.FlashID = QSPI_FLASH_ID_1;            // 第一片flash
    ; `$ b% g  I( a3 L0 Z2 F$ l5 K$ x# B
  26.   qspi_handle.Init.DualFlash = QSPI_DUALFLASH_DISABLE;   // 禁止双闪存模式
      n: _+ B5 b  H9 U6 A# T
  27.   HAL_QSPI_Init(&qspi_handle);      //QSPI初始化5 _% v3 J0 F& i) J; t8 C0 d
  28. }
    ; k3 T3 T1 r/ e, ]& a

  29. ! q6 S$ v7 _3 R1 B- D8 j* P. L
  30. void QSPIInit(void)  k. ~: N+ [' Y" ?6 a
  31. {4 I/ E! Y1 [! C7 t  e
  32.   qspi_gpio_init();/ M" B3 B) n  d. i# l0 A
  33.   qspi_mode_init();
    7 {( B- X  Z6 X% ?9 _, k  ]# o3 L
  34. }! r# F% f9 y7 \7 [& y6 `

  35. & W4 F* T9 e5 x3 w; g" [# q
  36. // QSPI发送命令, E- M8 z! y$ P6 }! }2 u9 f* ?2 m$ H
  37. // instruction:要发送的指令
    ; |5 n& M/ B9 s8 u
  38. // address:发送到的目的地址
      u3 p3 ~; e, \, y1 `
  39. // dummyCycles:空指令周期数3 b# D( d. g5 w; X0 u5 h  D$ ~
  40. // instructionMode:指令模式;QSPI_INSTRUCTION_NONE,QSPI_INSTRUCTION_1_LINE,QSPI_INSTRUCTION_2_LINE,QSPI_INSTRUCTION_4_LINE
    $ G! E( I4 o# f* r7 g5 t9 ]! m
  41. // addressMode:地址模式; QSPI_ADDRESS_NONE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_2_LINE,QSPI_ADDRESS_4_LINE
    ( [7 T! o' @5 m1 J5 [) r# k
  42. // addressSize:地址长度;QSPI_ADDRESS_8_BITS,QSPI_ADDRESS_16_BITS,QSPI_ADDRESS_24_BITS,QSPI_ADDRESS_32_BITS: s& t, ^- W+ D4 I& v
  43. // dataMode:数据模式; QSPI_DATA_NONE,QSPI_DATA_1_LINE,QSPI_DATA_2_LINE,QSPI_DATA_4_LINE
    & _# w" `" f- S; m5 T) M
  44. void QSPISendCMD(uint32_t instruction, uint32_t address, uint32_t dummyCycles, uint32_t instructionMode, uint32_t addressMode, uint32_t addressSize, uint32_t dataMode)$ v( |& ]9 f6 p$ H& M7 Y2 d
  45. {
    8 q0 G- o. o9 U( Z7 s
  46.   QSPI_CommandTypeDef Cmdhandler;
    * ?( Z  U! Q3 l" @

  47. & |3 [- \$ c3 ~
  48.   Cmdhandler.Instruction = instruction;           // 指令
    ( W7 K8 ~9 v9 W2 E
  49.   Cmdhandler.Address = address;                   // 地址
    # U$ S7 V9 U4 C# J
  50.   Cmdhandler.DummyCycles = dummyCycles;           // 设置空指令周期数# P1 F/ T6 w8 p  M9 z
  51.   Cmdhandler.InstructionMode = instructionMode;   // 指令模式
    8 `% n% e/ ~. e, O8 e, a
  52.   Cmdhandler.AddressMode = addressMode;           // 地址模式
      i" l6 o# \" U5 W& ^
  53.   Cmdhandler.AddressSize = addressSize;           // 地址长度
      }! e+ z3 e- ?
  54.   Cmdhandler.DataMode = dataMode;                 // 数据模式3 A3 c8 S- Z& ]+ e3 _! d) j, J
  55.   Cmdhandler.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 每次都发送指令2 N% @. w" W2 L) J4 T
  56.   Cmdhandler.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 无交替字节/ ~+ ]2 U1 r0 B: d$ T. z1 S
  57.   Cmdhandler.DdrMode = QSPI_DDR_MODE_DISABLE;           // 关闭DDR模式' K" {* K- `8 e# T# ^) Y6 D
  58.   Cmdhandler.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    $ D4 F0 A6 V' Z8 |. k  V  Z1 Y
  59. 0 L4 @' O4 P% [9 m8 M" A! @0 p" k
  60.   HAL_QSPI_Command(&qspi_handle, &Cmdhandler, 5000);0 P: u% @, A6 X) K
  61. }. l8 s+ r' _% X& c) ^  ^" \% |
  62. 7 p. s1 n9 g" r
  63. // 接收指定长度数据
    , S+ A' y6 f9 r2 u
  64. uint8_t QSPIReceive(uint8_t *buffer, uint32_t length)& j% m( f" }: b; \$ V; F
  65. {0 y4 }: p6 O% X+ t0 h, J
  66.   qspi_handle.Instance->DLR = length - 1;+ i$ Q0 q5 v5 Q3 N4 }
  67.         
    ; K' w0 O, ?3 X% B# I: b# X
  68.   if(HAL_QSPI_Receive(&qspi_handle, buffer, 5000) == HAL_OK)  }8 \' M! n9 }" m
  69.   {
      u, v6 v7 T" N+ Y: L( @
  70.     return 0;  //接收数据
      G, p) l1 z6 [3 U, U  J* `8 K
  71.   }
    , t7 n7 @8 e8 a: a3 _
  72.   else
    8 e. ~5 v) p4 W5 ]( ~; T
  73.   {
    6 r4 K- ^8 G" `
  74.     return 1;& N) U8 k* N& |) d6 e
  75.   }
    7 S1 ^7 b  k$ q' z1 [2 T
  76. }
    & R9 Z& i9 E: o' e& \, W

  77. * a' q6 ]$ M0 g0 ]& X6 R
  78. // 发送指定长度数据2 p( S4 G/ i- L8 x" H3 i
  79. uint8_t QSPITransmit(uint8_t *buffer, uint32_t length)7 M7 ^; O0 y/ U+ @* @9 n# Y* Y
  80. {
    # K: X. i% P- [
  81.   qspi_handle.Instance->DLR = length - 1;$ f# H1 Q- F9 m* ?
  82.   if(HAL_QSPI_Transmit(&qspi_handle, buffer, 5000) == HAL_OK); q0 d4 M  ^) x  A; J( K
  83.   {
    # X5 o: x& z% ^+ X+ o
  84.     return 0;  //发送数据2 K' j# \8 ]& X1 K  V# }
  85.   }
    % C2 y  x' z- J
  86.   else
    % o$ s# B! N* K+ @. Y
  87.   {
    3 s1 N8 d9 t9 F4 d
  88.     return 1;8 I( e. S% f& M$ v
  89.   }
    $ [% \; b; n0 D. L! {5 i+ m! U
  90. }
复制代码

9 S6 Z  a+ w: Z五、QSPI驱动W25Q256
2 e+ t6 c2 ^' O6 e: VW25Q256:32M  512块,每块16个扇区,每个扇区4K。具体指令看手册。封装了如下接口(初始化读写擦除等接口是以后移植文件系统的基础):
4 x7 N( O  j0 G  x8 t$ a5 r. y
/ N- b! v2 f% Z- G
  1. #define W25QXX_WRITE_ENABLE      0x066 K5 r0 d, x1 q+ \( u) L. B# a
  2. #define W25QXX_WRITE_DISABLE     0x04
    ' H; L. E8 m$ f" X7 [4 i5 c5 A
  3. #define W25QXX_READ_STATUS_REG1  0x05, s" c3 O3 n3 w3 t) z
  4. #define W25QXX_READ_STATUS_REG2  0x35: X: Z! R+ ]; E- e2 o2 v
  5. #define W25QXX_READ_STATUS_REG3  0x15) @% Q& S# N6 f0 H. L0 C. G
  6. #define W25QXX_WRITE_STATUS_REG1 0x01- G8 L2 k8 a) p, d0 w* r8 q
  7. #define W25QXX_WRITE_STATUS_REG2 0x31
    ; J# n6 K. c* n8 Q
  8. #define W25QXX_WRITE_STATUS_REG3 0x11
    6 L" V! t6 s% z/ d; N7 b; r5 c/ q
  9. #define W25QXX_READ_DATA         0x03$ {7 a% l9 x. Z5 P
  10. #define W25QXX_FAST_READ_DATA    0x0B+ l1 y" d: ]% ]/ w1 b
  11. #define W25QXX_FAST_READ_DUAL    0x3B
    4 C7 r* w7 c8 ^4 e) Q( M/ i0 c2 K
  12. #define W25QXX_PAGE_PROGRAM      0x02
    2 n( \2 P8 T3 _8 W
  13. #define W25QXX_BLOCK_ERASE       0xD8* u" j5 j" t2 o. ]
  14. #define W25QXX_SECTOR_ERASE      0x20
    * p6 c( ?: H- u# V, D
  15. #define W25QXX_CHIP_ERASE        0xC76 T; Z) M3 W# j& f
  16. #define W25QXX_DEVICEID          0xAB
    : m: u' _5 M  G% a2 S
  17. #define W25QXX_MANUFACT_DEVICEID 0x90
    9 T3 i0 N6 A) L5 p5 R5 _: S
  18. #define W25QXX_JEDEC_DEVICEID    0x9F
    + n2 ?' }8 n( [" p! e, Y& m% C
  19. #define W25QXX_EABLE_4BYTE_ADDR  0xB7
    0 d$ i! w: a" t/ }4 n7 U8 J" \
  20. #define W25QXX_EXIT_4BYTE_ADDR   0xE9+ @, t2 o( `+ S# [& F1 L; e  `7 [
  21. #define W25QXX_SET_READ_PARAM    0xC0
    " q+ w# K- {, H1 [9 a. B' |. Z6 G
  22. #define W25QXX_ENTER_QPIMODE     0x38" k5 G. l) t* H! n& P! S1 h  L; |
  23. #define W25QXX_EXIT_QPIMODE      0xFF
    % `0 F2 M4 U* G: w! l* Y

  24. 4 _* a, V2 s* M; [: q5 d# F) B
  25. uint8_t w25qxx_qpi_mode = 0; // QSPI模式标志:0,SPI模式;1,QPI模式.. c; g! ?# V. ]# D# ]
  26. 6 l$ J! v% ?, f" G+ t0 m9 T
  27. // 读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器0 c! n3 b9 I: r6 f3 X4 x
  28. // 状态寄存器1:5 O# x, d9 ]0 o
  29. // BIT7  6   5   4   3   2   1   0$ ~6 W( h- w) {! [* d; q% a2 a
  30. // SPR   RV  TB BP2 BP1 BP0 WEL BUSY) M- \+ C% J% g- {
  31. // SPR:默认0,状态寄存器保护位,配合WP使用: m3 R6 {# u* Q8 {$ ~; C
  32. // TB,BP2,BP1,BP0:FLASH区域写保护设置
    3 _7 d/ u  ~5 [' m: e
  33. // WEL:写使能锁定4 q4 ^7 f; ?- x' y) _0 t
  34. // BUSY:忙标记位(1,忙;0,空闲)% l/ a6 s7 a8 G' i- |$ G
  35. // 默认:0x00! O5 O( E8 V" X- L' Y
  36. // 状态寄存器2:9 U. O9 {( ]" l8 {2 c6 \6 D( f& C( T
  37. // BIT7  6   5   4   3   2   1   0
    ( {1 Z0 Q1 ^* v
  38. // SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
    % i# t) V- h' W7 c8 e
  39. // 状态寄存器3:2 I! H* j3 f, {/ H% G
  40. // BIT7      6    5    4   3   2   1   0
    7 u1 `* b8 i! {/ W0 @% U
  41. // HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
    1 k3 s7 Z! o3 w* A- s0 ?
  42. // reg:状态寄存器号,范:1~3
    9 D5 o. P0 x4 X. \6 ~$ [; a. u" s
  43. // 返回值:状态寄存器值
    0 u" ~8 \/ u/ R! }
  44. static uint8_t w25qxx_read_status(uint8_t reg)
    2 K6 @5 ?% D" p
  45. {
    " y4 l3 j9 @9 k8 y9 B
  46.   uint8_t value = 0, command = 0;
    ( }( @2 F2 X$ E5 S

  47. 9 {1 b+ ~, @  W- N3 T/ m
  48.   switch(reg)
    # }2 L7 Y$ a( Y# \/ ^2 e" Q5 \+ G
  49.   {
    ; [/ Y) R0 E3 w  f0 B
  50.   case 1:
    $ n& y. ~: b+ N$ Y( B
  51.     command = W25QXX_READ_STATUS_REG1;  // 读状态寄存器1指令
    & U- V  X" ]; E7 k9 ^9 M" J+ I
  52.     break;- O& Y+ U1 t: s. H7 M5 y2 g
  53. 3 ~+ h8 ^8 q7 Y* k0 Z1 z0 v3 {! p
  54.   case 2:
    8 [5 S  n* O# _: \; V7 z6 {9 Q
  55.     command = W25QXX_READ_STATUS_REG2;  // 读状态寄存器2指令
    5 k5 u/ U) \( S3 r2 z7 C
  56.     break;! ~& O* r! _& _9 M
  57. 6 M0 j7 z' U) z1 U# {" C
  58.   case 3:; ?, Z, Z( m. _3 V. A% |& x4 r
  59.     command = W25QXX_READ_STATUS_REG3;  // 读状态寄存器3指令
    ' M( }! @5 e; N2 V8 u8 k
  60.     break;  l; u" }7 s+ I$ n# p- o$ ?% }7 u% x
  61. 3 \# z' I2 ~7 P/ P
  62.   default:1 Y  V' W2 k# `. ^' l1 e
  63.     command = W25QXX_READ_STATUS_REG1;
    2 i* w. _2 B; a% K1 D
  64.     break;1 B! D- P: y  S( _- u9 x7 c
  65.   }8 v1 }/ ~3 s) s+ e: d$ G

  66. - ]( K  m3 Z# m- L6 f
  67.   if(w25qxx_qpi_mode)% b" h. N! g' ^. v6 U- X$ I' p
  68.   {
    : k, J7 `: T8 W$ i7 b* f
  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个字节数据
    ( s. [# w% p+ B& z$ o3 T. [
  70.   }3 ~6 a# ~- Y3 ?0 Y. G; o
  71.   else
    1 D" i4 S$ ~) H
  72.   {% x8 Y- n: K/ ]$ s/ q; z: O! z
  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个字节数据2 b% @  \; X( ?0 s
  74.   }
    * I, }/ b0 w5 G$ A5 }! s8 K5 t
  75. 5 ?2 A; Y7 T/ ]5 v# g2 C  T, H
  76.   QSPIReceive(&value, 1);1 {5 Y+ G$ L6 |0 \) H5 |

  77. * d) X# m8 |! Z. q% q/ G. R1 E
  78.   return value;) n3 h5 l! X4 r- r( b' k7 \
  79. }
    / |; C3 H) y- B
  80. ! z0 e3 _, r/ i8 e
  81. // 写状态寄存器
    - c5 O" c' e; e
  82. static void w25qxx_write_status(uint8_t reg, uint8_t status), ~! s2 q  p9 _9 M
  83. {
    4 z" ~. u& K3 h; Q$ r3 W
  84.   uint8_t command = 0;
    ' B% u0 F2 Q% y& \% Z+ M: G  {
  85. # \8 B) @7 u# l* ^1 [  t( o
  86.   switch(reg)
    ) e0 W* `2 U" u/ L% q
  87.   {
    - `* I# B: d% m" Q& T
  88.   case 1:; g5 g4 ]7 G( m- d0 p8 @1 l
  89.     command = W25QXX_WRITE_STATUS_REG1;  //写状态寄存器1指令
    7 \3 t( ]% X) B. @7 P# u" i9 j
  90.     break;
      ~  `( o/ S: s; X5 \% p1 N
  91.   case 2:
    ; @  D, }6 O, U5 @0 B
  92.     command = W25QXX_WRITE_STATUS_REG2;  //写状态寄存器2指令
    + M9 l3 }8 h! z( C
  93.     break;
    ' j2 `3 x1 Y0 n# j* W
  94.   case 3:
    - G! ?5 {# Q; f: S* e# O, F
  95.     command = W25QXX_WRITE_STATUS_REG3;  //写状态寄存器3指令2 @; h# |: m8 S2 }" n
  96.     break;
    ) J8 I7 H/ S/ t+ Y
  97.   default:  s/ P- z3 v2 x
  98.     command = W25QXX_WRITE_STATUS_REG1;$ d5 S' F/ D5 q# p8 A
  99.     break;: e, X5 Q9 M1 x8 I- k: b- u8 p$ m) w
  100.   }" `/ D! f7 P! V- G- [) D
  101.   if(w25qxx_qpi_mode)
    7 {, C: _. T( y  ]) a1 ^
  102.   {
    7 e+ h/ u6 M& X4 ]0 T7 [8 M
  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个字节数据, y" E0 C: R5 C% _" f/ I
  104.   }
    ' T, J! @; b  a
  105.   else
    * n3 b+ s/ P% E- \- t) E+ Y, m
  106.   {8 A0 r7 I" T) v9 E- w4 T& F- U4 o
  107.     QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_1_LINE); // SPI,写command指令,地址为0,单线传数据_8位地址_无地址_单线传输指令,无空周期,1个字节数据
    0 T# U. a6 U. a& C
  108.   }/ o4 l! ~- g& h5 p6 e/ ^
  109.   QSPITransmit(&status, sizeof(status));
    9 o1 _) Q7 Z: F1 k; F4 e+ [
  110. }" x9 `7 G1 t- {, I9 w7 O( [
  111. ' u: U- N& L- f# a0 [3 s8 B/ K
  112. // 写使能  将S1寄存器的WEL置位
    ( g9 q. Y/ O1 L4 ]( D! r
  113. static void w25qxx_write_enable(void)
    / K2 ?- B3 D* e8 V& I
  114. {( O: {' L& t1 @
  115.   if(w25qxx_qpi_mode)! F% x3 g0 _7 j' @0 c5 U
  116.   {3 n1 ]' f& L/ P$ c# n
  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个字节数据
    * P! H. s5 y" C0 W# O5 E
  118.   }
    , r5 o7 A* Q8 e5 D
  119.   else* E4 M& J# X$ F/ p0 v2 b8 q% ^
  120.   {
    2 A$ L2 w! Z: H- t: M1 J; d
  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个字节数据/ z5 p2 [% F; B, v& `! Z& p
  122.   }
    & w. U2 i; L- z/ T( o6 S) t! R; N
  123. }
    - q0 v* F. Z0 }' n# c  ?) I

  124. 7 G5 r1 i* z6 T2 G
  125. // 写失能  将WEL清零  j0 F& o* {0 @5 Q- o9 T
  126. void W25QXX_Write_Disable(void)
    $ ?3 e6 q9 [' f" f$ H; R, {, m
  127. {: c. Q, `( {& p% ]7 O' _
  128.   if(w25qxx_qpi_mode)
    , {" B5 C4 z" d, Q
  129.   {
    : `3 w% M8 C$ Y! H3 Z  L
  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个字节数据
    0 @1 Z3 N' H2 H' v" a
  131.   }1 h8 b8 |; e3 C7 s) [6 H
  132.   else
    , D1 m3 v' U/ Z4 P& u0 \
  133.   {
    : z# l  r! a/ 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个字节数据+ ~; N7 S. I+ h! P2 C
  135.   }
    3 J7 P3 w# S! z: i% L
  136. }
    1 L0 j% u+ W5 _$ I" U
  137. / v. Q: L7 i$ p1 P, {1 p
  138. // 等待空闲
    2 [, m* H' D2 m+ S. t; r
  139. void w25qxx_wait_busy(void)7 J9 ]- K3 C/ X0 ~- n- i& j
  140. {. r9 [6 V) k) o
  141.   while((w25qxx_read_status(1) & 0x01) == 0x01); // 等待BUSY位清空
    0 }5 q: {( U9 t4 Q" B3 C
  142. }/ ?, s, r6 p: v5 s

  143. $ n) B9 l; m+ w
  144. // W25QXX进入QSPI模式" w6 F( d1 \$ O( Y
  145. static void w25qxx_qspi_init(void)! D; M! T: p) e- i% o6 H
  146. {, D2 A% I% v( O  I
  147.   uint8_t reg2;
      x, W) g9 E) h5 v$ @7 X2 q; Z
  148.   reg2 = w25qxx_read_status(2); // 先读出状态寄存器2的原始值
    / V% N3 q+ H! p  _, y) X. H
  149.   if((reg2 & 0X02) == 0)  // QE位未使能6 {, h7 B+ A  e/ K) i
  150.   {
    0 i* |5 e1 k4 k6 k+ t
  151.     w25qxx_write_enable(); // 写使能
    ! K& v/ q$ a7 h
  152.     reg2 |= 1 << 1; // 使能QE位: q& r$ ^+ l  A5 U  J& Z5 L0 ~
  153.     w25qxx_write_status(2, reg2); // 写状态寄存器23 V' R* s  o, O' K- B2 \
  154.   }
      n6 ~7 e- ~1 M; Y7 v; \) J
  155. , |+ q6 v" t' P7 J8 K- Y) N
  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个字节数据0 u; D( l8 x  a" Q' \

  157.   T, E3 x, m! g" E6 D) I4 W
  158.   w25qxx_qpi_mode = 1; // 标记QSPI模式
    $ n: F2 G5 g4 }5 H: s
  159. }6 D8 v+ ]$ ^4 a% O
  160. 8 W( r, H; h2 B' h$ V
  161. // 0XEF13,表示芯片型号为W25Q80. y; P- B" w9 c+ @& Z( O
  162. // 0XEF14,表示芯片型号为W25Q16
    - u6 H6 z0 o( F
  163. // 0XEF15,表示芯片型号为W25Q32+ t! }, n& }8 D5 y, [/ r3 n. X
  164. // 0XEF16,表示芯片型号为W25Q64
    # y  Y2 j0 ?9 y# v5 v, b3 i* c
  165. // 0XEF17,表示芯片型号为W25Q128
    : P! t0 y( F2 h3 L" k2 n' `( I% N
  166. // 0XEF18,表示芯片型号为W25Q256
    & m5 P1 `, E9 R: B3 e
  167. static void w25qxx_id_get(void)8 `1 E3 T8 N5 R8 f
  168. {
    - t2 w9 ]6 H6 H0 }# o
  169.   uint8_t temp[2];/ T6 P  L0 f8 }& q/ {
  170.   uint16_t device_id;7 G1 ~! u0 s( A; t: I# Q

  171. ( o1 _9 E  U% Y, j+ K: Q
  172.   if(w25qxx_qpi_mode)
    : D: a( t. W4 t1 \4 _% K0 `0 p
  173.   {0 ]" W2 j0 L, `+ M% b- J% g+ X$ ]
  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个字节数据. J' O& F% h8 i  M
  175.   }: t/ e: U) {# m" t3 i6 A+ f! f
  176.   else
    , R! q6 j& K& z$ d. W; w
  177.   {
    " M7 R* {7 \) p
  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个字节数据
    6 y4 q7 ?5 [2 p: b2 [$ S! }: l
  179.   }! \6 [8 P( _& h2 f6 k' r, I
  180. . Z! L: R# c$ I3 \6 k
  181.   QSPIReceive(temp, 2);5 N7 \  E) c" t' l1 z

  182. , [% Q8 r& w& B0 ?: c- y' q
  183.   device_id = (temp[0] << 8) | temp[1];
    4 z! k! M1 A' H* k
  184. 1 z5 Q6 w1 ?# a# r& s( A
  185.   printf("QSPI Flash Device ID: %X\r\n", device_id);
    + ~  n5 S' m; `  O/ }: S  U% b
  186. }
    0 o$ u# s+ r. H- P7 t

  187. ; D+ [6 d( A: w" [  s) F
  188. void W25QXXInit(void)
    : Y1 f4 [! l- F2 t: v/ p' E
  189. {
    6 D% {7 q1 p! ^6 f. A
  190.   uint8_t temp;9 a/ I" K( D- \: x% g0 a* S
  191. / f, C8 ~7 @# u2 q
  192.   QSPIInit();
    , n3 l$ J. ~4 B1 k0 }- v# G) h
  193.   w25qxx_qspi_init(); // 使能QSPI模式
    " d! M3 z& K$ e$ y1 V
  194.   w25qxx_id_get();    // 读取FLASH ID.
      Z3 X2 n$ S* f7 O

  195. & K! B& {$ q- T/ p; V' p# S
  196.   temp = w25qxx_read_status(3); // 读取状态寄存器3,判断地址模式* f7 T6 O% [9 |9 ^* ?) N2 F% _
  197.   if((temp & 0X01) == 0) // 如果不是4字节地址模式,则进入4字节地址模式
    4 Z  f2 f- u$ {& C1 C8 P
  198.   {* Q! ]) P9 m. w3 z' Z
  199.     w25qxx_write_enable(); // 写使能3 {& u1 a, A& q7 ~  i8 u5 E# W
  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个字节数据% F5 I. v5 S( c2 \( ?- a
  201.   }* J# r) z7 q* d0 o" x! l
  202.   w25qxx_write_enable(); // 写使能
    # f" ~& S7 x: o0 v+ o4 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个字节数据' j) h  N% a# u, o
  204.   temp = 3 << 4;      // 设置P4&P5=11,8个dummy clocks,104M
    ' m' a( u- M6 e$ t" X2 Z; O
  205.   QSPITransmit(&temp, 1);
    - ?+ w; M; f% k& \7 d, G! Y
  206. }
    0 p5 h4 T  Z4 p5 m: X8 k* z
  207. 3 d: b% o1 T, A% L4 A. @
  208. // 擦除一块4096字节 最少需要150ms
    3 r, x: G: J: j& I% u
  209. void W25QXXSectorErase(uint32_t addr)4 O# R" D7 q  t6 ?$ p$ J9 q$ y
  210. {: i( B+ W5 Y: r: v# M* O+ o0 M5 @
  211.   //addr /= 4096;
      P* _8 w1 K0 b4 z( {. \" j. d! _
  212.   addr *= 4096;
    0 K+ X+ k# j/ {6 j

  213. + E7 |9 v. k4 B) D
  214.   w25qxx_write_enable();! ?& z$ v( f) @  x
  215.   w25qxx_wait_busy();
    4 o: X; L+ @; v9 |: Q, Y) \, F
  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个字节数据0 ?1 d' C% e* U- O# _+ o
  217.   w25qxx_wait_busy();8 t- o; t( a: b
  218. }9 G7 \5 U5 w1 H$ P8 H' }0 `+ a2 j

  219. . n8 ]" m5 r& m& L
  220. 8 m* f; o6 I. a& G; s
  221. // 擦除整个芯片3 W. ?0 N! L2 v/ G1 G
  222. void W25QXXChipErase(void)6 [: I& X( ]4 H
  223. {5 k. X  N( m+ }
  224.   w25qxx_write_enable();          //SET WEL' O' y: p5 Q2 A; b. \. q0 P' t
  225.   w25qxx_wait_busy();) m6 `3 r& Y3 _
  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个字节数据
    ) |& c& |+ M( n; V. G/ v
  227.   w25qxx_wait_busy();           //等待芯片擦除结束
    + l: T* Q/ P+ x4 o* \! v. b! }) a
  228. }
    ' d6 D& {4 c0 }
  229. " w# }4 f9 R, o% R) Z. u
  230. // 写最多256字节
    ' R# s' H0 ]- a* h( x8 J
  231. void W25QXXWritePage(uint32_t addr, uint8_t *buffer, uint16_t length)5 q6 ?: J; K6 s% Y
  232. {2 w/ u: a) [. U% n" U8 i3 `
  233.   w25qxx_write_enable();          //写使能1 c8 v) D2 c  y0 v
  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个数据
    2 d% Q  a- f: n1 E+ w0 D
  235.   QSPITransmit(buffer, length);4 h, H5 s4 B! [) G2 h
  236.   w25qxx_wait_busy();            //等待写入结束* V9 j/ Z2 B1 B
  237. }% ?1 [$ ~* w) `; ]

  238. ' w& K% x! L3 [4 e
  239. // 写最多65536字节 无擦除动作2 _0 Y$ L2 ?/ f6 v2 h/ ]
  240. void W25QXXWriteExt(uint32_t addr, uint8_t *buffer, uint16_t length)- h, K$ [6 L! K% n$ p4 P) F
  241. {# X$ O) ?" i8 M. X7 w7 d/ I  e# H
  242.   uint16_t pageremain = 256 - addr % 256; // 单页剩余的字节数
    & D# Q9 m, b0 E; @# ]
  243. / |/ a& A& u  D
  244.   if(addr + length - 1 > (32 * 1024 * 1024))
    9 u8 l! l0 b* b  }& Y+ R5 q
  245.   {* x; x4 j6 n  h$ t2 y, \, `
  246.     return;" w$ i. M. G3 j/ G
  247.   }0 P* e% v, _: B+ @$ {- u

  248. ) i* Q! T5 T: c" |
  249.   if(length <= pageremain)
    ' e1 L. j; m5 p* D4 [
  250.   {
    4 w# R5 o- ?7 ?" x/ w2 v
  251.     pageremain = length;  // 不大于256个字节
    # D# Z: T2 N  t  K7 L. j- V
  252.   }
    ; u# p* }( @' g0 q# O: W' Y1 L
  253.   while(1)
    / _- I9 s8 h+ v
  254.   {
    : a7 o8 o* E& P% P
  255.     // 分页写入* Y+ ]" _. D, |2 D4 d# `
  256.     W25QXXWritePage(addr, buffer, pageremain);
    3 H3 n% ]0 q% h3 r( u: F6 A8 d' ?. K
  257. " X4 k! y, V% g7 [% K; a1 `. A. f
  258.     if(length == pageremain)
    : W* w5 y  H. e# u/ d" N2 m2 D
  259.     {/ A; W0 F# u( A/ z8 b! |
  260.       break;  //写入结束了
    ( X7 @7 v7 a9 J1 J% Y" b
  261.     }2 Z  ]' S. s8 M* o6 ~
  262.     else1 @4 X( q" p! d
  263.     {
    5 I% y* h2 {$ w6 T: |1 [5 n& ~$ w  x
  264.       buffer += pageremain;
    ( K0 l+ ^$ I5 d, L, C
  265.       addr += pageremain;
    7 S3 |( {! @4 J- r

  266. : {% d; C6 r" {, ~1 S/ u
  267.       length -= pageremain; // 减去已经写入了的字节数
    : ~3 m0 p& m% `# q' Q
  268.       if(length > 256)
    ' ~  ?2 J3 B0 z4 D# ~
  269.       {7 z+ u' j7 B- W; V3 w/ m- o6 E
  270.         pageremain = 256;  // 一次可以写入256个字节
    $ o  N2 h$ }/ W6 H+ c4 m
  271.       }8 [# }6 g( {- O4 z0 u6 q# I
  272.       else/ V/ O+ X# q  h; I
  273.       {7 Z8 _: X: I) s/ U0 k, f* w/ Z
  274.         pageremain = length;  // 不够256个字节了
    $ N# d0 j$ P$ ^/ b  D  r8 E, ^
  275.       }
    ' ~# R7 M" C5 Y
  276.     }
    7 w  v* B' u- O5 \% A- A
  277.   }9 ?% K% P8 c: X2 o; o$ y8 D
  278. }6 }0 r, r$ w! o+ f& p7 L- C$ J
  279. 2 \- g% y& U. ~. _' r
  280. // 写最多65536字节 带擦除动作
    5 |: V6 M" V* t# L/ E1 Z+ r; V
  281. static uint8_t W25QXX_BUFFER[4096];8 V. r! ]2 g& q5 g8 a6 X
  282. void W25QXXWrite(uint32_t addr, uint8_t* buffer, uint16_t length)' o* I/ M5 ]7 _
  283. {
    ( v) T' w, R4 D6 o
  284.   uint32_t secpos;
    $ e; y0 {+ P. _, R% J/ {2 O2 g
  285.   uint16_t secoff, secremain, i;
    9 m0 f  m8 u1 X- k3 ?+ j
  286.   uint8_t * w25q_buf;5 D6 U0 [% d/ i
  287. ' P' v9 p) C2 L! A( D2 j
  288.   w25q_buf = W25QXX_BUFFER;/ _% B7 t5 R% s, @$ q0 d

  289.   R. |: j7 j6 Y9 x
  290.   secpos = addr / 4096; // 扇区地址
    ) u8 ~2 Y  E, ^
  291.   secoff = addr % 4096; // 在扇区内的偏移% w; F* {/ Y$ f; Z
  292.   secremain = 4096 - secoff; // 扇区剩余空间大小! _+ q1 o! o% b7 E$ I/ }

  293. 2 J- {  R' n$ W% l- x' e
  294.   if(length <= secremain)
    + g3 _0 d- g8 [% S" I
  295.   {
    & f- Z+ T' _. n
  296.     secremain = length;  // 不大于4096个字节0 o/ v" H* U% H6 h. p' s* B; R+ B
  297.   }7 \& r- `8 m8 c% f
  298.   while(1)
    9 j5 s  m. {' c, D/ ^
  299.   {
    ( }: j* w% E  O5 w2 t: u
  300.           WatchdogFeed();# p. U( v7 h1 W2 H$ r6 z; R, t% Z
  301.     W25QXXRead(secpos * 4096, w25q_buf, 4096); // 读出整个扇区的内容' a9 j, i% \) w- o( o9 a
  302.     for(i = 0; i < secremain; ++i)$ n7 j  @) b) ~; N8 Y; _
  303.     {
    3 Y+ _* B% g! a- r# @
  304.       if(w25q_buf[secoff + i] != 0XFF)$ d- Y/ o" X; O' w! l! T  R
  305.       {
    2 H6 x4 ]* L+ Y- K, W  k, g! [
  306.         break;  // 需要擦除
    & I1 ~9 H) U  z# p& `$ k
  307.       }2 d' l9 C2 a" z+ [2 [  Q
  308.     }; y% J$ [; I+ m. R/ R
  309.     if(i < secremain) // 需要擦除
    " T& ^5 m% m/ }
  310.     {7 q+ \; O0 \9 [4 w+ @7 S
  311.       W25QXXSectorErase(secpos); // 擦除这个扇区
    ( J1 {; E6 D; f4 D0 n% p! b
  312.       for(i = 0; i < secremain; ++i): b; f# G& I1 j7 ~
  313.       {% l7 k8 ~8 q7 r- ^/ A
  314.         w25q_buf[i + secoff] = buffer<i>;</i>
    . p+ L2 ]! O' A, O6 r9 b, L
  315.       }1 j! h, |. b; {' M5 l% S2 r
  316.       W25QXXWriteExt(secpos * 4096, w25q_buf, 4096); // 写入整个扇区2 o2 T7 n- d& N
  317. ' K. i, H' q8 @. k
  318.     }) w0 a5 r+ z1 E
  319.     else0 O4 H. E. l) D/ S2 H& y9 ^& z
  320.     {. O- |1 v' f) p' H% Z
  321.       W25QXXWriteExt(addr, buffer, secremain);  // 写已经擦除了的,直接写入扇区剩余区间.
    ! l" }0 y( f+ H2 g
  322.     }
    # T( |8 [0 S8 Y4 H3 ^  D) l" l
  323.     if(length == secremain)% P3 ?0 F( |6 h( k5 W# m
  324.     {
    & v: N9 _4 F: F( `  ]) ~0 z6 B
  325.       break;
    , }, @* f% V9 Z/ O2 {: D% d& _
  326.     }
    % U9 L8 R5 k4 C4 A
  327.     else
    : H, ?# z1 H6 N# o: i7 q2 x
  328.     {- ^7 y/ f& F7 y* \% ?
  329.       secpos++;
    8 c0 B& r6 B8 _! h
  330.       secoff = 0;& s* [6 Y/ j/ c; t4 o% [/ {% f# D0 r' K
  331. 1 w# v( v. i1 R' W1 o: v
  332.       buffer += secremain;( W8 ?  [! |5 O5 @, h: [
  333.       addr += secremain;8 Z, |# X* H% |3 D
  334.       length -= secremain;
    # {+ @1 L  O4 ~
  335.       if(length > 4096)# n/ ?# R6 ~8 b" t; d
  336.       {
    ( G/ v" T. m3 S5 L# u+ i
  337.         secremain = 4096;$ q. y1 F6 L, m' [# W% y$ n
  338.       }: z0 J  z" A  }7 K8 m2 Z
  339.       else4 O& j8 x+ I' V# u5 b( s2 Z
  340.       {; \: Q" E- c- |$ \& \
  341.         secremain = length;. r7 d, ~+ }' b$ @; \1 C
  342.       }5 I' `& e+ k! {0 B: {' I8 y" a
  343.     }3 D3 ]9 j! [8 ^: i
  344.   };
    . h$ f* E, A! e+ O# f8 T  H
  345. }- c! ?& v6 b0 F

  346. " L. y1 `- w9 s% n6 N6 c7 X
  347. % T3 O. U  t  V. b1 g* W- {+ A
  348. // 读取SPI FLASH,仅支持QPI模式  在指定地址开始读取指定长度的数据 65536+ R5 M: r# E( E# W& I* h6 h6 d
  349. void W25QXXRead(uint32_t addr, uint8_t *buffer, uint16_t length)
    7 f5 `9 E) j* ~4 i
  350. {* u6 `% R5 l) Y7 H& ?9 a+ ~
  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个数据
    1 J- L# ?% o' ]
  352.   QSPIReceive(buffer, length);5 B2 Q% `" R2 t4 u6 ^5 M
  353. }
复制代码
" z" _( R5 _$ r/ ?# v
也可以使用SPI操作W25QXX。/ O: ?/ D7 [+ M& M
8 `* ^- Q4 V8 B7 D8 g" w

( m$ x: w/ I. ?, l, u+ n: ~0 o. @* c1 `& Z* w! V6 ~' P+ _7 i
赞 收藏 评论0 发布时间:2021-12-11 12:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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