一、QSPI
- }6 `+ U, O3 b% h, A8 Y" v3 B SPI 是 Queued SPI 的简写,是 Motorola公司推出的 SPI 接口的扩展,比 SPI 应用更加广泛。在 SPI 协议的基础上,Motorola 公司对其功能进行了增强,增加了队列传输机制,推出了队列串行外围接口协议(即 QSPI 协议),使用该接口,用户可以一次性传输包含多达16个8位或16位数据的传输队列。一旦传输启动,直到传输结束,都不需要CPU干预,极大的提高了传输效率。该协议在ColdFire系列MCU得到广泛应用。与SPI相比,QSPI的最大结构特点是以80字节的RAM代替了SPI的发送和接收寄存器。QSPI 是一种专用的通信接口,连接单、双或四(条数据线) SPI Flash 存储介质。
8 V4 g5 r! k/ b( R$ D; K+ |+ J% K7 l. p/ s' f: r
该接口可以在以下三种模式下工作:# B# F+ M* w6 m9 X
① 间接模式:使用 QSPI 寄存器执行全部操作
! H v, \( S( ?0 r2 E3 t② 状态轮询模式:周期性读取外部 Flash 状态寄存器,而且标志位置 1 时会产生中断(如擦除或烧写完成,会产生中断)" @$ E, ]; g$ [& l: D) h( t1 T* I
③ 内存映射模式:外部 Flash 映射到微控制器地址空间,从而系统将其视作内部存储器。8 w, m! u2 R9 J8 k
; a) |) K8 S3 D: UQSPI通过6根线与SPI芯片通信,下图是内部框图:
! S2 ]( T, F' i% O- {! @2 x7 V$ z9 V9 K6 V" T# ] t
% z% }' ]! S$ F
) f+ h0 A$ I4 }0 c" v- z
QSPI每条命令,必须包含一个或多个阶段:指令、地址、交替字节、空指令和数据。3 u+ N1 f J. B5 I |% c, n- S5 p
# [- H) o' Z* L+ D9 K9 Z" A
QSPI发送命令:等待QSPI空闲;设置命令参数。
* r3 _7 B, {+ D* g
. p4 K2 L' s4 G$ P$ _QSPI读数据:设置数据传输长度;设置QSPI工作模式并设置地址;读取数据。% w8 x& I) Y) H! [; v0 r2 Z
: F5 X- \( J& t0 KQSPI写数据:设置数据传输长度;设置QSPI工作模式并设置地址;写数据。
2 n9 n4 k3 d* E8 Q1 o
" K9 B7 x8 j# x; N# o
& M6 `7 p2 H- j二、几个重要的函数. n; B( w- F; A: @
- HAL_StatusTypeDef HAL_QSPI_Init (QSPI_HandleTypeDef *hqspi); // 初始化
8 `0 t+ {- a' i1 p) x c - % K/ G" g1 \2 M2 [
- HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout); // 发送命令$ A1 _5 f, m. a* U" O- ~, L+ h
/ J k; h2 M$ W+ C% r) g- HAL_StatusTypeDef HAL_QSPI_Transmit (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 发送数据! o9 D! i7 f* P( A6 i3 M# r
4 w+ v2 {% n5 X: q* p5 _( @7 D- HAL_StatusTypeDef HAL_QSPI_Receive (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 接收数据
复制代码
6 d. X2 E2 ?, Z# ^3 d三、几个重要的结构) y- C; L9 o F
- // QSPI操作句柄
, S N; G0 G; M! |8 Z' M* ? - typedef struct, w* Z( j" c2 W
- {
. {: r6 O- N3 a9 t8 q - QUADSPI_TypeDef *Instance; /* QSPI registers base address */3 o1 x B) l4 M# q$ Y0 a
- QSPI_InitTypeDef Init; /* QSPI communication parameters */ u; m. H' x) p9 T( S+ z7 W
- uint8_t *pTxBuffPtr; /* Pointer to QSPI Tx transfer Buffer */
7 s/ |' t8 d3 y - __IO uint16_t TxXferSize; /* QSPI Tx Transfer size */7 B3 Y7 m3 T/ @1 W6 U$ i9 }
- __IO uint16_t TxXferCount; /* QSPI Tx Transfer Counter */, f$ }* `6 _9 Q! I
- uint8_t *pRxBuffPtr; /* Pointer to QSPI Rx transfer Buffer */# x" w- [, H1 o& s2 W
- __IO uint16_t RxXferSize; /* QSPI Rx Transfer size */
9 `, Q; `+ T) J- C, a* B9 V; V* g& v - __IO uint16_t RxXferCount; /* QSPI Rx Transfer Counter */4 j; ?7 S/ ^2 I' e5 E
- DMA_HandleTypeDef *hdma; /* QSPI Rx/Tx DMA Handle parameters */4 t H' T( O6 I5 P4 k, b
- __IO HAL_LockTypeDef Lock; /* Locking object */; s B! }. l3 M' t1 S# J
- __IO HAL_QSPI_StateTypeDef State; /* QSPI communication state */
8 j, H- Y- k* F4 U - __IO uint32_t ErrorCode; /* QSPI Error code */
$ y5 Y1 D% r- ^9 ]* }9 S/ w$ y - uint32_t Timeout; /* Timeout for the QSPI memory access */
5 Q {+ Y* P7 _+ ? - }QSPI_HandleTypeDef;& N' ?/ {1 ~3 V. j- Y7 b
- ! O, R% h' y" }& L( }
- // Instance:QSPI基地址 --- QUADSPI5 ]" Q( c b1 b# V" M- E
- // Init:设置QSPI参数
4 `) P0 f1 n8 r: ^) [ - // pTxBuffPtr,TxXferSize,TxXferCount:发送缓存指针 发送数据量 剩余数据量/ Y$ N4 b! V: @: [8 \$ ]
- // pRxBuffPtr,RxXferSize,RxXferCount:接收缓存指针 发送数据量 剩余数据量6 L5 Z6 e% h1 H' A( m7 g: a
- // hdma与DMA相关, 其他为过程变量不需要关心
复制代码- // 参数配置 时钟分频系数 FIFO阈值 采样移位 FLASH大小 片选高电平时间 时钟模式 闪存ID 双闪存模式设置5 d& Z; q4 k, Z: H
- typedef struct% p0 h0 S2 o4 t3 r% w
- {9 C I5 \& j' m4 t; [4 I
- uint32_t ClockPrescaler; /* Specifies the prescaler factor for generating clock based on the AHB clock.
( k o) @5 W% r1 T% Y - This parameter can be a number between 0 and 255 */ / Z8 E" j$ `6 i+ O% _+ G. N" R
- ! p8 t& l" o+ P
- uint32_t FifoThreshold; /* Specifies the threshold number of bytes in the FIFO (used only in indirect mode)# v K) ^! S. T. @, d& x# ~# O' `
- This parameter can be a value between 1 and 32 */
% w7 _5 i+ Z, ^ - . G, j" ^9 h; h. e0 N
- uint32_t SampleShifting; /* Specifies the Sample Shift. The data is sampled 1/2 clock cycle delay later to
' W6 j! s* f' [1 o& I# B' U, H - take in account external signal delays. (It should be QSPI_SAMPLE_SHIFTING_NONE in DDR mode): K/ v. H5 R5 e' U9 B2 N
- This parameter can be a value of @ref QSPI_SampleShifting */7 k# w3 h( M r, c% C
- & N. u1 |" o! k' f3 U
- uint32_t FlashSize; /* Specifies the Flash Size. FlashSize+1 is effectively the number of address bits
1 I; [, `2 A9 j9 }3 s) g1 v - required to address the flash memory. The flash capacity can be up to 4GB
5 ~" N, X+ i- a1 J- X" A - (addressed using 32 bits) in indirect mode, but the addressable space in % ?$ b6 P5 F& S
- memory-mapped mode is limited to 256MB
1 U7 o8 @& G# u4 {2 B( f# d) \; ] - This parameter can be a number between 0 and 31 */" a% b# x$ d( |( `
- 8 Y7 S7 R( C/ Z# _+ y6 r' J' \
- uint32_t ChipSelectHighTime; /* Specifies the Chip Select High Time. ChipSelectHighTime+1 defines the minimum number & x! G7 Y" q) ^
- of clock cycles which the chip select must remain high between commands./ l1 m) N5 A! q- `8 H* C
- This parameter can be a value of @ref QSPI_ChipSelectHighTime */
, Y$ ^+ e( p/ M6 D. a - & L( B! q! |0 P+ @7 Z7 n
- uint32_t ClockMode; /* Specifies the Clock Mode. It indicates the level that clock takes between commands.' w9 T& N2 ^, @
- This parameter can be a value of @ref QSPI_ClockMode */
@( x3 m& w) i - ~4 ?. r5 a9 p) M$ W3 k
- uint32_t FlashID; /* Specifies the Flash which will be used,
. b# W' t: Q6 Z - This parameter can be a value of @ref QSPI_Flash_Select */- L5 j+ h, b% |' w0 J: Z! t% g
- I! F) z1 l4 _- uint32_t DualFlash; /* Specifies the Dual Flash Mode State3 n; D' f. w% u& ^+ N5 O3 O. u
- This parameter can be a value of @ref QSPI_DualFlash_Mode */
$ k' V+ z2 j/ M' r0 r" ^ - }QSPI_InitTypeDef;
复制代码- // 采样移位 P) W) v" l2 w( i/ B
- #define QSPI_SAMPLE_SHIFTING_NONE ((uint32_t)0x00000000U) /*!<No clock cycle shift to sample data*/! b( B3 Y% b3 A& |* R/ m
- #define QSPI_SAMPLE_SHIFTING_HALFCYCLE ((uint32_t)QUADSPI_CR_SSHIFT) /*!<1/2 clock cycle shift to sample data*/
复制代码
' {9 b- V" e& N8 a8 `+ O1 \四、QSPI接口设计(仅供参考)2 h5 D! h! u! P1 c
- #define QSPI_CLK_ENABLE() __HAL_RCC_QSPI_CLK_ENABLE();
4 h+ y. H! L6 f3 F1 l: [. h) a - 5 \+ H. s1 A$ {
- #define QSPI_BK1_NCS_PORT GPIOB
, \& J9 {0 K+ ]' p& M% X( V2 K/ y - #define QSPI_BK1_NCS_PIN GPIO_PIN_6
9 [& i* }# k& s3 g. R5 h. h - #define QSPI_BK1_NCS_AF GPIO_AF10_QUADSPI
V/ n$ t0 A# Q2 M - #define QSPI_BK1_NCS_CONFIG() GPIOConfigExt(QSPI_BK1_NCS_PORT, QSPI_BK1_NCS_PIN, GPIO_MODE_AF_PP, GPIO_PULLUP, QSPI_BK1_NCS_AF). I7 J+ }# |2 P; N
- 5 v# F4 o/ o& n8 {1 G
- #define QSPI_BK1_CLK_PORT GPIOB, u, h1 j- t" _" a) M
- #define QSPI_BK1_CLK_PIN GPIO_PIN_2$ j' k9 @/ ?0 x* r
- #define QSPI_BK1_CLK_AF GPIO_AF9_QUADSPI
/ I3 o% f" U2 _$ S8 `0 d - #define QSPI_BK1_CLK_CONFIG() GPIOConfigExt(QSPI_BK1_CLK_PORT, QSPI_BK1_CLK_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_CLK_AF). ]1 @) Q. j/ R+ R! n$ q, G9 F$ Y
( R2 E7 b3 N: O- C2 Q+ Z, }- #define QSPI_BK1_IO0_PORT GPIOF, U+ d% f7 T& O# _
- #define QSPI_BK1_IO0_PIN GPIO_PIN_8
r: u; F4 M" X1 B' o/ j8 Z - #define QSPI_BK1_IO0_AF GPIO_AF10_QUADSPI/ j. b( M5 C( ~/ {2 E
- #define QSPI_BK1_IO0_CONFIG() GPIOConfigExt(QSPI_BK1_IO0_PORT, QSPI_BK1_IO0_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_IO0_AF)& R2 {+ A% G7 J
' r1 J, [/ [" j4 G4 b }' ^; y- #define QSPI_BK1_IO1_PORT GPIOF& [5 L$ ^1 ]3 I/ F5 v" i
- #define QSPI_BK1_IO1_PIN GPIO_PIN_9+ ]# ^+ I( j9 j) S0 u4 A
- #define QSPI_BK1_IO1_AF GPIO_AF10_QUADSPI% v% |/ w# y- {4 l. F
- #define QSPI_BK1_IO1_CONFIG() GPIOConfigExt(QSPI_BK1_IO1_PORT, QSPI_BK1_IO1_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_IO1_AF)
$ c/ S( Y9 P! H5 j% D# i2 W - % i; E0 p3 i) |# l2 l
- #define QSPI_BK1_IO2_PORT GPIOF
! t ~/ w+ p* Y T& n' a - #define QSPI_BK1_IO2_PIN GPIO_PIN_74 }: t# R2 f0 z+ b* M1 |8 s
- #define QSPI_BK1_IO2_AF GPIO_AF9_QUADSPI
p. a1 w7 J+ j z - #define QSPI_BK1_IO2_CONFIG() GPIOConfigExt(QSPI_BK1_IO2_PORT, QSPI_BK1_IO2_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_IO2_AF)7 i8 ]% {* B. a Q) g& X* G
- 8 U' L- v+ Q# ~7 L$ j1 Q
- #define QSPI_BK1_IO3_PORT GPIOF, X& e9 L6 p, ?7 K' c+ e
- #define QSPI_BK1_IO3_PIN GPIO_PIN_6
( r2 d; a; u4 T' r8 u1 @ K - #define QSPI_BK1_IO3_AF GPIO_AF9_QUADSPI* V' z: w3 f& N: x
- #define QSPI_BK1_IO3_CONFIG() GPIOConfigExt(QSPI_BK1_IO3_PORT, QSPI_BK1_IO3_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_IO3_AF)
复制代码- // 封装几个必要的接口
2 ]) D5 e B6 { T- t - static QSPI_HandleTypeDef qspi_handle;
4 ^" C2 \5 o F6 n+ x M
( ^$ }, E0 a0 p# M; v2 r# u% R' Q7 o- static void qspi_gpio_init(void)
0 j& w1 r) o" l# m% ` g - {! o9 y: [( ?) g6 Z; X& G! X$ f
- QSPI_CLK_ENABLE();
6 j3 E3 u! U8 b! R1 o3 |/ f; p - ( u4 V, C! N1 z3 N( l
- QSPI_BK1_NCS_CONFIG();* ?2 \7 q5 ~5 |; O7 J
- QSPI_BK1_CLK_CONFIG();
9 g: i6 T( P4 i, @ - QSPI_BK1_IO0_CONFIG();
8 A, j7 L) m/ i$ u$ C8 X& e4 g - QSPI_BK1_IO1_CONFIG();
) O. Z1 s L5 |" N- Y - QSPI_BK1_IO2_CONFIG();. d( A3 k: @$ }
- QSPI_BK1_IO3_CONFIG();! D8 N) D2 t2 M0 Y1 m$ b# C- z
- }
8 ^& ?5 |6 C, K" s. b% s. s
: o0 s! v1 R: s) r4 I- static void qspi_mode_init(void)
" S( W' Z0 S7 ^0 H$ k% a+ e - {( A' L9 H% u' x8 p5 L5 A$ T5 R
- qspi_handle.Instance = QUADSPI; // QSPI
s6 K# T& B5 }" i0 k( I" K - qspi_handle.Init.ClockPrescaler = 2; // QPSI分频比,W25Q256最大频率为104M 所以此处应该为2,QSPI频率就为216/(2+1)=72MHZ9 _' ]2 B4 _. E& G" v; @3 r. `- o
- qspi_handle.Init.FifoThreshold = 4; // FIFO阈值为4个字节
\: J7 A" a9 m; N+ O - qspi_handle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; // 采样移位半个周期(DDR模式下,必须设置为0)
3 M" U/ K# m x% b" r - qspi_handle.Init.FlashSize = POSITION_VAL(0X2000000) - 1; // SPI FLASH大小,W25Q256大小为32M字节+ c# L* B7 d G8 Q6 V8 ^2 l4 p
- qspi_handle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_4_CYCLE; // 片选高电平时间为4个时钟(13.8*4=55.2ns),即手册里面的tSHSL参数
! V, P/ \4 `' G - qspi_handle.Init.ClockMode = QSPI_CLOCK_MODE_0; // 模式0
# V, T, m1 x1 ?$ _; u9 I8 r3 ~5 C - qspi_handle.Init.FlashID = QSPI_FLASH_ID_1; // 第一片flash& v2 T9 |' @ Z: R# ~
- qspi_handle.Init.DualFlash = QSPI_DUALFLASH_DISABLE; // 禁止双闪存模式
# q6 l+ K+ `( p% v - HAL_QSPI_Init(&qspi_handle); //QSPI初始化
* `. P( N1 R7 H4 Q# G - }
4 w1 d9 i0 s4 g5 U( \) \& f
6 o7 N! w5 v' W) ?: |( V) g. K; C$ j+ R- void QSPIInit(void)
) M. J! }1 F5 ?3 X! \ - {
5 r5 H( y# h) K9 C - qspi_gpio_init();2 \ S; O' l) W- Z( ?; G
- qspi_mode_init();
7 [* N. L) h6 y" n2 n - }8 I' J+ l8 E0 s6 i, j
- $ m0 k' n& l3 J# Y
- // QSPI发送命令) a! |7 \$ G% d& H3 E! o `4 g
- // instruction:要发送的指令, V" `8 a+ w& P3 T
- // address:发送到的目的地址7 \6 q% e) G% j* b8 e& \3 g* {- |
- // dummyCycles:空指令周期数
) W! ?4 D1 `$ s+ j4 U - // instructionMode:指令模式;QSPI_INSTRUCTION_NONE,QSPI_INSTRUCTION_1_LINE,QSPI_INSTRUCTION_2_LINE,QSPI_INSTRUCTION_4_LINE% \4 x% w p$ P5 F3 q. |5 C% D% H
- // addressMode:地址模式; QSPI_ADDRESS_NONE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_2_LINE,QSPI_ADDRESS_4_LINE
. D, J I. N: u! s& S - // addressSize:地址长度;QSPI_ADDRESS_8_BITS,QSPI_ADDRESS_16_BITS,QSPI_ADDRESS_24_BITS,QSPI_ADDRESS_32_BITS
. f. {. x! X! s* A! f - // dataMode:数据模式; QSPI_DATA_NONE,QSPI_DATA_1_LINE,QSPI_DATA_2_LINE,QSPI_DATA_4_LINE
; u0 l- H( y6 }7 `( V - void QSPISendCMD(uint32_t instruction, uint32_t address, uint32_t dummyCycles, uint32_t instructionMode, uint32_t addressMode, uint32_t addressSize, uint32_t dataMode)
& m5 H8 i( [8 W2 O) R; v0 C" y& a& a - {
; g9 o: ]# H9 C8 M8 L& h9 J - QSPI_CommandTypeDef Cmdhandler;
6 v* [0 w9 Y0 K3 B. c' i# q$ R8 U
. {, [! u- N! R5 Q. s) _4 C+ }1 K- Cmdhandler.Instruction = instruction; // 指令
) c% S7 w& b- }; ?- d& d - Cmdhandler.Address = address; // 地址
6 t1 ~$ y R4 C [4 I. @& j - Cmdhandler.DummyCycles = dummyCycles; // 设置空指令周期数6 J& U& {$ f% u9 Z) M4 T
- Cmdhandler.InstructionMode = instructionMode; // 指令模式0 @$ _* {+ P% d( h8 D3 h
- Cmdhandler.AddressMode = addressMode; // 地址模式
; Y/ P7 c* a; b+ }0 m T/ } - Cmdhandler.AddressSize = addressSize; // 地址长度1 Y% x# F0 Z& V& i- ^9 H! H0 t
- Cmdhandler.DataMode = dataMode; // 数据模式. c. r7 \, f. l; o9 H0 a9 G J
- Cmdhandler.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 每次都发送指令, z! d: B1 ?4 Q4 J q
- Cmdhandler.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 无交替字节
W& Q/ T/ p# N1 T! n - Cmdhandler.DdrMode = QSPI_DDR_MODE_DISABLE; // 关闭DDR模式% L# ~- n5 u0 r
- Cmdhandler.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
8 G/ G2 p3 r$ k$ J2 a! T - + ]$ U3 q4 Z- G$ M% b* i4 K
- HAL_QSPI_Command(&qspi_handle, &Cmdhandler, 5000);
1 d' k* [' F$ s2 A {, m; d- Q9 u - }$ l! U& _/ @+ U( E- Y& b
- 5 E& R4 x' K) h: Y" L3 t ]/ f. T; C
- // 接收指定长度数据
4 o1 { a( h& G4 R; g+ ?9 l - uint8_t QSPIReceive(uint8_t *buffer, uint32_t length)
4 f5 D0 N" s" S& F5 r9 Q - {
, N- P* u: |' K6 s& O - qspi_handle.Instance->DLR = length - 1;
0 F6 u, f' Y4 [/ `& D& B* D -
. B& k1 P& R! j2 ?2 k+ t* S - if(HAL_QSPI_Receive(&qspi_handle, buffer, 5000) == HAL_OK)
" |, A R6 x5 z [8 T1 g- Z - {% a# s7 E! k: a+ f, a: A8 Z
- return 0; //接收数据! v9 r0 {. G+ B
- }
7 I9 Z4 f, |. ? - else
: g! n: A# R, F2 g# f - {
' V& [2 ]9 [$ ^9 n1 o9 a - return 1;
# F% ], T o" k7 w - }
5 ~ u5 P9 z/ n" D" [4 O) L' Q - }
, o8 A* _" h4 g( n
9 Q( P4 z, u5 h) p8 n; m p% h- // 发送指定长度数据
/ t: R8 Y; i' i1 V, U - uint8_t QSPITransmit(uint8_t *buffer, uint32_t length)6 n+ R/ P5 a4 H8 p2 k, n2 H: l* A
- {
) F. B- [: ~& w Q) ^. i3 I+ j - qspi_handle.Instance->DLR = length - 1;, w4 X+ g9 T2 W' ]7 c
- if(HAL_QSPI_Transmit(&qspi_handle, buffer, 5000) == HAL_OK)7 \, {+ `0 w: S8 J5 z- k2 s
- {
2 ]! o) ^! J/ t5 \ { - return 0; //发送数据, a) e# v# X" }0 i6 m" f( \! t0 f
- }
' m* P7 @! {: Q. W6 R+ c - else
# R1 ]8 d. N' K f& o - {. d- j* ^; w9 B0 l8 S- k) w
- return 1;
! X7 z9 |, W$ M( Q2 x3 } - }
; b% i9 g8 }* z0 A# ]) T* b - }
复制代码 ' l( [! J' T2 D- [9 g% [
五、QSPI驱动W25Q256
5 U8 ~- t/ }$ l5 Q" cW25Q256:32M 512块,每块16个扇区,每个扇区4K。具体指令看手册。封装了如下接口(初始化读写擦除等接口是以后移植文件系统的基础):$ V6 O; Z; q$ T0 _0 q8 i; }
' ?9 `2 g2 x# H0 M
- #define W25QXX_WRITE_ENABLE 0x06
1 Q s+ h# F9 } - #define W25QXX_WRITE_DISABLE 0x04: {$ {' K3 v# m6 y! G! _ f
- #define W25QXX_READ_STATUS_REG1 0x05
* q6 t7 G3 [0 n1 f; B/ A9 { - #define W25QXX_READ_STATUS_REG2 0x35* a4 l- q/ r. \# i" \; E _
- #define W25QXX_READ_STATUS_REG3 0x15
) S2 k7 a* }0 L* F P6 U - #define W25QXX_WRITE_STATUS_REG1 0x01
& a8 i$ i5 C+ M8 [) z/ [! W9 y - #define W25QXX_WRITE_STATUS_REG2 0x31
$ x( U0 t( i- b7 i" ? - #define W25QXX_WRITE_STATUS_REG3 0x11
% G6 k- y9 }' e - #define W25QXX_READ_DATA 0x03; V; D: }: O; O$ c6 s
- #define W25QXX_FAST_READ_DATA 0x0B; Y/ F" ~8 X( `+ m3 N
- #define W25QXX_FAST_READ_DUAL 0x3B+ L3 [& D, b* G; A5 C+ F
- #define W25QXX_PAGE_PROGRAM 0x02
. V8 ], D5 N6 U8 ^% W - #define W25QXX_BLOCK_ERASE 0xD8
+ l7 W4 V% d+ s5 ` - #define W25QXX_SECTOR_ERASE 0x20, v5 m! ^ H4 }3 M; y% B0 c
- #define W25QXX_CHIP_ERASE 0xC7
' W- x+ O- I% O% s/ W - #define W25QXX_DEVICEID 0xAB
' C) H4 d% i; ] - #define W25QXX_MANUFACT_DEVICEID 0x908 D. I& w- k: s2 r3 c* _/ N
- #define W25QXX_JEDEC_DEVICEID 0x9F' ]: N U3 Q* p$ `7 o: m# ^$ P
- #define W25QXX_EABLE_4BYTE_ADDR 0xB7
/ T1 D$ R, w$ ?( Q - #define W25QXX_EXIT_4BYTE_ADDR 0xE9
% F4 ]+ b* _4 u5 A: D2 B: b - #define W25QXX_SET_READ_PARAM 0xC0) ]" |* H3 J5 ], W' r2 T
- #define W25QXX_ENTER_QPIMODE 0x387 Z& }; G4 |" j9 {. y0 D+ z- n/ s
- #define W25QXX_EXIT_QPIMODE 0xFF
% T4 ?1 e$ B% V3 y, ^, Y4 q' `/ Y" ? - ' L' X; @' N. C
- uint8_t w25qxx_qpi_mode = 0; // QSPI模式标志:0,SPI模式;1,QPI模式.
/ o" n2 ?# Z5 V
3 N( j E; S$ C9 o+ ]! C- // 读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器0 W `& b0 Z: {' K
- // 状态寄存器1:+ S0 S, a' H( v! o
- // BIT7 6 5 4 3 2 1 0" b7 \! ~, {1 z7 @ V
- // SPR RV TB BP2 BP1 BP0 WEL BUSY
& w+ E+ Y3 _/ L/ d) n$ J, Y - // SPR:默认0,状态寄存器保护位,配合WP使用. q9 t6 Y# ~: B& b$ Q1 |2 d
- // TB,BP2,BP1,BP0:FLASH区域写保护设置
' q; u! W& P! Z( I! r - // WEL:写使能锁定& {+ O: z7 r7 ~) U; [& ]
- // BUSY:忙标记位(1,忙;0,空闲)
: h6 l+ {2 ?; o( \* w7 s* a - // 默认:0x007 a# \* X# O/ y& U. ^6 u( x) m( H
- // 状态寄存器2:
" G N$ v' a# Z5 j2 P; O - // BIT7 6 5 4 3 2 1 0
3 c, d% V4 x3 C; f7 ]8 u - // SUS CMP LB3 LB2 LB1 (R) QE SRP1$ A/ Y3 t8 f) k9 m7 c, B; P
- // 状态寄存器3:- \8 s) g, v* |5 ^! r' h2 N* t; X
- // BIT7 6 5 4 3 2 1 03 E0 y) T6 ^" }1 h' f' m
- // HOLD/RST DRV1 DRV0 (R) (R) WPS ADP ADS
! T$ a" D( z5 W% h& [6 C - // reg:状态寄存器号,范:1~36 z) x$ E2 c$ Y w
- // 返回值:状态寄存器值* O Z+ J4 K2 r( Y0 D9 e+ U
- static uint8_t w25qxx_read_status(uint8_t reg)
4 c3 d* ~* `7 a+ h Y& m1 ] - {
; \ |, w9 G+ c3 ?% W d, r - uint8_t value = 0, command = 0;; @. w1 b8 k& L2 M! {
- 7 A5 f4 k, D; w8 J: H2 O
- switch(reg)
% f( j- }# N* \9 T - {
2 A; u. A; a- b' R5 Q8 E - case 1:
. u7 ]8 k" g5 ^) v# d; M5 f - command = W25QXX_READ_STATUS_REG1; // 读状态寄存器1指令
5 Q- J- C4 u$ }' c" `% W - break;" n G( M {) M1 h
- ( h9 v( I! H: ]5 D& [7 V- g
- case 2:+ a. E/ X" o9 z9 w/ z, c0 }- Q2 f
- command = W25QXX_READ_STATUS_REG2; // 读状态寄存器2指令
# h$ }# e5 e3 c7 B2 ~ - break;
) l% i* z! J4 W0 [3 s. H, g- f - 6 ^6 B% ]8 ?/ o6 G9 {$ ^1 B
- case 3:2 ^2 ~3 }/ e; x
- command = W25QXX_READ_STATUS_REG3; // 读状态寄存器3指令, [' L! T) G% w, E* o2 w( j9 ^
- break;# d$ V( W% H) R6 Q1 z& ^
- ' w, n$ k! r2 J
- default:* ]) i1 @0 \' d y% M; e
- command = W25QXX_READ_STATUS_REG1;2 }: r( @( R5 V8 Z3 B
- break;
: Y5 f) Z2 B! \! S - }
9 ?- B- ^ D. V, a z( | - ; A; G1 A/ n; c7 X% y4 x
- if(w25qxx_qpi_mode)
3 A7 P% |- Z& n' v$ `6 B - { A- y: ^7 @5 u& L; H
- 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; \9 q. z' l& j: Y0 o2 F - }
+ O' o# A1 ^% M8 ^7 Y. y; N8 x - else
. ~* ]+ c& m, }7 ^- s; m - {
5 j% g3 O7 k7 J; L. h - QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_1_LINE); //SPI,写command指令,地址为0,单线传数据_8位地址_无地址_单线传输指令,无空周期,1个字节数据. }& I1 \& q$ Z7 ]" _
- }
W% h9 h" L0 T( J: S8 I- P& V
6 v3 O% b1 h/ ]4 v7 x- QSPIReceive(&value, 1);
A* v0 W! o6 `
7 D( y% ?) t8 q' N9 V- return value;' m- t+ W7 I) {4 q: T/ s
- }! Y! {8 G! s2 Z( j$ s8 ]
- # d+ b R5 Y& V( K2 p; f
- // 写状态寄存器1 C5 q9 i! ?0 s5 V& z% D
- static void w25qxx_write_status(uint8_t reg, uint8_t status)" z, [( P u0 v b9 }1 }# V
- {
6 { H- {. e! W: [3 C' ^6 r - uint8_t command = 0;1 m/ l1 g. }# w/ z2 d1 `9 t
- # [2 q/ }2 D2 Y8 i
- switch(reg) Z6 a8 M, ~0 r' o4 f( P) j0 U
- {
& p" H$ X; m. }6 a - case 1:
3 X, h# Y% |1 G - command = W25QXX_WRITE_STATUS_REG1; //写状态寄存器1指令, {$ M; [! x" V& \5 F
- break;- P0 c% l2 U* ]
- case 2:6 C: h$ x( o$ L; F7 j
- command = W25QXX_WRITE_STATUS_REG2; //写状态寄存器2指令, q. V! B. K+ F8 g$ o
- break;+ X8 |6 o6 b2 q* b" ^0 I
- case 3:
! y; Q; X* ^/ L: u" {, ]$ Y* } - command = W25QXX_WRITE_STATUS_REG3; //写状态寄存器3指令
( o6 L: a6 \( R8 W2 U - break;# y# r3 X0 S6 K* c: F" h: G& n
- default:0 O8 m! C: M6 N" m: o7 J' f
- command = W25QXX_WRITE_STATUS_REG1;) @6 `1 f6 U4 T; _& F; V
- break;
$ {7 h, \8 m% h+ l+ v8 h0 X/ q - }
& N' H) I3 n5 {3 V& N/ O - if(w25qxx_qpi_mode)4 V) x% J ]* H e o4 `
- {
+ p+ Z8 K/ Y. K. Z$ X# c - QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_4_LINES); // QPI,写command指令,地址为0,4线传数据_8位地址_无地址_4线传输指令,无空周期,1个字节数据
5 ]7 T: @# W% ?' S - }3 B- m o' t e- ?1 B3 m
- else
! r8 l' |! @" c8 R; e+ V - {
8 m1 h4 _( _- Q! r+ B# s - QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_1_LINE); // SPI,写command指令,地址为0,单线传数据_8位地址_无地址_单线传输指令,无空周期,1个字节数据
5 X' w( u$ f( X - }
* V+ ~, V( k+ j8 z* o3 Z - QSPITransmit(&status, sizeof(status));
2 y( G6 y3 o# o - }
* r) V; o: _; D# x H5 v3 V - ; {; h/ j( @ h. E
- // 写使能 将S1寄存器的WEL置位
) {6 H1 N! R( M, b3 k - static void w25qxx_write_enable(void)
* U( b& G: i4 k: \- | - {3 x/ Q( F1 T t. j+ p) V# [
- if(w25qxx_qpi_mode)
; x4 K* l4 o6 J - {+ @% r1 J' K) s. K+ }, ], ?0 M
- 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个字节数据
! J; I- ?0 _0 v7 }7 j" d* T& N - }5 z8 G. U1 E% u& S3 t
- else
9 r0 U$ h B! X- Q- x1 q - {
3 n- F. o5 F t; M - QSPISendCMD(W25QXX_WRITE_ENABLE, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // SPI,写使能指令,地址为0,无数据_8位地址_无地址_单线传输指令,无空周期,0个字节数据
% u2 T8 Q& j' t. A- w5 f! \ - }2 \% {% F3 O* X* y7 p
- }1 H3 A$ [1 i! Y* F1 Z
3 n: b, U5 R3 N1 O8 ]& l# j5 Y" l+ q- // 写失能 将WEL清零
& P) ^5 }+ d s! x' k( N) d2 z) s0 h0 A - void W25QXX_Write_Disable(void)
0 ?8 M+ Q# J5 B+ c2 q8 F - {7 e. s1 m* i3 F8 v1 \6 F+ g
- if(w25qxx_qpi_mode)" L& @* ]" p. A( a: v" \6 s
- {! f, ?& g# P% O6 K
- 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个字节数据
& k& S' X& B' S* ?( l% R. } - }; ]" ?7 Q+ }2 A' D/ p* H1 _/ `9 Z
- else' j m2 Q# x( J
- {
1 p+ R' M; R5 |' O; T1 u9 F - QSPISendCMD(W25QXX_WRITE_DISABLE, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // SPI,写禁止指令,地址为0,无数据_8位地址_无地址_单线传输指令,无空周期,0个字节数据
. L2 ?5 x. t5 k2 D8 P - }
O2 \/ D2 ^1 O7 `, w - }
4 z9 Q4 U7 |! }' i% {% M) N. ? - 3 J2 F9 R2 [! U7 ^& g
- // 等待空闲$ U. V3 {- y0 K" s) {
- void w25qxx_wait_busy(void)
! C1 q- F$ Q5 r - {- B/ W3 K& r: a' u
- while((w25qxx_read_status(1) & 0x01) == 0x01); // 等待BUSY位清空
{1 i* |" M2 v& U1 @! P - }
1 l3 N% [4 k* [/ u/ v
2 [4 ~$ }# R, F1 F- // W25QXX进入QSPI模式8 i! D6 z5 s5 W
- static void w25qxx_qspi_init(void)
H3 `) {9 C- h; P9 [4 ^$ k - {
- q: H3 n( e' _$ l - uint8_t reg2;
2 ~4 }% [. }! ]8 } - reg2 = w25qxx_read_status(2); // 先读出状态寄存器2的原始值
% B) G5 W; p/ p. z# t6 Q - if((reg2 & 0X02) == 0) // QE位未使能
" l1 ^, E( v( t* G( O# ]; K - {3 O' a5 |/ q6 ]) A2 z% n
- w25qxx_write_enable(); // 写使能
, m) W' r( y; B8 I" C$ z) V - reg2 |= 1 << 1; // 使能QE位
' C% z$ b+ X [+ ` - w25qxx_write_status(2, reg2); // 写状态寄存器2 T! v" x- ` @+ S" o
- }
- e1 a8 v5 h+ _3 `$ \' ` - 0 R& I& A; Z$ V9 H* U9 U: e
- QSPISendCMD(W25QXX_ENTER_QPIMODE, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // 写command指令,地址为0,无数据_8位地址_无地址_单线传输指令,无空周期,0个字节数据, S% H$ ? I% v' }
9 J& x3 j, k) K. l! R- w25qxx_qpi_mode = 1; // 标记QSPI模式 c! J i/ s# l8 }
- }
# d4 g* }: w% V# b3 e8 C
0 B. n1 q: x7 G6 v% y% O) ]- // 0XEF13,表示芯片型号为W25Q80& t0 k- v9 }; p
- // 0XEF14,表示芯片型号为W25Q164 l% G/ N( L* j8 x+ K6 O& O; h
- // 0XEF15,表示芯片型号为W25Q32+ g2 N/ Z# `. B4 Y0 r+ x' w1 i& a
- // 0XEF16,表示芯片型号为W25Q64
3 i( y% i& o+ y - // 0XEF17,表示芯片型号为W25Q128
, g; S) j* y$ G8 w - // 0XEF18,表示芯片型号为W25Q256
( l' e( t) b* s: Y+ T' x- L4 n - static void w25qxx_id_get(void)
* G/ C9 N) X9 @% z - {4 [5 q# }9 d7 s' ^- [
- uint8_t temp[2];
; V9 G0 A# o% T - uint16_t device_id;; a" `& O& h0 @+ R3 y) e
* \% l9 @; `% C# p: S* R- if(w25qxx_qpi_mode)
; `/ J1 f+ o) z' j7 g* X% ~ - {
. \% B U5 D4 c - 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个字节数据
" u0 i- Y; L R - }
9 x+ o' E8 A B - else4 r5 t( K. S j! e9 t8 b- \8 i
- {
! y' G. x5 b, F& g5 j - 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个字节数据
& E6 N+ v1 I" w, p - }
' G9 o: z+ i7 \5 h+ Q0 u7 @$ ] - - h' a6 W/ ^8 \- {1 G$ t
- QSPIReceive(temp, 2);
7 [- l8 M. u: U) P
- h n. @" F- @; j! |: S: s# k- device_id = (temp[0] << 8) | temp[1];
$ u% ]( E0 }- \7 A: A7 K- R - % l0 T+ K7 ]' _: ^
- printf("QSPI Flash Device ID: %X\r\n", device_id);
4 K" P$ b8 E4 V4 U% X - }. d. I7 I: y" w- X O4 D
% a( y" b! Z; S! \' x$ m- void W25QXXInit(void)
, }% [7 r$ o" B, Y+ \. ~ - {
7 q9 Z. k5 t2 J, @& D - uint8_t temp;& z) x8 S( [- [7 c
- ( N) n: D5 q" w) O. T
- QSPIInit();
2 O/ ~+ C# X2 s, z' W; [ - w25qxx_qspi_init(); // 使能QSPI模式
2 C9 P+ `/ w- e4 C4 P) E+ j - w25qxx_id_get(); // 读取FLASH ID.
* e& p+ P: W( Z+ ?4 S
" O+ b- y% v, [- temp = w25qxx_read_status(3); // 读取状态寄存器3,判断地址模式
. Z/ h1 N$ v! h/ U - if((temp & 0X01) == 0) // 如果不是4字节地址模式,则进入4字节地址模式
* Q2 K6 }- A; Q/ u. a1 K- a - {
) D- F4 [8 E+ |( I& K - w25qxx_write_enable(); // 写使能" {% ~8 Q8 O4 h0 ^
- 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个字节数据
$ |6 m5 N3 K- V - }! p9 _. _. b$ v7 _* r
- w25qxx_write_enable(); // 写使能# y6 ~; Y4 s. U+ t# T
- 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个字节数据
8 K& b$ t7 o' n- W! M n - temp = 3 << 4; // 设置P4&P5=11,8个dummy clocks,104M* {" u1 a5 ^- l5 i. G# p7 x- d% @
- QSPITransmit(&temp, 1);; \2 R6 V& w& E: }* s
- }
+ q+ x( f4 j8 \3 m8 h - : T( h, S+ C& K: W6 x
- // 擦除一块4096字节 最少需要150ms% c* \+ K1 H6 t( G, w
- void W25QXXSectorErase(uint32_t addr)
6 W/ p _; w$ B5 f. E - {
7 V. j% O# W8 q, e - //addr /= 4096;' Q" i/ f9 M+ j/ h1 ^6 G+ U$ R- n
- addr *= 4096;
) g" U3 ^ v9 ~( R$ [: X9 E U" C - 1 A* Q/ H) d. g( n/ P' D+ |, \ A
- w25qxx_write_enable();1 T- }. o& o, I# | l* I0 N/ M+ M
- w25qxx_wait_busy();
6 C6 M8 R( f3 `* g - 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个字节数据$ E( S9 O* D {/ }- g2 d: `/ S& u
- w25qxx_wait_busy();
3 p3 N, _ H4 I1 F: l - } J& [, u$ s2 ^; v# W7 z# }
- " a5 F+ z: m/ l+ f
- ! f. e! U2 [& p2 R+ b/ y* T1 \
- // 擦除整个芯片! r6 q- b% R$ M% z% w) [+ u3 d
- void W25QXXChipErase(void)2 P1 A' T; E& |6 H0 A
- {
* w9 c7 ?: {; T) l% A$ P! q9 ] - w25qxx_write_enable(); //SET WEL8 K$ b7 A2 a" s+ f' t3 E
- w25qxx_wait_busy();% Z& ~8 _4 Y3 a+ M- F' m
- 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 L. U- O( x" v$ ]+ b
- w25qxx_wait_busy(); //等待芯片擦除结束! _8 u g5 t& H8 m
- }
5 X* n, G9 }+ j/ F8 u' G- U - 5 b) h4 T5 I `* |
- // 写最多256字节9 T: }/ r( }8 {
- void W25QXXWritePage(uint32_t addr, uint8_t *buffer, uint16_t length)
4 ]5 ?8 s: c" w0 I - {! y. `% q. x$ a* c- A
- w25qxx_write_enable(); //写使能
+ w% i z9 y: q% {. C3 ~ - 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 x7 A. v. {& l& D5 H7 J& y& X
- QSPITransmit(buffer, length);+ c% a+ X2 G1 ^7 B7 N' i* L
- w25qxx_wait_busy(); //等待写入结束
8 @+ n4 e% a( D* ~5 y - }
) N0 e3 ]4 h' ]) s( m9 _
; r, R2 n' I4 Y {1 }0 ]; P0 j- // 写最多65536字节 无擦除动作+ l' k5 h6 q+ D/ l) U, ]
- void W25QXXWriteExt(uint32_t addr, uint8_t *buffer, uint16_t length)# O+ X n9 m6 X- ~ N: C! n
- {
! o2 o5 g5 R3 @ - uint16_t pageremain = 256 - addr % 256; // 单页剩余的字节数/ D y7 x8 n7 ^8 ?
- 4 y9 ?# y2 a& e$ Q
- if(addr + length - 1 > (32 * 1024 * 1024))1 ~3 {* x8 }3 _6 }& j9 y3 R0 Z/ z
- {
3 G) M. s& p& A7 ?* w - return;
! g2 j/ y& R$ d, n7 p9 E0 O; X - }" c3 E8 a! v$ d# `
- # Z( `! r3 j% L& G
- if(length <= pageremain)4 k& k/ J* { i
- {
+ l/ i F% I: Z8 \0 P - pageremain = length; // 不大于256个字节8 t! ^! U( a; {5 Y; ^6 h& D5 b
- }% u# ^) c6 [% a4 K" D$ C
- while(1)2 v3 N4 U: T0 K. N2 X9 t' V5 N
- {" W/ _. k& v3 X7 s
- // 分页写入0 |, y$ s$ E9 d% T2 Q+ y
- W25QXXWritePage(addr, buffer, pageremain);
4 G2 q4 a* W; m - + _! A- o" M( g" o2 U5 x9 r! ?
- if(length == pageremain)
- i4 b! Z7 b$ j3 E+ N+ X+ X; d - {
: h# b) k- O* c0 z2 C% g/ b - break; //写入结束了* K# \# P& Q7 p! a8 P( M
- }
; D* ?+ r! {+ `8 x - else* v$ c% i; {" |, T7 b Y7 U4 C2 u( @
- {
& X* M, q/ a4 F9 d3 ]+ u3 O. w - buffer += pageremain;! r8 g7 T% Y6 m
- addr += pageremain;
* k, b" ]8 |% [ - 4 E' Z7 l# b3 E- o# P! c5 W
- length -= pageremain; // 减去已经写入了的字节数. I; M& ^5 T, v% |
- if(length > 256)
; n, Y& [+ |! y5 ]5 c4 ` - {
- B g! Q$ I" B2 f. R3 e( Y - pageremain = 256; // 一次可以写入256个字节& z* n/ {2 W2 u# U
- }
* h1 m% a5 ]" S4 A" z/ A5 E; D - else2 P3 J' h4 u, I4 L+ M; Y! ^3 K
- {: m. `! H2 k$ D0 D {, @: i
- pageremain = length; // 不够256个字节了
& C2 F) T' [# U& y" P - }
" K: @+ ^' P) \% k3 e8 _, W - }
L9 I$ r4 k5 {3 F: g* g4 {: { W - }
0 S* }4 d( A; K6 n9 i5 F - }
" l: d- l5 l/ m" d' E$ N
6 s) c& w5 g) v: o- // 写最多65536字节 带擦除动作
7 p! L/ d1 q I: G, s6 t - static uint8_t W25QXX_BUFFER[4096];* K1 }% A' L6 q; J" E( I" ?
- void W25QXXWrite(uint32_t addr, uint8_t* buffer, uint16_t length)9 x% e' Y: h' H0 E0 {6 `
- {0 D% R% z$ z1 P8 W, I3 q
- uint32_t secpos;
: `' W' s8 J( o& k' P* V1 ? - uint16_t secoff, secremain, i;3 ^; j9 M4 e. f! L& j
- uint8_t * w25q_buf;
8 J7 A: D' c- w8 k. ?# q9 V4 o5 g - : X% T& j2 S" r' s2 U% x
- w25q_buf = W25QXX_BUFFER;/ j( y. [( ]4 s1 z( i. D
- ! |: T7 Y( W7 m/ U% ^) n# j
- secpos = addr / 4096; // 扇区地址 @$ ~. f+ y! O; h2 W, [
- secoff = addr % 4096; // 在扇区内的偏移( K$ A( m. Z4 L- O
- secremain = 4096 - secoff; // 扇区剩余空间大小( ? a( ^5 j# X( k
- " v. p) j2 M' F. t' ^4 M
- if(length <= secremain)! h: b7 z% @& h5 ~; F0 y2 I# g
- {6 O, j6 q+ a& j, x, U9 d3 P" k' e7 p
- secremain = length; // 不大于4096个字节" S, t$ s2 v W$ y$ v
- }
# i6 p5 P9 O0 d/ x& q - while(1)5 Z( \; X6 c! Y* e) v# f/ S; o
- {7 u1 @4 \. q7 ^2 w
- WatchdogFeed();& T5 c" g. [5 K: X
- W25QXXRead(secpos * 4096, w25q_buf, 4096); // 读出整个扇区的内容
- w# _& `, X q# u, C - for(i = 0; i < secremain; ++i)- g4 d7 M5 e5 h7 q) ^8 ~6 _
- {+ H. |$ G% Y& b2 ^: E) ^; U
- if(w25q_buf[secoff + i] != 0XFF)
% F- y0 A9 v" @ - {
4 }" |! @, G$ ^- X7 t7 s - break; // 需要擦除1 S. W8 T$ G" ^( e* G
- }* } y- T9 V" }' H
- }) J' P; x( ^2 z% J9 @
- if(i < secremain) // 需要擦除
2 A! n+ }- S' n3 A# w* `$ n# G - {; s) P4 C3 e; w6 j
- W25QXXSectorErase(secpos); // 擦除这个扇区6 U' r" K# a0 |
- for(i = 0; i < secremain; ++i)7 X4 A* V8 N8 I1 @, T& [
- {
# B9 f& F6 `8 v. o - w25q_buf[i + secoff] = buffer<i>;</i>1 W! S4 l3 p* c$ ]1 ?& X
- }1 D! R' G+ X, j% T
- W25QXXWriteExt(secpos * 4096, w25q_buf, 4096); // 写入整个扇区/ S$ L8 a. c4 e: n. p
- 9 S+ K; H$ Z- q g% V" o4 i$ N
- }
$ A, D y$ O6 i7 B1 w5 V/ b; Y - else
0 a! e- `, V+ L2 H- ^% s7 ], d) r- V3 j - {
5 m' i* }. g6 I3 x( ^ - W25QXXWriteExt(addr, buffer, secremain); // 写已经擦除了的,直接写入扇区剩余区间.4 c% E% P/ P6 |; j" Y$ g( v) I u. I P
- }" J6 e, H- K7 p/ Z/ [+ W
- if(length == secremain)
9 B; ]* G1 S/ s, |+ k7 o( C - {2 F! J2 b9 n4 _4 j( W
- break;
( P% D" |8 e. w6 i4 o - }7 ~: ]" q) D. Z5 h6 |4 o4 K
- else
8 S3 |/ \: p' i& M - {
& }' @6 [& D* _( z' L3 { - secpos++;% b5 M! ?) M& P3 g( ^
- secoff = 0;
3 i" d+ _, k G! ?
$ h1 e! l0 x `# G$ o- buffer += secremain;
; k; C' G9 y4 h: T8 ~% t, K, i( c* l: C - addr += secremain;
; P1 f5 M0 v/ @" X. F | - length -= secremain;; e* J. U9 e; [" R$ K- r
- if(length > 4096)
9 X( I2 P! X: \; M - {
# A2 X3 w/ W( I; i; m - secremain = 4096;+ H/ ~6 H" a7 Q7 E- h" V
- }
0 i8 h# S8 n( v5 h$ ^( t7 D - else+ [; ^7 f/ r% I/ [9 ?
- {
, X; U# K- p+ b! i: U. Q - secremain = length;: h' u5 J3 V$ ~5 Y
- }
, A I! |6 V3 [9 b9 u - }
+ U) j5 w" Y7 b' B( q% _9 T - };
8 n9 H" g: W* F' F! m* m - }
& l$ K; t' ?( c) C4 y
+ B: g# Y0 H1 L5 i8 D- ; k' P* u2 j/ v: F5 Y* z
- // 读取SPI FLASH,仅支持QPI模式 在指定地址开始读取指定长度的数据 65536
$ w- @$ P$ J- Y# V; P X9 B - void W25QXXRead(uint32_t addr, uint8_t *buffer, uint16_t length)8 C0 x/ _$ ]& M/ F/ N
- {
5 Q& @, ]( x+ g; }5 r3 O - 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个数据
7 g; W% e% J7 {5 {& w - QSPIReceive(buffer, length);7 {5 _; w$ G& t! ~( f& }1 ?
- }
复制代码
! Y% @: k' a( @, D% l也可以使用SPI操作W25QXX。( C& a1 x, b- l
0 x$ P* X. }. i
- ~; i2 k. R* V" Z& k2 u2 _3 a9 E/ }
|