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

【经验分享】STM32F7xx —— QSPI

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

/ u/ H( d. T& Y% D" [8 [, ?: t$ A0 s6 E7 m; J6 k3 M2 A; u
QSPI每条命令,必须包含一个或多个阶段:指令、地址、交替字节、空指令和数据。0 R9 J  x+ q9 C/ v+ c5 d6 p# T

6 G6 c, Z) d2 MQSPI发送命令:等待QSPI空闲;设置命令参数。. M1 V  n- Q, J- ^* ?% T
2 k5 K$ I- ]) n4 E$ ?) I
QSPI读数据:设置数据传输长度;设置QSPI工作模式并设置地址;读取数据。( O  i* @# L# Z# o  j9 t
/ G* }" ~( J" y% W9 D/ V
QSPI写数据:设置数据传输长度;设置QSPI工作模式并设置地址;写数据。$ h% ?" N2 w) T1 z
# O2 w( F8 {4 U& T( h

5 w; N( o8 r  ?- |/ Z; S7 A二、几个重要的函数0 h  w/ D# ~" }  }' n0 H  x# D! j
  1. HAL_StatusTypeDef     HAL_QSPI_Init     (QSPI_HandleTypeDef *hqspi); // 初始化
    ) c' P* e8 X4 O& I5 W% Q5 e5 g

  2. 9 |/ g& i* M7 L; z+ z( i8 G- O
  3. HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout); // 发送命令1 o+ i% p% O9 e+ J
  4. - ^' R- n+ n& B6 F2 H
  5. HAL_StatusTypeDef     HAL_QSPI_Transmit     (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 发送数据% j# E2 K' O! G2 ?# h1 l- c
  6. 9 ^: D) y+ {, c) |* a
  7. HAL_StatusTypeDef     HAL_QSPI_Receive      (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 接收数据
复制代码

! h! i7 z: r& U& G1 E三、几个重要的结构
# \' K5 B4 Q' h  R/ }
  1. // QSPI操作句柄2 q* V, W  @, s' K! W6 O- a7 c5 r
  2. typedef struct9 v% c: v& J4 }% I& z+ n, p' q/ R  b
  3. {  G* Q; x# d! J& ?( A* u  W# I; ^
  4.   QUADSPI_TypeDef            *Instance;        /* QSPI registers base address        */
    1 U' d4 }" |; }! U$ A7 e: O# O+ X. L
  5.   QSPI_InitTypeDef           Init;             /* QSPI communication parameters      */
    $ ^- }* v" K" Y, i2 E: t
  6.   uint8_t                    *pTxBuffPtr;      /* Pointer to QSPI Tx transfer Buffer */* ?. G! I2 ]% \4 g' `3 S) l+ U- n8 l& |
  7.   __IO uint16_t              TxXferSize;       /* QSPI Tx Transfer size              */  K: _& e: _9 V% L! @
  8.   __IO uint16_t              TxXferCount;      /* QSPI Tx Transfer Counter           */
    : a; K( R. J: s( d  c  b2 l6 o$ @
  9.   uint8_t                    *pRxBuffPtr;      /* Pointer to QSPI Rx transfer Buffer */
    6 i( ^7 k- Z/ O$ {9 y; a5 }
  10.   __IO uint16_t              RxXferSize;       /* QSPI Rx Transfer size              */
    , `+ y$ Q8 \5 a% R% k
  11.   __IO uint16_t              RxXferCount;      /* QSPI Rx Transfer Counter           */1 t9 i7 h/ d  `9 U2 K! Z
  12.   DMA_HandleTypeDef          *hdma;            /* QSPI Rx/Tx DMA Handle parameters   */
    - g0 i' u7 g. L* s
  13.   __IO HAL_LockTypeDef       Lock;             /* Locking object                     */1 D- q9 l0 |3 [7 P# t. |
  14.   __IO HAL_QSPI_StateTypeDef State;            /* QSPI communication state           */
    ' s6 m2 \3 V0 P+ \
  15.   __IO uint32_t              ErrorCode;        /* QSPI Error code                    */
    2 ]8 D$ j+ U9 w" E0 s* L
  16.   uint32_t                   Timeout;          /* Timeout for the QSPI memory access */
    4 W7 m0 R4 s5 o# |( T: \
  17. }QSPI_HandleTypeDef;
    1 x* S3 R5 \% x
  18. / u3 u" a. B: Z6 e1 u6 _# ?
  19. // Instance:QSPI基地址  ---  QUADSPI
    6 K$ H/ ]  n4 K
  20. // Init:设置QSPI参数
    1 [/ x& _* g6 s: H! [7 m1 e7 a+ }
  21. // pTxBuffPtr,TxXferSize,TxXferCount:发送缓存指针 发送数据量 剩余数据量3 ]8 t' ~4 c: s% g; f
  22. // pRxBuffPtr,RxXferSize,RxXferCount:接收缓存指针 发送数据量 剩余数据量/ P! H9 ]7 \! L' X" ^
  23. // hdma与DMA相关, 其他为过程变量不需要关心
复制代码
  1. // 参数配置 时钟分频系数  FIFO阈值  采样移位  FLASH大小  片选高电平时间  时钟模式  闪存ID  双闪存模式设置
    % a: q( p7 E$ s7 B. N+ M& k" j0 W
  2. typedef struct# |9 _) I/ {( Z+ m
  3. {9 X# B+ n0 m" E
  4.   uint32_t ClockPrescaler;     /* Specifies the prescaler factor for generating clock based on the AHB clock.
    ! b+ r9 O: W0 ?, g1 F4 E4 g
  5.                                   This parameter can be a number between 0 and 255 */   v" f  [* T+ T+ W- X

  6. 8 z, Q4 x  d  `& C: O4 C% x5 O% v
  7.   uint32_t FifoThreshold;      /* Specifies the threshold number of bytes in the FIFO (used only in indirect mode)+ f& K+ J% \" A- S+ W  p. H
  8.                                   This parameter can be a value between 1 and 32 */
    3 Y/ y9 K. V4 r! E2 U$ G, ], N% s

  9. 5 D4 b; Q, y9 L1 x- p# `
  10.   uint32_t SampleShifting;     /* Specifies the Sample Shift. The data is sampled 1/2 clock cycle delay later to
    / O7 m: C) p8 }) K
  11.                                   take in account external signal delays. (It should be QSPI_SAMPLE_SHIFTING_NONE in DDR mode)
    2 `3 ?3 C+ h# B* ^' b/ ~
  12.                                   This parameter can be a value of @ref QSPI_SampleShifting */
    ' ^7 l4 g% H- B

  13. 7 d$ I. E3 [6 ]4 E$ X$ t: K/ E- w! B
  14.   uint32_t FlashSize;          /* Specifies the Flash Size. FlashSize+1 is effectively the number of address bits
    # u; j: U2 T. F% c; x8 n8 J( n/ L
  15.                                   required to address the flash memory. The flash capacity can be up to 4GB
    ( R9 |0 Y2 Z1 Q$ a) R
  16.                                   (addressed using 32 bits) in indirect mode, but the addressable space in
    # D& L# J$ R2 l8 W. q
  17.                                   memory-mapped mode is limited to 256MB6 y0 W8 W' G. T/ `' A6 j
  18.                                   This parameter can be a number between 0 and 31 */  u6 u. a( G2 `+ w

  19. 6 R8 H: S1 c0 j
  20.   uint32_t ChipSelectHighTime; /* Specifies the Chip Select High Time. ChipSelectHighTime+1 defines the minimum number " x0 I. |2 m: v$ J
  21.                                   of clock cycles which the chip select must remain high between commands.
    2 _5 J0 c! k+ S' I* B0 q1 \
  22.                                   This parameter can be a value of @ref QSPI_ChipSelectHighTime */
    6 \, W7 o& ]9 m
  23. 4 w$ }5 @' r$ N( X5 A
  24.   uint32_t ClockMode;          /* Specifies the Clock Mode. It indicates the level that clock takes between commands., s4 z8 I: W, Z9 F# T
  25.                                   This parameter can be a value of @ref QSPI_ClockMode */
    9 r: A/ H% D1 \0 B( c
  26. ! l2 W0 N# r3 [+ J
  27.   uint32_t FlashID;            /* Specifies the Flash which will be used,
    : b1 t2 t4 y- F$ k
  28.                                   This parameter can be a value of @ref QSPI_Flash_Select */: v7 s9 o" N/ y* I9 N
  29. 0 P, J7 K+ N" y( ?
  30.   uint32_t DualFlash;          /* Specifies the Dual Flash Mode State
    2 K' `# t3 {0 l8 w
  31.                                   This parameter can be a value of @ref QSPI_DualFlash_Mode */                                               
    ( z# A; z! \9 ?
  32. }QSPI_InitTypeDef;
复制代码
  1. // 采样移位
    1 D: g0 s# W3 b' @5 r1 Y
  2. #define QSPI_SAMPLE_SHIFTING_NONE           ((uint32_t)0x00000000U)        /*!<No clock cycle shift to sample data*/
    3 R' _/ J1 h; a6 a$ g& x% i
  3. #define QSPI_SAMPLE_SHIFTING_HALFCYCLE      ((uint32_t)QUADSPI_CR_SSHIFT) /*!<1/2 clock cycle shift to sample data*/
复制代码
: k, m& m- j$ d& {, O
四、QSPI接口设计(仅供参考)

' \0 `) }  h0 `$ Y  U
  1. #define QSPI_CLK_ENABLE()         __HAL_RCC_QSPI_CLK_ENABLE();
    ' w: `6 W& u% g2 ~
  2. - r7 g- j/ `9 }8 N, U9 T: o
  3. #define QSPI_BK1_NCS_PORT         GPIOB
    ; S( c6 O4 {, r. c8 o9 @3 A4 l
  4. #define QSPI_BK1_NCS_PIN          GPIO_PIN_6
    + O+ s( ~. k. J+ Q9 _, A4 {
  5. #define QSPI_BK1_NCS_AF           GPIO_AF10_QUADSPI
    * D! ]9 |; 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)
    $ B3 M' D) R% W; |; Z. B; ?

  7. 9 l* u- ]# a6 Y  j
  8. #define QSPI_BK1_CLK_PORT         GPIOB
    5 x+ ?" X2 e9 C) Y; L' E
  9. #define QSPI_BK1_CLK_PIN          GPIO_PIN_28 R) l2 d( o4 j: J' X$ E% L& {% q; O
  10. #define QSPI_BK1_CLK_AF           GPIO_AF9_QUADSPI' f; Q) x; ?9 u7 w- o
  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)( V  v7 {+ F/ O
  12. 9 N/ R# o8 C( @( \9 Z
  13. #define QSPI_BK1_IO0_PORT         GPIOF' G7 }; i4 S' Z! U3 ?: l6 \
  14. #define QSPI_BK1_IO0_PIN          GPIO_PIN_8
    . s) X: K* I* }' f
  15. #define QSPI_BK1_IO0_AF           GPIO_AF10_QUADSPI( N/ O8 N# g  e* b, R; U( m' b# x
  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)4 L$ ?4 \' u9 Z2 ~

  17. ) g9 b9 ?4 I7 @
  18. #define QSPI_BK1_IO1_PORT         GPIOF
    : d& f6 R- D. P3 y9 i; T( d
  19. #define QSPI_BK1_IO1_PIN          GPIO_PIN_9
    ; D5 K& H6 n! |
  20. #define QSPI_BK1_IO1_AF           GPIO_AF10_QUADSPI
    1 }, f+ r7 n9 \
  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)5 `. W& l8 j8 J) I  G/ h

  22. + r+ E$ y6 Z+ b" J3 d  o
  23. #define QSPI_BK1_IO2_PORT         GPIOF
    + `( v2 b' [9 M( G6 b- d
  24. #define QSPI_BK1_IO2_PIN          GPIO_PIN_7
    ) o& I& ?' u7 }; B/ y# V$ L" B
  25. #define QSPI_BK1_IO2_AF           GPIO_AF9_QUADSPI  f/ V- p( R0 t7 K- U
  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). h: M1 K# {$ ^# T0 k: y% f8 F  N

  27. # G& n1 v! N6 q* o5 c8 q
  28. #define QSPI_BK1_IO3_PORT         GPIOF3 l% x- {8 j; k# I
  29. #define QSPI_BK1_IO3_PIN          GPIO_PIN_6( `! U+ f) M& ?
  30. #define QSPI_BK1_IO3_AF           GPIO_AF9_QUADSPI7 Q9 Q& o- b+ Y3 D, H1 }
  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. // 封装几个必要的接口
    ' z% D6 x/ A: t
  2. static QSPI_HandleTypeDef qspi_handle;
    $ J2 Y7 N5 i9 U7 r' T
  3. - n; X6 v2 `. d
  4. static void qspi_gpio_init(void)! }# ^5 l- X' K5 \; i9 D* Z2 `8 w. Z# C
  5. {4 V( F- _( ?: x; J
  6.   QSPI_CLK_ENABLE();
    ! E" Z# [1 V$ [

  7. * c  X: Z0 l5 G# L
  8.   QSPI_BK1_NCS_CONFIG();$ j% P; K. B! d' D. l0 v
  9.   QSPI_BK1_CLK_CONFIG();
    0 R2 r9 p5 Z, o3 M5 N
  10.   QSPI_BK1_IO0_CONFIG();! n, W! k% N3 Y2 F$ p* t
  11.   QSPI_BK1_IO1_CONFIG();- @4 \  v9 Z. a" j! S
  12.   QSPI_BK1_IO2_CONFIG();
    ! u" w) {& Z! m4 N; ]
  13.   QSPI_BK1_IO3_CONFIG();3 N" U9 r2 y3 s2 T3 _5 w/ O4 ~/ H
  14. }
    ! P9 F5 b( r; V7 }& u* z" Z/ D
  15. , U5 D7 K) p; m% R& M1 z7 [
  16. static void qspi_mode_init(void)- f' q: e: D' t+ G* M5 v) o/ I5 P+ C1 [) t
  17. {& X* k, K/ ^3 f* }
  18.   qspi_handle.Instance = QUADSPI;                        // QSPI
    % u; A. ]8 w/ R3 H" |. d
  19.   qspi_handle.Init.ClockPrescaler = 2;                   // QPSI分频比,W25Q256最大频率为104M 所以此处应该为2,QSPI频率就为216/(2+1)=72MHZ
    $ c0 @/ f$ K. j5 V8 y
  20.   qspi_handle.Init.FifoThreshold = 4;                    // FIFO阈值为4个字节2 S" g6 O, R* Y( B6 w8 n$ u0 `
  21.   qspi_handle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; // 采样移位半个周期(DDR模式下,必须设置为0)
    + G; E, D; |2 A% M) [( F( u8 {
  22.   qspi_handle.Init.FlashSize = POSITION_VAL(0X2000000) - 1; // SPI FLASH大小,W25Q256大小为32M字节
    2 G2 B5 U- {+ n: W' N! S) e- Q
  23.   qspi_handle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_4_CYCLE; // 片选高电平时间为4个时钟(13.8*4=55.2ns),即手册里面的tSHSL参数& s* d# I6 f1 B5 U
  24.   qspi_handle.Init.ClockMode = QSPI_CLOCK_MODE_0;        // 模式0- Y" i" r  ^; c; q% ?0 l" H0 Y
  25.   qspi_handle.Init.FlashID = QSPI_FLASH_ID_1;            // 第一片flash
    3 g* [! @0 o1 @. O/ y
  26.   qspi_handle.Init.DualFlash = QSPI_DUALFLASH_DISABLE;   // 禁止双闪存模式
    9 q6 _2 l3 r. r1 ~' S! Q1 N
  27.   HAL_QSPI_Init(&qspi_handle);      //QSPI初始化
    % ^; w' _) I1 q( R( d
  28. }0 ^7 W3 ^0 L6 q9 D% u
  29. ; r/ B( V, U4 Q* Z. D; H& C
  30. void QSPIInit(void)
    1 y& a. o7 a! ~2 A$ |  a( L- p
  31. {2 X" q  P0 F- @/ V0 x6 P$ @# V
  32.   qspi_gpio_init();
    / x* k8 e" m8 s
  33.   qspi_mode_init();" n' s6 Z! E6 h% c( u& N. O6 V5 u  W
  34. }2 g1 {9 J# a! {

  35.   K' [# t' Q5 Z% d
  36. // QSPI发送命令- b" Z- s% [% _6 t. y$ R) G
  37. // instruction:要发送的指令6 M0 O" O$ U, T6 R% B
  38. // address:发送到的目的地址
    7 q4 {+ |  o% ^* x7 N. k
  39. // dummyCycles:空指令周期数( P: @7 e' c) n6 X2 d% W
  40. // instructionMode:指令模式;QSPI_INSTRUCTION_NONE,QSPI_INSTRUCTION_1_LINE,QSPI_INSTRUCTION_2_LINE,QSPI_INSTRUCTION_4_LINE, z7 N/ j7 m  X: C8 u; p# Q
  41. // addressMode:地址模式; QSPI_ADDRESS_NONE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_2_LINE,QSPI_ADDRESS_4_LINE
    4 L3 {5 [4 P$ P7 u; Z$ B9 K" D
  42. // addressSize:地址长度;QSPI_ADDRESS_8_BITS,QSPI_ADDRESS_16_BITS,QSPI_ADDRESS_24_BITS,QSPI_ADDRESS_32_BITS7 N, Q4 D) g7 p/ o9 L* c
  43. // dataMode:数据模式; QSPI_DATA_NONE,QSPI_DATA_1_LINE,QSPI_DATA_2_LINE,QSPI_DATA_4_LINE& @8 Z8 W' ]/ }% @  n
  44. void QSPISendCMD(uint32_t instruction, uint32_t address, uint32_t dummyCycles, uint32_t instructionMode, uint32_t addressMode, uint32_t addressSize, uint32_t dataMode)
    5 m7 r% ^$ j9 N0 h3 }" k* u
  45. {  ~7 b: l* K' S
  46.   QSPI_CommandTypeDef Cmdhandler;4 z& Z6 p5 Z/ p& w5 l. A* h* A# D

  47. ' f2 Y+ C! ]' w0 J8 O& G
  48.   Cmdhandler.Instruction = instruction;           // 指令9 `' h+ c& }+ X- m( _: X; v& g
  49.   Cmdhandler.Address = address;                   // 地址8 X! u$ l" v' Q; R  `
  50.   Cmdhandler.DummyCycles = dummyCycles;           // 设置空指令周期数
    1 f: {$ [! M; P( x. [( _
  51.   Cmdhandler.InstructionMode = instructionMode;   // 指令模式
    ! X0 M: u, F, Z$ s  @1 H
  52.   Cmdhandler.AddressMode = addressMode;           // 地址模式
    ( K; Q. F4 J! M/ L
  53.   Cmdhandler.AddressSize = addressSize;           // 地址长度
    3 h$ ], d2 U0 N5 h" ^
  54.   Cmdhandler.DataMode = dataMode;                 // 数据模式! ]' I+ n! y$ V: E! u
  55.   Cmdhandler.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 每次都发送指令4 k9 C$ D* [- k/ H- t: Q
  56.   Cmdhandler.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 无交替字节
    " Z& f! E& Y+ f" P
  57.   Cmdhandler.DdrMode = QSPI_DDR_MODE_DISABLE;           // 关闭DDR模式/ U  |# V9 O% O
  58.   Cmdhandler.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    2 u/ W2 x# k  e7 z* }) s/ {+ F

  59. & V# b' j( r) e/ H
  60.   HAL_QSPI_Command(&qspi_handle, &Cmdhandler, 5000);
    # s# |& ]- Y8 r- E: K
  61. }
    7 j1 @3 ]0 K+ @. s  f+ [" G1 a7 q

  62. : @* v7 W! F8 f
  63. // 接收指定长度数据
    " g+ W# \+ E. o: H7 J0 s
  64. uint8_t QSPIReceive(uint8_t *buffer, uint32_t length)' K" M) f0 W) s8 @; j0 V' @$ r
  65. {0 o* M' g8 q3 d! F
  66.   qspi_handle.Instance->DLR = length - 1;9 x7 x, G4 h. |8 o
  67.         " _$ p# [( u' b
  68.   if(HAL_QSPI_Receive(&qspi_handle, buffer, 5000) == HAL_OK)
    4 v. r& T4 ~( v. x+ y' B, |5 n
  69.   {
    ! l+ P/ v, C, |+ u
  70.     return 0;  //接收数据
    - X% q3 R/ _; F# D
  71.   }" g# Q8 S  Y, g# Q: O
  72.   else0 I& ]- _7 t1 z
  73.   {
    0 Z! h* r0 d+ Y5 I
  74.     return 1;+ v4 `8 j% c+ |& z- |
  75.   }0 ?8 e/ Z  {8 m8 ]
  76. }
    1 N* [! W4 g5 k" Q! _) S) p

  77. + m9 b# o- D0 g& v, U8 O8 p
  78. // 发送指定长度数据; P2 s" t# x4 G* _
  79. uint8_t QSPITransmit(uint8_t *buffer, uint32_t length)
    8 x  v1 F& d" J6 u
  80. {
    3 S1 G! J4 ~$ D! N2 V$ \) _
  81.   qspi_handle.Instance->DLR = length - 1;  `% }% G+ y# f: B9 i* _
  82.   if(HAL_QSPI_Transmit(&qspi_handle, buffer, 5000) == HAL_OK)
    ( \$ Y3 @) A4 S. l: p
  83.   {
    0 z' q% @  ]8 |0 K
  84.     return 0;  //发送数据
    , h. a- Z- G/ e3 z3 o. g
  85.   }
    ; m1 J) t8 b8 \% M: Y& q0 B
  86.   else
    6 S2 [- C! {4 X# d' v8 K. }
  87.   {
    ' W$ }( k. ~8 I" P% O! \
  88.     return 1;
    4 K% k2 q3 {6 V( A3 v
  89.   }( i0 L, F, h9 _7 ~! I
  90. }
复制代码
! S- y2 H& j( g, J+ M
五、QSPI驱动W25Q256& Y4 J7 m$ k1 {/ Z
W25Q256:32M  512块,每块16个扇区,每个扇区4K。具体指令看手册。封装了如下接口(初始化读写擦除等接口是以后移植文件系统的基础):
  s8 q) E: S9 C: O( z- K+ h, y/ D' B. S
  1. #define W25QXX_WRITE_ENABLE      0x069 W. v9 _3 T6 M9 o/ g
  2. #define W25QXX_WRITE_DISABLE     0x04
    5 T* x/ Q6 V' [6 j2 e
  3. #define W25QXX_READ_STATUS_REG1  0x05
    # F8 n. v& h( Z# k( S) ^
  4. #define W25QXX_READ_STATUS_REG2  0x35/ ]7 p4 k9 z& E
  5. #define W25QXX_READ_STATUS_REG3  0x15
    . B/ R4 a% Y7 q5 A1 P8 m
  6. #define W25QXX_WRITE_STATUS_REG1 0x01
    % y" |2 [, N; |- i5 P. ?' P( e
  7. #define W25QXX_WRITE_STATUS_REG2 0x317 n' Y6 a6 x( R, E" A) r  B
  8. #define W25QXX_WRITE_STATUS_REG3 0x11
    ; v8 A* T9 S/ A, B
  9. #define W25QXX_READ_DATA         0x03( D; d8 o- K: d" J3 B( L3 l
  10. #define W25QXX_FAST_READ_DATA    0x0B4 m  ?7 y; ^" Q5 ~# y& @
  11. #define W25QXX_FAST_READ_DUAL    0x3B/ ^  j  T4 D! n# @3 c8 o
  12. #define W25QXX_PAGE_PROGRAM      0x02- G: m2 b4 J! _2 S1 v! E" h6 ]
  13. #define W25QXX_BLOCK_ERASE       0xD8
    8 M* x$ K- D5 q) _+ F: v- {
  14. #define W25QXX_SECTOR_ERASE      0x20
    ; b/ u. S4 A# y3 h: O
  15. #define W25QXX_CHIP_ERASE        0xC72 s* Q3 p- ~: H' _) }
  16. #define W25QXX_DEVICEID          0xAB  |8 y5 O5 V4 [* u" W, z# e
  17. #define W25QXX_MANUFACT_DEVICEID 0x90* U' o  O. b5 ]; E' \- a3 ^
  18. #define W25QXX_JEDEC_DEVICEID    0x9F2 a# f$ d' m) {
  19. #define W25QXX_EABLE_4BYTE_ADDR  0xB77 E' c4 t# ]+ m1 E
  20. #define W25QXX_EXIT_4BYTE_ADDR   0xE9, E2 k% }# b: e3 \9 S
  21. #define W25QXX_SET_READ_PARAM    0xC0
    ' L3 Y1 z' O& D
  22. #define W25QXX_ENTER_QPIMODE     0x38
    7 d% A6 i7 f0 `9 z5 l  s
  23. #define W25QXX_EXIT_QPIMODE      0xFF
    ! j) V9 u$ l' X, k1 v. z2 Y
  24. 7 N+ I2 u  \& B' b) w
  25. uint8_t w25qxx_qpi_mode = 0; // QSPI模式标志:0,SPI模式;1,QPI模式.
    ) {- [4 c; H( n0 R( Y

  26. 9 O" v; W& c' Y! @. O# {7 v
  27. // 读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器
    , e" U' S9 {2 d. r
  28. // 状态寄存器1:
    $ y# u* k" V/ }- E* N6 A! R1 K6 }4 |
  29. // BIT7  6   5   4   3   2   1   0
    ! \& [, @0 o9 ^3 R$ F$ D( D
  30. // SPR   RV  TB BP2 BP1 BP0 WEL BUSY4 {+ r! \# s$ x9 }% _
  31. // SPR:默认0,状态寄存器保护位,配合WP使用  c8 b/ B' {1 \! s; W& T' {% W; j( j
  32. // TB,BP2,BP1,BP0:FLASH区域写保护设置
    ( S0 C5 ], u4 ]
  33. // WEL:写使能锁定0 ?8 ?( O- Z2 ]: P( g
  34. // BUSY:忙标记位(1,忙;0,空闲)
    5 U0 I$ A# r3 `! r& M
  35. // 默认:0x00, ^6 s% ~* \1 p: W2 {
  36. // 状态寄存器2:- x+ w# G7 N8 l: C1 \1 b
  37. // BIT7  6   5   4   3   2   1   0! w! d7 c" _: K- t8 H
  38. // SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
    9 J: l2 w7 y0 z; _
  39. // 状态寄存器3:
    * H. d& `  v- Z+ n# x) T; c. j2 w
  40. // BIT7      6    5    4   3   2   1   0  q3 v  e. @+ C$ b* p
  41. // HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
    : b9 l* V: X: x- x' u
  42. // reg:状态寄存器号,范:1~3
    2 F3 B" x* r/ I
  43. // 返回值:状态寄存器值
    , t$ u2 ^/ u3 y& L4 h' L3 K  U
  44. static uint8_t w25qxx_read_status(uint8_t reg)
    " m+ Z6 N' g1 b2 s- W/ V6 G) |
  45. {8 M# E: ~1 m/ ~4 o' r6 [0 n
  46.   uint8_t value = 0, command = 0;- A$ ?( |* i. P
  47. * w! L. t9 n. v' x4 \+ l0 L. A2 Y
  48.   switch(reg)/ P; @1 t$ _) p0 e% `, E. Q8 O
  49.   {/ w1 Z! f6 Z' Y: B2 l% }
  50.   case 1:
    ' J5 `# f$ c  \' D- p- M6 m" o
  51.     command = W25QXX_READ_STATUS_REG1;  // 读状态寄存器1指令! {% A' e/ F/ S6 Y& r
  52.     break;0 ?# A$ ~& A$ l( ^+ n+ O

  53. - _1 ]7 y! Q  p* L7 V
  54.   case 2:
    0 m1 _! J7 `/ _
  55.     command = W25QXX_READ_STATUS_REG2;  // 读状态寄存器2指令
      t& B5 u- e+ {% f
  56.     break;& i9 ]" U* w2 Q- |1 r2 X" R
  57. 0 V( X/ U# A4 i( R
  58.   case 3:
    , R& J9 ^/ I+ ~
  59.     command = W25QXX_READ_STATUS_REG3;  // 读状态寄存器3指令' O. i) N2 z5 a( g' l
  60.     break;
    # ]$ _- Z7 u  ~3 C

  61. ' m% ^+ n# l5 u; w
  62.   default:
    + ^- m: K- s, C" d: i& ~
  63.     command = W25QXX_READ_STATUS_REG1;  s7 u7 V# \9 }- i) V, c% o# s# x) E
  64.     break;, m% I5 I' h+ _2 s# ?% @8 V
  65.   }2 f/ [7 U* k8 w( m3 i0 h
  66. * G$ I; d5 _# B# S* o
  67.   if(w25qxx_qpi_mode)
    / I, G) O9 A2 c5 g( |
  68.   {+ l8 q. Y# F  v' R, N8 D
  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个字节数据5 v  L+ |  W6 q* \$ Z
  70.   }
    * e  A/ k3 x: d2 I) W0 T
  71.   else. W( v4 t! n7 w, H8 I; _2 I. f% K2 V! K
  72.   {/ g) x  z7 h% k- ]
  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个字节数据
    ) k- c% n0 ]" K! @4 N8 W
  74.   }9 b/ A: ~- R4 R5 i1 L  ?
  75. - X- s! o" v  o, E$ h8 x
  76.   QSPIReceive(&value, 1);( A+ ]# j5 P$ a7 Q+ h2 @- |; A

  77. 3 Y* F$ o* i9 U/ Q
  78.   return value;
    7 |9 Y; D3 m' b7 q3 _
  79. }. O8 w( M+ [* w& J8 V9 W* U

  80. ' W8 F; x* G6 k# l5 C# R
  81. // 写状态寄存器
    7 L2 y8 U! _2 ^/ O
  82. static void w25qxx_write_status(uint8_t reg, uint8_t status)
    ' Z5 f0 n( E) m8 Y3 A1 Z
  83. {5 \3 i1 S: ?- F4 a% `# h
  84.   uint8_t command = 0;
    , d# V* `5 j! @. @4 H( y' }. C

  85. 1 {( T3 R/ J4 K$ i" P% J
  86.   switch(reg)
    " r9 l* L. h- G& C+ F% X. J
  87.   {  `# S/ R5 y1 s+ h( W
  88.   case 1:
    1 X. R5 T/ {" I2 A, m: A. T/ D
  89.     command = W25QXX_WRITE_STATUS_REG1;  //写状态寄存器1指令
    ) {; S- s) D  h0 {* r
  90.     break;
    8 w0 C* C! D& R' u6 a1 K+ V
  91.   case 2:2 W: g3 \9 p' u0 E- {! K- A
  92.     command = W25QXX_WRITE_STATUS_REG2;  //写状态寄存器2指令
    ) d, [  a8 H. o
  93.     break;2 H$ A- }( F! N0 r
  94.   case 3:
    * U6 A$ `( c7 V
  95.     command = W25QXX_WRITE_STATUS_REG3;  //写状态寄存器3指令
    1 J6 k% h3 h! P, N7 Q# A% x
  96.     break;
    7 _2 y" \1 p9 h
  97.   default:
    6 B# O1 m7 ^5 M5 P# A, U2 E
  98.     command = W25QXX_WRITE_STATUS_REG1;& \& n9 ~6 Q  Z- ?+ z) e
  99.     break;
    8 }6 x7 y$ D7 ~" J) o+ x
  100.   }
    - W, h6 K- A* j# r5 X7 b9 v
  101.   if(w25qxx_qpi_mode)0 b7 a; P+ O0 m4 y& k( a5 b0 s
  102.   {
    ' U( u- z1 b4 g" E) j# j
  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个字节数据# h; ]1 U0 L6 p
  104.   }
    9 D9 `1 N6 `1 m0 C- R" z8 W8 I9 q
  105.   else
    ) V6 x9 r8 ?) J) C+ g% ~6 t
  106.   {% J9 D. G& Y* p, l5 W
  107.     QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_1_LINE); // SPI,写command指令,地址为0,单线传数据_8位地址_无地址_单线传输指令,无空周期,1个字节数据
    3 A, `* y) g0 d6 a2 B5 S
  108.   }
    7 L; W0 ^2 y. ?; f3 N0 J- _
  109.   QSPITransmit(&status, sizeof(status));
    6 |) ~9 d( f) ~* M' Z
  110. }. A9 |% L7 E9 p1 j
  111. / G2 {4 j4 S6 e3 x5 V  ~! `  f3 }
  112. // 写使能  将S1寄存器的WEL置位
    . r  _. P* \3 I) \  z' y
  113. static void w25qxx_write_enable(void)" I! V6 ~- Y4 |/ Q, E  j
  114. {
    ' o, S5 ?8 A% y8 j* `9 E: z
  115.   if(w25qxx_qpi_mode)
      d- _7 s% ?- n$ M
  116.   {- M! m' m9 z* h  i0 M- d5 O: _
  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个字节数据
    6 [' @; J% m( {9 r# g8 N
  118.   }
    ( I. o* J! _# ~
  119.   else% P! ~; Q! a0 q3 T
  120.   {
    0 V0 ^0 S7 f7 ]9 E* ~, y6 I& 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个字节数据* t# H4 {% I/ C' f$ E' K- H' j
  122.   }# k1 ^* H; F  N  B3 |
  123. }
    5 b7 [4 m3 {, ], F' p2 L, g

  124. 8 h# B  z+ H9 p  f# ]
  125. // 写失能  将WEL清零
    / p; o& }3 E. u$ _
  126. void W25QXX_Write_Disable(void)7 D6 M: T, c. t9 y' F
  127. {
    5 \2 f  z6 @5 n0 |
  128.   if(w25qxx_qpi_mode)8 T$ s- D: `; L' f
  129.   {3 r& }) H4 `$ `  O7 T
  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个字节数据
    2 A1 f, E) E9 ^, {" d
  131.   }7 F" T8 A2 G+ {- Q; d
  132.   else
    6 G7 a' `! {3 g+ X( O( T# x: W% |
  133.   {. {2 l' P4 Y. ?1 c" S6 \+ g: K
  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个字节数据/ X2 y; s! Z( @& r
  135.   }; b& x' ?. a. g) w
  136. }5 W, a. ?1 Q" c* _) L

  137. 4 R. g' }. P) n. ~+ u
  138. // 等待空闲) q2 K% ^) y# n& j. ^# c% u
  139. void w25qxx_wait_busy(void)
    $ ^0 v8 c# h7 j
  140. {
    6 i' ~. E& m3 n% I' R  ?3 g
  141.   while((w25qxx_read_status(1) & 0x01) == 0x01); // 等待BUSY位清空
    1 ~0 M' ]' K- K
  142. }& m& o; x; n0 y9 U' }) M6 `0 n  h0 F

  143. ( u6 x) J5 Z( I8 p
  144. // W25QXX进入QSPI模式5 ~" Q0 y2 I+ s5 ~
  145. static void w25qxx_qspi_init(void)
    & W0 Q6 E( w6 `0 r2 o$ z3 g
  146. {
    $ [; U) w; r4 Y3 {6 `' y  n& L" g
  147.   uint8_t reg2;; X6 u1 p) b; _0 M
  148.   reg2 = w25qxx_read_status(2); // 先读出状态寄存器2的原始值
    ) z9 u# p; E; s3 A/ L
  149.   if((reg2 & 0X02) == 0)  // QE位未使能
    5 G* C7 U6 {6 J0 }5 t
  150.   {
    9 S7 H2 \/ C6 E6 m# @5 N* ^
  151.     w25qxx_write_enable(); // 写使能1 o- s  P& z! F( T
  152.     reg2 |= 1 << 1; // 使能QE位% ?& [. `4 r! V9 E4 \7 S# ?
  153.     w25qxx_write_status(2, reg2); // 写状态寄存器2
    1 R5 Y. O; C3 u; y9 M2 d2 ^
  154.   }
    9 Q+ M3 E. @2 Z$ f# m, A

  155. 2 T' k8 y: I1 K' Q, j( p$ R0 M
  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个字节数据+ _" r- U+ `5 ~
  157. & v& \8 |5 i% Y
  158.   w25qxx_qpi_mode = 1; // 标记QSPI模式
    7 ]8 @% [. d. w# a6 [% B& K/ M
  159. }
    ( U+ c- Y) o. L3 n, q/ G# h2 V5 s

  160. ! o6 \4 z1 F  v3 d
  161. // 0XEF13,表示芯片型号为W25Q80
    - N. a; x; |" e
  162. // 0XEF14,表示芯片型号为W25Q16& l9 p% C( Q! K# h9 x' J& d( i
  163. // 0XEF15,表示芯片型号为W25Q32
    ' {2 }8 H) ]% T: ~0 s4 o% D
  164. // 0XEF16,表示芯片型号为W25Q64
    7 _& b1 M  {3 a2 O8 ]
  165. // 0XEF17,表示芯片型号为W25Q128
    $ s' F: C0 A  ?6 C) E4 V* c. `. f1 f5 J
  166. // 0XEF18,表示芯片型号为W25Q256# s" e6 Q, R9 @+ e- M  v  k
  167. static void w25qxx_id_get(void)# @8 M0 L5 O- ^: X+ B
  168. {" a% A. S# C& O: _0 e$ d
  169.   uint8_t temp[2];8 V8 a; |) L8 t1 O3 K! k
  170.   uint16_t device_id;
    1 a* R* L! k) @6 Y& ?. s! d
  171. ; ^) Y0 j$ H) y' O- b8 J
  172.   if(w25qxx_qpi_mode)
    3 P+ c5 `# h5 ]: E" v
  173.   {" u& {* ?8 f( p1 e6 G. D: U
  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个字节数据
    & T/ @9 x; n  x0 h1 Y/ T  C
  175.   }
      Y3 u& U- k! p% Z
  176.   else
    & k1 C& C$ f: |5 \
  177.   {% R' S) @; q& P! p6 s* ^
  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个字节数据
    8 ?1 X# a8 x2 N7 S. b7 H
  179.   }
    . D3 V* O$ s& S* e# M  x; [! R
  180. # T2 [$ U0 u( ?5 w) z& ]
  181.   QSPIReceive(temp, 2);' k; p3 V7 Y/ C) V5 _8 v8 v

  182. ! R9 r3 e2 e8 Z: G9 S8 `2 c
  183.   device_id = (temp[0] << 8) | temp[1];. w, e6 o* C: Z. O6 n

  184. & V4 Z. L$ b; C
  185.   printf("QSPI Flash Device ID: %X\r\n", device_id);! `$ x$ J& Z5 x6 l0 A; V/ \$ u
  186. }
    # U" @. y4 _2 V" U% V- ^; o

  187. 4 @5 m* P0 n9 k) ~9 ]+ H
  188. void W25QXXInit(void)
    + u+ a1 f; Z. ?" D3 g7 f- J" N
  189. {
    $ M& y  s9 F" i" k0 _3 v
  190.   uint8_t temp;
    & [# O9 N8 Y$ W( t
  191. 5 B, J5 ?/ b  v" L( A
  192.   QSPIInit();" Z$ u* Y* n" Q- t9 D5 w
  193.   w25qxx_qspi_init(); // 使能QSPI模式
    - d! w2 G  M+ N# G" L& k
  194.   w25qxx_id_get();    // 读取FLASH ID.
    ) O+ b9 R; L* L* a8 r9 u
  195. - E1 K# U! I) \6 Q6 l
  196.   temp = w25qxx_read_status(3); // 读取状态寄存器3,判断地址模式
    ! O! P. V/ y' o! s2 |
  197.   if((temp & 0X01) == 0) // 如果不是4字节地址模式,则进入4字节地址模式
    ; T" u5 O$ h3 o9 e# d0 _1 ?) ^
  198.   {# e! t) \3 O! T: \3 o. n4 I: t
  199.     w25qxx_write_enable(); // 写使能
    % |" Q' ^7 B3 @; c. R. s( n4 m
  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个字节数据) ?( q, ]9 i+ b& I( ]7 t0 v
  201.   }8 ]7 O% |' p2 U4 }0 {' b0 X
  202.   w25qxx_write_enable(); // 写使能
    3 `; Y" O6 ~" g+ T) a. m
  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个字节数据& I' o: _) x1 K0 L1 {
  204.   temp = 3 << 4;      // 设置P4&P5=11,8个dummy clocks,104M7 z  C* j0 h# @1 M, G- ^& @
  205.   QSPITransmit(&temp, 1);) R* O& y- a+ Z1 E+ a6 O
  206. }
    2 n$ ]8 L, ~6 G' h. B

  207. 9 U+ h% t! x- q- b! P
  208. // 擦除一块4096字节 最少需要150ms9 H% L' m! A8 L
  209. void W25QXXSectorErase(uint32_t addr)0 H8 T( E1 H" V, z5 }
  210. {% U3 N; z" }1 ]) @9 f
  211.   //addr /= 4096;5 A# [, Q7 D& d  K2 P: t0 l
  212.   addr *= 4096;3 `# l2 m* L; K! f! ?* ^+ j

  213. / Z: w/ l3 r+ Z7 V/ q  V
  214.   w25qxx_write_enable();2 ?5 Z" j% j/ s+ \& s
  215.   w25qxx_wait_busy();7 O; D5 Z& S  F" j, L) _
  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个字节数据' Q% y# A% N5 |8 |+ ?" ?( |
  217.   w25qxx_wait_busy();
    4 H# m4 ~; U( j( h, ~* i
  218. }
    5 R" b; O% @" G2 v+ a: b; y1 V

  219. # q( Z, h. X2 S0 [

  220. 9 D6 e- x5 r" Z+ z* @# @- A
  221. // 擦除整个芯片
    3 ~) }  D9 G! X8 R
  222. void W25QXXChipErase(void)# j: C8 ^7 i9 E0 I3 u/ e* w
  223. {
    1 @+ L3 J2 {, m
  224.   w25qxx_write_enable();          //SET WEL
    ; m9 Z5 M& T: d; j$ P7 i& h
  225.   w25qxx_wait_busy();7 {  R9 t- {7 l; @
  226.   QSPISendCMD(W25QXX_CHIP_ERASE, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // QPI,写全片擦除指令,地址为0,无数据_8位地址_无地址_4线传输指令,无空周期,0个字节数据
    7 i6 Z- W# J0 h; A& ]- v
  227.   w25qxx_wait_busy();           //等待芯片擦除结束5 t0 Y$ ~$ V/ L
  228. }/ R5 L8 I/ O- k# y/ h
  229. 9 N0 r4 Y7 w  K5 U2 Q9 _/ p
  230. // 写最多256字节9 j( d% M; `7 u: _3 H
  231. void W25QXXWritePage(uint32_t addr, uint8_t *buffer, uint16_t length)( }4 x3 w9 L+ P# R- @2 m, p
  232. {
    ' o9 g6 e( }  _! ?1 P" `+ x
  233.   w25qxx_write_enable();          //写使能
    ( d; N3 d4 I) r( f$ e- r% Y
  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个数据
    " v7 j- q- d* f: ?
  235.   QSPITransmit(buffer, length);
    % \: \% v1 Z/ Z) |) v; D$ y
  236.   w25qxx_wait_busy();            //等待写入结束5 h! x+ P, I! N7 M% s; i+ z( I
  237. }
    6 b$ Z, f) E. C2 R3 _

  238. / ?' Y' Q$ M, b0 w
  239. // 写最多65536字节 无擦除动作
    ( b& I4 u/ E' I# S. D
  240. void W25QXXWriteExt(uint32_t addr, uint8_t *buffer, uint16_t length)7 v( |$ S) B0 A& d
  241. {
    * q5 u- p# Z4 W/ @7 S
  242.   uint16_t pageremain = 256 - addr % 256; // 单页剩余的字节数
    ; I2 E3 \- o! m

  243. ' I2 L5 Y# p+ i. y
  244.   if(addr + length - 1 > (32 * 1024 * 1024))4 T# U+ Y  q5 n) i
  245.   {
    6 ~, f" c7 T. F2 d/ I$ G" c
  246.     return;0 |* }0 v) K' U
  247.   }
    + m  @) s3 I  @1 c$ L
  248. ' m1 r2 u" O" r
  249.   if(length <= pageremain); E4 `- v3 v" x, \& z1 c0 K* ]9 t0 f
  250.   {
    ( r1 W9 z  k5 l' }0 x
  251.     pageremain = length;  // 不大于256个字节
    3 T! S0 F3 Y# `3 a! d' O# `# B
  252.   }
    8 j0 I0 \$ s+ a* ]
  253.   while(1)5 F+ J1 g3 U- O+ `/ B- N, C
  254.   {9 ?8 k& X. F# L' D/ `/ i8 g
  255.     // 分页写入
    3 B, A' |2 {4 |7 X% S0 w
  256.     W25QXXWritePage(addr, buffer, pageremain);
    $ D1 t* v% v6 m# O9 N( c, }3 r; a  E9 A
  257. / O/ P% O0 K( l
  258.     if(length == pageremain)& t% d# A( O) L: g9 \1 M! Y4 B
  259.     {7 q% l' T5 h" f8 R& C/ P. G
  260.       break;  //写入结束了
    9 J7 p& i2 s+ [! N2 A5 G, j# f$ p* F
  261.     }$ C% R; z3 I! [9 v
  262.     else
    3 N5 J1 D9 s) _$ n  N
  263.     {
    0 `$ L' g8 {# E: w8 c
  264.       buffer += pageremain;. k$ x( b% ~1 `! P' P0 ^
  265.       addr += pageremain;
    3 f( M- t$ }- Q, ~% t
  266. 7 b# E6 C' J( s
  267.       length -= pageremain; // 减去已经写入了的字节数: ?4 v, f9 w7 C* `3 q9 t
  268.       if(length > 256)
    , ~* }9 ^+ J; I- u6 E  H' U
  269.       {1 K( S- G* p3 m5 d) V
  270.         pageremain = 256;  // 一次可以写入256个字节" A. U& C& Q3 j+ W5 q
  271.       }
    $ @+ V* D' @9 G0 @! F8 j
  272.       else
    ' I3 d4 \) j5 A( W; e9 C$ B1 y8 c4 T
  273.       {
    ( O1 o. R+ r8 B; _2 S" Y8 ~
  274.         pageremain = length;  // 不够256个字节了2 J5 F4 J8 Y$ n' ^& m: b: @2 i
  275.       }3 O0 ?2 ]0 s5 q4 R* |2 V6 j; J
  276.     }
    3 d; o  t! P5 @" A5 v+ n
  277.   }
    . e$ T1 D; K3 m% E
  278. }8 J- _/ z0 x& @% ^! e0 z; \$ w  ]) o

  279. / g8 S+ G4 _: C5 [7 T5 W/ i+ I1 n4 G
  280. // 写最多65536字节 带擦除动作
    $ V; Z% {3 C2 k" t3 \$ v. A; G7 @+ O( R
  281. static uint8_t W25QXX_BUFFER[4096];$ c5 p1 x3 F- Y( X$ T8 G
  282. void W25QXXWrite(uint32_t addr, uint8_t* buffer, uint16_t length)
    & m4 q2 w0 a3 O
  283. {
    ! Y' U3 U3 O9 V4 ~/ m
  284.   uint32_t secpos;( a- ^9 a+ U7 l7 H- @6 |
  285.   uint16_t secoff, secremain, i;
    9 w4 L3 ~' @& d2 z( y" P% u/ t
  286.   uint8_t * w25q_buf;: Z; @7 P$ W- f
  287. # J1 Z/ x4 k4 h; `# b' l
  288.   w25q_buf = W25QXX_BUFFER;3 l# k* W9 k% M' u2 x7 A1 u

  289. 2 N% M% O4 i% n
  290.   secpos = addr / 4096; // 扇区地址4 C  A% D! B6 _$ h+ F; ~* P# j' I+ l) t
  291.   secoff = addr % 4096; // 在扇区内的偏移* i) s% S0 d8 ]( T: Z, \# ?
  292.   secremain = 4096 - secoff; // 扇区剩余空间大小# j' v0 p& n! g8 ]9 P

  293. ( L6 v2 t8 K/ e. |, I+ b( i
  294.   if(length <= secremain)( @0 s$ C2 p: Q* `  o, T
  295.   {* W! e$ H3 D2 l; |  k( p
  296.     secremain = length;  // 不大于4096个字节* G* B( V4 c9 r
  297.   }0 y) w& T3 E  l% i/ a& l
  298.   while(1): p( S8 D% S7 Z7 Z+ X; q; E- A2 ~  X4 s
  299.   {
    ' R# F% y1 N$ Y, y% Q4 l
  300.           WatchdogFeed();
    / V( U! c+ j, S  }/ x& i* x* C3 V5 G
  301.     W25QXXRead(secpos * 4096, w25q_buf, 4096); // 读出整个扇区的内容( p* t- D( {5 j# o) T9 O+ W
  302.     for(i = 0; i < secremain; ++i)
    1 z( J  J+ |( O
  303.     {
    5 B6 D% g, X5 p  t  Q& @
  304.       if(w25q_buf[secoff + i] != 0XFF)
    ' f8 m# L# V+ V8 o) l1 M+ @5 s
  305.       {0 W8 r/ E; ?! y9 K( l: Y6 p, i
  306.         break;  // 需要擦除
    4 B. ^! m3 F, s+ p+ Y* D
  307.       }7 O# y. V9 g! h' j5 d
  308.     }8 r2 A2 R0 U# S' F* b+ a3 H
  309.     if(i < secremain) // 需要擦除" D( F' y8 }' D& ~, y! n6 \4 ?! N
  310.     {
    0 s7 `1 z8 X2 Y  ~/ h" b- E5 p
  311.       W25QXXSectorErase(secpos); // 擦除这个扇区
    ) h- }! g: X( r( @+ b, t
  312.       for(i = 0; i < secremain; ++i)
    ( H9 E" u/ r& r" z& J
  313.       {
    8 S' c# w! K" A" i; T( o. x& r
  314.         w25q_buf[i + secoff] = buffer<i>;</i>
    4 A+ E, ~* r3 F, A9 K
  315.       }! o$ X% [6 [) j; Y0 _
  316.       W25QXXWriteExt(secpos * 4096, w25q_buf, 4096); // 写入整个扇区
    * K- E9 D' f0 L8 [* j- Z9 j! L' Z

  317. ( w0 z, n9 [" c
  318.     }
    5 c; j0 V7 J# I
  319.     else
    : I1 C- i  p8 o8 J# o3 K# h) ~# c
  320.     {
    % S7 c$ S4 t! G% m$ Q: Q
  321.       W25QXXWriteExt(addr, buffer, secremain);  // 写已经擦除了的,直接写入扇区剩余区间." d/ ~# \7 @, V2 K5 y) F, V
  322.     }; W* B/ X; {" P; u3 V  `
  323.     if(length == secremain)
    1 }0 L& n$ O  t4 w
  324.     {
    $ N/ }8 ^, t( N3 T1 p
  325.       break;1 \: m( `0 s, S6 ^/ Z5 F
  326.     }
    & k" J: e; u8 v* ^
  327.     else
    / k* J5 t# a- m/ h, \" D
  328.     {1 {7 V; b, {- s* o( M, k, W5 d
  329.       secpos++;
    4 ]. P$ S  f, F
  330.       secoff = 0;
    4 l" g- m5 g# ~' D9 _5 n5 r

  331. 3 a6 ?5 h# M0 w2 c5 @9 X( A+ |! o
  332.       buffer += secremain;) x/ k* |9 B3 V
  333.       addr += secremain;
    4 M" d, x( Y( ?
  334.       length -= secremain;
    + j0 a9 k7 g- Z
  335.       if(length > 4096): o- {2 r5 l9 {- d  M
  336.       {
    * e2 v9 ^2 V/ \
  337.         secremain = 4096;) U. i; c) v. x) m7 A
  338.       }
    7 w% A  t4 e2 k" [. G* d. V
  339.       else
    ! ]0 V5 l/ _2 V! Y5 q
  340.       {
    . t2 F6 I1 {% D$ e
  341.         secremain = length;
    9 v2 A# r* v. W- R( w
  342.       }
    : @% F  Q2 K, Q/ F* E6 G
  343.     }
    + i3 B0 Y* }  ?7 M. P% }3 w' E) f7 M
  344.   };0 U1 ]# P5 T, ^* @* q8 O
  345. }
    7 p4 Z) O4 n6 y5 `; U- z6 V6 s
  346. # d* J; v+ s7 G, [) S1 H

  347. & }) i, Q. {! x; y5 W6 X& H
  348. // 读取SPI FLASH,仅支持QPI模式  在指定地址开始读取指定长度的数据 65536; d; k$ S1 |  m, S9 F1 {! k+ p- x7 g
  349. void W25QXXRead(uint32_t addr, uint8_t *buffer, uint16_t length)
    0 V/ j- ^3 c, C# C
  350. {
    $ L; T! b+ u, ^; \: u
  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个数据6 e: I  W" D7 Y. F
  352.   QSPIReceive(buffer, length);
    ' S; r3 B& Y" U: D2 V$ Z/ w% F, L
  353. }
复制代码
, c2 _3 V. Q3 D: W3 _* j9 a
也可以使用SPI操作W25QXX。
3 y  d7 [. T+ p/ w2 Z$ r, }/ [9 H9 {" |8 u3 i0 z
5 ]6 M4 z. E( _

* X0 O8 I* n/ Z! W; `( _
收藏 评论0 发布时间:2021-12-11 12:00

举报

0个回答

所属标签

相似技术帖

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