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