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

【经验分享】STM32F7xx —— QSPI

[复制链接]
STMCU小助手 发布时间:2021-12-11 12:00
一、QSPI7 [9 n( d; y5 j9 T, J9 {  B8 H2 K
        SPI 是 Queued SPI 的简写,是 Motorola公司推出的 SPI 接口的扩展,比 SPI 应用更加广泛。在 SPI 协议的基础上,Motorola 公司对其功能进行了增强,增加了队列传输机制,推出了队列串行外围接口协议(即 QSPI 协议),使用该接口,用户可以一次性传输包含多达16个8位或16位数据的传输队列。一旦传输启动,直到传输结束,都不需要CPU干预,极大的提高了传输效率。该协议在ColdFire系列MCU得到广泛应用。与SPI相比,QSPI的最大结构特点是以80字节的RAM代替了SPI的发送和接收寄存器。QSPI 是一种专用的通信接口,连接单、双或四(条数据线) SPI Flash 存储介质。
9 X* M- U# A( a: J* d$ x0 }) }5 x7 b1 p/ k' B0 \, K) l: E
该接口可以在以下三种模式下工作:. a9 F& X/ p* F/ |* f0 ~* d
① 间接模式:使用 QSPI 寄存器执行全部操作
$ N# J! z5 W8 E② 状态轮询模式:周期性读取外部 Flash 状态寄存器,而且标志位置 1 时会产生中断(如擦除或烧写完成,会产生中断)
  z  ~+ d5 W3 B! l* p$ P③ 内存映射模式:外部 Flash 映射到微控制器地址空间,从而系统将其视作内部存储器。
  x& x# d) ^1 o$ y
  o; a' q3 E  N+ J, BQSPI通过6根线与SPI芯片通信,下图是内部框图:; u2 w/ p' G) k- }

2 n3 d# C: ]+ e2 y. v& I' L. r- U
20190123173205949.png
% ^) A6 s7 M9 B! X
2 N) N& H/ N; w% C: K' c1 z% O
QSPI每条命令,必须包含一个或多个阶段:指令、地址、交替字节、空指令和数据。
8 J; L8 r- M) K1 b! c! o/ @/ {! ~9 j) h$ U( p- s/ K
QSPI发送命令:等待QSPI空闲;设置命令参数。7 S, ?5 c+ x5 U3 n* y4 b- k- E3 @
7 u4 S6 x, e/ J: S4 E) B! J2 p' M
QSPI读数据:设置数据传输长度;设置QSPI工作模式并设置地址;读取数据。# ~- p9 k% t0 M3 |" |' R

6 ?$ @& Q+ @) G* u: Y9 e3 XQSPI写数据:设置数据传输长度;设置QSPI工作模式并设置地址;写数据。; E1 [* [' Z6 @5 z& m
2 r6 F! d3 k% l, X+ _. H5 C7 I: z
& i6 @0 ~' O9 p8 \* h9 H
二、几个重要的函数7 K  ~, A1 P# U! d, w8 ^3 ]% b7 Z4 z
  1. HAL_StatusTypeDef     HAL_QSPI_Init     (QSPI_HandleTypeDef *hqspi); // 初始化
    3 M- R0 e+ v. t1 |

  2. 2 N9 o6 P2 R) Q* d# n) Q. C
  3. HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout); // 发送命令
    7 r4 A& ]- b% m
  4. 6 p3 Q( P/ E6 U8 o
  5. HAL_StatusTypeDef     HAL_QSPI_Transmit     (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 发送数据3 p, R, E; d7 z  K8 G! Y
  6. 3 \% D1 j- }5 E  v9 ^
  7. HAL_StatusTypeDef     HAL_QSPI_Receive      (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 接收数据
复制代码
; r; `, B* U  c& J% w4 |& H+ B9 @( y
三、几个重要的结构$ {: o2 L2 G8 }. X6 \
  1. // QSPI操作句柄
    5 C! J# D( z8 C! Y/ A$ p
  2. typedef struct
    ! M* F) ]% A3 O" R( A- U
  3. {8 l- i: s) N% c$ r9 V! _0 H( ^8 S3 q. s
  4.   QUADSPI_TypeDef            *Instance;        /* QSPI registers base address        */! V+ i; C" |+ e  f1 _
  5.   QSPI_InitTypeDef           Init;             /* QSPI communication parameters      */
    1 Y# M5 j3 G5 \9 ~. U' W
  6.   uint8_t                    *pTxBuffPtr;      /* Pointer to QSPI Tx transfer Buffer */8 p8 ?& s, C- P' x5 K7 e
  7.   __IO uint16_t              TxXferSize;       /* QSPI Tx Transfer size              */* P' V/ u5 I8 n  N* o7 ^
  8.   __IO uint16_t              TxXferCount;      /* QSPI Tx Transfer Counter           */+ ~. d, N; W2 h, o- W
  9.   uint8_t                    *pRxBuffPtr;      /* Pointer to QSPI Rx transfer Buffer */
    ( r- d, M1 y& v( F
  10.   __IO uint16_t              RxXferSize;       /* QSPI Rx Transfer size              */2 d3 H2 g3 B' Q4 J, v
  11.   __IO uint16_t              RxXferCount;      /* QSPI Rx Transfer Counter           */
    2 k8 m; j: @8 h+ G8 |  X
  12.   DMA_HandleTypeDef          *hdma;            /* QSPI Rx/Tx DMA Handle parameters   */6 U" n; v! A! b% c  J4 d
  13.   __IO HAL_LockTypeDef       Lock;             /* Locking object                     */
    * g2 _/ K( d' v* a) i
  14.   __IO HAL_QSPI_StateTypeDef State;            /* QSPI communication state           */
    # d# k9 x" k) t$ z  @7 E
  15.   __IO uint32_t              ErrorCode;        /* QSPI Error code                    *// [1 e1 d6 @8 |9 }% L% p8 I+ ^6 j
  16.   uint32_t                   Timeout;          /* Timeout for the QSPI memory access */ , h) g# q( `! s; p3 g* y) ^
  17. }QSPI_HandleTypeDef;  U; ?3 A3 W' {% g# S; J$ P2 m
  18. ' n3 [; G  m4 @7 D" O
  19. // Instance:QSPI基地址  ---  QUADSPI
    % _' Q& J! V3 v* J  }2 P
  20. // Init:设置QSPI参数
    . |$ ~, ~4 v4 l* P, D3 h
  21. // pTxBuffPtr,TxXferSize,TxXferCount:发送缓存指针 发送数据量 剩余数据量6 J! a' U" L, r$ B( N; Q* T2 o' I
  22. // pRxBuffPtr,RxXferSize,RxXferCount:接收缓存指针 发送数据量 剩余数据量
    7 }' i" g  @. H  s& T- V
  23. // hdma与DMA相关, 其他为过程变量不需要关心
复制代码
  1. // 参数配置 时钟分频系数  FIFO阈值  采样移位  FLASH大小  片选高电平时间  时钟模式  闪存ID  双闪存模式设置
    " L, e: P1 O' j4 E4 \  d( U
  2. typedef struct. T7 U1 z, u$ l4 O9 V% n
  3. {
    3 \5 Q) g) ^7 n+ o# G3 S- A
  4.   uint32_t ClockPrescaler;     /* Specifies the prescaler factor for generating clock based on the AHB clock.
    8 u: D) F% X( \  d- n% k% x+ P
  5.                                   This parameter can be a number between 0 and 255 */
    # v6 n' E& |& [$ D

  6. % k3 x8 Q  t. {
  7.   uint32_t FifoThreshold;      /* Specifies the threshold number of bytes in the FIFO (used only in indirect mode)0 P/ A$ N* A* N, J! ^9 b& [  k  |
  8.                                   This parameter can be a value between 1 and 32 */9 J: [5 {( E: t' ?* X8 N: W

  9. ) Q6 [; b: K6 _2 ]3 o5 w, }
  10.   uint32_t SampleShifting;     /* Specifies the Sample Shift. The data is sampled 1/2 clock cycle delay later to ( A; g8 @5 m9 d, Z
  11.                                   take in account external signal delays. (It should be QSPI_SAMPLE_SHIFTING_NONE in DDR mode)( x. f8 c: w* ~( C
  12.                                   This parameter can be a value of @ref QSPI_SampleShifting */
    # `, q5 Y* e; L+ l3 j

  13. , b* _4 S4 {! B: P% A
  14.   uint32_t FlashSize;          /* Specifies the Flash Size. FlashSize+1 is effectively the number of address bits
    7 G# c( ~( m9 T$ r
  15.                                   required to address the flash memory. The flash capacity can be up to 4GB
    + J- @+ i( C; _2 m) T
  16.                                   (addressed using 32 bits) in indirect mode, but the addressable space in / o( ?+ ]' B5 g4 a
  17.                                   memory-mapped mode is limited to 256MB
    : t" q: [6 S9 T- t. [
  18.                                   This parameter can be a number between 0 and 31 */
    ' l, A2 ]$ ?' o6 h& V

  19. * ?/ a5 ?3 f6 S1 O  e1 g
  20.   uint32_t ChipSelectHighTime; /* Specifies the Chip Select High Time. ChipSelectHighTime+1 defines the minimum number
    $ E$ x  v9 F& h0 T; O1 E
  21.                                   of clock cycles which the chip select must remain high between commands.
    ( n$ r$ K9 @1 |: z2 K
  22.                                   This parameter can be a value of @ref QSPI_ChipSelectHighTime */
    % f( A1 r, w3 y2 ]' Q6 P( W
  23.   g/ R7 n6 z0 d9 U
  24.   uint32_t ClockMode;          /* Specifies the Clock Mode. It indicates the level that clock takes between commands.$ q9 N/ z, H- d" b/ P0 L
  25.                                   This parameter can be a value of @ref QSPI_ClockMode */8 t8 d' H9 o6 m$ g, B7 e: w
  26. 8 A7 S, w/ U2 _
  27.   uint32_t FlashID;            /* Specifies the Flash which will be used,0 v" v/ U$ I; E$ R" s& Y
  28.                                   This parameter can be a value of @ref QSPI_Flash_Select */
    ( \# G" ~+ I) H: D7 }$ L

  29. 5 N  |" I5 V6 h7 {+ j/ Z
  30.   uint32_t DualFlash;          /* Specifies the Dual Flash Mode State
    ! _. S0 ]  e6 k1 g( T6 \! P  N
  31.                                   This parameter can be a value of @ref QSPI_DualFlash_Mode */                                               ; I- _, e& ]2 M$ @8 U
  32. }QSPI_InitTypeDef;
复制代码
  1. // 采样移位' @0 C3 _, v' \+ N; h3 T
  2. #define QSPI_SAMPLE_SHIFTING_NONE           ((uint32_t)0x00000000U)        /*!<No clock cycle shift to sample data*/
    " F) J  ]) z( b! A# i2 R7 r8 @
  3. #define QSPI_SAMPLE_SHIFTING_HALFCYCLE      ((uint32_t)QUADSPI_CR_SSHIFT) /*!<1/2 clock cycle shift to sample data*/
复制代码
6 f- E- J7 L- d* `5 S, K
四、QSPI接口设计(仅供参考)
4 ]% t4 P9 N& D! K- f  f  s
  1. #define QSPI_CLK_ENABLE()         __HAL_RCC_QSPI_CLK_ENABLE();
    ) f4 n+ F) Y9 W3 j( U

  2. 3 D/ X, a: L! }6 e
  3. #define QSPI_BK1_NCS_PORT         GPIOB
      ~& H" K" c3 M6 C7 G0 y/ i
  4. #define QSPI_BK1_NCS_PIN          GPIO_PIN_6
    8 h+ E0 I7 X* _' k
  5. #define QSPI_BK1_NCS_AF           GPIO_AF10_QUADSPI
    " g' \8 ~! i- {% N# _" i( N
  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)+ s; O2 u; z7 k
  7. ) Q* H4 k# r  ~' t
  8. #define QSPI_BK1_CLK_PORT         GPIOB
    / z) F6 j; L- v
  9. #define QSPI_BK1_CLK_PIN          GPIO_PIN_2, G8 E5 [* B+ K& q" f- ~  Y
  10. #define QSPI_BK1_CLK_AF           GPIO_AF9_QUADSPI
    5 a- `$ G( E" z/ y
  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)! J$ ]) b5 h. D1 r. H& }

  12. " N5 q& I$ C) p
  13. #define QSPI_BK1_IO0_PORT         GPIOF
    / g5 \# j8 g# q, y5 x8 k+ r" M5 y
  14. #define QSPI_BK1_IO0_PIN          GPIO_PIN_8
    1 \( B1 l/ d: V4 w: O: }% w  g
  15. #define QSPI_BK1_IO0_AF           GPIO_AF10_QUADSPI% M% V: u2 @1 C! j' `3 P% m
  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)* w6 Y2 C( P  L  M7 Q" i

  17. 8 k' `5 W- r, q; A" j  F5 e- R
  18. #define QSPI_BK1_IO1_PORT         GPIOF9 v  C- ^( N8 u5 J+ @- _1 I, D
  19. #define QSPI_BK1_IO1_PIN          GPIO_PIN_9: K. O; u; M2 W/ T1 {' d; n, D; W- P! K
  20. #define QSPI_BK1_IO1_AF           GPIO_AF10_QUADSPI( ]+ w/ N  L. S
  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); O  R0 {- _( i
  22. ( u7 h- N# c6 N7 X' N+ y8 r
  23. #define QSPI_BK1_IO2_PORT         GPIOF
    - r$ M5 _6 t7 \: U
  24. #define QSPI_BK1_IO2_PIN          GPIO_PIN_7
      M% I& c7 E7 s% q# o
  25. #define QSPI_BK1_IO2_AF           GPIO_AF9_QUADSPI
    % j1 r3 [+ A! T, o4 `: h
  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)% a- ?& f9 M3 S: g4 a
  27. 0 H  U* Z; g3 H9 Y: D
  28. #define QSPI_BK1_IO3_PORT         GPIOF
    6 Q0 E5 H& _+ o& c: R
  29. #define QSPI_BK1_IO3_PIN          GPIO_PIN_6
    $ ^. m4 M+ ]: b0 Q/ L2 L! b" i
  30. #define QSPI_BK1_IO3_AF           GPIO_AF9_QUADSPI
    & ?& i$ R: g# m# z
  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. // 封装几个必要的接口" d, `. R# h, b1 s5 N  d. ^3 J5 T
  2. static QSPI_HandleTypeDef qspi_handle;: v0 I4 ]3 C, [4 D  m
  3. $ q" X  x4 F6 V
  4. static void qspi_gpio_init(void)
    6 f& v" X( G' G
  5. {) }3 f5 Y$ p& ~# S3 J. b9 ~! P
  6.   QSPI_CLK_ENABLE();
    % D  u, Q+ h. K0 a  s
  7. + M1 c. \5 I" k/ j. i
  8.   QSPI_BK1_NCS_CONFIG();) H. q8 y6 I' S, i# h- W
  9.   QSPI_BK1_CLK_CONFIG();
    + J; ]6 a; V7 n( v! O* p7 S+ `3 G
  10.   QSPI_BK1_IO0_CONFIG();, M( L) o- w4 ~- ?" R/ K; j  m
  11.   QSPI_BK1_IO1_CONFIG();7 m1 v9 a8 G  i
  12.   QSPI_BK1_IO2_CONFIG();
    8 q( A* d6 |- V: Y
  13.   QSPI_BK1_IO3_CONFIG();+ g! X) _; I, L* x' v; i5 X# B0 {
  14. }
    8 U1 W2 `0 b4 w
  15. * D7 R- b  t# z$ y$ C
  16. static void qspi_mode_init(void)8 C( R# |2 T* L. L, F) s. i/ h
  17. {
    # S: q* {+ {# c5 I5 e3 [
  18.   qspi_handle.Instance = QUADSPI;                        // QSPI  f; [$ ?, v3 K
  19.   qspi_handle.Init.ClockPrescaler = 2;                   // QPSI分频比,W25Q256最大频率为104M 所以此处应该为2,QSPI频率就为216/(2+1)=72MHZ
    ) v: T3 F0 u' j9 h$ u
  20.   qspi_handle.Init.FifoThreshold = 4;                    // FIFO阈值为4个字节
    2 G( P5 O4 p( Q9 l; S% }
  21.   qspi_handle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; // 采样移位半个周期(DDR模式下,必须设置为0)
    / E/ G: E/ |: u' z( P  {2 }
  22.   qspi_handle.Init.FlashSize = POSITION_VAL(0X2000000) - 1; // SPI FLASH大小,W25Q256大小为32M字节
    : g8 K# n" `5 ]4 [) E* P
  23.   qspi_handle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_4_CYCLE; // 片选高电平时间为4个时钟(13.8*4=55.2ns),即手册里面的tSHSL参数
      v& {4 Z* t5 `6 o8 \7 R
  24.   qspi_handle.Init.ClockMode = QSPI_CLOCK_MODE_0;        // 模式0
    ; V( G0 p0 d8 K0 T8 J- I
  25.   qspi_handle.Init.FlashID = QSPI_FLASH_ID_1;            // 第一片flash2 }: W" d! {  T& i- X
  26.   qspi_handle.Init.DualFlash = QSPI_DUALFLASH_DISABLE;   // 禁止双闪存模式* x: U2 ^2 F- L# F7 [
  27.   HAL_QSPI_Init(&qspi_handle);      //QSPI初始化% U9 e1 I1 I+ r$ [1 O4 E
  28. }
    9 e, f3 e! w: o* z- }
  29. 9 T  q$ V5 k4 L' r
  30. void QSPIInit(void)
    & C* X0 R3 T" @2 s) [/ m
  31. {
    , ]* `4 N; y6 B
  32.   qspi_gpio_init();
    ; C# m9 ~0 Y  c; I4 l1 F
  33.   qspi_mode_init();8 h9 D' N5 t& h4 M( K, W
  34. }
    $ w; A1 X& H' H8 Z4 P

  35. 5 b0 c% J+ r% a
  36. // QSPI发送命令
    8 ]5 S: A1 G3 K) I' _. M
  37. // instruction:要发送的指令8 o- n/ o# J' u1 j6 M+ [- F
  38. // address:发送到的目的地址* p4 c: f9 u( |2 K
  39. // dummyCycles:空指令周期数! d4 e) N! j% n  w
  40. // instructionMode:指令模式;QSPI_INSTRUCTION_NONE,QSPI_INSTRUCTION_1_LINE,QSPI_INSTRUCTION_2_LINE,QSPI_INSTRUCTION_4_LINE
    ( j  o6 [" [8 e+ x3 m# T
  41. // addressMode:地址模式; QSPI_ADDRESS_NONE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_2_LINE,QSPI_ADDRESS_4_LINE) F6 `8 \: J: v: g7 A& P( J
  42. // addressSize:地址长度;QSPI_ADDRESS_8_BITS,QSPI_ADDRESS_16_BITS,QSPI_ADDRESS_24_BITS,QSPI_ADDRESS_32_BITS
    ) D: T1 B& {0 D# V1 _
  43. // dataMode:数据模式; QSPI_DATA_NONE,QSPI_DATA_1_LINE,QSPI_DATA_2_LINE,QSPI_DATA_4_LINE
    / X4 k1 x0 }; |/ ]* O
  44. void QSPISendCMD(uint32_t instruction, uint32_t address, uint32_t dummyCycles, uint32_t instructionMode, uint32_t addressMode, uint32_t addressSize, uint32_t dataMode)- d( c" J( K$ c+ a' P
  45. {4 |) ?7 U& `- E" I1 [7 d+ X
  46.   QSPI_CommandTypeDef Cmdhandler;
    " H0 a6 L- n7 Z: h% {7 l4 [
  47. ' T$ x* q% t+ Z/ J+ k
  48.   Cmdhandler.Instruction = instruction;           // 指令
    , S$ c' I4 O& G* \9 y  ^
  49.   Cmdhandler.Address = address;                   // 地址$ W6 _& }( Q0 S3 {4 ?! Y
  50.   Cmdhandler.DummyCycles = dummyCycles;           // 设置空指令周期数
    . ~! {. n8 T) \* h
  51.   Cmdhandler.InstructionMode = instructionMode;   // 指令模式
    : M  W1 O% B% B# f3 ^1 j" k9 t5 [
  52.   Cmdhandler.AddressMode = addressMode;           // 地址模式
    3 @2 X* y3 W9 m* y" s
  53.   Cmdhandler.AddressSize = addressSize;           // 地址长度1 i* n% P! C! w+ P5 e# G
  54.   Cmdhandler.DataMode = dataMode;                 // 数据模式
    - s, r' m- f- H. N( y) k
  55.   Cmdhandler.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 每次都发送指令6 n  ]9 d8 S' h* n+ [; o
  56.   Cmdhandler.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 无交替字节2 N) f& \$ `( c  \8 h) L" V# \
  57.   Cmdhandler.DdrMode = QSPI_DDR_MODE_DISABLE;           // 关闭DDR模式6 ?: `3 U; X0 \2 l9 F
  58.   Cmdhandler.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;) g8 M# G" h; V+ \0 J; h
  59. * O2 M& j% h& P! V* M) E! D: {. Q" s
  60.   HAL_QSPI_Command(&qspi_handle, &Cmdhandler, 5000);
    # v( w. T8 Z& I# U8 A$ O3 B9 H& H- _- v
  61. }
    ' A" {" C2 W: X
  62. / l1 A, m; C0 a8 G0 G
  63. // 接收指定长度数据$ i0 }7 K( H3 a6 Y% J9 a; V
  64. uint8_t QSPIReceive(uint8_t *buffer, uint32_t length)
    8 v$ I5 v3 N0 D% k
  65. {
      w  k6 P* @- ]; N6 l' M" W6 a
  66.   qspi_handle.Instance->DLR = length - 1;
    # k+ W9 x2 g. ^- y  g4 k: _9 n7 K
  67.         
    . `) l5 v7 Q! ^
  68.   if(HAL_QSPI_Receive(&qspi_handle, buffer, 5000) == HAL_OK)
    9 J/ B+ O' M: p. [
  69.   {) S5 ^% F( \# G7 A
  70.     return 0;  //接收数据
    ! X7 U- u  u+ E8 r" |4 L
  71.   }! G. G! E# j& {+ R
  72.   else
    ( `. i7 j5 G& T8 o' Y3 ?
  73.   {" m( B( @" T. e4 m- N- L
  74.     return 1;& X: a4 P. V7 [7 N% P" e) p
  75.   }/ K0 _- A* T- R# q& z
  76. }& B# c& \4 K) O  Z6 b" _, O- k' j

  77. - @: w' u% ~  |0 D  r7 W' v
  78. // 发送指定长度数据9 D0 U8 J+ U# m
  79. uint8_t QSPITransmit(uint8_t *buffer, uint32_t length)
    * R; E" J6 `9 r. ^( X
  80. {
    3 y9 i" v1 x3 R  r& `
  81.   qspi_handle.Instance->DLR = length - 1;
    1 z5 j4 h9 `! W, v, _; o/ ^3 {( H1 ]$ d3 C
  82.   if(HAL_QSPI_Transmit(&qspi_handle, buffer, 5000) == HAL_OK)
    , o* ^9 B" `% m2 X3 v: @
  83.   {2 j: N) a$ o7 I5 r/ d
  84.     return 0;  //发送数据
    . ^/ d1 {' w* |
  85.   }* H# v0 ]/ p+ B3 x7 W
  86.   else1 k0 ?* y4 N9 _# t, @5 {. }: q
  87.   {
    - Z2 [7 s: k) O$ ^' V( x# K4 `
  88.     return 1;) l, \5 E- D; c* L9 Q* D
  89.   }
    1 h4 X- {2 x% Q/ w
  90. }
复制代码

3 E1 W# A% q, Y1 \( a. [五、QSPI驱动W25Q2562 ]2 D2 K5 b2 b) K
W25Q256:32M  512块,每块16个扇区,每个扇区4K。具体指令看手册。封装了如下接口(初始化读写擦除等接口是以后移植文件系统的基础):
8 G/ v/ b' d; f5 F* @8 m! A7 O( l. b! Q
  1. #define W25QXX_WRITE_ENABLE      0x06+ w  z2 C: B* \7 w2 y1 l8 j
  2. #define W25QXX_WRITE_DISABLE     0x04% ]6 g. f( I# q  c
  3. #define W25QXX_READ_STATUS_REG1  0x05
    3 ~7 X; k0 T) [. D# z
  4. #define W25QXX_READ_STATUS_REG2  0x355 X8 o/ `5 |0 J$ l
  5. #define W25QXX_READ_STATUS_REG3  0x15
    ) ?" K! y) n& f
  6. #define W25QXX_WRITE_STATUS_REG1 0x01
    ( j7 M, T. z, V
  7. #define W25QXX_WRITE_STATUS_REG2 0x31
    " k8 ^, l" l1 W+ A" _3 S1 \& K, y
  8. #define W25QXX_WRITE_STATUS_REG3 0x11
    ( ~% V* i. `2 `7 ^9 j3 P
  9. #define W25QXX_READ_DATA         0x03
    - |8 m; D  u8 r; a! h5 l
  10. #define W25QXX_FAST_READ_DATA    0x0B
    2 v$ m; U) T6 f
  11. #define W25QXX_FAST_READ_DUAL    0x3B  ]$ _7 {7 T* ~# x, n3 ]$ I
  12. #define W25QXX_PAGE_PROGRAM      0x021 m& K- j  a4 H! Y! Z' T
  13. #define W25QXX_BLOCK_ERASE       0xD8
    7 g# l0 v6 K# |6 b4 a( Q
  14. #define W25QXX_SECTOR_ERASE      0x20
    ' z5 ~/ y- g! }- |0 V3 a' h" D
  15. #define W25QXX_CHIP_ERASE        0xC7
    ' S, B3 G' o! l5 ]2 p9 C
  16. #define W25QXX_DEVICEID          0xAB" T0 S4 B9 _) q1 E& z: A
  17. #define W25QXX_MANUFACT_DEVICEID 0x90( N/ y$ R& z1 l- {3 N. `
  18. #define W25QXX_JEDEC_DEVICEID    0x9F
    5 M9 M+ G0 w' a! O' x7 W
  19. #define W25QXX_EABLE_4BYTE_ADDR  0xB7
    & x* m$ _2 o7 O7 T+ K
  20. #define W25QXX_EXIT_4BYTE_ADDR   0xE90 D7 D- L: a4 G1 o* W, V
  21. #define W25QXX_SET_READ_PARAM    0xC0
    0 x1 _/ g2 ^4 k; @. }
  22. #define W25QXX_ENTER_QPIMODE     0x38
    9 @) c# J6 `6 v0 \2 v9 F6 s
  23. #define W25QXX_EXIT_QPIMODE      0xFF6 V( ]  ~$ c# `  a- N$ A6 d

  24. / @! g6 _1 z$ _( O# R% q5 H
  25. uint8_t w25qxx_qpi_mode = 0; // QSPI模式标志:0,SPI模式;1,QPI模式.
    ' N( o; f5 e" |/ Y; \
  26. ; f# a# _3 q/ t/ {- Z: t
  27. // 读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器
    3 d! d  V+ R) T8 G3 A( K
  28. // 状态寄存器1:
    4 T3 s0 b( E0 ~$ m
  29. // BIT7  6   5   4   3   2   1   07 D( v0 a9 ]* W% Q1 s: K
  30. // SPR   RV  TB BP2 BP1 BP0 WEL BUSY
    ' E4 m, j6 O' c' |0 `
  31. // SPR:默认0,状态寄存器保护位,配合WP使用
    4 P8 E! o, x5 B: e* J# N6 p, d
  32. // TB,BP2,BP1,BP0:FLASH区域写保护设置
    3 Q$ ^: w; M. A7 j5 P/ t+ [
  33. // WEL:写使能锁定
    " H; N2 ~, ~) D: P
  34. // BUSY:忙标记位(1,忙;0,空闲)
    " P; W; ]. Y) D
  35. // 默认:0x00; C- S. `% f5 y
  36. // 状态寄存器2:
    1 R/ F9 U2 `, I; h/ f6 d
  37. // BIT7  6   5   4   3   2   1   0% W5 y! \, S7 D% H
  38. // SUS   CMP LB3 LB2 LB1 (R) QE  SRP11 I- m' H5 N4 [% s9 N4 L
  39. // 状态寄存器3:
    + u2 n, L3 M% U* u# h
  40. // BIT7      6    5    4   3   2   1   08 u1 a# c: K8 }* x9 t
  41. // HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
    ' ?  S9 B. `  N6 t; K0 P! z4 N
  42. // reg:状态寄存器号,范:1~35 O) O3 f2 x- |8 }
  43. // 返回值:状态寄存器值
    : Q% v# P& q9 M
  44. static uint8_t w25qxx_read_status(uint8_t reg)* U, t0 K8 F6 ^; V$ @' R5 ~
  45. {
    2 c3 M3 ^8 x0 \
  46.   uint8_t value = 0, command = 0;. ~4 r! A' H# u6 l1 L; c* l- w

  47. ' D5 Y7 q. J9 ^2 A6 A
  48.   switch(reg)
    ' A& N+ Y; ?: F5 }" }
  49.   {' q' ]+ E3 w7 u2 \9 |1 u3 G
  50.   case 1:
    / p, C2 K# ^5 I( X5 H9 o
  51.     command = W25QXX_READ_STATUS_REG1;  // 读状态寄存器1指令% B) w3 P8 F6 o! e# {! D
  52.     break;
    ) Q# Q0 T2 z- o0 H# b* i
  53. 6 ]: I- }* Z( u( u* \3 O( c
  54.   case 2:) Q0 r3 p. }" m
  55.     command = W25QXX_READ_STATUS_REG2;  // 读状态寄存器2指令, |$ f/ }! t. Z" L
  56.     break;
    4 e9 \* p  M3 L. r) G- o# ?' y5 ?8 Q
  57. ! e3 R& X4 T0 ?. ^+ P
  58.   case 3:
    % Y& D1 r9 O* g5 s
  59.     command = W25QXX_READ_STATUS_REG3;  // 读状态寄存器3指令
    / a9 m5 g" v( a3 `# M& p
  60.     break;
    ( ?' c4 {8 ~$ ^( ?  ]% W) ?

  61. 5 C6 u( y% g2 J% V
  62.   default:
    ' K: i' @, j; i2 r% S
  63.     command = W25QXX_READ_STATUS_REG1;
    9 J% z: K! m9 Y+ r( O9 s0 K, |, v
  64.     break;) W* d& x* V# }  J) I
  65.   }5 W" ?3 P8 [; Y; P# h: O+ F, H

  66. % \7 m9 v8 X- o; C
  67.   if(w25qxx_qpi_mode)* j2 L+ g4 U( r- q2 S( `
  68.   {
    : n9 O$ _$ w: r! N9 V
  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个字节数据/ B* g$ C0 q3 i# x1 r
  70.   }
    # Q5 N* ?# s1 ?; Y0 s
  71.   else/ u( [( C  h. z8 m6 ]  n7 I
  72.   {
    ; T4 Z8 O0 L1 m/ h
  73.     QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_1_LINE);  //SPI,写command指令,地址为0,单线传数据_8位地址_无地址_单线传输指令,无空周期,1个字节数据
    . D- Y$ u" {) j" e
  74.   }7 C* M8 o! }8 C3 C* ^
  75. 3 \  }0 D4 c7 ?- {
  76.   QSPIReceive(&value, 1);
    ; N# ?1 X5 p" F1 t& n/ V

  77. ( S4 D, `+ z8 m2 b8 A
  78.   return value;% X, [( U$ t5 m+ m# {4 ?; C( ^
  79. }* R( l* X# J& A0 `3 [  {; u( z

  80. / o" Z! t1 A0 g4 {) _% Y
  81. // 写状态寄存器
    % o, B$ L/ Y3 l( ~
  82. static void w25qxx_write_status(uint8_t reg, uint8_t status)
    8 N2 n- X) \* g* J- [
  83. {
    1 n* A. a" t) S6 q: Q! Y$ n' ^
  84.   uint8_t command = 0;
    4 N/ ]+ k( q! d
  85. ) |. S- {$ x& N; C9 Y' ~
  86.   switch(reg)  O& @  Q  N$ M6 L
  87.   {2 a$ u* x' E$ c* ]; V6 O1 x# s8 j
  88.   case 1:
    # I! K" M; q$ x5 I
  89.     command = W25QXX_WRITE_STATUS_REG1;  //写状态寄存器1指令
    4 [8 ]4 I( o# f' i1 U" p1 L, X
  90.     break;
    6 n; D9 b( M& a4 Q0 j- [6 l( A9 D& x! v
  91.   case 2:* L6 f9 t3 N9 \! p* r
  92.     command = W25QXX_WRITE_STATUS_REG2;  //写状态寄存器2指令0 m* q3 H( d/ l# v4 x  f( R4 Q
  93.     break;6 i9 F0 }9 W+ m
  94.   case 3:
    - B6 g2 d- A; n$ H4 p
  95.     command = W25QXX_WRITE_STATUS_REG3;  //写状态寄存器3指令
    3 y4 i3 c6 q2 [$ x- ?
  96.     break;4 C6 ]( C5 B$ r! W
  97.   default:
    # v/ f) O+ E; @; h) |
  98.     command = W25QXX_WRITE_STATUS_REG1;0 I& q5 a) u/ T3 o2 w0 l' u1 o
  99.     break;
    " Y/ {$ T. g2 r; m+ p" O
  100.   }
    7 y9 E6 ^9 r) v) D' y; n
  101.   if(w25qxx_qpi_mode)
    4 N" Z% N% a% \" u* ]3 Q" q( D  k6 N5 q
  102.   {8 M* Q" q' k, I" r! f8 a* d
  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个字节数据, P3 ?. g4 p/ @4 {
  104.   }$ K3 k3 W) U0 z7 G
  105.   else
    . T9 G+ b6 P+ U% `1 D
  106.   {
    ) y( A9 z% ~2 _
  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个字节数据& w$ b7 h9 u6 x4 `
  108.   }
    - P6 f' I6 R. S! c( \
  109.   QSPITransmit(&status, sizeof(status));/ x, g  z* \# J9 O/ T$ m
  110. }
    . p  g+ Q5 p( ]& S1 o& S+ ?0 ]

  111.   `/ H) Y4 p0 Q1 Y  L" A
  112. // 写使能  将S1寄存器的WEL置位
    + t$ {) G9 D% h" \9 k, s) A6 X' L+ ]
  113. static void w25qxx_write_enable(void)  x' Z8 x; p- u( p( P: a5 `5 w/ J
  114. {6 a8 n' r7 D+ [" u! ]' m
  115.   if(w25qxx_qpi_mode)
    ( x# j/ M3 W, I
  116.   {
    : {. {+ G) q$ m. I# w
  117.     QSPISendCMD(W25QXX_WRITE_ENABLE, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // QPI,写使能指令,地址为0,无数据_8位地址_无地址_4线传输指令,无空周期,0个字节数据
    # D% }+ e  x, J" C3 f, C
  118.   }& Z- ]7 R3 F$ v5 ]9 v* N
  119.   else+ ]! N" k+ q6 U$ p* L
  120.   {
    ' W2 l- v7 t9 ~. |, R5 z
  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个字节数据3 _9 j0 t9 y) \& k
  122.   }3 S& K5 ?4 R+ j6 n0 d9 E; w
  123. }
    3 |; z" K# a' O1 i9 D( b% \
  124. * _& @# H# S2 u' A% m* g8 P  G
  125. // 写失能  将WEL清零& H# B# {7 n0 z
  126. void W25QXX_Write_Disable(void)8 ]- E" f; `$ c9 V/ t) o. H
  127. {3 ^9 e: q8 t5 O5 ~* U) x) q/ K' v
  128.   if(w25qxx_qpi_mode)) G# e8 {3 K# f+ S
  129.   {
    - t: n* M4 ]$ B* y* u8 f; @
  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个字节数据
    1 X* M' y' I2 U; _/ j
  131.   }0 z; t0 C0 y/ l1 C6 {5 a' d
  132.   else
    . q, \& Y* D& p6 F, d' s
  133.   {) P0 ~. H9 B* V/ u: b4 _
  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个字节数据
    , i% o) I, S  @( B" {! x# B$ M
  135.   }$ i1 c, C6 j2 {* T' h
  136. }9 g! W; W  ~0 ]* `# r7 D

  137. " K8 {7 D% X7 B+ H
  138. // 等待空闲/ [! g& f3 [8 p! h; L
  139. void w25qxx_wait_busy(void)
    . }- ]2 C! \7 e' z
  140. {
    0 R5 C4 _8 q3 I
  141.   while((w25qxx_read_status(1) & 0x01) == 0x01); // 等待BUSY位清空. r% P# \6 ~( i$ @6 C
  142. }
    - }0 y7 |- d( e% n$ P# K! `
  143. / Z# t* g: z0 h0 F; V* Q0 F; }
  144. // W25QXX进入QSPI模式7 R1 t6 q2 p1 T& E9 i6 A
  145. static void w25qxx_qspi_init(void)
    9 X# Z# M( L( b5 u
  146. {
    8 K0 q- a# S2 X7 A3 v
  147.   uint8_t reg2;
    ' T9 o& Y9 u) B: [6 d
  148.   reg2 = w25qxx_read_status(2); // 先读出状态寄存器2的原始值! k9 {0 H% [* n* Z
  149.   if((reg2 & 0X02) == 0)  // QE位未使能- \4 G% |1 q' W3 s
  150.   {2 ~% p$ `1 }& ^: t) @& S
  151.     w25qxx_write_enable(); // 写使能- {" ^$ g& u' D: y% k
  152.     reg2 |= 1 << 1; // 使能QE位
    0 D6 A) P' L  b. k. d
  153.     w25qxx_write_status(2, reg2); // 写状态寄存器2# r- {. _; }; I" O: [+ L/ {* a
  154.   }
    $ d# B$ H, ^+ d& M4 F& K# M

  155. $ H, P" h+ _# M" Y8 j9 b
  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个字节数据3 ~0 O0 Z0 F: _( @& |& i4 v+ d

  157. . ~6 I2 b" n/ c5 u! M1 G4 |
  158.   w25qxx_qpi_mode = 1; // 标记QSPI模式2 w, b" Z0 n- j0 d* y7 z
  159. }2 V. s+ {- [& Z" Q( p1 P( K0 [) \
  160. % H, ~, V/ p9 W6 D! V# |
  161. // 0XEF13,表示芯片型号为W25Q80
    4 G' @+ @" U  r3 t) [, T
  162. // 0XEF14,表示芯片型号为W25Q16
      u% ~6 O4 q/ X# l9 I) `
  163. // 0XEF15,表示芯片型号为W25Q32
    9 Y, L. ]( ]* Z. U8 N8 a: W
  164. // 0XEF16,表示芯片型号为W25Q64( q2 J% M" f0 V! c2 `" ]
  165. // 0XEF17,表示芯片型号为W25Q1283 i8 s& c& t- w
  166. // 0XEF18,表示芯片型号为W25Q256
    % E; g# I6 M; X: U. c) ?) T; n# q
  167. static void w25qxx_id_get(void)& T8 x4 |5 ^) `% O( s
  168. {9 O7 a- U1 g# c3 x& O! h
  169.   uint8_t temp[2];2 j8 Z' t; H( D0 j3 z4 m" O
  170.   uint16_t device_id;
    9 v$ c: p- V5 t' L: i. x0 J
  171. + i( F  m7 p: h9 Z6 a" w
  172.   if(w25qxx_qpi_mode)$ Y4 q4 H9 ^: J$ v6 M/ u" \
  173.   {
    $ N3 m5 h( E( U1 n; |
  174.     QSPISendCMD(W25QXX_MANUFACT_DEVICEID, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_24_BITS, QSPI_DATA_4_LINES); // QPI,读id,地址为0,4线传输数据_24位地址_4线传输地址_4线传输指令,无空周期,2个字节数据
    & D  _) T" c$ d1 O: G$ \. ^
  175.   }, V, P7 b# x7 t# w
  176.   else" z" }; Z6 ^$ G. K4 G  t) a* H
  177.   {
    8 n4 P& I, {: q5 ^% W) w( V
  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个字节数据
    ; M1 Q" p. G1 T! e1 {
  179.   }
    8 J. i5 p  H% B' r

  180. $ A! g( o5 H% j" S. p. ]
  181.   QSPIReceive(temp, 2);5 a) I! |$ U' Q- C

  182. 7 @& H6 D% d4 J* z
  183.   device_id = (temp[0] << 8) | temp[1];
    9 L* b  _+ Y# i

  184.   G1 q% `* ?/ R! ]* |$ ^8 s
  185.   printf("QSPI Flash Device ID: %X\r\n", device_id);5 t& a8 Z  G- s* s
  186. }+ u" m! v  ^: _. z0 \) ^  f

  187. * E4 Z# ]6 w5 \$ Z- y" p. k  d# \
  188. void W25QXXInit(void)
    5 Y3 m+ _1 `4 B( |0 s3 ?
  189. {- ~8 {) ?$ `1 j4 {, C1 W! f
  190.   uint8_t temp;
    8 Q% s$ v" _/ ?! ]8 @6 |) a- K

  191. % g* F. P2 ?+ }* H
  192.   QSPIInit();* ^- d6 T, A3 E! B# H2 D
  193.   w25qxx_qspi_init(); // 使能QSPI模式
    8 I: a1 S) S2 p, T7 _# x
  194.   w25qxx_id_get();    // 读取FLASH ID." N/ U- \8 h& m0 n
  195. 2 L& K, g8 R" s# t9 Z/ S! Y7 o# B
  196.   temp = w25qxx_read_status(3); // 读取状态寄存器3,判断地址模式2 w& D; k4 N) h" i
  197.   if((temp & 0X01) == 0) // 如果不是4字节地址模式,则进入4字节地址模式5 V4 W( D1 b1 g& `
  198.   {
    % T% F1 ^8 s: i. v
  199.     w25qxx_write_enable(); // 写使能. }6 `; m- r+ L, e
  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个字节数据
      n0 ]/ C" u/ v1 @
  201.   }- u; p, [5 u* P. _& C  d+ }7 f
  202.   w25qxx_write_enable(); // 写使能
    4 U# B) c) n1 C5 T/ {
  203.   QSPISendCMD(W25QXX_SET_READ_PARAM, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_4_LINES); // QPI,设置读参数指令,地址为0,4线传数据_8位地址_无地址_4线传输指令,无空周期,1个字节数据
    1 u2 ^) r9 Y8 ]4 H  _8 _
  204.   temp = 3 << 4;      // 设置P4&P5=11,8个dummy clocks,104M
    + l# r$ @! z" \  V
  205.   QSPITransmit(&temp, 1);. p. V" @3 U) a4 [+ F' I' p
  206. }
    - s! f! P- L  E8 Y  A- f0 j/ \

  207. & U9 ?' k' k  x- ?
  208. // 擦除一块4096字节 最少需要150ms
    2 v+ P# Q& S& T& M% c# d- A' J7 k# W
  209. void W25QXXSectorErase(uint32_t addr)* Z( t* X5 ?4 h: P* d
  210. {1 v4 f1 B2 C, }( O" ~  n. N
  211.   //addr /= 4096;0 L. Z! L" {  u4 R4 P
  212.   addr *= 4096;
    2 K  y5 p% k: o/ F( Z! U3 M1 _) O6 z

  213. ) o( \7 W" `9 ~% G# J( c$ z
  214.   w25qxx_write_enable();
    - ^( K, G1 Q& g% L& V* Z" o
  215.   w25qxx_wait_busy();
    8 Q( V& K4 a3 N9 K
  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个字节数据
    * N1 e* T6 B' g9 E0 }
  217.   w25qxx_wait_busy();
    9 m: G, X6 N) `, c: n4 N/ D4 ]3 @
  218. }/ Y  E& M  |" X1 E
  219. 1 `% g8 `5 _3 Q5 Y
  220. & [* @9 Z9 G! L; y: J0 H
  221. // 擦除整个芯片& o- M% n1 t# ^
  222. void W25QXXChipErase(void)
    . z+ K( e$ i* x8 J' d
  223. {; {2 l3 S+ y( u
  224.   w25qxx_write_enable();          //SET WEL
      y0 r$ T- f2 X9 x' l  m* H
  225.   w25qxx_wait_busy();9 Y. @2 I- I  X0 l. i: v" C
  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个字节数据3 I6 @4 p8 T) G" f! |! l- T0 b  {; q
  227.   w25qxx_wait_busy();           //等待芯片擦除结束6 K$ F! c* f1 k8 M3 e% F3 N2 X
  228. }
    ; Y0 p! V' Q8 c& g) j

  229. # `: P% j& q+ J, y# C. Z$ ]$ g3 I
  230. // 写最多256字节. c1 _: R: ]* f: N2 x
  231. void W25QXXWritePage(uint32_t addr, uint8_t *buffer, uint16_t length)) H4 r6 s9 n0 h' _& B" f
  232. {: V$ T7 c' b, ?3 Q
  233.   w25qxx_write_enable();          //写使能. j) d" l4 U- A( b) Y3 G; d/ }
  234.   QSPISendCMD(W25QXX_PAGE_PROGRAM, addr, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_32_BITS, QSPI_DATA_4_LINES); // QPI,页写指令,地址为WriteAddr,4线传输数据_32位地址_4线传输地址_4线传输指令,无空周期,NumByteToWrite个数据0 p8 q7 S8 d& H% `" m
  235.   QSPITransmit(buffer, length);
    ! r, H/ a. n4 I
  236.   w25qxx_wait_busy();            //等待写入结束# f7 X* K) y" i6 d7 N
  237. }
    ' ~& F7 p. _) h% H+ _$ s# Q

  238. 3 ?9 Y5 e5 R, f8 V
  239. // 写最多65536字节 无擦除动作
    ) q+ m* C- c0 [4 i3 c$ U8 P- O6 U" G
  240. void W25QXXWriteExt(uint32_t addr, uint8_t *buffer, uint16_t length)# v* O0 S' Z' s
  241. {4 P. i- A5 e/ l8 T
  242.   uint16_t pageremain = 256 - addr % 256; // 单页剩余的字节数
      c4 D+ l: B+ A8 e

  243. " V' n  o- S7 I# T" F5 y0 Q
  244.   if(addr + length - 1 > (32 * 1024 * 1024))
    1 l: C3 Z$ p" Z
  245.   {
      I- B, V% o+ e, J* \
  246.     return;+ X4 q6 Y# ~* v$ f
  247.   }, T& a; i1 z+ q) M% S

  248. - c& A4 m, U- [- j3 F. P
  249.   if(length <= pageremain)6 J( t8 [; j- K3 L8 q. j; x
  250.   {
    ) q% n. Z0 R! e( K; C1 w
  251.     pageremain = length;  // 不大于256个字节) j  A5 B8 ]3 m3 F; U
  252.   }
    ; c0 W0 r1 S$ R. E
  253.   while(1)
    * ^& ]+ u# l) K+ d
  254.   {
    $ v# g2 {& a) i7 L3 e6 Z# N
  255.     // 分页写入% G( I: o% e0 Z. j
  256.     W25QXXWritePage(addr, buffer, pageremain);
    : N2 y( \0 a$ Z3 H5 `0 j) y
  257. " F8 `* K& R7 m) u: o
  258.     if(length == pageremain)( t. K- s4 e& ~. W+ ]; ?9 [8 h
  259.     {
    * a' i9 C7 y& I- S, g/ `/ p2 K
  260.       break;  //写入结束了
    : d. l! Y8 j7 p8 X% W8 b' M
  261.     }# `. f9 }8 B- j& @. @
  262.     else
    3 L8 V) {8 H5 ~- p0 z) D+ j8 k  y
  263.     {
    " t0 {2 D; l' C1 q* m# n$ e* y2 e
  264.       buffer += pageremain;1 v: ?# O8 h5 [( L6 l! t- f
  265.       addr += pageremain;
    ' b$ Y9 w4 e* @
  266. ; Y" b% P" L9 U% f- I3 J
  267.       length -= pageremain; // 减去已经写入了的字节数
    + E3 I% F5 h; m& V2 J9 @3 K" K
  268.       if(length > 256)
    0 U* M$ d+ Q6 X3 Q+ [& I) S3 T9 r
  269.       {/ q" L; w9 ?5 v  I6 d0 M
  270.         pageremain = 256;  // 一次可以写入256个字节1 O0 F& |5 p: [4 x, p
  271.       }
    # i7 G. X- M2 H1 l# v7 s  f
  272.       else% i$ C6 c" K" z9 i3 `
  273.       {/ ~9 }% I0 Q; R+ @
  274.         pageremain = length;  // 不够256个字节了, r0 O: K- n: o1 A# ~/ S! C8 C
  275.       }% B+ i. @5 v( c* q( A: p
  276.     }
    % D" u, F' `9 r/ E9 H3 R
  277.   }: {9 G0 T* q& \6 k8 j6 n+ j: s+ X
  278. }
    7 C. Q0 V0 {- Q
  279. 7 u, l3 Y& K; \4 u, z' D1 N
  280. // 写最多65536字节 带擦除动作
    7 Q3 P# e, \2 s- s
  281. static uint8_t W25QXX_BUFFER[4096];
    + H# R4 _' f: X. q% q3 ]+ b7 S, t
  282. void W25QXXWrite(uint32_t addr, uint8_t* buffer, uint16_t length)
    ( R* ?; K0 h  j2 v! r' d
  283. {
    , {4 S( @0 K8 j4 j- W1 J
  284.   uint32_t secpos;0 B3 Z% p; x# z6 _. r) _* s
  285.   uint16_t secoff, secremain, i;
    2 l8 ^4 i7 r6 Y" L
  286.   uint8_t * w25q_buf;
    9 e! ]; U' R( h# t

  287. - J! ^4 S4 `" f
  288.   w25q_buf = W25QXX_BUFFER;7 S/ Z8 v1 s9 V: p
  289. 2 D0 p$ @; d7 r! X9 u
  290.   secpos = addr / 4096; // 扇区地址
    2 ]# b2 C! [6 @; n4 w
  291.   secoff = addr % 4096; // 在扇区内的偏移" j) B, ^+ C7 P: O% B8 c: N# }
  292.   secremain = 4096 - secoff; // 扇区剩余空间大小) e/ ?* L3 U/ ?7 a

  293. 1 [& i7 s+ _1 |! G. F1 z( i. r
  294.   if(length <= secremain)5 a" ?4 j( d0 N# P" E1 _: n
  295.   {
      }3 p" b# k# F- L
  296.     secremain = length;  // 不大于4096个字节
    , E, A2 M9 I' ?2 ^6 y& x
  297.   }$ {3 g* H4 U: D2 J6 y5 l! J
  298.   while(1)
    8 c& y1 s6 ~8 W8 d0 n
  299.   {
    # v6 [0 A: @  Q5 N1 a+ C
  300.           WatchdogFeed();5 J* f9 B" b6 i9 A  _* i3 z: i2 I" f5 O% _2 L
  301.     W25QXXRead(secpos * 4096, w25q_buf, 4096); // 读出整个扇区的内容; x5 I& R' F7 m) F: ]
  302.     for(i = 0; i < secremain; ++i)! l+ D/ u6 z8 B
  303.     {
    ' Z4 h; N! M" G, \- _' ^$ u. A
  304.       if(w25q_buf[secoff + i] != 0XFF)
    * Z7 q  L" O  B- _
  305.       {# R2 B% H6 g2 S
  306.         break;  // 需要擦除& I; V: k" Z- b$ o8 m
  307.       }
    5 v7 L4 b. I/ R0 P' M& N, ~; X
  308.     }
    6 p* ]) M  b+ I( O+ T
  309.     if(i < secremain) // 需要擦除
    ! `$ M/ S# M4 s5 p( T
  310.     {
    + i; m( K' f* g
  311.       W25QXXSectorErase(secpos); // 擦除这个扇区
    * h. I8 E2 A( z3 W& d
  312.       for(i = 0; i < secremain; ++i)
    " T7 g6 l2 {  w% @
  313.       {) j1 E; |* \5 Q$ s% g
  314.         w25q_buf[i + secoff] = buffer<i>;</i>
    8 G8 R( F) g% }  R- ]" S
  315.       }: L4 n! `, @' E( l- I
  316.       W25QXXWriteExt(secpos * 4096, w25q_buf, 4096); // 写入整个扇区$ g' p* ~5 b' R' \7 j4 ^+ l3 }2 z9 s

  317.   f8 w" o8 ]. B  Q9 a" V1 T( F* g
  318.     }
    ( x0 {2 H+ h, v* i% q. q5 j
  319.     else/ t6 u+ m2 D& |2 r1 b/ I5 c
  320.     {
    + \% ?' P: w' q& E" g" q" v8 j
  321.       W25QXXWriteExt(addr, buffer, secremain);  // 写已经擦除了的,直接写入扇区剩余区间.
    $ q% k' x8 d, p2 n/ t9 }
  322.     }
    . Q2 ^% ]5 y5 }
  323.     if(length == secremain)
    2 c# M7 B, t" n9 l0 `+ h
  324.     {
    3 a+ Q  F  {+ q/ _" w
  325.       break;+ y! t$ Z& d4 Y# C
  326.     }' C% ]* [# w2 u! V/ g( m% k
  327.     else
    . L0 O# a: V' h" \
  328.     {
    7 Z2 w; e) F7 b
  329.       secpos++;3 w; Q6 w+ t9 a, ~6 q) d, O
  330.       secoff = 0;. W; U  q  Y2 `, Y* w. a- j0 I# p
  331. / A7 F4 c" F9 d% P8 V
  332.       buffer += secremain;
    ! N7 s4 d1 l6 C
  333.       addr += secremain;; {# Q9 X; H; b3 N, l( P6 a
  334.       length -= secremain;: w- L4 g$ ]' g7 j
  335.       if(length > 4096)  @1 I3 c# O( J, w0 ]# s
  336.       {. [8 A* C, B! T& S! U6 M. c4 t* ^' g4 L
  337.         secremain = 4096;
    7 q( i1 k5 `$ h- J
  338.       }! l8 N* G' h- R
  339.       else7 F7 R' t2 N! u5 p4 @, j9 K: T
  340.       {" `  [/ g8 C+ B+ S. D
  341.         secremain = length;" o3 N: A2 o( v: }, |* x  R
  342.       }
    6 A) X& a3 ?4 |7 a4 g1 k1 W5 e
  343.     }
    ' B& r4 X# Q) Q1 y9 w7 y
  344.   };3 z/ `+ r/ p2 S
  345. }
    / e! T! j: m5 F" t, ^# {6 P. o
  346. , C4 v: }) G) l+ }" d! O
  347. 3 Z3 W: w3 i) \( v7 j; O# q
  348. // 读取SPI FLASH,仅支持QPI模式  在指定地址开始读取指定长度的数据 65536$ L) v% d" W( ]
  349. void W25QXXRead(uint32_t addr, uint8_t *buffer, uint16_t length)
    # [! x4 n8 S$ }5 X
  350. {2 C( p) r3 S/ P
  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个数据
    ! n/ |" i! [  v* S
  352.   QSPIReceive(buffer, length);
    . \, Z4 e$ D$ G  W$ P7 |
  353. }
复制代码
  j2 y" t: A+ g$ v: l3 s8 I4 }) [! J9 @
也可以使用SPI操作W25QXX。. j& l% `, l5 _, f, p! V% {1 ^
2 I* I2 Y$ L* c& W- H( d2 O& k

$ d; ^5 @' Z/ n$ z
. t$ m' m; S! J. [$ p
赞 收藏 评论0 发布时间:2021-12-11 12:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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