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