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