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