在头文件中简单修改一下片选引脚IO即可使用。8 \) ]- P4 {. a5 O) t L
8 `% @9 l. M; {/ u6 Q源文件, {* \6 Z+ N7 w
# g; H& g3 T1 ]% f2 f' P- /**' k( M. {! [" L X3 c
- * @file w25qxx.c
h8 S, F3 q. n. ?& r1 T& ^ - *
, X, s5 P4 i3 ]* e. ?2 I3 ^ - * @brief Create by AnKun on 2020/6/18+ z+ ]0 u& r/ [) ]6 Q' D& i
- *+ H4 ?1 J1 W7 d p4 J
- */, \5 ~. T7 f' F D' B
- - V3 t. ?0 {. @
- #include "w25qxx.h"
' @+ c) r0 d6 E# v - #include "spi.h"2 _: p1 t2 J: ?' k
2 y h2 M* G& O: ]/ m& r- E- uint16_t W25QXX_TYPE = 0;7 s N1 Q; @/ V: A
- uint32_t W25QXX_SIZE = 0;
4 C) w7 p2 o" }- H* b6 G. _# t' q - uint8_t W25QXX_UID[8];3 C5 m' A. y8 ]$ W% Z7 v
- # ~+ E2 w7 X- |. g5 f# ~9 i
- static void delay_us(uint32_t us)& d8 b, r% e) ]8 I# l `; x' t7 F- W
- {" k% U6 R/ G& R; Z* H7 x
- uint32_t delay = (HAL_RCC_GetHCLKFreq() / 4000000 * us);
' [5 k( p+ c* q e# B/ e: y* \+ p* C* n% A - while (delay--)+ ^( b: d3 ]& A9 B& t/ }
- {. S, `) B) g8 t/ I/ H+ @2 ~0 i8 Q. ^
- ;
8 _2 e: s! k! G2 f/ b1 B1 l; G$ N - }( M# l, _$ p: W$ C* \! K
- }- P% K. c# u+ V) `; S7 p
9 d2 F7 X- H, x N. b# `, V- //SPI读写一个字节( a/ ?/ a9 w7 `
- //TxData:要写入的字节
1 r2 T* {% t, Q: k2 W - //返回值:读取到的字节/ p( _. A1 }1 _/ p
- static uint8_t W25QXX_SPI_ReadWriteByte(uint8_t TxData) k! t6 Y ~8 z
- {" {+ K+ j9 t8 |( F
- uint8_t RxData = 0X00;9 x: J& ~# O8 Q! d2 O+ [$ g( A
- if(HAL_SPI_TransmitReceive(W25QXX_SPI_Handle, &TxData, &RxData, 1, 10) != HAL_OK)/ }# L: l) ~9 O5 x" d' e' o
- {
% O# N# Z- I$ G/ x2 R - RxData = 0XFF;3 e5 ~- |& u, f, @! y
- }
: a c4 x3 `5 r# [ - return RxData;
+ y3 [" ?7 j- [, a5 | - }
( u1 g3 U7 O: I/ L+ h7 _* e# _4 \ - : z3 i' S' |! \5 t. {: _( s
- //4Kbytes为一个Sector
6 x" M2 i% U* `" K0 m; Q - //16个扇区为1个Block1 `6 j) Q, u" u( g! r4 M
- //W25Q1289 M: l* z8 h7 d. ~
- //容量为16M字节,共有128个Block,4096个Sector
) \( x5 Y3 h* A- [( J0 x
& D# U4 y; _9 q- V" u- //初始化SPI FLASH的IO口
5 {( g& c, K8 E, M9 y% } - int W25QXX_Init(void)0 k) n [/ o4 D' Q
- {
6 M- S/ u; S: L# b - MX_SPI1_Init();
, I+ y+ T- n/ v1 ]0 { - W25QXX_CS_L(); /* 拉低选中 */
, L8 `* Z1 i/ s( w - W25QXX_SPI_ReadWriteByte(0XFF);$ I3 k6 R8 |% c5 E( A* w
- W25QXX_CS_H(); /* 拉高取消 */
# ~3 o$ q4 ^1 d& g% t# U0 z - W25QXX_TYPE = W25QXX_ReadID(); // 读取FLASH ID.
z' N# O3 R8 x! Z8 k% k4 E - W25QXX_SIZE = W25QXX_ReadCapacity(); // 读取容量
* Y y* t, r# h" o0 ?7 p - W25QXX_ReadUniqueID(W25QXX_UID); // 读取唯一ID
! b1 F4 V9 F5 e2 ^ - if((W25QXX_TYPE & 0XEF00) != 0XEF00)
7 j% y% X; ?4 h7 Z: e1 D - {
% h- @1 R$ Q7 c6 p$ A% z( R9 } - return -1;$ t0 {3 e9 f$ G5 H6 D$ E
- }3 H* R7 E4 V/ V9 v. O6 k
- return 0;' F+ R% `7 n A0 d0 r$ r$ a5 w
- }
( c, M' U) z$ g! o - ' J0 |" L: }6 _7 ]- K5 T
- //读取W25QXX的状态寄存器
/ h9 C: z3 @1 @$ Q. Q' x* r - //BIT7 6 5 4 3 2 1 0
% C0 O, ^$ `9 x - //SPR RV TB BP2 BP1 BP0 WEL BUSY% }6 H7 V& |- S- r; H
- //SPR:默认0,状态寄存器保护位,配合WP使用
6 S+ L& `- _( s# p - //TB,BP2,BP1,BP0:FLASH区域写保护设置3 F, j: X: [5 K
- //WEL:写使能锁定
1 q# q: W) d J& P - //BUSY:忙标记位(1,忙;0,空闲). N* t7 g4 `6 G
- //默认:0x00( {, }3 ?9 `& Q$ c# i
- uint8_t W25QXX_ReadSR(void)
2 p8 {! f# Q9 m3 Z# k+ J - {* B! w9 t3 a" M7 K* M- r
- uint8_t byte = 0;8 u2 L6 }1 b, z; _
- W25QXX_CS_L(); //使能器件1 i4 ], d" V9 y6 D. L! T
- W25QXX_SPI_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令% z. E! X: e7 [" P' D
- byte = W25QXX_SPI_ReadWriteByte(0Xff); //读取一个字节" i6 _7 |4 O# i8 ~5 D: N
- W25QXX_CS_H(); //取消片选) C0 u9 y. G, m' _4 p
- return byte;
2 n. d( Q% |5 I7 U% K - }3 D0 }8 m7 \8 d$ o _* R
- //写W25QXX状态寄存器
+ s* U& I. q1 l - //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!* m7 i: G) y, ~. S' b6 H5 o
- void W25QXX_Write_SR(uint8_t sr)
: |; y$ s; h Y& [0 w( t - {
$ K% m" m; u/ j - W25QXX_CS_L(); //使能器件
0 R! H- |/ p# q! C' R8 {# C - W25QXX_SPI_ReadWriteByte(W25X_WriteStatusReg); //发送写取状态寄存器命令
6 v& Z, y7 O0 M3 |& q - W25QXX_SPI_ReadWriteByte(sr); //写入一个字节5 [! X( ^$ h0 O5 e8 G S
- W25QXX_CS_H(); //取消片选
* s" i3 [1 @: ~& U - }( T+ w: U. b' |" A+ z2 _0 }8 o8 b
- //W25QXX写使能8 R* s; v2 B5 c( R* _ S- v
- //将WEL置位
- L# e# S5 f4 U% O+ F/ e& [ - void W25QXX_Write_Enable(void)6 L' L) j. s4 r9 k( G. F
- {8 V9 F# m5 V& b5 D/ S
- W25QXX_CS_L(); //使能器件" n' I6 ]! l+ P5 {5 n& ^& a& A9 M
- W25QXX_SPI_ReadWriteByte(W25X_WriteEnable); //发送写使能$ C6 c, M9 G9 P% L' f4 O9 q' J
- W25QXX_CS_H(); //取消片选0 S, H" _* X7 f( ^6 N* V# t
- }
" O/ G' ]. t. k7 | - //W25QXX写禁止
( M' q( J( e- N - //将WEL清零2 N8 _5 a/ a! q s
- void W25QXX_Write_Disable(void)
r. N5 I, m0 A( e5 T; U- E3 R- d - {
) y7 [+ f" R7 [) e - W25QXX_CS_L(); //使能器件" S7 F, I8 U3 c
- W25QXX_SPI_ReadWriteByte(W25X_WriteDisable); //发送写禁止指令9 n: f* B; E3 t) m* j- K
- W25QXX_CS_H(); //取消片选) \3 v5 Z0 ~ g2 m* e
- }
8 O8 S6 p5 b( G8 S - //读取芯片ID
9 B x8 d7 a* e" C - //返回值如下:# l. i, ^- B* G& s
- //0XEF13,表示芯片型号为W25Q80/ G: i3 b. H' H) k: E
- //0XEF14,表示芯片型号为W25Q16
" H1 }2 f3 L% K0 F! T - //0XEF15,表示芯片型号为W25Q327 c8 N4 `. P0 q9 O" l! B9 X
- //0XEF16,表示芯片型号为W25Q64! M; X( Z0 f1 B) ~1 J/ ?
- //0XEF17,表示芯片型号为W25Q128: O& E. G- @3 V5 L) C- P# F9 ]
- uint16_t W25QXX_ReadID(void)
: B. B4 ]: Z6 m6 C$ p6 y. h - {
. T: W. T7 Y0 i3 O/ @' O9 h - uint16_t Temp = 0;
5 u9 v n7 z! `; T, n0 t - W25QXX_CS_L();
. G' m D1 _0 B, f - W25QXX_SPI_ReadWriteByte(0x90); //发送读取ID命令" d! y: a3 E# j- V) V
- W25QXX_SPI_ReadWriteByte(0x00);
! e! S$ K" T6 G- {* l# Q - W25QXX_SPI_ReadWriteByte(0x00);) O& r& R. F/ i* k" A/ t @$ n
- W25QXX_SPI_ReadWriteByte(0x00);7 x" W, Q% a; C4 p t( f: z1 C
- Temp |= W25QXX_SPI_ReadWriteByte(0xFF) << 8;
3 t" q2 N" O! R3 P* ~5 s, ] - Temp |= W25QXX_SPI_ReadWriteByte(0xFF);
4 z' T4 i! g+ }; S7 E8 m: v; y - W25QXX_CS_H();
3 u2 y: w! s/ i2 v- w - return Temp;
, ?! ?+ G3 T6 Y; O2 U - }
1 X0 i/ t7 x' f
! L, M# D9 T# q& s+ T- j- uint32_t W25QXX_ReadCapacity(void)$ ?0 D% C( W7 {: f# C( ^! A4 {
- {9 Y( s E. I* ?: v' [
- int i = 0;
) k. `0 j0 i3 T6 ]: T. n - uint8_t arr[4] = {0,0,0,0};5 N8 b9 d" ~8 t5 s7 ]: X* C3 h
- W25QXX_CS_L();
, |: S( p7 L: R9 w, r6 q/ } - W25QXX_SPI_ReadWriteByte(0x5A);- W/ N' ]) i& l7 T1 R9 x
- W25QXX_SPI_ReadWriteByte(0x00);# X/ m$ D6 m* y) |% h: M1 y2 i
- W25QXX_SPI_ReadWriteByte(0x00);
* M' A, ?$ N/ G6 l6 j6 o) [6 W - W25QXX_SPI_ReadWriteByte(0x84);: q; o4 w- }% X* w! s2 {
- W25QXX_SPI_ReadWriteByte(0x00);
2 ]9 w' y& Y! _ g - for(i = 0; i < sizeof(arr); i++)
* z3 M7 x9 N" z; t$ n% b - {
5 C3 ~# B- z% t, c- M3 { - arr<i> = W25QXX_SPI_ReadWriteByte(0xFF);" C# C6 L- W1 N, D! Q+ X9 B$ p
- }
9 b4 \" J8 k; d2 g- x5 u - W25QXX_CS_H();
- M0 n; E( s3 O0 u' b* k4 Z: g$ D - return ((((*(uint32_t *)arr)) + 1) >> 3);+ v* Y) z9 h/ D
- }4 r( {) T5 V+ ~; m5 ~! T+ n3 e
4 J. m% P6 V d" k5 \: E! Z! R. }- void W25QXX_ReadUniqueID(uint8_t UID[8])
; c7 U7 a- Y( J. n( H - {1 M' \/ D: W- [' S
- int i = 0;
' i0 `: u! ?2 _" t - W25QXX_CS_L();
( k k1 B1 g# J1 E3 M - W25QXX_SPI_ReadWriteByte(0x4B);
9 W) V0 b) K+ p! T! Y1 L$ g2 n& A! K - W25QXX_SPI_ReadWriteByte(0x00);
- L" T' R/ j" r+ g4 K3 u - W25QXX_SPI_ReadWriteByte(0x00);1 m/ M/ N7 t) O* N9 I
- W25QXX_SPI_ReadWriteByte(0x00);
, b! `+ X( E$ q7 x - W25QXX_SPI_ReadWriteByte(0x00);
! U7 Z0 t1 F1 |; V3 F& u - for(i = 0; i < 8; i++)/ x C" H" a+ @6 g
- {
: j( O/ `1 L+ i - UID<i> = W25QXX_SPI_ReadWriteByte(0xFF);. P6 Z5 V m0 `/ |- Y
- }% K4 c, {! e/ `0 o: z* x/ A* N" w
- W25QXX_CS_H();7 S1 g* [ ]6 [ v
- }: f& |9 O `! p( j
+ K5 `7 h9 j8 T1 E) o2 K- //读取SPI FLASH
5 t8 [) v( @/ R# Z - //在指定地址开始读取指定长度的数据
+ P: t, ~5 ~& i; M" b1 }0 O! ] a - //pBuffer:数据存储区
6 s/ V) U% _- z5 d( O - //ReadAddr:开始读取的地址(24bit)
$ I0 L0 {) w/ L& y. j: w - //NumByteToRead:要读取的字节数(最大65535)% I( n C; L2 s* I+ T0 @$ q
- void W25QXX_Read(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
: P1 G# F* g5 `2 L) l. n - {# ~* m6 a3 v- {& z5 T6 e0 r
- uint16_t i;7 ?8 w/ L2 ~& j- _) L
- W25QXX_CS_L(); //使能器件! n' b3 W" x7 `' }
- W25QXX_SPI_ReadWriteByte(W25X_ReadData); //发送读取命令
$ o+ B" A: }+ d& q8 ?: v - W25QXX_SPI_ReadWriteByte((uint8_t)((ReadAddr) >> 16)); //发送24bit地址
; b, V% P% p" o+ j - W25QXX_SPI_ReadWriteByte((uint8_t)((ReadAddr) >> 8));0 M0 `3 M) z: Q& Z- d; r
- W25QXX_SPI_ReadWriteByte((uint8_t)ReadAddr);' t/ O9 G6 F7 ~' s2 r8 H% e
- for (i = 0; i < NumByteToRead; i++)
& {" N9 `0 f6 `3 Q$ P" _+ a) g& ? - {
B& H( o: Y$ n5 P" H" r - pBuffer<i> = W25QXX_SPI_ReadWriteByte(0XFF); //循环读数7 ?9 Y1 d; C7 V$ | S) C
- }
5 _, w' B O5 G: I) e/ l/ S1 { - W25QXX_CS_H();2 l' b. C# Q" y2 a) e7 q
- }) x' S% T# k' Q$ a
- //SPI在一页(0~65535)内写入少于256个字节的数据
0 o% Z6 E A* r8 }4 I - //在指定地址开始写入最大256字节的数据
8 Y. B$ }& T2 h. a+ f - //pBuffer:数据存储区# c/ I! \* q; V( ]
- //WriteAddr:开始写入的地址(24bit)
0 [. q" k- ]+ u2 }- \ - //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!* R. { {+ i! m) }; C( l' Z
- void W25QXX_Write_Page(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
9 `% ]! j3 ^* e" o3 f - {; y" ~7 Z7 I& l
- uint16_t i;
4 D( n& ~+ D# q' d* Q - W25QXX_Write_Enable(); //SET WEL
7 r T0 C1 v+ @5 i( k - W25QXX_CS_L(); //使能器件( V$ j* S& l% a/ T) }+ u
- W25QXX_SPI_ReadWriteByte(W25X_PageProgram); //发送写页命令: |, S" V+ Y9 a1 Z! E
- W25QXX_SPI_ReadWriteByte((uint8_t)((WriteAddr) >> 16)); //发送24bit地址9 U( c, m4 |( O$ e6 X" }" y
- W25QXX_SPI_ReadWriteByte((uint8_t)((WriteAddr) >> 8));) y. G- l1 S4 \; Y. C* I
- W25QXX_SPI_ReadWriteByte((uint8_t)WriteAddr);* P) u9 g5 Y$ ^0 W7 w+ I
- for (i = 0; i < NumByteToWrite; i++)$ Z% l+ B0 o4 Q @; J I5 N
- W25QXX_SPI_ReadWriteByte(pBuffer<i>); //循环写数0 y6 A* l, }- {, j! b
- W25QXX_CS_H(); //取消片选
/ B4 n& ~0 [/ B0 x: [/ s+ Z - W25QXX_Wait_Busy(); //等待写入结束; y, ^* v$ w" t; r, m4 h
- }: g0 O$ w: \: R r S
- //无检验写SPI FLASH1 O* B0 N: T2 H: F; c
- //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败! p1 O) o( R' k# b) O5 B5 c
- //具有自动换页功能. ]9 s" ?3 I3 x0 M1 U w
- //在指定地址开始写入指定长度的数据,但是要确保地址不越界!$ T9 {1 i3 j2 c( m" ^( n0 H' N
- //pBuffer:数据存储区1 G* s0 g2 \' o9 M7 P
- //WriteAddr:开始写入的地址(24bit)9 r; q! j4 Q2 _9 M9 x# f4 I
- //NumByteToWrite:要写入的字节数(最大65535)! r! C5 k) N; x- ]6 L
- //CHECK OK5 Q. D$ O1 E+ u7 e9 O- J2 d% T
- void W25QXX_Write_NoCheck(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
4 o, X/ U" r: R6 `6 O - {
8 S5 n) ]% V! k/ _ - uint16_t pageremain;- u" I! ?$ S0 z1 J/ Q) B0 M, m# h
- pageremain = 256 - WriteAddr % 256; //单页剩余的字节数
3 H% m2 v) X! D* a7 W# _% l/ P - if (NumByteToWrite <= pageremain)+ c- `$ F" ^/ V9 L
- pageremain = NumByteToWrite; //不大于256个字节6 P: b6 c }; O& g W% b. U% q
- while (1)
/ E R- V# V- O! J. J! X - {) p7 ?5 [* i1 f/ X5 k
- W25QXX_Write_Page(pBuffer, WriteAddr, pageremain); T! Y6 H" X9 y4 C9 o2 X
- if (NumByteToWrite == pageremain)& ]9 \! c( G$ [* s0 a9 v: d. W- S
- break; //写入结束了
: l6 U o6 _* z/ _$ {2 I! W6 q - else //NumByteToWrite>pageremain" h0 y5 Q$ y8 Q1 d- T9 l
- {0 ?) Z6 V; i+ [
- pBuffer += pageremain;
5 r8 M' x+ n, M1 P7 t# l4 g' n - WriteAddr += pageremain;1 V% y" y( R! V
- " L! M# ~$ ^' @$ L P; g8 e
- NumByteToWrite -= pageremain; //减去已经写入了的字节数+ d: `4 T7 o- z! @
- if (NumByteToWrite > 256)
4 m# }: A$ V# I; }* B - pageremain = 256; //一次可以写入256个字节
( U1 M3 M/ M, |* ^# V s } - else
. F1 _' {) b/ W! C5 U - pageremain = NumByteToWrite; //不够256个字节了" _* n; j6 v5 Y* b9 o4 o! e
- }
! u: ]/ h) S. y8 y9 m# f - };
7 S3 J G$ ]4 r4 T - }5 T% Y# Q& W' K9 u
- //写SPI FLASH2 W# F. ]/ ^4 g
- //在指定地址开始写入指定长度的数据
4 T/ s. H c0 Z% A R% x. H - //该函数带擦除操作!& [ t8 `1 [: O/ K) j W! {6 }
- //pBuffer:数据存储区- B) x! m0 ~! o) }7 ?
- //WriteAddr:开始写入的地址(24bit)
" v/ f- Y* a$ ~/ j* | - //NumByteToWrite:要写入的字节数(最大65535)
* u6 S1 y- \* b0 | - uint8_t W25QXX_BUFFER[4096];; R p4 R$ _7 x# ^; I6 F8 i5 q$ B/ R6 v
- void W25QXX_Write(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
. |8 S2 Y0 I2 k - {
! P! h4 l$ _0 ]2 z2 R - uint32_t secpos;
& U7 e( W+ h2 h - uint16_t secoff;
7 \- K, A( T6 T# }! Q M - uint16_t secremain;1 W; |. o, V+ ]* P( B; W
- uint16_t i;* U& O$ A- s" l# `" y! H1 A
- uint8_t *W25QXX_BUF;
! H: _8 u9 I" f' J. N - W25QXX_BUF = W25QXX_BUFFER;
# E* m3 U* L0 \2 |3 G - secpos = WriteAddr / 4096; //扇区地址3 K, w; @! U. O1 ?3 T2 D3 Q
- secoff = WriteAddr % 4096; //在扇区内的偏移/ M: N, W4 q+ N5 R( H. f+ c+ A
- secremain = 4096 - secoff; //扇区剩余空间大小
( b0 \; I- j0 d2 x/ d/ J4 @ - if (NumByteToWrite <= secremain)
7 p) B E6 \- O/ W6 M3 _: q - secremain = NumByteToWrite; //不大于4096个字节
) S7 _- `% I% ? - while (1)
5 U, ]8 t5 @* e# o5 P s1 T - {9 ]0 p5 n& e( P7 [1 p. C( O
- W25QXX_Read(W25QXX_BUF, secpos * 4096, 4096); //读出整个扇区的内容
" ~& g# q }* Z9 R) A' w% ? - for (i = 0; i < secremain; i++) //校验数据
1 K6 k- g9 B7 r1 k9 y - {5 ~) p8 V1 R1 I
- if (W25QXX_BUF[secoff + i] != 0XFF)) c% d) c5 A3 z% ^
- break; //需要擦除
. J: c/ |- F4 r7 n - }
( @1 [$ I' q7 M. ^2 F9 y; I: k - if (i < secremain) //需要擦除5 P+ H& Q4 p7 j z
- {( V Y# o! x2 R/ w3 I& b1 g3 Z
- W25QXX_Erase_Sector(secpos); //擦除这个扇区6 r+ T9 b$ q* ^5 v( H9 b( E& U" X6 }
- for (i = 0; i < secremain; i++) //复制
' |7 P/ w" a' p/ D% n' C - {* a/ O7 P. t) {# y7 w+ s3 o
- W25QXX_BUF[i + secoff] = pBuffer<i>;- @8 r9 ]9 K8 a, i9 h* v2 y
- }; D& `/ p9 @, P0 H: m7 n3 z+ V0 j$ G* |
- W25QXX_Write_NoCheck(W25QXX_BUF, secpos * 4096, 4096); //写入整个扇区& L# Q8 W3 ~) ~0 X6 U
: S: n k* U' g' v! l& M- }1 X. U4 l- e1 l6 ?2 F6 y
- else9 O$ w3 x7 A4 F4 c9 \5 f
- W25QXX_Write_NoCheck(pBuffer, WriteAddr, secremain); //写已经擦除了的,直接写入扇区剩余区间.3 h' ^2 X. b: s& \$ l( e. M& d
- if (NumByteToWrite == secremain)% K4 E9 {/ ^6 y& i* h* a
- break; //写入结束了/ A; _# I# Z, ^, K$ u$ C
- else //写入未结束2 n1 v" R$ L) r, r, J0 r, I
- {
$ y: m- z" T- l1 s' i" C& u - secpos++; //扇区地址增1
$ m' M: q7 J7 B, r1 U - secoff = 0; //偏移位置为0/ ~3 N. H+ `3 J: Y% ]0 a4 a
& [4 @2 ]0 @' ]& H+ U7 C- pBuffer += secremain; //指针偏移
( [9 ^; O+ [# ]* `' L - WriteAddr += secremain; //写地址偏移+ H8 _) `2 a0 V0 e1 F
- NumByteToWrite -= secremain; //字节数递减+ X) L- t4 e. r0 M) Y
- if (NumByteToWrite > 4096)! ~8 N9 g% M: B/ _+ D: g g5 g
- secremain = 4096; //下一个扇区还是写不完
5 t4 o7 H3 ]4 j) i z# o - else
& c6 Z1 j4 S9 E4 C, C) O7 P - secremain = NumByteToWrite; //下一个扇区可以写完了, W1 o2 _) h1 N/ U( a
- }
3 d% @- C ~* } - };3 E) X, @1 o; g
- }: ?' T4 n8 l3 U/ X; M% O) _
2 J- N6 E" V) L, f' J1 I" @) r- //擦除整个芯片9 |# D2 E/ q0 u3 S( }
- //等待时间超长...
/ z# J- _# \8 C( b) y# P) e% N3 Y - void W25QXX_Erase_Chip(void)
0 E X5 `7 E. [1 A1 } - {' h; W# _ t& M$ k L- `. M* u9 @
- W25QXX_Write_Enable(); //SET WEL
1 {; X+ F9 P( \! }9 \ C3 D - W25QXX_Wait_Busy();
3 ]& ?6 R5 V3 W# @ - W25QXX_CS_L(); //使能器件) Q, z2 p. P/ l. j7 P* i
- W25QXX_SPI_ReadWriteByte(W25X_ChipErase); //发送片擦除命令
5 ~! u+ G( X, f1 G6 i& n - W25QXX_CS_H(); //取消片选
% u) c; @- F& t% N' f. u - W25QXX_Wait_Busy(); //等待芯片擦除结束# E. a/ Z: ?' N/ t m4 T6 _
- }2 h7 m8 f3 @- l2 i, Q& L$ k+ _
- //擦除一个扇区
! _6 V* T! V" |; ?- c3 ~* c - //Dst_Addr:扇区地址 根据实际容量设置
6 j- I" d* m5 Q9 u: A/ M- ? - //擦除一个山区的最少时间:150ms
d# f* \* u8 L3 S+ w) q& B3 I - void W25QXX_Erase_Sector(uint32_t Dst_Addr)
9 d% G/ ]+ r/ e* Z3 b# J! H - { K# @6 F+ m e
- //监视falsh擦除情况,测试用0 U8 X; }5 n! M' a
- Dst_Addr *= 4096;% v0 V3 x) C x
- W25QXX_Write_Enable(); //SET WEL+ ?, t7 Q- E% Z8 i' C2 k$ c
- W25QXX_Wait_Busy();
/ M+ H. @. \8 Z% Z, I( w. W - W25QXX_CS_L(); //使能器件
1 h0 `% Y- {8 ^' d) y - W25QXX_SPI_ReadWriteByte(W25X_SectorErase); //发送扇区擦除指令
" U) D$ ? {9 a, ^3 { - W25QXX_SPI_ReadWriteByte((uint8_t)((Dst_Addr) >> 16)); //发送24bit地址6 W8 }0 }# g+ ^9 ]
- W25QXX_SPI_ReadWriteByte((uint8_t)((Dst_Addr) >> 8));
% |+ q$ h$ m7 A7 g - W25QXX_SPI_ReadWriteByte((uint8_t)Dst_Addr);
2 |6 t2 b6 W4 N$ U5 I) {5 w - W25QXX_CS_H(); //取消片选( G( a/ D p# N/ p, S0 X6 X9 ^
- W25QXX_Wait_Busy(); //等待擦除完成5 b# I1 R1 D5 C
- } I- @* C4 M5 t3 _
- //等待空闲" y) A1 M* S7 M% }# z% s7 A
- void W25QXX_Wait_Busy(void)
. ~, p& ?" {! B' ` - {
5 p4 _" h) b6 d8 I0 y6 {) E7 d - while ((W25QXX_ReadSR() & 0x01) == 0x01); // 等待BUSY位清空
! T3 b+ m2 n' p, f% x" B f - }
" D0 s6 L* @2 D$ r - //进入掉电模式
7 s3 q) [0 i- U7 z: U6 \+ ` - void W25QXX_PowerDown(void)) c$ h( I8 d1 F1 M; Z
- {
5 p" e4 K1 f, M- m+ q5 a2 D - W25QXX_CS_L(); //使能器件
3 k2 d$ V" M) U1 _! G2 _) e - W25QXX_SPI_ReadWriteByte(W25X_PowerDown); //发送掉电命令
) ?" Y) @% [0 ?( M& \/ G5 b. _8 |' O - W25QXX_CS_H(); //取消片选) M# Z2 H! T1 F' o9 S
- delay_us(3); //等待TPD
( _' x7 ?9 l7 Q* [1 g& ] - }8 f J6 ^/ d. j( p8 [$ }
- //唤醒: S& w: c$ R% s- z2 e( R
- void W25QXX_WAKEUP(void)* z o8 f9 X. H0 H$ {7 M* A0 v
- {
6 J$ N' J9 F8 v1 D# ?# t - W25QXX_CS_L(); //使能器件$ b- Y% P( Q$ q( T& C- z
- W25QXX_SPI_ReadWriteByte(W25X_ReleasePowerDown); // send W25X_PowerDown command 0xAB; Q% c4 K; P, I
- W25QXX_CS_H(); //取消片选
$ Z) S1 R/ Z: ? Z - delay_us(3); //等待TRES1/ R1 [9 P) Z9 H9 m
- }</i></i></i></i></i>
复制代码
/ x4 X; e }! }2 Z" Z' W( |" Y* p6 i' ` q' e* ]* e! v' y
头文件3 o# ~9 E6 t1 r- b3 |
- #ifndef __W25QXX_H7 S. T+ w* J: y( A8 k
- #define __W25QXX_H8 e$ G; `* Y4 G- a0 W7 H5 i; W
: A* N+ ]- m' g* X4 r$ `- @) G( ]$ h- #include <main.h>& e( ]. R5 z" m
& i( d# e" b1 c5 t9 B- #define W25QXX_SPI_Handle (&hspi1)
3 _ a* S" d* O7 x! D6 g - 9 b( t6 O& h8 l% t5 L
- //W25X系列/Q系列芯片列表1 Q% Q7 ~$ q3 u' C
- //W25Q80 ID 0XEF13
* t# U% H* b$ y- O - //W25Q16 ID 0XEF148 l6 r: c' s0 z- _
- //W25Q32 ID 0XEF15
, e% W9 F# X# A2 `( l$ ^5 x4 \; Y - //W25Q64 ID 0XEF168 w5 E9 y# c" x6 |
- //W25Q128 ID 0XEF172 Z* U1 u4 r& r* J$ L
- #define W25Q80 0XEF13 [" ~; @( _! ?3 ]
- #define W25Q16 0XEF14
( S9 ^8 u: w( h. X! H* H4 m$ u) B - #define W25Q32 0XEF15
% k5 S1 o. W8 X1 ^6 o: ]& M; I- j+ s - #define W25Q64 0XEF16, p9 y- o: T; ~; z+ j: e+ b# ^5 G3 L
- #define W25Q128 0XEF17" B5 h8 G; `1 ^8 V* h
- 6 x$ r) w0 A' a: P7 ]1 c& S
- ( q' L7 G4 y2 K, w
- #define W25QXX_CS_L() HAL_GPIO_WritePin(W25QXX_CS_GPIO_Port, W25QXX_CS_Pin, GPIO_PIN_RESET)/ U L5 I) G- H& l2 {
- #define W25QXX_CS_H() HAL_GPIO_WritePin(W25QXX_CS_GPIO_Port, W25QXX_CS_Pin, GPIO_PIN_SET)7 o/ m4 \3 W. Y% \1 d
- ! L" B* a. E7 [- P, m! H# M
- 4 T' S4 g! H3 @ ^* |! @1 N+ Q$ ?# K
- extern uint16_t W25QXX_TYPE;
# R: u- P' b1 v, L) y8 G% W - extern uint32_t W25QXX_SIZE;( a3 U; R7 z& {- }; {$ C
- extern uint8_t W25QXX_UID[8];
: B9 y. M" ] h3 u - + X* }" k K9 e- t0 S
5 i$ \/ m. v% s) P1 i2 X; T' Q3 i3 k7 L- //1 _, K5 r) H. ~$ a( d, y6 Z$ e
- //指令表
3 j2 j+ h* u4 h% Z3 y7 D! N - #define W25X_WriteEnable 0x06
' o8 L6 n: ^, E! ^: |- u - #define W25X_WriteDisable 0x043 \, n* t c, A+ m( _7 n2 Q. z3 g
- #define W25X_ReadStatusReg 0x05
9 {5 W- P+ f/ O9 x" K0 m - #define W25X_WriteStatusReg 0x01/ s! h9 P4 A5 w2 [2 E+ B
- #define W25X_ReadData 0x03 \6 K, `4 V4 Z- I/ g9 j% q
- #define W25X_FastReadData 0x0B8 M+ T" J9 Z4 d$ Z
- #define W25X_FastReadDual 0x3B" U) C0 @& i) F0 h
- #define W25X_PageProgram 0x02
$ L; |* t2 t2 ~) o6 ], j: j* D+ ~ - #define W25X_BlockErase 0xD88 v; z. U; U/ x' {1 v# r
- #define W25X_SectorErase 0x204 v; b% L" S- A
- #define W25X_ChipErase 0xC7
' ^3 J" X9 I- y* ]5 z0 P5 l - #define W25X_PowerDown 0xB95 {9 s* {4 G& A
- #define W25X_ReleasePowerDown 0xAB
( Q, r9 R1 K6 c3 ^ - #define W25X_DeviceID 0xAB! W5 t% `$ n. i! w% P& @
- #define W25X_ManufactDeviceID 0x908 W7 A1 J) I+ g1 q; Z9 B8 k
- #define W25X_JedecDeviceID 0x9F/ M" g1 j( P0 o' {
- ^# q1 p% N7 S4 \, h: [
- int W25QXX_Init(void);
( D5 E* @! P& A: l% G9 r - void W25QXX_ReadUniqueID(uint8_t UID[8]);& e% t4 X; ~4 i4 P9 j+ {1 n% B
- uint16_t W25QXX_ReadID(void); //读取FLASH ID
) F" v$ ~: o M x - uint8_t W25QXX_ReadSR(void); //读取状态寄存器. Q$ @* f( D! r3 {* J& L3 ~
- void W25QXX_Write_SR(uint8_t sr); //写状态寄存器1 p0 f# `+ s/ h' y
- void W25QXX_Write_Enable(void); //写使能+ Z# N9 c+ u: g) Z/ N
- void W25QXX_Write_Disable(void); //写保护
; N& w; i: ~/ v5 c& E Y4 { - void W25QXX_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);% \$ X5 J( B: J- h
- void W25QXX_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead); //读取flash
- Y/ m4 c& o7 `+ A- E, a; Z( @ - void W25QXX_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);//写入flash) U: \8 R5 d5 j
- void W25QXX_Erase_Chip(void); //整片擦除
& z- V; C; G+ B2 R" f* K }$ K - void W25QXX_Erase_Sector(uint32_t Dst_Addr); //扇区擦除$ q' T( G% v3 ~
- void W25QXX_Wait_Busy(void); //等待空闲
6 _# }/ G8 Y5 t1 S3 o( X$ w# o% u - void W25QXX_PowerDown(void); //进入掉电模式: W4 o8 O: _5 d- v) G
- void W25QXX_WAKEUP(void); //唤醒# l" j. y/ x1 y, b& t
- uint32_t W25QXX_ReadCapacity(void);
+ n& I- }- R; f1 B - 3 T& i' _3 ?! @9 r6 y
- #endif
/ z) ^; E0 ^. w& F5 [
& ~% U' T6 S# b5 b' U) M4 B
+ J& h4 ?- @ J4 h5 t( M; `! A- $ ]$ \3 l( x3 G
复制代码
- G% n$ s# l0 {' M! b3 n |