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

【经验分享】STM32F7xx —— QSPI

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

) k6 @9 w4 i( Q9 p/ k' X6 M 该接口可以在以下三种模式下工作:( A+ S$ f5 B" d1 U2 t0 a
① 间接模式:使用 QSPI 寄存器执行全部操作# I% s# }( [7 L8 e9 ^2 Y
② 状态轮询模式:周期性读取外部 Flash 状态寄存器,而且标志位置 1 时会产生中断(如擦除或烧写完成,会产生中断)2 {0 j5 i' \2 Z. w4 K& G7 B
③ 内存映射模式:外部 Flash 映射到微控制器地址空间,从而系统将其视作内部存储器。# X: D% D5 I8 R! i
! q& g: @' t  O- J: p" r
QSPI通过6根线与SPI芯片通信,下图是内部框图:  Z8 I9 T3 k, I; T% L

7 m2 H$ e! {# p
20190123173205949.png

! b6 m7 x0 B* B# Z9 {
9 P, }$ l: K$ w! s! M; P0 XQSPI每条命令,必须包含一个或多个阶段:指令、地址、交替字节、空指令和数据。
1 S0 M* {2 {# V: l9 U: |! i' o  q7 S* A: y
QSPI发送命令:等待QSPI空闲;设置命令参数。4 C6 ]5 K; m3 ]
7 |9 T0 n: ^0 f
QSPI读数据:设置数据传输长度;设置QSPI工作模式并设置地址;读取数据。
/ j/ \- z9 g6 s+ O! j! N3 y5 `# v1 B- i
QSPI写数据:设置数据传输长度;设置QSPI工作模式并设置地址;写数据。! R% j/ N1 g' ~) X3 f% N* s) w
" h5 g' O/ w) B& G6 Y$ K

' H2 ^7 Z6 _# @8 b* b( O二、几个重要的函数
: N) E* B" U: F7 A5 m0 j" x
  1. HAL_StatusTypeDef     HAL_QSPI_Init     (QSPI_HandleTypeDef *hqspi); // 初始化
    , Z1 G* n6 g1 u$ t: l! w( ?

  2. 5 |0 \- m& h& c% o
  3. HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout); // 发送命令4 G' z; q9 C/ O" n! Z
  4. 6 K' O* D9 _6 u7 I5 m9 u
  5. HAL_StatusTypeDef     HAL_QSPI_Transmit     (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 发送数据
    ; X2 K5 n- R8 v
  6. 5 D6 Z% y( Y% _* ]
  7. HAL_StatusTypeDef     HAL_QSPI_Receive      (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 接收数据
复制代码
0 Y% B6 J4 ]6 S
三、几个重要的结构
$ ]$ w& `: d. W3 o
  1. // QSPI操作句柄
    ' @& j: A% Z; |" C! o
  2. typedef struct
    + B6 L% [' A$ C4 T  G. x' p6 z
  3. {9 i, S( X3 N! o' f& E3 h
  4.   QUADSPI_TypeDef            *Instance;        /* QSPI registers base address        *// T0 b9 H, Y; E) S" @% |2 y
  5.   QSPI_InitTypeDef           Init;             /* QSPI communication parameters      */' S4 V6 O* p# g( o
  6.   uint8_t                    *pTxBuffPtr;      /* Pointer to QSPI Tx transfer Buffer */
    , Y" F9 P8 x1 K8 K! d
  7.   __IO uint16_t              TxXferSize;       /* QSPI Tx Transfer size              */
    % T. Z. r) k4 |! U
  8.   __IO uint16_t              TxXferCount;      /* QSPI Tx Transfer Counter           *// z% s2 x( D; N  {
  9.   uint8_t                    *pRxBuffPtr;      /* Pointer to QSPI Rx transfer Buffer */
    & ~0 I5 M& }0 e) B& l% ?7 Q
  10.   __IO uint16_t              RxXferSize;       /* QSPI Rx Transfer size              */: c' b- ?8 I: Y4 j2 D
  11.   __IO uint16_t              RxXferCount;      /* QSPI Rx Transfer Counter           */
    - L0 w$ l- g9 \) j3 K( O) }# l
  12.   DMA_HandleTypeDef          *hdma;            /* QSPI Rx/Tx DMA Handle parameters   */
    , j3 k# T9 r% T# M  `
  13.   __IO HAL_LockTypeDef       Lock;             /* Locking object                     */
    " M- y, E% ^1 j5 h4 K, w
  14.   __IO HAL_QSPI_StateTypeDef State;            /* QSPI communication state           */& b6 E! y6 [& A1 ^6 t
  15.   __IO uint32_t              ErrorCode;        /* QSPI Error code                    */1 s  g# \9 ^6 u" E* \
  16.   uint32_t                   Timeout;          /* Timeout for the QSPI memory access */
    4 E6 ~4 w# A; d" L
  17. }QSPI_HandleTypeDef;
      o6 j  g1 k& K! }* u9 M3 @! x
  18. ' P2 m$ S2 D& w( o
  19. // Instance:QSPI基地址  ---  QUADSPI
    8 T3 I2 T9 G1 M( R6 j' M$ q
  20. // Init:设置QSPI参数
    0 i% ~* f' \% f6 f+ w6 o- u0 K
  21. // pTxBuffPtr,TxXferSize,TxXferCount:发送缓存指针 发送数据量 剩余数据量
    : C1 \; j, h, w! [9 l7 w1 j6 F. B/ M7 E
  22. // pRxBuffPtr,RxXferSize,RxXferCount:接收缓存指针 发送数据量 剩余数据量( B2 V; [! x' l% P. O" A: B' M! t
  23. // hdma与DMA相关, 其他为过程变量不需要关心
复制代码
  1. // 参数配置 时钟分频系数  FIFO阈值  采样移位  FLASH大小  片选高电平时间  时钟模式  闪存ID  双闪存模式设置: `5 a( Y- m6 g0 Y8 v& }) K
  2. typedef struct! F) d0 N% Z) ?, H3 _! @
  3. {8 S  o! Z0 A1 S- r+ `+ v
  4.   uint32_t ClockPrescaler;     /* Specifies the prescaler factor for generating clock based on the AHB clock.. C( W6 z% F8 s4 i
  5.                                   This parameter can be a number between 0 and 255 */ ' ?' ?/ }, w# p: ^2 I
  6. / w$ w3 _& B0 g' L$ z
  7.   uint32_t FifoThreshold;      /* Specifies the threshold number of bytes in the FIFO (used only in indirect mode)# B1 R" g6 B9 b( a" Q- A; r
  8.                                   This parameter can be a value between 1 and 32 */- ^* y- ?# X3 A. Q' _0 a  W  V% ~
  9. 2 d0 z) \9 \! ]& T: S8 Q
  10.   uint32_t SampleShifting;     /* Specifies the Sample Shift. The data is sampled 1/2 clock cycle delay later to
    - Q- i' ~( h9 d
  11.                                   take in account external signal delays. (It should be QSPI_SAMPLE_SHIFTING_NONE in DDR mode)& v# L) p$ N8 _
  12.                                   This parameter can be a value of @ref QSPI_SampleShifting *// \* Z0 n" ^5 t7 v7 R8 l) P
  13. ( }+ ]2 p. I; \) J
  14.   uint32_t FlashSize;          /* Specifies the Flash Size. FlashSize+1 is effectively the number of address bits
    ; x9 Z; ]6 i( M* W6 Y, c6 [
  15.                                   required to address the flash memory. The flash capacity can be up to 4GB 2 O) h  ?! y) |: [& H- t
  16.                                   (addressed using 32 bits) in indirect mode, but the addressable space in
    1 H& y$ P9 Z7 ?4 i6 Z) x
  17.                                   memory-mapped mode is limited to 256MB' ^' C; s& J! v4 i
  18.                                   This parameter can be a number between 0 and 31 */
    ) P. s' I/ }9 F4 N8 q

  19. 9 B. t7 ^; c% I3 b
  20.   uint32_t ChipSelectHighTime; /* Specifies the Chip Select High Time. ChipSelectHighTime+1 defines the minimum number 8 h- b5 Z3 [, F8 t
  21.                                   of clock cycles which the chip select must remain high between commands.
    3 L) H% C- o3 R  j
  22.                                   This parameter can be a value of @ref QSPI_ChipSelectHighTime */
    , {3 U1 ?6 |3 a  ]# n4 Y9 |( v9 @
  23. " @9 p- k& j2 k  D; T; S' {
  24.   uint32_t ClockMode;          /* Specifies the Clock Mode. It indicates the level that clock takes between commands.3 W" `* W. H6 u' e2 U
  25.                                   This parameter can be a value of @ref QSPI_ClockMode */
    * c( I# b5 I! }% X7 z  `

  26. 9 l8 y  x( a, c& ^9 f
  27.   uint32_t FlashID;            /* Specifies the Flash which will be used,1 w6 a6 ~! m1 Q1 X: D
  28.                                   This parameter can be a value of @ref QSPI_Flash_Select */* D2 e$ P4 F) T; o: M) K

  29. 2 Q2 o2 b5 E" r. S0 [$ A8 O
  30.   uint32_t DualFlash;          /* Specifies the Dual Flash Mode State  N) Y' T# A" [0 W! E  l
  31.                                   This parameter can be a value of @ref QSPI_DualFlash_Mode */                                               
    / z* Y: @2 z  L/ X3 ~2 z4 e3 s
  32. }QSPI_InitTypeDef;
复制代码
  1. // 采样移位9 Y2 O8 N3 H$ U
  2. #define QSPI_SAMPLE_SHIFTING_NONE           ((uint32_t)0x00000000U)        /*!<No clock cycle shift to sample data*/1 b% W; I* O, D8 U# ^
  3. #define QSPI_SAMPLE_SHIFTING_HALFCYCLE      ((uint32_t)QUADSPI_CR_SSHIFT) /*!<1/2 clock cycle shift to sample data*/
复制代码
; v% x( V+ C2 M( l, v
四、QSPI接口设计(仅供参考)
7 w# ~" _$ ]6 C1 z9 x
  1. #define QSPI_CLK_ENABLE()         __HAL_RCC_QSPI_CLK_ENABLE();  `# s2 Z7 Y. N6 g; x8 G
  2. 0 {# I0 L0 ^5 P. H
  3. #define QSPI_BK1_NCS_PORT         GPIOB
    ! R' m/ t8 R1 w& U0 N
  4. #define QSPI_BK1_NCS_PIN          GPIO_PIN_6' [5 e; d* T2 W1 @% ^/ R0 Y( \
  5. #define QSPI_BK1_NCS_AF           GPIO_AF10_QUADSPI
    2 q0 s8 t' \0 p, s6 T# _
  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)
    - g% F, ~) y8 `4 `8 u) j" C# J+ Y

  7. + O! L  _9 `8 \( ~6 t$ i$ M
  8. #define QSPI_BK1_CLK_PORT         GPIOB
    - o- y2 W: U; g& i
  9. #define QSPI_BK1_CLK_PIN          GPIO_PIN_2
    7 g/ S: f( Y4 W
  10. #define QSPI_BK1_CLK_AF           GPIO_AF9_QUADSPI
    4 {, \% r# F9 ^% @) b
  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)
    8 D) E( ], \+ c  l: Y9 M* H7 s

  12. - j# {# d) J- g7 s: ~
  13. #define QSPI_BK1_IO0_PORT         GPIOF- L; y+ I# K3 o
  14. #define QSPI_BK1_IO0_PIN          GPIO_PIN_88 i! s; `, M# J$ v
  15. #define QSPI_BK1_IO0_AF           GPIO_AF10_QUADSPI$ c- R$ b0 {% l
  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)
    + ?% c! r" ~; [+ c7 a  L' S" C

  17. # w  a* Q* ?  a- h
  18. #define QSPI_BK1_IO1_PORT         GPIOF
    9 ]0 n) u: Y  c8 }  d. R
  19. #define QSPI_BK1_IO1_PIN          GPIO_PIN_98 A  J+ c# i# Y: H: Q
  20. #define QSPI_BK1_IO1_AF           GPIO_AF10_QUADSPI2 \0 U% R; A' `3 D6 y6 C% p
  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)
    7 \- N) U5 z5 X8 r$ ~( _( J. A9 n
  22. 3 @+ s2 C( M5 G! K, V0 O
  23. #define QSPI_BK1_IO2_PORT         GPIOF
    / u1 p) ~9 `" ?
  24. #define QSPI_BK1_IO2_PIN          GPIO_PIN_7  J" c6 L" f  F% T+ J# j7 L6 b. \
  25. #define QSPI_BK1_IO2_AF           GPIO_AF9_QUADSPI
    9 B! T% h9 w+ R( v4 J( D2 ^
  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)
    ! W. V* _! r) T/ \- o

  27. 2 m2 X% y3 X! S1 p# G
  28. #define QSPI_BK1_IO3_PORT         GPIOF( {( g6 M' J, F0 R- W
  29. #define QSPI_BK1_IO3_PIN          GPIO_PIN_6
    * ~7 P1 M( A+ j$ S1 ~& _
  30. #define QSPI_BK1_IO3_AF           GPIO_AF9_QUADSPI, \! s  F: ?2 A
  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. // 封装几个必要的接口
    8 O% h2 K9 G+ `% `9 n+ c
  2. static QSPI_HandleTypeDef qspi_handle;* c- U6 w: \2 q2 F. t# J
  3. , d9 D2 w8 p; l
  4. static void qspi_gpio_init(void)
    / l! T0 E% g7 D
  5. {8 j/ [) I$ V3 o9 B  U1 O
  6.   QSPI_CLK_ENABLE();
    3 e% {0 }  r3 y/ o0 {% A% r

  7. * @- ^0 j$ w2 l
  8.   QSPI_BK1_NCS_CONFIG();
    0 M( Z! W' E4 K0 j. l
  9.   QSPI_BK1_CLK_CONFIG();
    1 ]2 o3 S3 j# a) u5 q
  10.   QSPI_BK1_IO0_CONFIG();4 I8 U, R+ d  Y2 l- I1 m
  11.   QSPI_BK1_IO1_CONFIG();
    / \1 W9 d/ S1 E4 y
  12.   QSPI_BK1_IO2_CONFIG();
    9 ~# A) W6 c# N# B) U' _; |1 D; e8 {
  13.   QSPI_BK1_IO3_CONFIG();/ F, p; }$ o, L' j8 v$ Z* ~4 r' s
  14. }0 K( ~8 o9 O; s2 G- p5 C
  15. + f; a: {% w" b- `1 ?7 N! _
  16. static void qspi_mode_init(void)* S0 Q4 _0 z2 c$ B, W+ I' }$ x" w: s
  17. {5 u6 G! d# r$ Y) o6 _! T- ]
  18.   qspi_handle.Instance = QUADSPI;                        // QSPI
    1 P0 I, z8 l% I7 e8 K: k0 n9 S3 S3 q% F
  19.   qspi_handle.Init.ClockPrescaler = 2;                   // QPSI分频比,W25Q256最大频率为104M 所以此处应该为2,QSPI频率就为216/(2+1)=72MHZ) z& n# E- N- J( ~# G1 t
  20.   qspi_handle.Init.FifoThreshold = 4;                    // FIFO阈值为4个字节
    / E1 {& A5 u3 O2 e  W
  21.   qspi_handle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; // 采样移位半个周期(DDR模式下,必须设置为0)
    : r% B5 T0 l/ h
  22.   qspi_handle.Init.FlashSize = POSITION_VAL(0X2000000) - 1; // SPI FLASH大小,W25Q256大小为32M字节! J$ Z$ f$ ^4 T+ t# }8 x1 ]  Q
  23.   qspi_handle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_4_CYCLE; // 片选高电平时间为4个时钟(13.8*4=55.2ns),即手册里面的tSHSL参数6 o: f: e% V5 x* q4 ^" J9 {8 N2 A
  24.   qspi_handle.Init.ClockMode = QSPI_CLOCK_MODE_0;        // 模式0: c+ f4 |3 T" q- V1 D9 Z7 S2 h" i
  25.   qspi_handle.Init.FlashID = QSPI_FLASH_ID_1;            // 第一片flash
    : m3 R* O  U8 z! g( S: [
  26.   qspi_handle.Init.DualFlash = QSPI_DUALFLASH_DISABLE;   // 禁止双闪存模式/ W, E& n$ f  Z+ E1 D. j
  27.   HAL_QSPI_Init(&qspi_handle);      //QSPI初始化3 _& U6 G5 h0 f
  28. }
    . a2 H7 a8 k5 G* x  E, I
  29. : l+ Y& M2 {# U- k  ]3 I6 k
  30. void QSPIInit(void)" U4 O- _, P7 W' x. }* v
  31. {
    3 j) m! r$ t9 z
  32.   qspi_gpio_init();
    5 w& h9 r; ^. ^
  33.   qspi_mode_init();
    - _* V" Y! e0 y: W/ g4 I
  34. }) ^% n% X, d9 w" k2 l
  35. / v: k7 \" y# l" B. A
  36. // QSPI发送命令
    . i6 H# t$ c( A: n
  37. // instruction:要发送的指令
    - j; _2 @3 I- v+ K. l; l0 k  V! B
  38. // address:发送到的目的地址4 e" t2 _# A( `; s; J8 T, X
  39. // dummyCycles:空指令周期数0 l7 h5 g( Q9 Y! }2 B+ z% Q
  40. // instructionMode:指令模式;QSPI_INSTRUCTION_NONE,QSPI_INSTRUCTION_1_LINE,QSPI_INSTRUCTION_2_LINE,QSPI_INSTRUCTION_4_LINE
    6 B, H' B# j$ c7 b) U5 ~
  41. // addressMode:地址模式; QSPI_ADDRESS_NONE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_2_LINE,QSPI_ADDRESS_4_LINE( ]( U8 Z2 D" b  E  k  B0 k( o* E$ r
  42. // addressSize:地址长度;QSPI_ADDRESS_8_BITS,QSPI_ADDRESS_16_BITS,QSPI_ADDRESS_24_BITS,QSPI_ADDRESS_32_BITS7 ~' F0 o  N  J7 V% t; j5 z
  43. // dataMode:数据模式; QSPI_DATA_NONE,QSPI_DATA_1_LINE,QSPI_DATA_2_LINE,QSPI_DATA_4_LINE
    - H& p- t" V/ T6 b0 Y* a# t2 @
  44. void QSPISendCMD(uint32_t instruction, uint32_t address, uint32_t dummyCycles, uint32_t instructionMode, uint32_t addressMode, uint32_t addressSize, uint32_t dataMode)4 T- k1 X% \( O2 [0 _8 D
  45. {
    . |* f' v/ i' ]2 o
  46.   QSPI_CommandTypeDef Cmdhandler;7 U( W$ K; `8 ~3 Y9 p
  47. - x! }; W# l# s) e
  48.   Cmdhandler.Instruction = instruction;           // 指令1 A* N' I, O1 C8 q! x. C
  49.   Cmdhandler.Address = address;                   // 地址
    & U' m9 C) J& p4 s. Q% w
  50.   Cmdhandler.DummyCycles = dummyCycles;           // 设置空指令周期数
    3 o/ i8 d" g; y( P
  51.   Cmdhandler.InstructionMode = instructionMode;   // 指令模式6 ~2 b! b/ m) q/ i3 v$ p4 b8 f7 v
  52.   Cmdhandler.AddressMode = addressMode;           // 地址模式
    / ?8 n1 J. E7 X+ @+ X1 E
  53.   Cmdhandler.AddressSize = addressSize;           // 地址长度
    9 Y8 n1 ^9 e3 c- U
  54.   Cmdhandler.DataMode = dataMode;                 // 数据模式
    1 U7 V% D: o7 C$ F. s; q
  55.   Cmdhandler.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 每次都发送指令9 e( q* \1 s; C- r1 k
  56.   Cmdhandler.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 无交替字节
    ' ?2 X, x7 |/ c5 G6 R, `/ Z
  57.   Cmdhandler.DdrMode = QSPI_DDR_MODE_DISABLE;           // 关闭DDR模式" ]$ c( X! P* s/ G
  58.   Cmdhandler.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    ) K( {/ u: R: Z. M  q

  59. ) Z* m8 w1 Q7 Q' d
  60.   HAL_QSPI_Command(&qspi_handle, &Cmdhandler, 5000);
    - i4 \: Y* b( d1 j9 P
  61. }
    5 u: o" r5 E. _6 `: \

  62. ; I3 G* T8 e6 u7 w9 f" T7 C6 |
  63. // 接收指定长度数据
    $ ^8 ]) y+ M& e" B
  64. uint8_t QSPIReceive(uint8_t *buffer, uint32_t length)
    2 r1 ~: V/ B- y. ]8 Q
  65. {
    4 d! N. ^9 z: c7 A! G! z" ~
  66.   qspi_handle.Instance->DLR = length - 1;0 s" y) L* m: }! z; D( S- j1 o% n: F
  67.         , f- g3 `5 z8 X, d4 \
  68.   if(HAL_QSPI_Receive(&qspi_handle, buffer, 5000) == HAL_OK)* f* A3 u3 M% r+ \
  69.   {2 B+ ?" ]- _6 n  @  Y0 N
  70.     return 0;  //接收数据+ F) z9 a5 m+ N$ d( G6 H
  71.   }
    , s6 c  ~& V$ H/ d
  72.   else
    : \( C7 Y+ g5 ?6 {9 r
  73.   {' I. t0 R* L3 ?0 z- U# \1 a. I
  74.     return 1;
    / ?$ I6 [, o: t1 [0 L
  75.   }: W' h( N' p8 y1 k( o
  76. }
      z, v7 T1 |) r) v2 Y4 j1 m' X5 Y

  77. $ ]5 p) Q: `+ N
  78. // 发送指定长度数据& P3 j' X" w0 i% S3 u; e# G7 }
  79. uint8_t QSPITransmit(uint8_t *buffer, uint32_t length); S/ L+ }+ s/ |% B* ]2 x; d8 q
  80. {
    : ~* N/ w4 U0 N8 `2 ^, F) t2 _
  81.   qspi_handle.Instance->DLR = length - 1;
    , \  L$ E, Y5 f* D5 y
  82.   if(HAL_QSPI_Transmit(&qspi_handle, buffer, 5000) == HAL_OK)
    % `9 Z6 {3 g" u9 `( S: A; t
  83.   {
    : w6 s: Q3 p' I) S
  84.     return 0;  //发送数据% p& f& q. S3 U( [( J$ B  u( Z8 v! B
  85.   }) {! W3 V& o6 H8 t
  86.   else2 H6 G2 J; z  t/ N' P
  87.   {1 M( e9 c9 X) C) V9 N2 D4 B. F
  88.     return 1;
    ) K' Z( ?0 G( D! b* R7 a# K1 J0 @
  89.   }& O  _3 l0 [3 f4 B; `  E9 ]# a) [
  90. }
复制代码
% g7 L) {: I& {% {
五、QSPI驱动W25Q256
3 I" ]1 I, H0 l+ s' YW25Q256:32M  512块,每块16个扇区,每个扇区4K。具体指令看手册。封装了如下接口(初始化读写擦除等接口是以后移植文件系统的基础):
% p9 f1 {! h# P9 Z" j
3 `0 w4 [3 g& Z  q
  1. #define W25QXX_WRITE_ENABLE      0x06
    ; `: d3 G: S5 w* B
  2. #define W25QXX_WRITE_DISABLE     0x043 R! p* i& Q( x6 V3 m! T* |  o
  3. #define W25QXX_READ_STATUS_REG1  0x05
    0 C+ I+ K0 R* v7 Q" l- i
  4. #define W25QXX_READ_STATUS_REG2  0x35
    6 D8 W- p- `- b+ K2 M4 H2 a7 p: @& N
  5. #define W25QXX_READ_STATUS_REG3  0x15" p! \! E% K# U. r& d. l
  6. #define W25QXX_WRITE_STATUS_REG1 0x01
    1 K0 a* S4 _. S, E2 L! D
  7. #define W25QXX_WRITE_STATUS_REG2 0x31
    # v) a' a7 B. ]# }. W
  8. #define W25QXX_WRITE_STATUS_REG3 0x11: u0 C: }, H& Q: w) [
  9. #define W25QXX_READ_DATA         0x03
    8 B, L0 x( j( S+ z3 j, f( O! k
  10. #define W25QXX_FAST_READ_DATA    0x0B
    % n, k+ {( \' s6 L! q8 V$ G
  11. #define W25QXX_FAST_READ_DUAL    0x3B
      N' R2 N# z! H/ R9 \, N7 I7 Z) m
  12. #define W25QXX_PAGE_PROGRAM      0x02. e8 V* G6 a6 H$ w) c
  13. #define W25QXX_BLOCK_ERASE       0xD8
    * u1 G7 c3 F* T4 z2 H+ w
  14. #define W25QXX_SECTOR_ERASE      0x20
    5 T) M/ y1 ]1 w( J
  15. #define W25QXX_CHIP_ERASE        0xC77 j1 N* C, r( w  ?; p) u
  16. #define W25QXX_DEVICEID          0xAB
    : Q7 N% X" I$ b/ D" W$ o
  17. #define W25QXX_MANUFACT_DEVICEID 0x90' Y7 M4 S" f" `4 O4 w7 E# L5 T/ v
  18. #define W25QXX_JEDEC_DEVICEID    0x9F
    2 m' w1 S) [6 K0 G0 b" L
  19. #define W25QXX_EABLE_4BYTE_ADDR  0xB7
    ! q9 |) }' Q& P; }5 N& {! a3 i
  20. #define W25QXX_EXIT_4BYTE_ADDR   0xE9
    + T9 Y0 Y3 h  `6 c' N: ]% ^& x
  21. #define W25QXX_SET_READ_PARAM    0xC0
    9 H3 {3 W* Y/ O$ R
  22. #define W25QXX_ENTER_QPIMODE     0x38
    9 Q1 H% n- p* L) y8 c2 _6 W
  23. #define W25QXX_EXIT_QPIMODE      0xFF8 }8 c8 |! g3 Q, N) J: S$ ]% V, t
  24. ) H) {4 }& h: O* r
  25. uint8_t w25qxx_qpi_mode = 0; // QSPI模式标志:0,SPI模式;1,QPI模式.& Z6 H* P5 N3 Y5 x" C4 h) @

  26. : q4 e* ?/ Z9 X) j
  27. // 读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器/ Q: q$ {/ {& ]9 F, b4 y; p
  28. // 状态寄存器1:' x* f: ]7 O, W7 V
  29. // BIT7  6   5   4   3   2   1   0
    ! k7 z; ]* x+ g+ j0 f2 W
  30. // SPR   RV  TB BP2 BP1 BP0 WEL BUSY' s9 D7 `# [$ P7 _: h4 Z
  31. // SPR:默认0,状态寄存器保护位,配合WP使用
    " Q) Z, Y) v: j8 B
  32. // TB,BP2,BP1,BP0:FLASH区域写保护设置8 V5 G  [$ T$ \7 C/ m" v4 @6 m+ m
  33. // WEL:写使能锁定
    # E' B# D# s; m
  34. // BUSY:忙标记位(1,忙;0,空闲)9 b5 m/ z4 X# P
  35. // 默认:0x00" ^8 N2 Z7 v; e2 b( d
  36. // 状态寄存器2:
    % W7 Q8 p4 R6 z8 `
  37. // BIT7  6   5   4   3   2   1   0  ?/ l- s( s& X! l
  38. // SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
    2 _3 b, E# B3 ~# c( y2 w4 ~! W3 o8 T
  39. // 状态寄存器3:  R" d( L& }, l. D+ T, B% {
  40. // BIT7      6    5    4   3   2   1   02 B1 d+ }7 ~* x# f/ }
  41. // HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS- T) [) C. U1 O/ t* J
  42. // reg:状态寄存器号,范:1~3
      l! z# \4 h$ b) w! X; K" G
  43. // 返回值:状态寄存器值5 F- j9 K6 h0 A: {3 u) Z
  44. static uint8_t w25qxx_read_status(uint8_t reg)6 r8 P0 V; L; C3 G: U
  45. {! ?; n3 l. Y2 ~7 @1 E& ]; |
  46.   uint8_t value = 0, command = 0;
    9 P, O  D! M& F, l7 `- d1 y
  47. $ O! S* Q! ~3 @1 M3 D7 g
  48.   switch(reg)
    " j+ O0 T- `4 C: v
  49.   {
    + p/ |& r7 L7 M6 K
  50.   case 1:
    4 ~8 G% C6 U! C, R
  51.     command = W25QXX_READ_STATUS_REG1;  // 读状态寄存器1指令
    ; @/ T/ \9 F. R8 L! S9 y7 H
  52.     break;
    : J, j& n8 M8 ?

  53. 8 r7 K' l% h' \5 W9 i/ X2 x' K
  54.   case 2:( M4 p, d$ O# v% {% _; x
  55.     command = W25QXX_READ_STATUS_REG2;  // 读状态寄存器2指令
    # ?2 A  D. p# D$ t' c
  56.     break;" m. A! H2 \& c- Q
  57.   s+ R: `8 }3 |& y8 x
  58.   case 3:, _$ R+ j5 I2 r. w! y
  59.     command = W25QXX_READ_STATUS_REG3;  // 读状态寄存器3指令2 L2 j3 ^: y1 ]! c- i
  60.     break;
    5 d8 _7 j. v% G9 ~8 `3 P

  61.   w2 i; B/ E" B$ y; M7 x* C; L! u
  62.   default:
    . f* z# h: y9 D  w3 z2 U: X& L
  63.     command = W25QXX_READ_STATUS_REG1;0 i3 y: B9 s# m+ J/ ]* `" @4 D
  64.     break;4 h$ s, E' v$ y9 `- c& G! |
  65.   }
    9 Q* h+ i5 ^: ^, P. K3 y
  66. 6 e( y8 F8 ?3 J# Q% ]- m+ K8 \/ |
  67.   if(w25qxx_qpi_mode); A% P( Q8 \0 h7 q# y' |
  68.   {- n" m6 X: N$ l' ~  Q
  69.     QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_4_LINES);  //QPI,写command指令,地址为0,4线传数据_8位地址_无地址_4线传输指令,无空周期,1个字节数据) A- T0 @& ~' I. ]% {1 N* {0 b2 o
  70.   }
    6 ?! O9 V- ?9 ]! A4 X5 N6 J; n
  71.   else
    / \) Q5 t* U* n, E
  72.   {) N5 ?' n9 c  A7 m0 R% C
  73.     QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_1_LINE);  //SPI,写command指令,地址为0,单线传数据_8位地址_无地址_单线传输指令,无空周期,1个字节数据2 C) m  Y& q1 b
  74.   }
    - l+ h4 |1 t7 ^( I' b5 c3 A3 g
  75. 7 Z/ `) q7 U& {8 c$ N/ L0 H
  76.   QSPIReceive(&value, 1);& J' Y7 Z0 \2 O5 e/ |/ }
  77. - s& h" b2 l* `7 L! i
  78.   return value;' u& |4 u  j9 e4 K5 g' x
  79. }8 r9 D( E& P' s5 z' T. ^8 [* j

  80. 5 A% z, U" d- K6 [
  81. // 写状态寄存器
    % _3 {- U* O5 h9 _; U
  82. static void w25qxx_write_status(uint8_t reg, uint8_t status)3 X1 n1 p7 o+ d2 O% [9 O& \
  83. {8 N$ V: E7 R1 o+ r  G: `" A
  84.   uint8_t command = 0;
    * K: K3 R6 I/ m: {

  85. ( W! p" Q) p; i
  86.   switch(reg)
    ) ^4 p$ p1 n/ j- h& T) ?6 K. |; |
  87.   {
    / \1 G+ ]7 N* G; [$ k! z
  88.   case 1:
    * s# _. ]' C0 ~+ B% f
  89.     command = W25QXX_WRITE_STATUS_REG1;  //写状态寄存器1指令0 p' ?5 x- A0 G7 E) t7 T
  90.     break;
    " K! ^( ]! S/ ]$ u( u$ j; T
  91.   case 2:8 N% W0 A3 O) A' x% }  ?" X, ~
  92.     command = W25QXX_WRITE_STATUS_REG2;  //写状态寄存器2指令
    # ]6 z6 F% T9 ]) Y1 h4 @( \% J$ R
  93.     break;+ ~1 G6 O4 }! D( M
  94.   case 3:! r  I& j; }' [% S
  95.     command = W25QXX_WRITE_STATUS_REG3;  //写状态寄存器3指令
    % W) p! P' A; `3 L* w0 {8 K
  96.     break;, n" Z3 F1 ~7 j" {; T
  97.   default:
    5 }5 A) t5 ?9 {6 f3 ^# A! k+ @
  98.     command = W25QXX_WRITE_STATUS_REG1;3 `& r$ `, @$ j0 \1 O
  99.     break;: c% Z0 V$ v7 o3 ]4 g+ \9 s
  100.   }
    . a8 i1 @1 e; T7 _# J& c- a! I
  101.   if(w25qxx_qpi_mode)( s8 x, m+ ~) X: f% C
  102.   {$ V: |; y9 U7 b0 @: T% E
  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个字节数据& B2 f1 S2 f: P2 D- R
  104.   }1 O0 l* r4 t. v7 ]- Q" `
  105.   else
    / ^3 j5 n; S4 n# J2 y
  106.   {  s. y: }/ m+ N4 J" D5 m" `/ A
  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个字节数据
    8 |1 t  u4 r! S( f/ G
  108.   }% D5 l+ e8 z' X: u
  109.   QSPITransmit(&status, sizeof(status));0 _! G# w' Q) d  H3 P7 k8 |
  110. }
    ; F  X! l3 s3 S; D; z  ^

  111. : ~1 E& J8 L' ^1 K. e# v; E
  112. // 写使能  将S1寄存器的WEL置位
    $ X" G0 a4 F4 P0 A2 C1 ~
  113. static void w25qxx_write_enable(void)
      s4 v: f# \: {" x$ C! q4 a" W0 |
  114. {
    2 D; z& `. A2 S/ c
  115.   if(w25qxx_qpi_mode)
    4 U8 w% h7 H8 {9 }7 M# T6 K
  116.   {
    7 {4 O) a/ {. E9 i
  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个字节数据1 J2 i- O) S8 x1 I4 h- |) `
  118.   }7 g2 n3 r- t; B$ B% S1 l
  119.   else2 o0 W, t: H7 ?# W' X- n
  120.   {8 o& P$ ^% [$ I9 S5 E( a. s) J' R) i9 r
  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个字节数据" D/ [  P* G( x0 T4 P
  122.   }
    ! a  K% ?& o9 g
  123. }
    # {. t- {2 R* `+ I
  124. 5 D: S" M1 O: g+ u% M& ]
  125. // 写失能  将WEL清零
    * {+ v/ l3 x. Q' b1 M# T, K& d4 m
  126. void W25QXX_Write_Disable(void); t- [/ g& d" c; |2 c: p/ T6 x5 W
  127. {
    ; P- l$ l* z! ]
  128.   if(w25qxx_qpi_mode)& g- P1 n' O6 S0 ]5 f
  129.   {
    : b- f  J1 b* v; S
  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个字节数据
    7 G8 t8 Y8 o) J5 m7 `, W3 Z
  131.   }$ N& [: {3 B, j& t6 `
  132.   else
    / G$ [. b* T) G) o# N1 t7 j8 S& N
  133.   {
    ; m& S" u, G4 a/ X: {
  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个字节数据
    - u3 @& J$ P, q
  135.   }
    ' |& |, {& o0 T/ {9 R
  136. }5 `2 Y4 W# d4 q0 L

  137. 1 |! f8 d% T: `- j. o/ H
  138. // 等待空闲/ b% I* B3 y) Q! D
  139. void w25qxx_wait_busy(void): s, @; [. ^- E! e. S
  140. {0 q, i: ?. u7 ]! D6 T% }) @, P. l
  141.   while((w25qxx_read_status(1) & 0x01) == 0x01); // 等待BUSY位清空
    % s8 L3 ^; w- c
  142. }
    6 [) p# G& X( C# M' v
  143. / w6 u! v: r- m2 C) m) x
  144. // W25QXX进入QSPI模式
    8 H7 Y0 j+ ]" N7 A9 _
  145. static void w25qxx_qspi_init(void)
    5 g0 H/ t& N( h7 s7 {# {7 ^+ Z
  146. {
    2 {+ |0 |; y: f' t$ i* Q" x2 U# a5 h
  147.   uint8_t reg2;1 a5 n' t# Z) t" J' G" v
  148.   reg2 = w25qxx_read_status(2); // 先读出状态寄存器2的原始值2 p" o* F- h* H" X2 n$ b# p
  149.   if((reg2 & 0X02) == 0)  // QE位未使能: R, g+ A' w/ m5 J# l9 K, |% d' F8 u
  150.   {
    7 Z( D, H& w+ }  c
  151.     w25qxx_write_enable(); // 写使能# j- O" A) p8 R
  152.     reg2 |= 1 << 1; // 使能QE位
    2 ?( C* d1 K6 y* C- v+ B
  153.     w25qxx_write_status(2, reg2); // 写状态寄存器2
    . k4 ^2 A, C1 E6 @8 u4 x
  154.   }
    ) |3 h+ Y% W$ R& ^' w5 n
  155. 3 v/ J% D* \- M! |) X' ?5 H
  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个字节数据
    : P6 _4 O- D( W8 D' n

  157. - K" w) J! i9 G( w5 M; Q9 P
  158.   w25qxx_qpi_mode = 1; // 标记QSPI模式( P0 S7 I! r3 L
  159. }
    * ~# T6 C. Z$ n2 }+ z

  160. . ^" N) G" K- x1 l2 p1 h+ ~
  161. // 0XEF13,表示芯片型号为W25Q80  l1 D& i; m: T
  162. // 0XEF14,表示芯片型号为W25Q16, ^6 T- ~: z2 E) \$ F
  163. // 0XEF15,表示芯片型号为W25Q32
    7 o' Z; t/ g" Y: Q/ m# J) V# z
  164. // 0XEF16,表示芯片型号为W25Q64# Z8 O$ @) h+ v# V
  165. // 0XEF17,表示芯片型号为W25Q128$ c  n( P/ {; A/ T+ N
  166. // 0XEF18,表示芯片型号为W25Q256
    # m+ n0 j" N. b! J
  167. static void w25qxx_id_get(void)! b+ H' N3 l9 l+ e# d# P) ?) M& U
  168. {
    + j- d" N6 V/ @2 e; p3 f
  169.   uint8_t temp[2];4 d7 M. J, B$ U9 D' w! D0 ]
  170.   uint16_t device_id;0 I" o4 a. x" g0 l$ l) c
  171. . t6 Y/ z) u: w. f/ D4 B
  172.   if(w25qxx_qpi_mode)9 h% N+ x6 h* d3 W
  173.   {/ _( p( w4 q" _+ 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个字节数据) E% J1 x- _+ A4 M* K- l3 p0 R8 s
  175.   }9 H' l6 ~# {  {3 `4 y* r& H
  176.   else- L% x) i$ T. R  w
  177.   {) q, Q$ ~% f4 S8 t+ |8 d
  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个字节数据1 q5 l* Z1 q1 u$ ?
  179.   }& n, y) K  q: j* ]6 h& ]
  180. 9 f9 r" f5 V7 k9 G6 O! j
  181.   QSPIReceive(temp, 2);
    $ ~  t& |! E7 U, P9 `0 [

  182. 0 J! k3 h8 [3 u, ~9 Y
  183.   device_id = (temp[0] << 8) | temp[1];
    , E' O/ {8 ?! y- u

  184.   s; P1 ?# k& D3 ^
  185.   printf("QSPI Flash Device ID: %X\r\n", device_id);
    3 E) y- M- r9 C3 p+ j; b+ n
  186. }
    % X0 ^) {3 U( D' p, d
  187. 0 a# V% H3 ?1 s0 R. s
  188. void W25QXXInit(void)# }+ A- f) I( _+ h! `- c
  189. {
    " f  p& N2 C. E5 J' p
  190.   uint8_t temp;3 {/ P# ?: i' |9 M7 N2 B2 f4 ^
  191. * H/ O* |1 D. h9 v, j: U8 [
  192.   QSPIInit();
    ( j$ |; s$ |/ b0 E% y# J4 ?& c
  193.   w25qxx_qspi_init(); // 使能QSPI模式; v) e( q7 X4 d) V7 ?" P2 ^' p7 D- Q
  194.   w25qxx_id_get();    // 读取FLASH ID.
    - O$ S5 ^5 t# p) H9 T. o, k
  195. & ]5 [% _4 G; \5 K. z6 f% N$ q9 E
  196.   temp = w25qxx_read_status(3); // 读取状态寄存器3,判断地址模式% M+ U# X( E9 u
  197.   if((temp & 0X01) == 0) // 如果不是4字节地址模式,则进入4字节地址模式7 u# L: ~5 G! r5 s9 u8 B
  198.   {
    5 i- g9 G; T& G
  199.     w25qxx_write_enable(); // 写使能
    , ?# c+ y4 i& k
  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个字节数据
    0 q9 S7 C; m! R$ i: M* C
  201.   }. t( Q" [: V5 x6 n1 J+ [
  202.   w25qxx_write_enable(); // 写使能
    / {3 T/ Y9 v6 c( r9 _* C
  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个字节数据
    6 w4 D' j  b/ s2 c* N( Z
  204.   temp = 3 << 4;      // 设置P4&P5=11,8个dummy clocks,104M) t3 G( X5 R" ^( [
  205.   QSPITransmit(&temp, 1);
    + a! F) e% X5 \" I2 z  M) T- K
  206. }
    ! ~" A- P2 j$ q7 a, b

  207. 8 |4 d5 S' |8 T2 e7 c8 G+ ?
  208. // 擦除一块4096字节 最少需要150ms
    5 V' l2 W( ]% H9 D# F% z
  209. void W25QXXSectorErase(uint32_t addr)6 i9 u9 ~6 Q! Q6 t
  210. {
    7 v$ f- m8 i6 }, d& M: k
  211.   //addr /= 4096;
    0 O% G' c, _* A1 i4 |9 M! ?. f6 s+ _% j
  212.   addr *= 4096;; f& N$ K/ J9 x2 W

  213. ' U: i; B; t; X. f9 H  E; g
  214.   w25qxx_write_enable();
    $ S8 G* I- a9 `& {
  215.   w25qxx_wait_busy();
    : u$ s; r# S8 V# c) s, `* ]
  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个字节数据
    $ X0 M, s' e6 U8 O
  217.   w25qxx_wait_busy();
    1 A4 w" [; f" j# I
  218. }
    2 u5 [& j" ~$ ~, j: p6 y7 }- y9 W

  219. 2 ?  h7 c6 a/ A$ v; k. w! s3 `* i
  220. * L0 ~6 ~6 a0 j% d6 j
  221. // 擦除整个芯片
    & E2 Q7 {/ U6 B9 U( g* r( ]& Y
  222. void W25QXXChipErase(void)& y3 ~4 E8 S# |. R' \
  223. {
    * }. l3 H9 `. y4 ?% M! q
  224.   w25qxx_write_enable();          //SET WEL0 r9 \2 }" I% b! w* Y3 \
  225.   w25qxx_wait_busy();
    1 \# J! W( v* \* E! n
  226.   QSPISendCMD(W25QXX_CHIP_ERASE, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // QPI,写全片擦除指令,地址为0,无数据_8位地址_无地址_4线传输指令,无空周期,0个字节数据
    0 _5 N( {& _8 ?' {' \
  227.   w25qxx_wait_busy();           //等待芯片擦除结束! T8 {# v' _4 L$ ]3 n$ u+ O
  228. }
    * N  ~; C7 ]2 U4 t: y

  229.   e% G# `" a5 [  v0 y) J
  230. // 写最多256字节, i1 F4 W7 t, W# g9 ]1 {
  231. void W25QXXWritePage(uint32_t addr, uint8_t *buffer, uint16_t length)7 `/ F- V1 S( e! o" ?
  232. {
    6 Q% O& I5 t/ z$ q( A( L; {+ w
  233.   w25qxx_write_enable();          //写使能
    6 ^: s( f9 |  W/ V! N% @
  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个数据
    4 v9 q5 D0 L  |# X' a3 _" }
  235.   QSPITransmit(buffer, length);, r" h8 p+ {% a7 @
  236.   w25qxx_wait_busy();            //等待写入结束+ @5 n7 l# B' d4 |7 q! W
  237. }, x* ?" h* V+ T) O
  238. 0 \7 B, o" t& b' ^2 B0 N4 z
  239. // 写最多65536字节 无擦除动作
    ( t7 ?# Y" B& v' g/ H$ z& m* X
  240. void W25QXXWriteExt(uint32_t addr, uint8_t *buffer, uint16_t length)
    ; O4 ]) c5 m  Q$ M. |4 R3 a
  241. {1 B2 P' l- Y! d  q. L
  242.   uint16_t pageremain = 256 - addr % 256; // 单页剩余的字节数; v9 q3 `3 T5 y6 C

  243. - U+ q4 J  y" i5 z# I7 \- w
  244.   if(addr + length - 1 > (32 * 1024 * 1024))
      C# L  Z- E5 s4 e% |; y% b
  245.   {. k+ d4 y% w6 z  R0 f4 S& f& O/ b
  246.     return;
    ' |$ W5 o/ N  O
  247.   }
    ' V5 M% r9 Q" L
  248. 7 k  K, \5 {6 l  o/ ^. ]) K
  249.   if(length <= pageremain)
    8 U: q1 i. h* e. b" p# X: y. R
  250.   {4 \* @4 y6 i$ d) U! P$ u# M& q! e' C
  251.     pageremain = length;  // 不大于256个字节
    - p/ {9 f. z2 K; b& L8 _5 }' T
  252.   }
    ; N9 t5 w+ ~; L" H) w
  253.   while(1)& g7 \& Q. M' n4 L
  254.   {0 ]! H: n; T  Q  M" v! x2 y
  255.     // 分页写入2 u+ m7 d$ i( f
  256.     W25QXXWritePage(addr, buffer, pageremain);$ n. _) e8 l9 c' p0 \1 c/ t" E) M
  257. ; b3 W9 s' R- g/ P. }6 d* @
  258.     if(length == pageremain)8 n. U: s( g" S- P! H3 B2 N6 B
  259.     {
      N, _/ X# g. L/ A# F* _
  260.       break;  //写入结束了0 y1 c3 k7 ~7 q0 l% |" a/ u3 N
  261.     }
    7 P) x/ q( N2 ?
  262.     else
    " _- {& Z  Z5 J8 G# U0 K
  263.     {
    7 C: d+ d" B, _6 m
  264.       buffer += pageremain;
    0 x$ u" D# L8 p% A/ R! m2 g) m
  265.       addr += pageremain;
    ! j, a: }7 Z1 C! T/ ?+ c) n+ ~
  266. # w" R/ O; H" i5 C5 ~! s% B
  267.       length -= pageremain; // 减去已经写入了的字节数
    , M) P+ {- K" @0 d8 k9 u- P
  268.       if(length > 256)) C' z" ?. w6 ]( B- \
  269.       {& K& g1 r" p; R# `
  270.         pageremain = 256;  // 一次可以写入256个字节( @7 I! s, R( T7 a. N
  271.       }0 F4 O2 D8 s  y: U. Q
  272.       else( U2 C1 i; f7 d2 Y
  273.       {* y7 k7 f8 ?$ q& F! ?8 P" N  T" o. L
  274.         pageremain = length;  // 不够256个字节了$ y9 _5 `& {* T
  275.       }
    ! s& k0 n7 g" l, w; \  Y5 E
  276.     }6 Q$ Z* z/ n% X# \$ g* g/ j
  277.   }
    9 Z. j! ?, U, ^  ?: i
  278. }" @6 H  H6 p6 G- d/ b- T2 I
  279. , z. ]! ]( y; P  e4 F) y
  280. // 写最多65536字节 带擦除动作 % ?$ M% n( I. d- N0 K
  281. static uint8_t W25QXX_BUFFER[4096];
    9 {; U& V) J2 A$ g* _* w
  282. void W25QXXWrite(uint32_t addr, uint8_t* buffer, uint16_t length)9 B8 y# v5 I9 R! D! X* b9 a
  283. {6 {3 ?% r6 i5 J  r+ B$ q
  284.   uint32_t secpos;% B; W' Q, r  ?
  285.   uint16_t secoff, secremain, i;
    ) P! z! ?" A& |- `9 D
  286.   uint8_t * w25q_buf;. \6 W, K4 I, L/ I3 \

  287. * A5 l4 j& E0 x6 f+ b) [
  288.   w25q_buf = W25QXX_BUFFER;
    ( Y/ {: v7 v: u0 [( y) ~( j

  289. 4 B$ ?( b9 v( K# n4 |
  290.   secpos = addr / 4096; // 扇区地址
    8 @, ?3 G& d' o5 u! f; _
  291.   secoff = addr % 4096; // 在扇区内的偏移, p/ `7 _& m; P
  292.   secremain = 4096 - secoff; // 扇区剩余空间大小; t: o3 ~: s* @

  293. 7 _$ d) |9 j, j0 l# E: h
  294.   if(length <= secremain)
    ! ^6 E- ^2 q# c7 s0 ]
  295.   {( n" R5 \8 y9 H3 |' w9 B. X
  296.     secremain = length;  // 不大于4096个字节7 ]; c& @0 I$ D( S# a
  297.   }  X& _$ f* |/ z* ]; c
  298.   while(1)
    ' e" X, U) B! o' U5 d
  299.   {/ e0 w- I! Z4 J# p. r, v
  300.           WatchdogFeed();1 [6 b/ ^/ [) x# @
  301.     W25QXXRead(secpos * 4096, w25q_buf, 4096); // 读出整个扇区的内容
    ) H: W! S+ M2 y/ l' ]+ G
  302.     for(i = 0; i < secremain; ++i)- w$ A6 v: t, v/ ~) c; K
  303.     {
    7 H8 f# J  p) ~& a7 N. S4 s& T1 q
  304.       if(w25q_buf[secoff + i] != 0XFF)
    " k/ y2 L- {! |& l) B
  305.       {$ O6 p( A1 A' X2 ~2 ]
  306.         break;  // 需要擦除; m& D+ @: }* i* Y
  307.       }
    ' ]/ B; \+ G7 W9 ?% Q( d
  308.     }
    5 i+ R3 T" [& \& J
  309.     if(i < secremain) // 需要擦除
    7 C, S/ q1 h$ M) X: W9 }2 T
  310.     {& G* [7 ]+ H: G0 {% }& n# c0 z5 [4 O
  311.       W25QXXSectorErase(secpos); // 擦除这个扇区7 x: y, e' t% f
  312.       for(i = 0; i < secremain; ++i)* e# Z% r+ [! U0 U- I
  313.       {& t% x! m5 x4 B8 Z7 @- u2 m
  314.         w25q_buf[i + secoff] = buffer<i>;</i>
    % e3 |) j  s5 k/ K
  315.       }! p: q+ d) o! h0 }( n/ b
  316.       W25QXXWriteExt(secpos * 4096, w25q_buf, 4096); // 写入整个扇区
    7 D; h6 e& d& g4 [! k( s

  317. # v6 ]7 v7 ]. d6 l# z" u
  318.     }9 X0 C3 Q: d3 ]9 l, X/ H  B' r
  319.     else, u5 U3 H8 j$ J- t2 {
  320.     {7 W6 I. ~5 Q; P; u; D$ L
  321.       W25QXXWriteExt(addr, buffer, secremain);  // 写已经擦除了的,直接写入扇区剩余区间.
    4 `- L( ?+ Z9 q( D0 R2 R
  322.     }: s" g& L* `# ~6 t: y
  323.     if(length == secremain)
    $ X7 X: ]% a5 v- O' A, B% J
  324.     {) h+ f4 t! \9 T, d& g# v! p$ l  H
  325.       break;
    4 u* f: h1 r5 N3 F9 Z, {2 g0 `
  326.     }0 p7 r7 {; i* Q
  327.     else
    % A/ U! d5 E% ~5 e/ j
  328.     {3 [; k! p- P8 p/ o5 a9 @
  329.       secpos++;
    4 c1 L+ o4 _  j  [* W* h+ c$ P
  330.       secoff = 0;
    5 s; P. v4 Z- f" x- f2 W8 R; V5 ~

  331. ! z, J# [- k  {' D
  332.       buffer += secremain;7 d' r0 t2 ?' C) s8 B
  333.       addr += secremain;! Z/ Z- i% N5 Y
  334.       length -= secremain;
    + {! z* ^0 K5 h" D. S
  335.       if(length > 4096)
    + [7 h4 I% u& d- W
  336.       {7 Z" L/ h3 [5 W& v' a. O
  337.         secremain = 4096;# ]' ^: D6 `7 n- f" j( g4 m
  338.       }
    ; K& [' i) \9 I3 M
  339.       else0 B8 G) C+ F; s9 g9 z6 S. ~; C+ K
  340.       {% }8 q6 |) @5 T2 s2 N
  341.         secremain = length;
    : F+ q$ N7 f4 ?2 z5 u) f, [! V
  342.       }0 W5 q+ D4 Y9 ^9 q/ ]
  343.     }
    ( t* Y. d; U+ i+ K+ _+ t
  344.   };
    1 v" H) D. z) u+ _
  345. }
    % E& a5 _; Z! I9 n* H

  346. . G2 N7 @) n9 l7 K. ^3 q. f; @
  347. 5 E, g/ ], W1 B
  348. // 读取SPI FLASH,仅支持QPI模式  在指定地址开始读取指定长度的数据 65536
    ( Q- y6 s+ R0 [3 p9 k/ {& W, G
  349. void W25QXXRead(uint32_t addr, uint8_t *buffer, uint16_t length)0 v: W; i  s! I9 n9 a
  350. {
    * c; K# z( ~/ J% }5 G$ Q% ^
  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个数据
    & b  y. r. a  l- X
  352.   QSPIReceive(buffer, length);
    0 h# Q' q5 ]: D& P2 |9 X5 M
  353. }
复制代码

" v. B. `  S& K8 _' p- o$ m: o  T也可以使用SPI操作W25QXX。
: M! w+ c( j# v1 k
4 ?% U3 m3 h: {3 [5 P
" t( o) F2 C5 H" O; o
3 G' \& P5 K/ _5 l" e
收藏 评论0 发布时间:2021-12-11 12:00

举报

0个回答

所属标签

相似技术帖

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