一、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
! 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- HAL_StatusTypeDef HAL_QSPI_Init (QSPI_HandleTypeDef *hqspi); // 初始化
, Z1 G* n6 g1 u$ t: l! w( ?
5 |0 \- m& h& c% o- HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout); // 发送命令4 G' z; q9 C/ O" n! Z
- 6 K' O* D9 _6 u7 I5 m9 u
- HAL_StatusTypeDef HAL_QSPI_Transmit (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 发送数据
; X2 K5 n- R8 v - 5 D6 Z% y( Y% _* ]
- HAL_StatusTypeDef HAL_QSPI_Receive (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 接收数据
复制代码 0 Y% B6 J4 ]6 S
三、几个重要的结构
$ ]$ w& `: d. W3 o- // QSPI操作句柄
' @& j: A% Z; |" C! o - typedef struct
+ B6 L% [' A$ C4 T G. x' p6 z - {9 i, S( X3 N! o' f& E3 h
- QUADSPI_TypeDef *Instance; /* QSPI registers base address *// T0 b9 H, Y; E) S" @% |2 y
- QSPI_InitTypeDef Init; /* QSPI communication parameters */' S4 V6 O* p# g( o
- uint8_t *pTxBuffPtr; /* Pointer to QSPI Tx transfer Buffer */
, Y" F9 P8 x1 K8 K! d - __IO uint16_t TxXferSize; /* QSPI Tx Transfer size */
% T. Z. r) k4 |! U - __IO uint16_t TxXferCount; /* QSPI Tx Transfer Counter *// z% s2 x( D; N {
- uint8_t *pRxBuffPtr; /* Pointer to QSPI Rx transfer Buffer */
& ~0 I5 M& }0 e) B& l% ?7 Q - __IO uint16_t RxXferSize; /* QSPI Rx Transfer size */: c' b- ?8 I: Y4 j2 D
- __IO uint16_t RxXferCount; /* QSPI Rx Transfer Counter */
- L0 w$ l- g9 \) j3 K( O) }# l - DMA_HandleTypeDef *hdma; /* QSPI Rx/Tx DMA Handle parameters */
, j3 k# T9 r% T# M ` - __IO HAL_LockTypeDef Lock; /* Locking object */
" M- y, E% ^1 j5 h4 K, w - __IO HAL_QSPI_StateTypeDef State; /* QSPI communication state */& b6 E! y6 [& A1 ^6 t
- __IO uint32_t ErrorCode; /* QSPI Error code */1 s g# \9 ^6 u" E* \
- uint32_t Timeout; /* Timeout for the QSPI memory access */
4 E6 ~4 w# A; d" L - }QSPI_HandleTypeDef;
o6 j g1 k& K! }* u9 M3 @! x - ' P2 m$ S2 D& w( o
- // Instance:QSPI基地址 --- QUADSPI
8 T3 I2 T9 G1 M( R6 j' M$ q - // Init:设置QSPI参数
0 i% ~* f' \% f6 f+ w6 o- u0 K - // pTxBuffPtr,TxXferSize,TxXferCount:发送缓存指针 发送数据量 剩余数据量
: C1 \; j, h, w! [9 l7 w1 j6 F. B/ M7 E - // pRxBuffPtr,RxXferSize,RxXferCount:接收缓存指针 发送数据量 剩余数据量( B2 V; [! x' l% P. O" A: B' M! t
- // hdma与DMA相关, 其他为过程变量不需要关心
复制代码- // 参数配置 时钟分频系数 FIFO阈值 采样移位 FLASH大小 片选高电平时间 时钟模式 闪存ID 双闪存模式设置: `5 a( Y- m6 g0 Y8 v& }) K
- typedef struct! F) d0 N% Z) ?, H3 _! @
- {8 S o! Z0 A1 S- r+ `+ v
- uint32_t ClockPrescaler; /* Specifies the prescaler factor for generating clock based on the AHB clock.. C( W6 z% F8 s4 i
- This parameter can be a number between 0 and 255 */ ' ?' ?/ }, w# p: ^2 I
- / w$ w3 _& B0 g' L$ z
- 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
- This parameter can be a value between 1 and 32 */- ^* y- ?# X3 A. Q' _0 a W V% ~
- 2 d0 z) \9 \! ]& T: S8 Q
- uint32_t SampleShifting; /* Specifies the Sample Shift. The data is sampled 1/2 clock cycle delay later to
- Q- i' ~( h9 d - take in account external signal delays. (It should be QSPI_SAMPLE_SHIFTING_NONE in DDR mode)& v# L) p$ N8 _
- This parameter can be a value of @ref QSPI_SampleShifting *// \* Z0 n" ^5 t7 v7 R8 l) P
- ( }+ ]2 p. I; \) J
- uint32_t FlashSize; /* Specifies the Flash Size. FlashSize+1 is effectively the number of address bits
; x9 Z; ]6 i( M* W6 Y, c6 [ - required to address the flash memory. The flash capacity can be up to 4GB 2 O) h ?! y) |: [& H- t
- (addressed using 32 bits) in indirect mode, but the addressable space in
1 H& y$ P9 Z7 ?4 i6 Z) x - memory-mapped mode is limited to 256MB' ^' C; s& J! v4 i
- This parameter can be a number between 0 and 31 */
) P. s' I/ }9 F4 N8 q
9 B. t7 ^; c% I3 b- uint32_t ChipSelectHighTime; /* Specifies the Chip Select High Time. ChipSelectHighTime+1 defines the minimum number 8 h- b5 Z3 [, F8 t
- of clock cycles which the chip select must remain high between commands.
3 L) H% C- o3 R j - This parameter can be a value of @ref QSPI_ChipSelectHighTime */
, {3 U1 ?6 |3 a ]# n4 Y9 |( v9 @ - " @9 p- k& j2 k D; T; S' {
- uint32_t ClockMode; /* Specifies the Clock Mode. It indicates the level that clock takes between commands.3 W" `* W. H6 u' e2 U
- This parameter can be a value of @ref QSPI_ClockMode */
* c( I# b5 I! }% X7 z `
9 l8 y x( a, c& ^9 f- uint32_t FlashID; /* Specifies the Flash which will be used,1 w6 a6 ~! m1 Q1 X: D
- This parameter can be a value of @ref QSPI_Flash_Select */* D2 e$ P4 F) T; o: M) K
2 Q2 o2 b5 E" r. S0 [$ A8 O- uint32_t DualFlash; /* Specifies the Dual Flash Mode State N) Y' T# A" [0 W! E l
- This parameter can be a value of @ref QSPI_DualFlash_Mode */
/ z* Y: @2 z L/ X3 ~2 z4 e3 s - }QSPI_InitTypeDef;
复制代码- // 采样移位9 Y2 O8 N3 H$ U
- #define QSPI_SAMPLE_SHIFTING_NONE ((uint32_t)0x00000000U) /*!<No clock cycle shift to sample data*/1 b% W; I* O, D8 U# ^
- #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
- #define QSPI_CLK_ENABLE() __HAL_RCC_QSPI_CLK_ENABLE(); `# s2 Z7 Y. N6 g; x8 G
- 0 {# I0 L0 ^5 P. H
- #define QSPI_BK1_NCS_PORT GPIOB
! R' m/ t8 R1 w& U0 N - #define QSPI_BK1_NCS_PIN GPIO_PIN_6' [5 e; d* T2 W1 @% ^/ R0 Y( \
- #define QSPI_BK1_NCS_AF GPIO_AF10_QUADSPI
2 q0 s8 t' \0 p, s6 T# _ - #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
+ O! L _9 `8 \( ~6 t$ i$ M- #define QSPI_BK1_CLK_PORT GPIOB
- o- y2 W: U; g& i - #define QSPI_BK1_CLK_PIN GPIO_PIN_2
7 g/ S: f( Y4 W - #define QSPI_BK1_CLK_AF GPIO_AF9_QUADSPI
4 {, \% r# F9 ^% @) b - #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
- j# {# d) J- g7 s: ~- #define QSPI_BK1_IO0_PORT GPIOF- L; y+ I# K3 o
- #define QSPI_BK1_IO0_PIN GPIO_PIN_88 i! s; `, M# J$ v
- #define QSPI_BK1_IO0_AF GPIO_AF10_QUADSPI$ c- R$ b0 {% l
- #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
# w a* Q* ? a- h- #define QSPI_BK1_IO1_PORT GPIOF
9 ]0 n) u: Y c8 } d. R - #define QSPI_BK1_IO1_PIN GPIO_PIN_98 A J+ c# i# Y: H: Q
- #define QSPI_BK1_IO1_AF GPIO_AF10_QUADSPI2 \0 U% R; A' `3 D6 y6 C% p
- #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 - 3 @+ s2 C( M5 G! K, V0 O
- #define QSPI_BK1_IO2_PORT GPIOF
/ u1 p) ~9 `" ? - #define QSPI_BK1_IO2_PIN GPIO_PIN_7 J" c6 L" f F% T+ J# j7 L6 b. \
- #define QSPI_BK1_IO2_AF GPIO_AF9_QUADSPI
9 B! T% h9 w+ R( v4 J( D2 ^ - #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
2 m2 X% y3 X! S1 p# G- #define QSPI_BK1_IO3_PORT GPIOF( {( g6 M' J, F0 R- W
- #define QSPI_BK1_IO3_PIN GPIO_PIN_6
* ~7 P1 M( A+ j$ S1 ~& _ - #define QSPI_BK1_IO3_AF GPIO_AF9_QUADSPI, \! s F: ?2 A
- #define QSPI_BK1_IO3_CONFIG() GPIOConfigExt(QSPI_BK1_IO3_PORT, QSPI_BK1_IO3_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_IO3_AF)
复制代码- // 封装几个必要的接口
8 O% h2 K9 G+ `% `9 n+ c - static QSPI_HandleTypeDef qspi_handle;* c- U6 w: \2 q2 F. t# J
- , d9 D2 w8 p; l
- static void qspi_gpio_init(void)
/ l! T0 E% g7 D - {8 j/ [) I$ V3 o9 B U1 O
- QSPI_CLK_ENABLE();
3 e% {0 } r3 y/ o0 {% A% r
* @- ^0 j$ w2 l- QSPI_BK1_NCS_CONFIG();
0 M( Z! W' E4 K0 j. l - QSPI_BK1_CLK_CONFIG();
1 ]2 o3 S3 j# a) u5 q - QSPI_BK1_IO0_CONFIG();4 I8 U, R+ d Y2 l- I1 m
- QSPI_BK1_IO1_CONFIG();
/ \1 W9 d/ S1 E4 y - QSPI_BK1_IO2_CONFIG();
9 ~# A) W6 c# N# B) U' _; |1 D; e8 { - QSPI_BK1_IO3_CONFIG();/ F, p; }$ o, L' j8 v$ Z* ~4 r' s
- }0 K( ~8 o9 O; s2 G- p5 C
- + f; a: {% w" b- `1 ?7 N! _
- static void qspi_mode_init(void)* S0 Q4 _0 z2 c$ B, W+ I' }$ x" w: s
- {5 u6 G! d# r$ Y) o6 _! T- ]
- qspi_handle.Instance = QUADSPI; // QSPI
1 P0 I, z8 l% I7 e8 K: k0 n9 S3 S3 q% F - qspi_handle.Init.ClockPrescaler = 2; // QPSI分频比,W25Q256最大频率为104M 所以此处应该为2,QSPI频率就为216/(2+1)=72MHZ) z& n# E- N- J( ~# G1 t
- qspi_handle.Init.FifoThreshold = 4; // FIFO阈值为4个字节
/ E1 {& A5 u3 O2 e W - qspi_handle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; // 采样移位半个周期(DDR模式下,必须设置为0)
: r% B5 T0 l/ h - qspi_handle.Init.FlashSize = POSITION_VAL(0X2000000) - 1; // SPI FLASH大小,W25Q256大小为32M字节! J$ Z$ f$ ^4 T+ t# }8 x1 ] Q
- 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
- qspi_handle.Init.ClockMode = QSPI_CLOCK_MODE_0; // 模式0: c+ f4 |3 T" q- V1 D9 Z7 S2 h" i
- qspi_handle.Init.FlashID = QSPI_FLASH_ID_1; // 第一片flash
: m3 R* O U8 z! g( S: [ - qspi_handle.Init.DualFlash = QSPI_DUALFLASH_DISABLE; // 禁止双闪存模式/ W, E& n$ f Z+ E1 D. j
- HAL_QSPI_Init(&qspi_handle); //QSPI初始化3 _& U6 G5 h0 f
- }
. a2 H7 a8 k5 G* x E, I - : l+ Y& M2 {# U- k ]3 I6 k
- void QSPIInit(void)" U4 O- _, P7 W' x. }* v
- {
3 j) m! r$ t9 z - qspi_gpio_init();
5 w& h9 r; ^. ^ - qspi_mode_init();
- _* V" Y! e0 y: W/ g4 I - }) ^% n% X, d9 w" k2 l
- / v: k7 \" y# l" B. A
- // QSPI发送命令
. i6 H# t$ c( A: n - // instruction:要发送的指令
- j; _2 @3 I- v+ K. l; l0 k V! B - // address:发送到的目的地址4 e" t2 _# A( `; s; J8 T, X
- // dummyCycles:空指令周期数0 l7 h5 g( Q9 Y! }2 B+ z% Q
- // instructionMode:指令模式;QSPI_INSTRUCTION_NONE,QSPI_INSTRUCTION_1_LINE,QSPI_INSTRUCTION_2_LINE,QSPI_INSTRUCTION_4_LINE
6 B, H' B# j$ c7 b) U5 ~ - // 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
- // 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
- // 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 @ - 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
- {
. |* f' v/ i' ]2 o - QSPI_CommandTypeDef Cmdhandler;7 U( W$ K; `8 ~3 Y9 p
- - x! }; W# l# s) e
- Cmdhandler.Instruction = instruction; // 指令1 A* N' I, O1 C8 q! x. C
- Cmdhandler.Address = address; // 地址
& U' m9 C) J& p4 s. Q% w - Cmdhandler.DummyCycles = dummyCycles; // 设置空指令周期数
3 o/ i8 d" g; y( P - Cmdhandler.InstructionMode = instructionMode; // 指令模式6 ~2 b! b/ m) q/ i3 v$ p4 b8 f7 v
- Cmdhandler.AddressMode = addressMode; // 地址模式
/ ?8 n1 J. E7 X+ @+ X1 E - Cmdhandler.AddressSize = addressSize; // 地址长度
9 Y8 n1 ^9 e3 c- U - Cmdhandler.DataMode = dataMode; // 数据模式
1 U7 V% D: o7 C$ F. s; q - Cmdhandler.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 每次都发送指令9 e( q* \1 s; C- r1 k
- Cmdhandler.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 无交替字节
' ?2 X, x7 |/ c5 G6 R, `/ Z - Cmdhandler.DdrMode = QSPI_DDR_MODE_DISABLE; // 关闭DDR模式" ]$ c( X! P* s/ G
- Cmdhandler.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
) K( {/ u: R: Z. M q
) Z* m8 w1 Q7 Q' d- HAL_QSPI_Command(&qspi_handle, &Cmdhandler, 5000);
- i4 \: Y* b( d1 j9 P - }
5 u: o" r5 E. _6 `: \
; I3 G* T8 e6 u7 w9 f" T7 C6 |- // 接收指定长度数据
$ ^8 ]) y+ M& e" B - uint8_t QSPIReceive(uint8_t *buffer, uint32_t length)
2 r1 ~: V/ B- y. ]8 Q - {
4 d! N. ^9 z: c7 A! G! z" ~ - qspi_handle.Instance->DLR = length - 1;0 s" y) L* m: }! z; D( S- j1 o% n: F
- , f- g3 `5 z8 X, d4 \
- if(HAL_QSPI_Receive(&qspi_handle, buffer, 5000) == HAL_OK)* f* A3 u3 M% r+ \
- {2 B+ ?" ]- _6 n @ Y0 N
- return 0; //接收数据+ F) z9 a5 m+ N$ d( G6 H
- }
, s6 c ~& V$ H/ d - else
: \( C7 Y+ g5 ?6 {9 r - {' I. t0 R* L3 ?0 z- U# \1 a. I
- return 1;
/ ?$ I6 [, o: t1 [0 L - }: W' h( N' p8 y1 k( o
- }
z, v7 T1 |) r) v2 Y4 j1 m' X5 Y
$ ]5 p) Q: `+ N- // 发送指定长度数据& P3 j' X" w0 i% S3 u; e# G7 }
- uint8_t QSPITransmit(uint8_t *buffer, uint32_t length); S/ L+ }+ s/ |% B* ]2 x; d8 q
- {
: ~* N/ w4 U0 N8 `2 ^, F) t2 _ - qspi_handle.Instance->DLR = length - 1;
, \ L$ E, Y5 f* D5 y - if(HAL_QSPI_Transmit(&qspi_handle, buffer, 5000) == HAL_OK)
% `9 Z6 {3 g" u9 `( S: A; t - {
: w6 s: Q3 p' I) S - return 0; //发送数据% p& f& q. S3 U( [( J$ B u( Z8 v! B
- }) {! W3 V& o6 H8 t
- else2 H6 G2 J; z t/ N' P
- {1 M( e9 c9 X) C) V9 N2 D4 B. F
- return 1;
) K' Z( ?0 G( D! b* R7 a# K1 J0 @ - }& O _3 l0 [3 f4 B; ` E9 ]# a) [
- }
复制代码 % 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- #define W25QXX_WRITE_ENABLE 0x06
; `: d3 G: S5 w* B - #define W25QXX_WRITE_DISABLE 0x043 R! p* i& Q( x6 V3 m! T* | o
- #define W25QXX_READ_STATUS_REG1 0x05
0 C+ I+ K0 R* v7 Q" l- i - #define W25QXX_READ_STATUS_REG2 0x35
6 D8 W- p- `- b+ K2 M4 H2 a7 p: @& N - #define W25QXX_READ_STATUS_REG3 0x15" p! \! E% K# U. r& d. l
- #define W25QXX_WRITE_STATUS_REG1 0x01
1 K0 a* S4 _. S, E2 L! D - #define W25QXX_WRITE_STATUS_REG2 0x31
# v) a' a7 B. ]# }. W - #define W25QXX_WRITE_STATUS_REG3 0x11: u0 C: }, H& Q: w) [
- #define W25QXX_READ_DATA 0x03
8 B, L0 x( j( S+ z3 j, f( O! k - #define W25QXX_FAST_READ_DATA 0x0B
% n, k+ {( \' s6 L! q8 V$ G - #define W25QXX_FAST_READ_DUAL 0x3B
N' R2 N# z! H/ R9 \, N7 I7 Z) m - #define W25QXX_PAGE_PROGRAM 0x02. e8 V* G6 a6 H$ w) c
- #define W25QXX_BLOCK_ERASE 0xD8
* u1 G7 c3 F* T4 z2 H+ w - #define W25QXX_SECTOR_ERASE 0x20
5 T) M/ y1 ]1 w( J - #define W25QXX_CHIP_ERASE 0xC77 j1 N* C, r( w ?; p) u
- #define W25QXX_DEVICEID 0xAB
: Q7 N% X" I$ b/ D" W$ o - #define W25QXX_MANUFACT_DEVICEID 0x90' Y7 M4 S" f" `4 O4 w7 E# L5 T/ v
- #define W25QXX_JEDEC_DEVICEID 0x9F
2 m' w1 S) [6 K0 G0 b" L - #define W25QXX_EABLE_4BYTE_ADDR 0xB7
! q9 |) }' Q& P; }5 N& {! a3 i - #define W25QXX_EXIT_4BYTE_ADDR 0xE9
+ T9 Y0 Y3 h `6 c' N: ]% ^& x - #define W25QXX_SET_READ_PARAM 0xC0
9 H3 {3 W* Y/ O$ R - #define W25QXX_ENTER_QPIMODE 0x38
9 Q1 H% n- p* L) y8 c2 _6 W - #define W25QXX_EXIT_QPIMODE 0xFF8 }8 c8 |! g3 Q, N) J: S$ ]% V, t
- ) H) {4 }& h: O* r
- uint8_t w25qxx_qpi_mode = 0; // QSPI模式标志:0,SPI模式;1,QPI模式.& Z6 H* P5 N3 Y5 x" C4 h) @
: q4 e* ?/ Z9 X) j- // 读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器/ Q: q$ {/ {& ]9 F, b4 y; p
- // 状态寄存器1:' x* f: ]7 O, W7 V
- // BIT7 6 5 4 3 2 1 0
! k7 z; ]* x+ g+ j0 f2 W - // SPR RV TB BP2 BP1 BP0 WEL BUSY' s9 D7 `# [$ P7 _: h4 Z
- // SPR:默认0,状态寄存器保护位,配合WP使用
" Q) Z, Y) v: j8 B - // TB,BP2,BP1,BP0:FLASH区域写保护设置8 V5 G [$ T$ \7 C/ m" v4 @6 m+ m
- // WEL:写使能锁定
# E' B# D# s; m - // BUSY:忙标记位(1,忙;0,空闲)9 b5 m/ z4 X# P
- // 默认:0x00" ^8 N2 Z7 v; e2 b( d
- // 状态寄存器2:
% W7 Q8 p4 R6 z8 ` - // BIT7 6 5 4 3 2 1 0 ?/ l- s( s& X! l
- // SUS CMP LB3 LB2 LB1 (R) QE SRP1
2 _3 b, E# B3 ~# c( y2 w4 ~! W3 o8 T - // 状态寄存器3: R" d( L& }, l. D+ T, B% {
- // BIT7 6 5 4 3 2 1 02 B1 d+ }7 ~* x# f/ }
- // HOLD/RST DRV1 DRV0 (R) (R) WPS ADP ADS- T) [) C. U1 O/ t* J
- // reg:状态寄存器号,范:1~3
l! z# \4 h$ b) w! X; K" G - // 返回值:状态寄存器值5 F- j9 K6 h0 A: {3 u) Z
- static uint8_t w25qxx_read_status(uint8_t reg)6 r8 P0 V; L; C3 G: U
- {! ?; n3 l. Y2 ~7 @1 E& ]; |
- uint8_t value = 0, command = 0;
9 P, O D! M& F, l7 `- d1 y - $ O! S* Q! ~3 @1 M3 D7 g
- switch(reg)
" j+ O0 T- `4 C: v - {
+ p/ |& r7 L7 M6 K - case 1:
4 ~8 G% C6 U! C, R - command = W25QXX_READ_STATUS_REG1; // 读状态寄存器1指令
; @/ T/ \9 F. R8 L! S9 y7 H - break;
: J, j& n8 M8 ?
8 r7 K' l% h' \5 W9 i/ X2 x' K- case 2:( M4 p, d$ O# v% {% _; x
- command = W25QXX_READ_STATUS_REG2; // 读状态寄存器2指令
# ?2 A D. p# D$ t' c - break;" m. A! H2 \& c- Q
- s+ R: `8 }3 |& y8 x
- case 3:, _$ R+ j5 I2 r. w! y
- command = W25QXX_READ_STATUS_REG3; // 读状态寄存器3指令2 L2 j3 ^: y1 ]! c- i
- break;
5 d8 _7 j. v% G9 ~8 `3 P
w2 i; B/ E" B$ y; M7 x* C; L! u- default:
. f* z# h: y9 D w3 z2 U: X& L - command = W25QXX_READ_STATUS_REG1;0 i3 y: B9 s# m+ J/ ]* `" @4 D
- break;4 h$ s, E' v$ y9 `- c& G! |
- }
9 Q* h+ i5 ^: ^, P. K3 y - 6 e( y8 F8 ?3 J# Q% ]- m+ K8 \/ |
- if(w25qxx_qpi_mode); A% P( Q8 \0 h7 q# y' |
- {- n" m6 X: N$ l' ~ Q
- 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
- }
6 ?! O9 V- ?9 ]! A4 X5 N6 J; n - else
/ \) Q5 t* U* n, E - {) N5 ?' n9 c A7 m0 R% C
- 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
- }
- l+ h4 |1 t7 ^( I' b5 c3 A3 g - 7 Z/ `) q7 U& {8 c$ N/ L0 H
- QSPIReceive(&value, 1);& J' Y7 Z0 \2 O5 e/ |/ }
- - s& h" b2 l* `7 L! i
- return value;' u& |4 u j9 e4 K5 g' x
- }8 r9 D( E& P' s5 z' T. ^8 [* j
5 A% z, U" d- K6 [- // 写状态寄存器
% _3 {- U* O5 h9 _; U - static void w25qxx_write_status(uint8_t reg, uint8_t status)3 X1 n1 p7 o+ d2 O% [9 O& \
- {8 N$ V: E7 R1 o+ r G: `" A
- uint8_t command = 0;
* K: K3 R6 I/ m: {
( W! p" Q) p; i- switch(reg)
) ^4 p$ p1 n/ j- h& T) ?6 K. |; | - {
/ \1 G+ ]7 N* G; [$ k! z - case 1:
* s# _. ]' C0 ~+ B% f - command = W25QXX_WRITE_STATUS_REG1; //写状态寄存器1指令0 p' ?5 x- A0 G7 E) t7 T
- break;
" K! ^( ]! S/ ]$ u( u$ j; T - case 2:8 N% W0 A3 O) A' x% } ?" X, ~
- command = W25QXX_WRITE_STATUS_REG2; //写状态寄存器2指令
# ]6 z6 F% T9 ]) Y1 h4 @( \% J$ R - break;+ ~1 G6 O4 }! D( M
- case 3:! r I& j; }' [% S
- command = W25QXX_WRITE_STATUS_REG3; //写状态寄存器3指令
% W) p! P' A; `3 L* w0 {8 K - break;, n" Z3 F1 ~7 j" {; T
- default:
5 }5 A) t5 ?9 {6 f3 ^# A! k+ @ - command = W25QXX_WRITE_STATUS_REG1;3 `& r$ `, @$ j0 \1 O
- break;: c% Z0 V$ v7 o3 ]4 g+ \9 s
- }
. a8 i1 @1 e; T7 _# J& c- a! I - if(w25qxx_qpi_mode)( s8 x, m+ ~) X: f% C
- {$ V: |; y9 U7 b0 @: T% E
- 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
- }1 O0 l* r4 t. v7 ]- Q" `
- else
/ ^3 j5 n; S4 n# J2 y - { s. y: }/ m+ N4 J" D5 m" `/ A
- 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 - }% D5 l+ e8 z' X: u
- QSPITransmit(&status, sizeof(status));0 _! G# w' Q) d H3 P7 k8 |
- }
; F X! l3 s3 S; D; z ^
: ~1 E& J8 L' ^1 K. e# v; E- // 写使能 将S1寄存器的WEL置位
$ X" G0 a4 F4 P0 A2 C1 ~ - static void w25qxx_write_enable(void)
s4 v: f# \: {" x$ C! q4 a" W0 | - {
2 D; z& `. A2 S/ c - if(w25qxx_qpi_mode)
4 U8 w% h7 H8 {9 }7 M# T6 K - {
7 {4 O) a/ {. E9 i - 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- |) `
- }7 g2 n3 r- t; B$ B% S1 l
- else2 o0 W, t: H7 ?# W' X- n
- {8 o& P$ ^% [$ I9 S5 E( a. s) J' R) i9 r
- 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
- }
! a K% ?& o9 g - }
# {. t- {2 R* `+ I - 5 D: S" M1 O: g+ u% M& ]
- // 写失能 将WEL清零
* {+ v/ l3 x. Q' b1 M# T, K& d4 m - void W25QXX_Write_Disable(void); t- [/ g& d" c; |2 c: p/ T6 x5 W
- {
; P- l$ l* z! ] - if(w25qxx_qpi_mode)& g- P1 n' O6 S0 ]5 f
- {
: b- f J1 b* v; S - 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 - }$ N& [: {3 B, j& t6 `
- else
/ G$ [. b* T) G) o# N1 t7 j8 S& N - {
; m& S" u, G4 a/ X: { - 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 - }
' |& |, {& o0 T/ {9 R - }5 `2 Y4 W# d4 q0 L
1 |! f8 d% T: `- j. o/ H- // 等待空闲/ b% I* B3 y) Q! D
- void w25qxx_wait_busy(void): s, @; [. ^- E! e. S
- {0 q, i: ?. u7 ]! D6 T% }) @, P. l
- while((w25qxx_read_status(1) & 0x01) == 0x01); // 等待BUSY位清空
% s8 L3 ^; w- c - }
6 [) p# G& X( C# M' v - / w6 u! v: r- m2 C) m) x
- // W25QXX进入QSPI模式
8 H7 Y0 j+ ]" N7 A9 _ - static void w25qxx_qspi_init(void)
5 g0 H/ t& N( h7 s7 {# {7 ^+ Z - {
2 {+ |0 |; y: f' t$ i* Q" x2 U# a5 h - uint8_t reg2;1 a5 n' t# Z) t" J' G" v
- reg2 = w25qxx_read_status(2); // 先读出状态寄存器2的原始值2 p" o* F- h* H" X2 n$ b# p
- if((reg2 & 0X02) == 0) // QE位未使能: R, g+ A' w/ m5 J# l9 K, |% d' F8 u
- {
7 Z( D, H& w+ } c - w25qxx_write_enable(); // 写使能# j- O" A) p8 R
- reg2 |= 1 << 1; // 使能QE位
2 ?( C* d1 K6 y* C- v+ B - w25qxx_write_status(2, reg2); // 写状态寄存器2
. k4 ^2 A, C1 E6 @8 u4 x - }
) |3 h+ Y% W$ R& ^' w5 n - 3 v/ J% D* \- M! |) X' ?5 H
- 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
- K" w) J! i9 G( w5 M; Q9 P- w25qxx_qpi_mode = 1; // 标记QSPI模式( P0 S7 I! r3 L
- }
* ~# T6 C. Z$ n2 }+ z
. ^" N) G" K- x1 l2 p1 h+ ~- // 0XEF13,表示芯片型号为W25Q80 l1 D& i; m: T
- // 0XEF14,表示芯片型号为W25Q16, ^6 T- ~: z2 E) \$ F
- // 0XEF15,表示芯片型号为W25Q32
7 o' Z; t/ g" Y: Q/ m# J) V# z - // 0XEF16,表示芯片型号为W25Q64# Z8 O$ @) h+ v# V
- // 0XEF17,表示芯片型号为W25Q128$ c n( P/ {; A/ T+ N
- // 0XEF18,表示芯片型号为W25Q256
# m+ n0 j" N. b! J - static void w25qxx_id_get(void)! b+ H' N3 l9 l+ e# d# P) ?) M& U
- {
+ j- d" N6 V/ @2 e; p3 f - uint8_t temp[2];4 d7 M. J, B$ U9 D' w! D0 ]
- uint16_t device_id;0 I" o4 a. x" g0 l$ l) c
- . t6 Y/ z) u: w. f/ D4 B
- if(w25qxx_qpi_mode)9 h% N+ x6 h* d3 W
- {/ _( p( w4 q" _+ X
- 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
- }9 H' l6 ~# { {3 `4 y* r& H
- else- L% x) i$ T. R w
- {) q, Q$ ~% f4 S8 t+ |8 d
- 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$ ?
- }& n, y) K q: j* ]6 h& ]
- 9 f9 r" f5 V7 k9 G6 O! j
- QSPIReceive(temp, 2);
$ ~ t& |! E7 U, P9 `0 [
0 J! k3 h8 [3 u, ~9 Y- device_id = (temp[0] << 8) | temp[1];
, E' O/ {8 ?! y- u
s; P1 ?# k& D3 ^- printf("QSPI Flash Device ID: %X\r\n", device_id);
3 E) y- M- r9 C3 p+ j; b+ n - }
% X0 ^) {3 U( D' p, d - 0 a# V% H3 ?1 s0 R. s
- void W25QXXInit(void)# }+ A- f) I( _+ h! `- c
- {
" f p& N2 C. E5 J' p - uint8_t temp;3 {/ P# ?: i' |9 M7 N2 B2 f4 ^
- * H/ O* |1 D. h9 v, j: U8 [
- QSPIInit();
( j$ |; s$ |/ b0 E% y# J4 ?& c - w25qxx_qspi_init(); // 使能QSPI模式; v) e( q7 X4 d) V7 ?" P2 ^' p7 D- Q
- w25qxx_id_get(); // 读取FLASH ID.
- O$ S5 ^5 t# p) H9 T. o, k - & ]5 [% _4 G; \5 K. z6 f% N$ q9 E
- temp = w25qxx_read_status(3); // 读取状态寄存器3,判断地址模式% M+ U# X( E9 u
- if((temp & 0X01) == 0) // 如果不是4字节地址模式,则进入4字节地址模式7 u# L: ~5 G! r5 s9 u8 B
- {
5 i- g9 G; T& G - w25qxx_write_enable(); // 写使能
, ?# c+ y4 i& k - 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 - }. t( Q" [: V5 x6 n1 J+ [
- w25qxx_write_enable(); // 写使能
/ {3 T/ Y9 v6 c( r9 _* C - 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 - temp = 3 << 4; // 设置P4&P5=11,8个dummy clocks,104M) t3 G( X5 R" ^( [
- QSPITransmit(&temp, 1);
+ a! F) e% X5 \" I2 z M) T- K - }
! ~" A- P2 j$ q7 a, b
8 |4 d5 S' |8 T2 e7 c8 G+ ?- // 擦除一块4096字节 最少需要150ms
5 V' l2 W( ]% H9 D# F% z - void W25QXXSectorErase(uint32_t addr)6 i9 u9 ~6 Q! Q6 t
- {
7 v$ f- m8 i6 }, d& M: k - //addr /= 4096;
0 O% G' c, _* A1 i4 |9 M! ?. f6 s+ _% j - addr *= 4096;; f& N$ K/ J9 x2 W
' U: i; B; t; X. f9 H E; g- w25qxx_write_enable();
$ S8 G* I- a9 `& { - w25qxx_wait_busy();
: u$ s; r# S8 V# c) s, `* ] - 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 - w25qxx_wait_busy();
1 A4 w" [; f" j# I - }
2 u5 [& j" ~$ ~, j: p6 y7 }- y9 W
2 ? h7 c6 a/ A$ v; k. w! s3 `* i- * L0 ~6 ~6 a0 j% d6 j
- // 擦除整个芯片
& E2 Q7 {/ U6 B9 U( g* r( ]& Y - void W25QXXChipErase(void)& y3 ~4 E8 S# |. R' \
- {
* }. l3 H9 `. y4 ?% M! q - w25qxx_write_enable(); //SET WEL0 r9 \2 }" I% b! w* Y3 \
- w25qxx_wait_busy();
1 \# J! W( v* \* E! n - 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 ?' {' \ - w25qxx_wait_busy(); //等待芯片擦除结束! T8 {# v' _4 L$ ]3 n$ u+ O
- }
* N ~; C7 ]2 U4 t: y
e% G# `" a5 [ v0 y) J- // 写最多256字节, i1 F4 W7 t, W# g9 ]1 {
- void W25QXXWritePage(uint32_t addr, uint8_t *buffer, uint16_t length)7 `/ F- V1 S( e! o" ?
- {
6 Q% O& I5 t/ z$ q( A( L; {+ w - w25qxx_write_enable(); //写使能
6 ^: s( f9 | W/ V! N% @ - 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 _" } - QSPITransmit(buffer, length);, r" h8 p+ {% a7 @
- w25qxx_wait_busy(); //等待写入结束+ @5 n7 l# B' d4 |7 q! W
- }, x* ?" h* V+ T) O
- 0 \7 B, o" t& b' ^2 B0 N4 z
- // 写最多65536字节 无擦除动作
( t7 ?# Y" B& v' g/ H$ z& m* X - void W25QXXWriteExt(uint32_t addr, uint8_t *buffer, uint16_t length)
; O4 ]) c5 m Q$ M. |4 R3 a - {1 B2 P' l- Y! d q. L
- uint16_t pageremain = 256 - addr % 256; // 单页剩余的字节数; v9 q3 `3 T5 y6 C
- U+ q4 J y" i5 z# I7 \- w- if(addr + length - 1 > (32 * 1024 * 1024))
C# L Z- E5 s4 e% |; y% b - {. k+ d4 y% w6 z R0 f4 S& f& O/ b
- return;
' |$ W5 o/ N O - }
' V5 M% r9 Q" L - 7 k K, \5 {6 l o/ ^. ]) K
- if(length <= pageremain)
8 U: q1 i. h* e. b" p# X: y. R - {4 \* @4 y6 i$ d) U! P$ u# M& q! e' C
- pageremain = length; // 不大于256个字节
- p/ {9 f. z2 K; b& L8 _5 }' T - }
; N9 t5 w+ ~; L" H) w - while(1)& g7 \& Q. M' n4 L
- {0 ]! H: n; T Q M" v! x2 y
- // 分页写入2 u+ m7 d$ i( f
- W25QXXWritePage(addr, buffer, pageremain);$ n. _) e8 l9 c' p0 \1 c/ t" E) M
- ; b3 W9 s' R- g/ P. }6 d* @
- if(length == pageremain)8 n. U: s( g" S- P! H3 B2 N6 B
- {
N, _/ X# g. L/ A# F* _ - break; //写入结束了0 y1 c3 k7 ~7 q0 l% |" a/ u3 N
- }
7 P) x/ q( N2 ? - else
" _- {& Z Z5 J8 G# U0 K - {
7 C: d+ d" B, _6 m - buffer += pageremain;
0 x$ u" D# L8 p% A/ R! m2 g) m - addr += pageremain;
! j, a: }7 Z1 C! T/ ?+ c) n+ ~ - # w" R/ O; H" i5 C5 ~! s% B
- length -= pageremain; // 减去已经写入了的字节数
, M) P+ {- K" @0 d8 k9 u- P - if(length > 256)) C' z" ?. w6 ]( B- \
- {& K& g1 r" p; R# `
- pageremain = 256; // 一次可以写入256个字节( @7 I! s, R( T7 a. N
- }0 F4 O2 D8 s y: U. Q
- else( U2 C1 i; f7 d2 Y
- {* y7 k7 f8 ?$ q& F! ?8 P" N T" o. L
- pageremain = length; // 不够256个字节了$ y9 _5 `& {* T
- }
! s& k0 n7 g" l, w; \ Y5 E - }6 Q$ Z* z/ n% X# \$ g* g/ j
- }
9 Z. j! ?, U, ^ ?: i - }" @6 H H6 p6 G- d/ b- T2 I
- , z. ]! ]( y; P e4 F) y
- // 写最多65536字节 带擦除动作 % ?$ M% n( I. d- N0 K
- static uint8_t W25QXX_BUFFER[4096];
9 {; U& V) J2 A$ g* _* w - void W25QXXWrite(uint32_t addr, uint8_t* buffer, uint16_t length)9 B8 y# v5 I9 R! D! X* b9 a
- {6 {3 ?% r6 i5 J r+ B$ q
- uint32_t secpos;% B; W' Q, r ?
- uint16_t secoff, secremain, i;
) P! z! ?" A& |- `9 D - uint8_t * w25q_buf;. \6 W, K4 I, L/ I3 \
* A5 l4 j& E0 x6 f+ b) [- w25q_buf = W25QXX_BUFFER;
( Y/ {: v7 v: u0 [( y) ~( j
4 B$ ?( b9 v( K# n4 |- secpos = addr / 4096; // 扇区地址
8 @, ?3 G& d' o5 u! f; _ - secoff = addr % 4096; // 在扇区内的偏移, p/ `7 _& m; P
- secremain = 4096 - secoff; // 扇区剩余空间大小; t: o3 ~: s* @
7 _$ d) |9 j, j0 l# E: h- if(length <= secremain)
! ^6 E- ^2 q# c7 s0 ] - {( n" R5 \8 y9 H3 |' w9 B. X
- secremain = length; // 不大于4096个字节7 ]; c& @0 I$ D( S# a
- } X& _$ f* |/ z* ]; c
- while(1)
' e" X, U) B! o' U5 d - {/ e0 w- I! Z4 J# p. r, v
- WatchdogFeed();1 [6 b/ ^/ [) x# @
- W25QXXRead(secpos * 4096, w25q_buf, 4096); // 读出整个扇区的内容
) H: W! S+ M2 y/ l' ]+ G - for(i = 0; i < secremain; ++i)- w$ A6 v: t, v/ ~) c; K
- {
7 H8 f# J p) ~& a7 N. S4 s& T1 q - if(w25q_buf[secoff + i] != 0XFF)
" k/ y2 L- {! |& l) B - {$ O6 p( A1 A' X2 ~2 ]
- break; // 需要擦除; m& D+ @: }* i* Y
- }
' ]/ B; \+ G7 W9 ?% Q( d - }
5 i+ R3 T" [& \& J - if(i < secremain) // 需要擦除
7 C, S/ q1 h$ M) X: W9 }2 T - {& G* [7 ]+ H: G0 {% }& n# c0 z5 [4 O
- W25QXXSectorErase(secpos); // 擦除这个扇区7 x: y, e' t% f
- for(i = 0; i < secremain; ++i)* e# Z% r+ [! U0 U- I
- {& t% x! m5 x4 B8 Z7 @- u2 m
- w25q_buf[i + secoff] = buffer<i>;</i>
% e3 |) j s5 k/ K - }! p: q+ d) o! h0 }( n/ b
- W25QXXWriteExt(secpos * 4096, w25q_buf, 4096); // 写入整个扇区
7 D; h6 e& d& g4 [! k( s
# v6 ]7 v7 ]. d6 l# z" u- }9 X0 C3 Q: d3 ]9 l, X/ H B' r
- else, u5 U3 H8 j$ J- t2 {
- {7 W6 I. ~5 Q; P; u; D$ L
- W25QXXWriteExt(addr, buffer, secremain); // 写已经擦除了的,直接写入扇区剩余区间.
4 `- L( ?+ Z9 q( D0 R2 R - }: s" g& L* `# ~6 t: y
- if(length == secremain)
$ X7 X: ]% a5 v- O' A, B% J - {) h+ f4 t! \9 T, d& g# v! p$ l H
- break;
4 u* f: h1 r5 N3 F9 Z, {2 g0 ` - }0 p7 r7 {; i* Q
- else
% A/ U! d5 E% ~5 e/ j - {3 [; k! p- P8 p/ o5 a9 @
- secpos++;
4 c1 L+ o4 _ j [* W* h+ c$ P - secoff = 0;
5 s; P. v4 Z- f" x- f2 W8 R; V5 ~
! z, J# [- k {' D- buffer += secremain;7 d' r0 t2 ?' C) s8 B
- addr += secremain;! Z/ Z- i% N5 Y
- length -= secremain;
+ {! z* ^0 K5 h" D. S - if(length > 4096)
+ [7 h4 I% u& d- W - {7 Z" L/ h3 [5 W& v' a. O
- secremain = 4096;# ]' ^: D6 `7 n- f" j( g4 m
- }
; K& [' i) \9 I3 M - else0 B8 G) C+ F; s9 g9 z6 S. ~; C+ K
- {% }8 q6 |) @5 T2 s2 N
- secremain = length;
: F+ q$ N7 f4 ?2 z5 u) f, [! V - }0 W5 q+ D4 Y9 ^9 q/ ]
- }
( t* Y. d; U+ i+ K+ _+ t - };
1 v" H) D. z) u+ _ - }
% E& a5 _; Z! I9 n* H
. G2 N7 @) n9 l7 K. ^3 q. f; @- 5 E, g/ ], W1 B
- // 读取SPI FLASH,仅支持QPI模式 在指定地址开始读取指定长度的数据 65536
( Q- y6 s+ R0 [3 p9 k/ {& W, G - void W25QXXRead(uint32_t addr, uint8_t *buffer, uint16_t length)0 v: W; i s! I9 n9 a
- {
* c; K# z( ~/ J% }5 G$ Q% ^ - 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 - QSPIReceive(buffer, length);
0 h# Q' q5 ]: D& P2 |9 X5 M - }
复制代码
" 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 |