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