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

【经验分享】STM32F7xx —— QSPI

[复制链接]
STMCU小助手 发布时间:2021-12-11 12:00
一、QSPI% _/ ^- R3 e7 J- W8 l
        SPI 是 Queued SPI 的简写,是 Motorola公司推出的 SPI 接口的扩展,比 SPI 应用更加广泛。在 SPI 协议的基础上,Motorola 公司对其功能进行了增强,增加了队列传输机制,推出了队列串行外围接口协议(即 QSPI 协议),使用该接口,用户可以一次性传输包含多达16个8位或16位数据的传输队列。一旦传输启动,直到传输结束,都不需要CPU干预,极大的提高了传输效率。该协议在ColdFire系列MCU得到广泛应用。与SPI相比,QSPI的最大结构特点是以80字节的RAM代替了SPI的发送和接收寄存器。QSPI 是一种专用的通信接口,连接单、双或四(条数据线) SPI Flash 存储介质。  b" _: y! L/ f% Q

. X: F1 W+ ]+ \% \ 该接口可以在以下三种模式下工作:
7 w" n' a; e8 r, n4 a' d5 h& W1 _+ S① 间接模式:使用 QSPI 寄存器执行全部操作- ^% o8 ]! u$ g7 q% V1 o
② 状态轮询模式:周期性读取外部 Flash 状态寄存器,而且标志位置 1 时会产生中断(如擦除或烧写完成,会产生中断)5 I; z( X# G+ Z3 B7 [4 P9 V
③ 内存映射模式:外部 Flash 映射到微控制器地址空间,从而系统将其视作内部存储器。
3 G/ U  ~2 W) {! X2 u& _% m" T: J1 `6 G& h$ [
QSPI通过6根线与SPI芯片通信,下图是内部框图:
: R6 @' l: @. c
. X! t. \! `! `* V9 Q
20190123173205949.png

/ m& f0 M. k" a' d5 ]) J$ t6 e3 |* H4 G( H% m
QSPI每条命令,必须包含一个或多个阶段:指令、地址、交替字节、空指令和数据。: Y. M7 [/ t# T7 G& v% `5 N
2 ^1 r5 @9 n! C: T; @* Z
QSPI发送命令:等待QSPI空闲;设置命令参数。6 @3 B* |# `/ I, m5 y
3 S' N; y) a- t! l% K
QSPI读数据:设置数据传输长度;设置QSPI工作模式并设置地址;读取数据。
" V. D, }' G" J+ w
4 C( g8 R; @6 I+ aQSPI写数据:设置数据传输长度;设置QSPI工作模式并设置地址;写数据。
: c( b8 e* C- [, G' ~- w; u9 B) M. l" \" |2 A* I  ]" t- p
3 a1 z7 p5 d6 O; K
二、几个重要的函数9 q9 r0 V( n! R6 j2 C5 U
  1. HAL_StatusTypeDef     HAL_QSPI_Init     (QSPI_HandleTypeDef *hqspi); // 初始化& y( v  B, p7 b1 g+ ~
  2. 5 D6 v" ^& z4 g6 K
  3. HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout); // 发送命令6 T* P( g: m+ C. |
  4. % Z5 Z1 b& l+ b' ~3 ^# _+ H" U
  5. HAL_StatusTypeDef     HAL_QSPI_Transmit     (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 发送数据3 e/ j- W, B; S7 ]% t7 L( @

  6. & }8 D7 t& B" n% g
  7. HAL_StatusTypeDef     HAL_QSPI_Receive      (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 接收数据
复制代码

, ~1 I/ S: u; G三、几个重要的结构* Y2 K4 ^; y) q% @1 a: ~
  1. // QSPI操作句柄
    2 b* H( _0 @. o/ f; `2 X
  2. typedef struct: Q0 i7 D( b1 f  u
  3. {/ @, m3 m# B+ j8 g
  4.   QUADSPI_TypeDef            *Instance;        /* QSPI registers base address        */
    ( I* t" L; |, ~6 ~8 m4 u* Y
  5.   QSPI_InitTypeDef           Init;             /* QSPI communication parameters      */
    % S0 w5 c  y6 J3 \
  6.   uint8_t                    *pTxBuffPtr;      /* Pointer to QSPI Tx transfer Buffer */& r7 X) X" H# e" M1 I- Q
  7.   __IO uint16_t              TxXferSize;       /* QSPI Tx Transfer size              */8 W& _+ J# T! I0 b: {4 W8 G
  8.   __IO uint16_t              TxXferCount;      /* QSPI Tx Transfer Counter           */% U/ b7 t2 b6 R; V( \, |/ Q5 [
  9.   uint8_t                    *pRxBuffPtr;      /* Pointer to QSPI Rx transfer Buffer */
    5 R7 a3 t2 _6 F6 D9 F; B
  10.   __IO uint16_t              RxXferSize;       /* QSPI Rx Transfer size              */
    % \3 v8 c) s6 d
  11.   __IO uint16_t              RxXferCount;      /* QSPI Rx Transfer Counter           */5 u7 X/ E2 g6 \6 L# a6 [1 ?
  12.   DMA_HandleTypeDef          *hdma;            /* QSPI Rx/Tx DMA Handle parameters   */( n$ f+ p$ j& ?
  13.   __IO HAL_LockTypeDef       Lock;             /* Locking object                     */; |2 f& @/ N' @( @. _
  14.   __IO HAL_QSPI_StateTypeDef State;            /* QSPI communication state           */. D" C/ `# g. ]" N
  15.   __IO uint32_t              ErrorCode;        /* QSPI Error code                    */
      J0 c2 N- t1 G. R
  16.   uint32_t                   Timeout;          /* Timeout for the QSPI memory access */
    ( u5 f. Y. O3 c( v% O
  17. }QSPI_HandleTypeDef;
    1 X/ R- |! _( F' `; T3 m% k
  18. 0 ?. m" x/ ?7 R
  19. // Instance:QSPI基地址  ---  QUADSPI9 A4 D. |0 K) b# t
  20. // Init:设置QSPI参数
    * h! ~2 r/ p$ X9 [; [
  21. // pTxBuffPtr,TxXferSize,TxXferCount:发送缓存指针 发送数据量 剩余数据量! p( M; l  u) s; I9 {: p1 }7 U
  22. // pRxBuffPtr,RxXferSize,RxXferCount:接收缓存指针 发送数据量 剩余数据量
    : C; P5 i9 S- i$ l
  23. // hdma与DMA相关, 其他为过程变量不需要关心
复制代码
  1. // 参数配置 时钟分频系数  FIFO阈值  采样移位  FLASH大小  片选高电平时间  时钟模式  闪存ID  双闪存模式设置" Q" C; x; S# v- G4 L! K9 a% Y3 a
  2. typedef struct
    ) b$ O; }7 m, `1 B  Q6 X
  3. {
    , R$ L7 u* s# Q# ^" A; q( [
  4.   uint32_t ClockPrescaler;     /* Specifies the prescaler factor for generating clock based on the AHB clock.
    0 i  B& {0 V! j: q: A: }9 T
  5.                                   This parameter can be a number between 0 and 255 */
    8 \. q, j9 m. E- I
  6. 4 e! L* I& ^7 p$ M$ s) J
  7.   uint32_t FifoThreshold;      /* Specifies the threshold number of bytes in the FIFO (used only in indirect mode)* V, P  x& T0 S( C; A1 x
  8.                                   This parameter can be a value between 1 and 32 */
    8 L2 F3 H6 v' o) B" u5 F8 }  Q$ M7 Y6 {

  9. 1 H7 I9 c. Z5 p7 D5 Y5 s0 p5 C$ e
  10.   uint32_t SampleShifting;     /* Specifies the Sample Shift. The data is sampled 1/2 clock cycle delay later to 1 I0 A- U% x2 \9 o* N. s5 |
  11.                                   take in account external signal delays. (It should be QSPI_SAMPLE_SHIFTING_NONE in DDR mode)! W4 j  P% k& e
  12.                                   This parameter can be a value of @ref QSPI_SampleShifting */2 m- D7 w% D0 y2 X

  13. 3 J2 o+ ]8 N1 s5 w5 }- n$ i
  14.   uint32_t FlashSize;          /* Specifies the Flash Size. FlashSize+1 is effectively the number of address bits
    ) {; }* v. E2 u5 g. y
  15.                                   required to address the flash memory. The flash capacity can be up to 4GB / p4 Q5 @$ {2 x/ `6 j4 t: W
  16.                                   (addressed using 32 bits) in indirect mode, but the addressable space in 7 U/ y- G! \: @! ]4 g& ?9 {
  17.                                   memory-mapped mode is limited to 256MB
    6 r* r0 a& n$ t& D! z2 F
  18.                                   This parameter can be a number between 0 and 31 */
    $ I; x( x% v. h& Y  z6 O
  19. % t% V9 X  s- J2 I
  20.   uint32_t ChipSelectHighTime; /* Specifies the Chip Select High Time. ChipSelectHighTime+1 defines the minimum number
    # u* g/ @) h& t& M; J
  21.                                   of clock cycles which the chip select must remain high between commands.7 W- O- N% }+ U: [
  22.                                   This parameter can be a value of @ref QSPI_ChipSelectHighTime */ $ x' M0 v8 t, Q5 e

  23. , a7 _1 S; J6 `/ F9 p$ f! d% L/ o
  24.   uint32_t ClockMode;          /* Specifies the Clock Mode. It indicates the level that clock takes between commands.
    / n$ L5 D( z, A& Z( q2 \
  25.                                   This parameter can be a value of @ref QSPI_ClockMode */
    7 P7 u7 V. \! X5 m# e

  26. ' f: h* S( f& r9 [. {
  27.   uint32_t FlashID;            /* Specifies the Flash which will be used,
    * C0 ~3 V' f" q/ K
  28.                                   This parameter can be a value of @ref QSPI_Flash_Select */
    7 i7 i4 l. P( e6 ], f* `; z
  29. & z" w, S. A* C0 c' R
  30.   uint32_t DualFlash;          /* Specifies the Dual Flash Mode State
    % L) e- S+ I. b9 l
  31.                                   This parameter can be a value of @ref QSPI_DualFlash_Mode */                                               % R& Z1 @- V( o8 e
  32. }QSPI_InitTypeDef;
复制代码
  1. // 采样移位
    2 u; _/ A( D) }' x) ]) x' Q
  2. #define QSPI_SAMPLE_SHIFTING_NONE           ((uint32_t)0x00000000U)        /*!<No clock cycle shift to sample data*/. V7 m$ y4 _0 D9 d
  3. #define QSPI_SAMPLE_SHIFTING_HALFCYCLE      ((uint32_t)QUADSPI_CR_SSHIFT) /*!<1/2 clock cycle shift to sample data*/
复制代码
' n: [% b+ a# ?2 q- c' `4 d) X" ~9 v
四、QSPI接口设计(仅供参考)
2 b( g2 |9 S* d: O
  1. #define QSPI_CLK_ENABLE()         __HAL_RCC_QSPI_CLK_ENABLE();# ?9 y3 l" C2 G7 V* l/ y# J
  2. ; A/ I5 G& A2 j: j- J$ b' F* Z
  3. #define QSPI_BK1_NCS_PORT         GPIOB8 V9 l+ y3 ^6 L) L
  4. #define QSPI_BK1_NCS_PIN          GPIO_PIN_6, e5 x4 i, }% _. N+ L- m2 q4 p
  5. #define QSPI_BK1_NCS_AF           GPIO_AF10_QUADSPI) L4 A+ d& H, M. L
  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)- v) ?: U0 L  t# m. k

  7. - r, o+ G& d( J
  8. #define QSPI_BK1_CLK_PORT         GPIOB
    6 B6 k& N$ v+ Y- D0 h5 D* q
  9. #define QSPI_BK1_CLK_PIN          GPIO_PIN_2
    ( v$ }4 X7 u+ I- h. s
  10. #define QSPI_BK1_CLK_AF           GPIO_AF9_QUADSPI  I9 E  X6 W" t
  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)
    / R% ?1 f. [0 }3 O# z* ]

  12. 4 c$ d1 E! h2 t$ A; _8 }
  13. #define QSPI_BK1_IO0_PORT         GPIOF) V7 T! {3 L1 [) N' ?8 O7 P
  14. #define QSPI_BK1_IO0_PIN          GPIO_PIN_8
    " m& `& `7 r  u: H$ F4 Y
  15. #define QSPI_BK1_IO0_AF           GPIO_AF10_QUADSPI6 U' B' m- x+ d$ ?! ~% N, I$ h& Y7 o
  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)8 D) q* m( M$ J! f3 s8 q& [
  17. ( N# l6 `" Q1 l/ K$ U6 G( I9 f) n
  18. #define QSPI_BK1_IO1_PORT         GPIOF9 x; D4 P( L. ~& n( g% ~7 h0 C7 W
  19. #define QSPI_BK1_IO1_PIN          GPIO_PIN_9
    % c" @% f# M( q( n% B6 z. ]
  20. #define QSPI_BK1_IO1_AF           GPIO_AF10_QUADSPI$ e, M/ Y3 H4 j$ D- J! s& Y
  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)
    6 [. _% ]! Y! s
  22. 6 M" E+ z. j9 W/ \. S  ?
  23. #define QSPI_BK1_IO2_PORT         GPIOF
    3 y6 K8 H  x+ O% Z6 D; H" V; G0 t
  24. #define QSPI_BK1_IO2_PIN          GPIO_PIN_7
    ) C, [( q  ~2 ]5 v$ n( j/ M3 w
  25. #define QSPI_BK1_IO2_AF           GPIO_AF9_QUADSPI
    0 \4 T/ Z3 q2 p- d3 n& V! D+ ~) n. w
  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' z# D- f) x  {- c

  27.   z9 m6 r# z5 Q" s4 V" ~0 n8 U
  28. #define QSPI_BK1_IO3_PORT         GPIOF
    2 o" i% w* m' D
  29. #define QSPI_BK1_IO3_PIN          GPIO_PIN_6
    ; K0 C9 ^. y& m6 c
  30. #define QSPI_BK1_IO3_AF           GPIO_AF9_QUADSPI% e2 P& d# F: Y$ c
  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. // 封装几个必要的接口) ]/ j3 M: m) c. h8 T
  2. static QSPI_HandleTypeDef qspi_handle;
    & n  |8 P" G0 ]9 Z  i# [
  3. # G. @: X! M% z9 E: O: C
  4. static void qspi_gpio_init(void)
    4 U" c) D* x% _5 X- R9 W9 y( S
  5. {
    0 Y% {! J, y) `8 g
  6.   QSPI_CLK_ENABLE();$ f: ?( t/ }! m( X+ d' u' x* D# T' `- D

  7. , J( ?  Y/ @( A6 d+ D  p
  8.   QSPI_BK1_NCS_CONFIG();. g; a( m, \. r8 e4 p
  9.   QSPI_BK1_CLK_CONFIG();. K$ [& Q. v1 Z% }
  10.   QSPI_BK1_IO0_CONFIG();/ G$ y+ v' u4 [$ A# V( y
  11.   QSPI_BK1_IO1_CONFIG();
    5 [" B" S7 h9 o+ t2 v
  12.   QSPI_BK1_IO2_CONFIG();+ ?% @+ j+ Y+ \
  13.   QSPI_BK1_IO3_CONFIG();
    8 H( a' ?/ k7 d( }$ b4 z
  14. }9 ~, {$ i1 @; {/ }0 Z9 U6 w6 d
  15. 6 ~2 l# g, A6 I$ F0 x
  16. static void qspi_mode_init(void)
    ( F& d1 ?+ ]' }# b% y# r
  17. {7 c) G9 `' t% p' y4 p; d. V
  18.   qspi_handle.Instance = QUADSPI;                        // QSPI; l* F% k, m. l
  19.   qspi_handle.Init.ClockPrescaler = 2;                   // QPSI分频比,W25Q256最大频率为104M 所以此处应该为2,QSPI频率就为216/(2+1)=72MHZ
    % P* g: a  w9 d3 c: Q0 k" [7 l
  20.   qspi_handle.Init.FifoThreshold = 4;                    // FIFO阈值为4个字节
    , t7 v0 X/ C$ I/ ~& |+ N& z
  21.   qspi_handle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; // 采样移位半个周期(DDR模式下,必须设置为0)+ O  }+ S/ z. z6 q5 y, ?
  22.   qspi_handle.Init.FlashSize = POSITION_VAL(0X2000000) - 1; // SPI FLASH大小,W25Q256大小为32M字节. z5 B+ l' N, |! a" ]- c5 E
  23.   qspi_handle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_4_CYCLE; // 片选高电平时间为4个时钟(13.8*4=55.2ns),即手册里面的tSHSL参数
    $ L$ j) J' O9 F" G( m& u' V' S+ p
  24.   qspi_handle.Init.ClockMode = QSPI_CLOCK_MODE_0;        // 模式0# w: B: C4 G4 `1 [
  25.   qspi_handle.Init.FlashID = QSPI_FLASH_ID_1;            // 第一片flash0 B! [+ k; {5 t7 R, V1 d9 d1 {
  26.   qspi_handle.Init.DualFlash = QSPI_DUALFLASH_DISABLE;   // 禁止双闪存模式
    6 c- {% y1 r' q( Z9 ?% s0 p
  27.   HAL_QSPI_Init(&qspi_handle);      //QSPI初始化
    . e: }3 W, U/ y$ a- {2 v2 _! ?
  28. }. X$ H- {, P  K
  29. 4 A5 \( ]+ w3 t8 X: H- ]) L- \- n
  30. void QSPIInit(void)% f, I) \: |4 g5 V! K% d
  31. {
    , |) V& ~: |! a7 Z& O
  32.   qspi_gpio_init();
    , Q  y# G2 o0 G
  33.   qspi_mode_init();
    6 |% @# K6 |1 Y0 t' v
  34. }
    : ^, p3 c# X6 L* A" n# `
  35. 6 a" [' m' K$ T5 n- n  k. p& I2 k, h
  36. // QSPI发送命令$ i6 t% c5 f/ M9 g% ]4 X# ~
  37. // instruction:要发送的指令
    $ C# ?2 Q7 h9 m
  38. // address:发送到的目的地址
    5 ~- L( C3 @8 `3 K
  39. // dummyCycles:空指令周期数' `* [) u8 I& D
  40. // instructionMode:指令模式;QSPI_INSTRUCTION_NONE,QSPI_INSTRUCTION_1_LINE,QSPI_INSTRUCTION_2_LINE,QSPI_INSTRUCTION_4_LINE
    " ^7 }- E: A/ m& d- n9 t# A5 K
  41. // addressMode:地址模式; QSPI_ADDRESS_NONE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_2_LINE,QSPI_ADDRESS_4_LINE
    7 E$ _& _2 h. y
  42. // addressSize:地址长度;QSPI_ADDRESS_8_BITS,QSPI_ADDRESS_16_BITS,QSPI_ADDRESS_24_BITS,QSPI_ADDRESS_32_BITS4 w& I2 P9 G2 G/ w9 S7 X3 I: W( C
  43. // dataMode:数据模式; QSPI_DATA_NONE,QSPI_DATA_1_LINE,QSPI_DATA_2_LINE,QSPI_DATA_4_LINE
    % r0 |9 [; x# g: J0 I
  44. void QSPISendCMD(uint32_t instruction, uint32_t address, uint32_t dummyCycles, uint32_t instructionMode, uint32_t addressMode, uint32_t addressSize, uint32_t dataMode)
    7 r0 R% u& @( O2 @; k  _0 U
  45. {
    3 P3 n' {0 z/ S, k0 B
  46.   QSPI_CommandTypeDef Cmdhandler;
    & c/ b1 a4 a9 s0 M& }5 A) T

  47. 5 q$ ^, Q0 g! e4 d0 J
  48.   Cmdhandler.Instruction = instruction;           // 指令* z$ x* ~' }, A4 d& a
  49.   Cmdhandler.Address = address;                   // 地址6 y( G8 Q5 B1 x2 d+ \
  50.   Cmdhandler.DummyCycles = dummyCycles;           // 设置空指令周期数
    8 p% r( B) s4 R* E5 i' G+ ]# w
  51.   Cmdhandler.InstructionMode = instructionMode;   // 指令模式
    0 i; u8 A2 F6 z
  52.   Cmdhandler.AddressMode = addressMode;           // 地址模式
    ! ?; O  D5 Y; w8 ]! J4 X4 S
  53.   Cmdhandler.AddressSize = addressSize;           // 地址长度1 N$ W( g. G6 j) p# i' l5 h
  54.   Cmdhandler.DataMode = dataMode;                 // 数据模式
    1 V" h1 C2 F; o( }2 F" X+ h
  55.   Cmdhandler.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 每次都发送指令
    0 y2 Q& G) Y" ?0 @+ A  ?
  56.   Cmdhandler.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 无交替字节. ?% N/ D. q$ o: T5 X0 u7 }4 n
  57.   Cmdhandler.DdrMode = QSPI_DDR_MODE_DISABLE;           // 关闭DDR模式4 x( x+ D' _& ~+ h# |3 E
  58.   Cmdhandler.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    ' _4 U( X* ?) R2 B! M# l

  59. 1 Q. B8 t" j& D+ Q# s' {
  60.   HAL_QSPI_Command(&qspi_handle, &Cmdhandler, 5000);; D: h# C2 [: ^: J
  61. }
    8 m2 V' L) q  G

  62. 0 B9 K8 g* `# X% F! A3 i
  63. // 接收指定长度数据
    / {) W; F+ s) i# d6 M/ m
  64. uint8_t QSPIReceive(uint8_t *buffer, uint32_t length)2 }1 G8 J; _) m) L; D
  65. {
    5 \+ R0 D1 {5 x' \0 J
  66.   qspi_handle.Instance->DLR = length - 1;
    ' @/ A2 m9 j1 I/ t. d
  67.         ! b2 V2 h5 o! M3 w- H+ m6 K
  68.   if(HAL_QSPI_Receive(&qspi_handle, buffer, 5000) == HAL_OK)
    " @3 |; [  [+ t# N
  69.   {
    3 W' J1 |( g/ d/ ~. p5 f/ v# B
  70.     return 0;  //接收数据
    3 w. `, M% r  A9 K
  71.   }" O0 H6 L0 v: s+ o3 p4 M
  72.   else- O9 C: g* u5 n  ~5 [# H
  73.   {* S7 y' g" k# e& k7 z
  74.     return 1;' U1 u2 r- e, S1 \( P
  75.   }/ D+ p; d& c0 ~: U, n: {
  76. }' R3 f% N/ |1 m% @6 e+ O

  77. : q. M7 [+ [5 f) o1 D
  78. // 发送指定长度数据
    8 z$ ?3 G- d- }* e% m
  79. uint8_t QSPITransmit(uint8_t *buffer, uint32_t length)4 {! e1 Y& h9 y
  80. {& ^: W; X7 J: Z* j! c
  81.   qspi_handle.Instance->DLR = length - 1;" T; |4 M4 C) g2 \
  82.   if(HAL_QSPI_Transmit(&qspi_handle, buffer, 5000) == HAL_OK)) N% z/ ^' L% j: O; F7 N+ ]" x" q
  83.   {  \: \9 N: g5 N7 z1 f# I6 H4 b" M
  84.     return 0;  //发送数据
    ' ~: l, c  b" p5 Y  p0 T, O
  85.   }
    6 L' y2 }( w2 p- a2 n3 j" E! ]$ H8 S# ?1 Z) y
  86.   else) H/ S% d* f/ ~, ~
  87.   {
    , q5 G  @' z5 y
  88.     return 1;, e4 e/ r  P, O8 _, \$ }5 ?
  89.   }- Q9 a+ a& R" f
  90. }
复制代码
& {- G; A% J4 T% v) d- J
五、QSPI驱动W25Q256
0 `4 c( ^# v1 c: ?* b1 EW25Q256:32M  512块,每块16个扇区,每个扇区4K。具体指令看手册。封装了如下接口(初始化读写擦除等接口是以后移植文件系统的基础):
% N  D7 X( h8 X* `( S6 c0 P5 u3 m8 H( }6 m# |: r" i6 G
  1. #define W25QXX_WRITE_ENABLE      0x06
    9 k% u  c0 a9 h& I1 @
  2. #define W25QXX_WRITE_DISABLE     0x042 y% E3 {* ^2 e" W2 z
  3. #define W25QXX_READ_STATUS_REG1  0x05
    ! R2 a, p4 B9 y2 a6 F, c" X
  4. #define W25QXX_READ_STATUS_REG2  0x353 w( ^# w" D5 @
  5. #define W25QXX_READ_STATUS_REG3  0x15
    # J1 f* x7 d1 e7 A
  6. #define W25QXX_WRITE_STATUS_REG1 0x01
    ' |/ q+ c$ h) p  c/ ]9 L
  7. #define W25QXX_WRITE_STATUS_REG2 0x31
    7 f& O0 \) S! s* Q, Q! m
  8. #define W25QXX_WRITE_STATUS_REG3 0x11
    0 p8 `" n+ x0 g. `+ q  V! u
  9. #define W25QXX_READ_DATA         0x03  \# o' J; {# G3 @/ z
  10. #define W25QXX_FAST_READ_DATA    0x0B
    9 ?( q4 A# c( c" }' r( M! w
  11. #define W25QXX_FAST_READ_DUAL    0x3B  j# A5 t# J9 t7 |" v
  12. #define W25QXX_PAGE_PROGRAM      0x02  \" F! L( [1 V& m1 S- h5 H
  13. #define W25QXX_BLOCK_ERASE       0xD8
    9 R. ~$ k0 Z, i: ^: M( ?
  14. #define W25QXX_SECTOR_ERASE      0x20* @- n: L4 r; D/ N4 D
  15. #define W25QXX_CHIP_ERASE        0xC7
    $ U  Z- X0 k2 r* H- L% P. S
  16. #define W25QXX_DEVICEID          0xAB: M1 {4 @2 b3 l2 ~5 v" ^2 L% k( o
  17. #define W25QXX_MANUFACT_DEVICEID 0x90+ T- [9 ~$ }. S' |5 ?. S9 c- b
  18. #define W25QXX_JEDEC_DEVICEID    0x9F
    8 ^7 m: _  w$ e: k7 l& Z, |3 _
  19. #define W25QXX_EABLE_4BYTE_ADDR  0xB7; k8 O1 x) p2 b! B
  20. #define W25QXX_EXIT_4BYTE_ADDR   0xE9
    * [) n$ k" B% B+ ?. o& y
  21. #define W25QXX_SET_READ_PARAM    0xC0) ^! [- d( l# f' o7 B
  22. #define W25QXX_ENTER_QPIMODE     0x38
    + T" B  Y; L, M+ Z, a
  23. #define W25QXX_EXIT_QPIMODE      0xFF2 `8 U2 n6 t: ]+ g' h1 L
  24. 0 w- M+ p) \/ w. {3 ]/ Z
  25. uint8_t w25qxx_qpi_mode = 0; // QSPI模式标志:0,SPI模式;1,QPI模式.
    / J1 w7 @+ d$ |  V6 O) S
  26. ! D" S! Z% L  u
  27. // 读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器
    ( c* `3 ^- ?, l2 ]& L9 ~1 g
  28. // 状态寄存器1:
    : p4 I* j3 O) t8 w
  29. // BIT7  6   5   4   3   2   1   0
    1 l, n! ~- Z1 [- U0 z
  30. // SPR   RV  TB BP2 BP1 BP0 WEL BUSY
    3 v7 t; X( I, x9 B1 g
  31. // SPR:默认0,状态寄存器保护位,配合WP使用
    9 w. l% [  s8 }5 q8 Z& c
  32. // TB,BP2,BP1,BP0:FLASH区域写保护设置
    3 ~: ~3 m! b, s+ S$ o! j: h6 C
  33. // WEL:写使能锁定( K8 \: u" f, ~; h& E/ u0 {: v- i
  34. // BUSY:忙标记位(1,忙;0,空闲)3 C$ x9 V  o4 d; g7 V3 W
  35. // 默认:0x00/ ?5 S: x" W/ k$ |
  36. // 状态寄存器2:5 U' z& T2 c3 a/ L# V3 l: w: v, W
  37. // BIT7  6   5   4   3   2   1   0
    , c6 V$ S: x! J5 G
  38. // SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
    9 a" i! [' J, I$ q! K0 A  R
  39. // 状态寄存器3:5 v1 o7 {+ Y( M5 E" w8 d
  40. // BIT7      6    5    4   3   2   1   0
    ) ^! d( R  n8 i+ d$ t6 r) S
  41. // HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
    9 {0 W5 q3 A! t9 y
  42. // reg:状态寄存器号,范:1~3% U- c' A- n  g7 K8 g
  43. // 返回值:状态寄存器值
    ) d9 L5 `8 |' Q( J! K4 F1 P0 R
  44. static uint8_t w25qxx_read_status(uint8_t reg)
    : u1 r3 e% u7 s- i) _8 T+ ?
  45. {
    % X+ G$ w) {. M0 ?9 o
  46.   uint8_t value = 0, command = 0;% }- ?9 J$ Z( n4 d% l: H
  47. ! B: w% S; u1 _2 X6 p3 X2 Q# b
  48.   switch(reg), S0 a. u0 v8 d- `- E( |' `. V& W
  49.   {* x! ~1 s/ p1 D
  50.   case 1:
    4 i( a& T2 K4 x( t
  51.     command = W25QXX_READ_STATUS_REG1;  // 读状态寄存器1指令
    , R  A- U5 b$ O4 Q% V; ]& v
  52.     break;; e4 o# j4 n* Y' U
  53. ' d" `+ X) I4 d  C+ j2 W' m
  54.   case 2:3 d' c& d7 g$ i2 }
  55.     command = W25QXX_READ_STATUS_REG2;  // 读状态寄存器2指令  I" l! v! n3 i0 f
  56.     break;
    ; m, |" ^% s. G, g7 j0 Z9 \6 B

  57. , H# C6 }/ {- y+ m! X
  58.   case 3:4 ~9 z$ R8 G( n8 H) F& O. i
  59.     command = W25QXX_READ_STATUS_REG3;  // 读状态寄存器3指令
    9 h" J0 x" y3 c* ^
  60.     break;
    ' Q) ?, v4 V, g! T
  61. 3 i' e: |# [. |
  62.   default:
    ( A% X0 }- X* y7 R4 E$ {
  63.     command = W25QXX_READ_STATUS_REG1;6 y- A' C3 }5 B( l0 u% k+ q: O
  64.     break;
    6 \8 Y. f0 f7 m/ b/ y2 i# V
  65.   }8 P  A( z4 x. Q' d5 K

  66. 5 B- C) o/ O+ E8 w, J( b% x
  67.   if(w25qxx_qpi_mode)9 t$ w, `* [9 e% q* @  C4 g7 Z
  68.   {
    ) K4 F' a% _+ l6 B6 U9 ~/ o
  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个字节数据  u* v; v5 y& N0 K' f
  70.   }8 s8 {1 l" M/ P5 C( y7 [
  71.   else3 ?2 I8 B; a/ s  [. X
  72.   {
    5 I- j, N# V8 V( n
  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个字节数据. O$ T; V: D% a  D
  74.   }* c2 n0 v, b6 K0 K* C# x4 y. Y

  75. & i; X' v& f/ O0 P. D) G
  76.   QSPIReceive(&value, 1);9 j, C. y% \2 k: }* }
  77. - F. z1 X5 y% S4 u& ^. [6 Y$ L# d
  78.   return value;
    ! b, [2 q2 g8 z4 s7 Z# |
  79. }
    9 b1 H8 P; i1 ?# X' e# e# J

  80. 8 Z7 D  D9 {7 `1 o3 ^
  81. // 写状态寄存器
    $ U: t, h; v, i% ^3 O4 q
  82. static void w25qxx_write_status(uint8_t reg, uint8_t status)
    : \' O9 v5 ?* t9 u8 U7 j3 [
  83. {+ r, Q, y2 u# q& D! E& U* k' V. T
  84.   uint8_t command = 0;+ u7 L, y" V: F2 k
  85. : p7 _0 I" B$ Y+ Y- _
  86.   switch(reg)
    6 X( }4 J+ H* j8 r& _* z5 w7 x& P
  87.   {. T7 D) v: c4 F' m+ {1 Z7 \- R0 ]
  88.   case 1:
    & q; d5 K# j4 r  g, C/ ~
  89.     command = W25QXX_WRITE_STATUS_REG1;  //写状态寄存器1指令, H. k& q) _5 i9 @6 c1 r
  90.     break;
    3 \6 X( N$ Q+ N
  91.   case 2:* u8 g  W! W/ Y$ ~! |( z% P
  92.     command = W25QXX_WRITE_STATUS_REG2;  //写状态寄存器2指令( Z- t5 v5 T% ?; A4 ]3 f5 O
  93.     break;
    ) m& S( P- Z! N7 x
  94.   case 3:
    2 e( U3 o0 i2 D; S  R
  95.     command = W25QXX_WRITE_STATUS_REG3;  //写状态寄存器3指令
    7 P) p' V- c$ t7 a& t( z- [
  96.     break;( @4 l8 E# O$ D2 L9 Y7 s; Y$ X
  97.   default:3 L- }2 @; \8 x; N& ^
  98.     command = W25QXX_WRITE_STATUS_REG1;% }: X9 r7 J3 x6 K0 F
  99.     break;
    ' I& q  k, s, N" s8 `
  100.   }1 e! y1 v2 U; R" F
  101.   if(w25qxx_qpi_mode)  N6 g2 E# Z: s8 O- V+ H
  102.   {' H( g2 ?, B6 \+ f8 _
  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个字节数据. N- a, [4 z1 n2 `: b  T
  104.   }# _7 m. A4 y# R9 b; L1 e+ U! p
  105.   else" n" i6 r: {' X% M4 p! V
  106.   {! m/ ?8 E4 t! |6 l
  107.     QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_1_LINE); // SPI,写command指令,地址为0,单线传数据_8位地址_无地址_单线传输指令,无空周期,1个字节数据
    7 C' W% T6 m; F4 a2 L/ D
  108.   }
    & @6 s3 D- W7 O# r  s! t0 }8 @' }# g
  109.   QSPITransmit(&status, sizeof(status));
    : f7 C1 ]! D4 o+ p* y$ Y
  110. }
    ( Y! }& p9 x/ |8 ~
  111. & {* @+ {( d6 P% L& {8 K5 w
  112. // 写使能  将S1寄存器的WEL置位6 y6 h3 w! ^" W" H# T6 ^# A) P1 w% k
  113. static void w25qxx_write_enable(void)0 k. S8 @" D+ `+ B3 c* e5 v) {
  114. {8 E8 k4 T3 @9 ^9 ^; {: r
  115.   if(w25qxx_qpi_mode)
    , R( z5 G. m) `; z3 o0 _
  116.   {
    . X. n; V. B+ J7 v1 J
  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个字节数据  Q! T) t$ u2 k* i; ~! U
  118.   }
    + E1 [5 }: @1 O% [
  119.   else
    8 [, L! P  |; W8 R9 w. |6 o7 d
  120.   {  i) T4 J/ ^! T  W+ E. p% }0 {3 ]
  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个字节数据
    & ~6 C, _% W5 `" J* g6 U$ ]
  122.   }" N; H- Y) P, z, X* }
  123. }
    2 p% [# m1 q/ ~, U3 x

  124. 3 g) [' U- m/ s, v9 o) A8 B
  125. // 写失能  将WEL清零, m* L! \4 i* m) s4 q3 ~+ [
  126. void W25QXX_Write_Disable(void)
    ) y5 t' I0 R+ F  X
  127. {( |# B* n" i" T3 J1 c, d) K# l* b
  128.   if(w25qxx_qpi_mode)$ U" s- q3 h  i" V8 x9 M! l
  129.   {$ ~  S2 {; k3 z$ B
  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 Z! }7 F) l4 ?  @
  131.   }# l/ Y5 M/ S7 l6 H* F& ]' g
  132.   else
    8 P- }9 T% N, {+ R( I2 Y" ?: w
  133.   {9 S7 l( m( U# a% k* d1 s
  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个字节数据
    4 T( o+ ~3 g$ K4 p, t
  135.   }
    . C; M4 `1 P3 m" }2 l
  136. }) @8 m/ R3 W  o! ^
  137. 8 o0 E- b3 {8 o+ J4 ]
  138. // 等待空闲
    6 r! [! m* {, U# ^+ x* K" ~4 m  ~$ s7 Y
  139. void w25qxx_wait_busy(void)
    ) J2 j$ H7 E0 N+ i2 D0 J
  140. {& c+ [) q% `9 {8 d  C$ r7 n/ ?2 x5 X
  141.   while((w25qxx_read_status(1) & 0x01) == 0x01); // 等待BUSY位清空
    # ?' n0 L" B$ H2 a$ U
  142. }' L5 u% b( Y+ H# Y/ B! r
  143. % U  r$ I! b" e  t% b8 D3 A. S& y
  144. // W25QXX进入QSPI模式
    # x3 f6 W, X5 @# H% P0 Y4 @
  145. static void w25qxx_qspi_init(void)( C% }6 G! ]! \. S
  146. {
    & p& o7 t* Q$ w+ S0 A# R  p
  147.   uint8_t reg2;
    ; q" Q, O- Q/ i' n8 Z# N
  148.   reg2 = w25qxx_read_status(2); // 先读出状态寄存器2的原始值
    & n0 H; F; a9 {7 A  K) v  u! k
  149.   if((reg2 & 0X02) == 0)  // QE位未使能
    ! ?" P+ ?& V+ k& i
  150.   {
    4 ~9 C( B4 N2 F& l/ }; h; p' H0 G4 x
  151.     w25qxx_write_enable(); // 写使能$ m0 `6 r& Q4 V( d+ ?
  152.     reg2 |= 1 << 1; // 使能QE位
    % D* H. }6 o; l- i& I; t
  153.     w25qxx_write_status(2, reg2); // 写状态寄存器2
    * b9 E. R  Q3 y) J/ O
  154.   }
    7 c2 m" l7 n# o+ M- T3 O
  155. ( c6 w  m; D' G% p* X$ 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个字节数据  t! R1 v. w. ], g- r

  157. : q% C5 Z9 W  {) ]/ J* a
  158.   w25qxx_qpi_mode = 1; // 标记QSPI模式
    % P7 `8 `- L. k9 L. d
  159. }& I( w, I5 l) T, a1 H1 l2 Y9 _, x

  160. # W) y) l1 |. `: i
  161. // 0XEF13,表示芯片型号为W25Q80
    ( W) ]7 b+ @- ]) F1 V" F" q3 K) I
  162. // 0XEF14,表示芯片型号为W25Q16: m* c+ w+ Y% S$ v7 l
  163. // 0XEF15,表示芯片型号为W25Q32% D9 q7 E% S2 Q1 c+ s
  164. // 0XEF16,表示芯片型号为W25Q64+ c( D0 R" V0 w6 y+ E- V; {
  165. // 0XEF17,表示芯片型号为W25Q128
    : s5 n$ Q0 t* o' N6 `" e  `
  166. // 0XEF18,表示芯片型号为W25Q2569 C1 T* s0 @, T7 a
  167. static void w25qxx_id_get(void); q2 F9 p% |4 P- ]4 z% ?. G: Q
  168. {1 z6 y+ y1 @' z
  169.   uint8_t temp[2];. s9 B5 Y  R* z# [: Y( R% Q8 S
  170.   uint16_t device_id;8 [+ |; @3 P  I$ e; t- Q
  171. , q$ @& S* `7 z! N8 M! E0 N
  172.   if(w25qxx_qpi_mode)
    6 {* B- T1 l& F  h
  173.   {" r/ c! a. q: J  `' P& C/ 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个字节数据
    # D1 X$ f, I, N, Z& }
  175.   }
    / N8 ]3 s+ _3 Y' `8 p  v' r1 T3 V
  176.   else7 z' z8 Y0 C' \+ F( _% ^/ P
  177.   {6 a. p# \% K* o( `4 A& t
  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个字节数据
    4 S# V; u) j' Z' Q
  179.   }% r' n& T2 Q: P# p; E
  180. + ~: D1 o) Z( f" u- n
  181.   QSPIReceive(temp, 2);8 G. d' P/ h; E- E  H
  182. & Z* S! r! J* b3 X
  183.   device_id = (temp[0] << 8) | temp[1];
    2 H- G2 T; ?6 F( [, a

  184. % u/ ~$ n% c4 i& V" }
  185.   printf("QSPI Flash Device ID: %X\r\n", device_id);# s- _/ U1 v7 h6 l- ]+ d# f$ m
  186. }
    * u& C2 a' _# }4 l. G' D3 g0 g
  187. . i$ {5 B+ w) {3 |6 q" D2 n% j
  188. void W25QXXInit(void)
    8 b+ k+ W7 s$ G0 ~$ l9 T
  189. {4 V0 L# A2 c: |$ h% W0 w8 K5 }8 i
  190.   uint8_t temp;
    8 F& V9 [' z) K1 Y2 q$ W8 {
  191. 0 T3 S# J# F5 O: ~7 {# D5 n' [
  192.   QSPIInit();5 T3 a# |- c4 G4 {0 `% G
  193.   w25qxx_qspi_init(); // 使能QSPI模式3 _- o  J* |' ~- g/ o1 q" }" o4 X
  194.   w25qxx_id_get();    // 读取FLASH ID.$ F, C+ ?# d5 Q* @, `

  195. % d8 M% K8 l5 h4 ?, I
  196.   temp = w25qxx_read_status(3); // 读取状态寄存器3,判断地址模式
    + g5 Q" ^( t8 T4 D, V
  197.   if((temp & 0X01) == 0) // 如果不是4字节地址模式,则进入4字节地址模式
    6 D3 L/ C  ?: V( h. s
  198.   {$ s; w& h+ N9 p9 t7 {
  199.     w25qxx_write_enable(); // 写使能
    ( T2 j) u! M6 K8 {7 j; c
  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个字节数据# E$ V: |1 |) S7 E( c  w. \- j2 A
  201.   }+ `" m  x: }" x! {
  202.   w25qxx_write_enable(); // 写使能
    0 A0 j' d7 C/ y9 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个字节数据; D- k& U# d( q* v
  204.   temp = 3 << 4;      // 设置P4&P5=11,8个dummy clocks,104M, _' b- Z& R" Z8 T
  205.   QSPITransmit(&temp, 1);
    2 X( Q6 U8 A6 S+ w* j
  206. }
    1 M: `2 W' ^& W7 h4 ^. E& z

  207. & a- L. q) N/ m8 A, G# L
  208. // 擦除一块4096字节 最少需要150ms1 u% e6 ^/ r: x3 i
  209. void W25QXXSectorErase(uint32_t addr)
    + _' b  t# I0 v$ `# f
  210. {1 R9 @& m" u; b3 E* p6 `" Q  y8 Z
  211.   //addr /= 4096;
    , J2 e# R* V/ H" o7 s
  212.   addr *= 4096;$ }* b- M) t) w5 f

  213. * E. ^8 S- K) ^# [7 f
  214.   w25qxx_write_enable();  {8 r8 b! R4 y% N1 B3 k
  215.   w25qxx_wait_busy();( s' z: B) r" m, j- o
  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个字节数据3 B0 n3 F) @2 g+ w: U3 @% A+ O. ]
  217.   w25qxx_wait_busy();
    + }+ N/ _1 M; G' O
  218. }4 N& f3 m# q/ f- y

  219. 7 ]8 @7 C: Q3 ^/ f: T

  220. # F7 R9 I2 Z4 \8 x$ s
  221. // 擦除整个芯片( m' L! [/ G: O( P% F  r, X
  222. void W25QXXChipErase(void)+ `: }; {- n* a# i" q' l
  223. {6 @2 W, E: T9 W
  224.   w25qxx_write_enable();          //SET WEL' C* ]7 z# d2 }" v% U
  225.   w25qxx_wait_busy();' E# y7 q! y) }+ \& A! H
  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个字节数据# ]5 k+ S- `0 u) |
  227.   w25qxx_wait_busy();           //等待芯片擦除结束
    7 b4 I& K( m' H4 L% R1 R1 C
  228. }( O" @- c, A8 f$ j4 N
  229. 9 Z, [8 e+ p7 J+ P7 n
  230. // 写最多256字节
    - |7 [* V7 }7 t/ p: X
  231. void W25QXXWritePage(uint32_t addr, uint8_t *buffer, uint16_t length)
    9 x5 [5 \7 h, n6 a) m3 P
  232. {
    * N$ H1 @, A$ Q1 p4 w! b/ ~+ }
  233.   w25qxx_write_enable();          //写使能# s$ g  H# g9 m
  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个数据
    6 {9 k, P# \) E; n  M
  235.   QSPITransmit(buffer, length);5 v4 f" }. X7 c/ A2 B: A. ?( R
  236.   w25qxx_wait_busy();            //等待写入结束
    # B( k' l6 t! V/ A$ y# z
  237. }$ S4 b5 J  |9 w. c
  238. : r/ o5 S# n  l0 o* K+ X
  239. // 写最多65536字节 无擦除动作; C. L' g, O& y2 }
  240. void W25QXXWriteExt(uint32_t addr, uint8_t *buffer, uint16_t length)
    6 W2 P& A! [/ m1 D' V+ P2 D% p: B) M$ C2 K
  241. {2 \! X( d. j2 a
  242.   uint16_t pageremain = 256 - addr % 256; // 单页剩余的字节数1 h+ p8 d8 g8 i/ F7 }

  243. ( ?; N7 A; u/ X" t% j
  244.   if(addr + length - 1 > (32 * 1024 * 1024))
    9 s0 |0 x# S0 Y' `5 a/ }- C5 c7 l* W
  245.   {( T; Y, f- {) I
  246.     return;0 f3 c* Y% W: a& o% I" o, L! `
  247.   }
    ) _% @* w9 L* `# M) L

  248. & C, x) D* O" d' k* h0 ~
  249.   if(length <= pageremain)3 t1 x5 @7 D3 I! H% `
  250.   {
    " C+ ^4 o* o9 ?) c/ r9 X
  251.     pageremain = length;  // 不大于256个字节+ c$ F6 @3 f  [! N
  252.   }+ e' C2 W' e3 }) q$ ^9 a8 ^0 Z- A3 \
  253.   while(1)
    7 C* O0 H5 x/ M
  254.   {
    1 Z+ z/ a6 Q3 }5 M; a1 O, P8 a
  255.     // 分页写入
    % ~+ V$ f9 o' u$ n
  256.     W25QXXWritePage(addr, buffer, pageremain);
    , \' j5 _' j  d* J4 l
  257. - _6 ~5 z0 D$ e; H! ?. r/ g8 X$ i
  258.     if(length == pageremain): J& c  z; t7 ~9 _. z
  259.     {
    1 j5 B" z5 E: M3 H& n! P  r8 [
  260.       break;  //写入结束了
    & N4 s( l3 Z4 K/ R6 X2 M. S  H
  261.     }+ d' J" K3 g- Y3 S0 f
  262.     else
    " l% \+ W; ~, g& ^2 t9 T4 H
  263.     {$ c4 \! n; l- M& w
  264.       buffer += pageremain;
    ( z/ j9 ~% d" ^7 ]& |
  265.       addr += pageremain;7 v& t7 Y( q0 _/ S! o+ ]

  266. / a/ e/ O, V. M# ]
  267.       length -= pageremain; // 减去已经写入了的字节数( R  }5 u" m# C# w
  268.       if(length > 256)& z& A& t# a9 w8 v. H
  269.       {2 n, m) Z8 ?6 C8 O
  270.         pageremain = 256;  // 一次可以写入256个字节
    - o, B! i# p) ^7 G* [* _- F
  271.       }; r' h* l2 q- B2 y
  272.       else" n2 a: w" x5 o3 L$ N
  273.       {. Z$ }: H. r% o+ f" b- ^
  274.         pageremain = length;  // 不够256个字节了* o# f. m3 X- B' k# I1 }
  275.       }
    " e: ?# ?' B4 b
  276.     }% V& l- }" |& E/ M' B
  277.   }
    4 Z7 {1 ^# h  [, {0 s4 s
  278. }8 h6 N8 z9 l% ^9 w
  279. % X5 n# i! B4 @! ^
  280. // 写最多65536字节 带擦除动作
    " b8 D2 q( ^: W, Q
  281. static uint8_t W25QXX_BUFFER[4096];
      u) R9 i8 j% e" v- m- t
  282. void W25QXXWrite(uint32_t addr, uint8_t* buffer, uint16_t length)3 m4 i9 }. f8 r5 k
  283. {7 A7 y7 S% k& S5 S
  284.   uint32_t secpos;% r* @2 t6 d' e! t. e
  285.   uint16_t secoff, secremain, i;
    - U7 h% P) ?, w" E
  286.   uint8_t * w25q_buf;
    : |; q6 v6 m, N0 K) ]* E

  287. ; m7 a5 E# A. s7 v6 H3 Y
  288.   w25q_buf = W25QXX_BUFFER;3 s# ]7 X& c0 |' d5 Z8 q

  289. + a) T3 Z- v; L
  290.   secpos = addr / 4096; // 扇区地址% X2 d2 o$ L) S; `& V" |& b& q* Z; K1 H
  291.   secoff = addr % 4096; // 在扇区内的偏移
    # {6 |+ S  O, a
  292.   secremain = 4096 - secoff; // 扇区剩余空间大小8 a, F; O8 ]9 J, @+ l# r4 Y% B
  293. 9 }: f# a0 C" C6 z* b
  294.   if(length <= secremain)
    5 ]; N* n9 F7 h$ D+ u
  295.   {4 s  T/ v$ G% P1 s9 Q9 C& b$ u
  296.     secremain = length;  // 不大于4096个字节- w% M. e: j) r( f. @$ V1 _
  297.   }
    * g+ a% o4 e# l- F
  298.   while(1)
    $ _: z9 K: j2 W6 x+ @3 B
  299.   {) b6 r" ^0 F* m4 h/ ^" h
  300.           WatchdogFeed();
      ~5 W* J& E6 g9 |5 G# n
  301.     W25QXXRead(secpos * 4096, w25q_buf, 4096); // 读出整个扇区的内容  B  E, k& u/ _1 P% k; @
  302.     for(i = 0; i < secremain; ++i)
    8 N( m" O; `! q. _  B
  303.     {
    8 s" P4 r% T  p/ [, @
  304.       if(w25q_buf[secoff + i] != 0XFF)
      q; n4 R$ H: P; A; Z4 }
  305.       {! b2 i: T8 x2 f& s; O: \; R
  306.         break;  // 需要擦除
    5 Q3 ]6 i3 `2 n) S* B
  307.       }! o# j. z& R# D% |
  308.     }7 K: Y$ Z1 t, r  S+ d% s
  309.     if(i < secremain) // 需要擦除8 M  g- J+ C# q# Z0 `7 t
  310.     {! D* L# G$ R, m& U* T& l
  311.       W25QXXSectorErase(secpos); // 擦除这个扇区" u7 l! h8 ~; Y4 m, I4 Y/ l
  312.       for(i = 0; i < secremain; ++i)
    ( j4 C* ?. _* O
  313.       {  I) y5 B% x% t5 E* m
  314.         w25q_buf[i + secoff] = buffer<i>;</i>
    ! o* |9 V0 h  u
  315.       }
    4 ^6 K1 q5 \) |/ m8 U' q7 `* a
  316.       W25QXXWriteExt(secpos * 4096, w25q_buf, 4096); // 写入整个扇区
    2 ?# f* T* A: Y
  317. / w, z5 G5 I, V" S, q- ]
  318.     }$ M7 L6 Z7 {) m( I* n: I7 _
  319.     else- s1 \( h/ b2 r' R& K: C' m
  320.     {& a& b8 R/ o, t, I/ {2 E& I
  321.       W25QXXWriteExt(addr, buffer, secremain);  // 写已经擦除了的,直接写入扇区剩余区间.* q1 c$ h8 c1 @" E5 Y% V7 i
  322.     }! M7 e" ]) V- y) W0 M* w
  323.     if(length == secremain)
    1 I7 }- }) j  K$ m  U% _% C1 P- e
  324.     {: G, S/ T" {* ?4 A9 z' [' p
  325.       break;
    ! H9 P- E: l( r9 ], n$ Q, g
  326.     }
    $ B$ i) _% t& ]: T9 B% |' w/ U5 W1 n5 @
  327.     else
    " a# i% F: v& N2 J; e( o# S
  328.     {
    ! C$ ?2 q( X* _2 W" P" n) j& F
  329.       secpos++;
    * z3 a0 o- _' k1 h9 }
  330.       secoff = 0;
    0 O1 S$ x; J, o: Q2 B7 m% n7 S: {
  331. * ]/ y- p8 ?- H# v, J# H
  332.       buffer += secremain;1 j5 _6 }$ M6 {8 m3 x
  333.       addr += secremain;
    4 c  y; y; }% v$ q; S2 C# x
  334.       length -= secremain;
    # x5 z& I$ u0 ~% t
  335.       if(length > 4096)) {& e. I( T2 x/ N% ?$ A+ @
  336.       {8 L* H& R2 D- {. n2 f' f8 Z
  337.         secremain = 4096;
    * c/ X2 v; W* r5 i
  338.       }3 J& t% Z% Z" u
  339.       else; R, i) i9 u: a8 Z. W% g
  340.       {; T$ `: _( w4 C4 F7 m5 g
  341.         secremain = length;2 y- {" U3 S0 g& X6 C( x
  342.       }' G) |$ t$ j' C8 p: Q
  343.     }: g5 \' z  l( l/ y! l7 ]% W( h
  344.   };
    1 a9 a9 W7 [  U
  345. }
    ( G; f) g- y* D# ~8 k/ O: H. }' F
  346. 7 O3 C' ~, j, W1 g

  347. & h- X. \: ?0 O- h
  348. // 读取SPI FLASH,仅支持QPI模式  在指定地址开始读取指定长度的数据 65536$ ?9 \4 i  Q1 x7 S, {- B8 V( B( `
  349. void W25QXXRead(uint32_t addr, uint8_t *buffer, uint16_t length)3 _  ?! X8 J% o3 B. t$ d
  350. {
    6 Q6 a# M! |' H5 K7 |9 x+ e
  351.   QSPISendCMD(W25QXX_FAST_READ_DATA, addr, 8, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_32_BITS, QSPI_DATA_4_LINES); // QPI,快速读数据,地址为ReadAddr,4线传输数据_32位地址_4线传输地址_4线传输指令,8空周期,length个数据
    * H- Q2 K' ?5 m4 O( Q
  352.   QSPIReceive(buffer, length);
    ; Q2 _  |' E: i& ?8 }
  353. }
复制代码
8 O. E! e7 n4 S6 q& l: @
也可以使用SPI操作W25QXX。
+ V* k1 \. B. ~; ]* F5 o; n, l" R" Q7 \6 y- _; Q( y% B

- t' U: F. F, m) s8 K4 V2 z8 V! N! @8 n; z0 q9 r; H
收藏 评论0 发布时间:2021-12-11 12:00

举报

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