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