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