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