一、实验前期准备; s% l$ F J; ^" |& e# M! Q; X4 ]4 \" J
本次实验的 MCU 是 STM32F103C8T6 芯片,通过 SPI 通信实现 W25Q128 的读写操作。. b, S4 s' @3 N$ f c5 E
/ x7 T+ p$ m7 z. ]1. 原理图" ?# _. u& p P) g0 [' ]
; F2 {! m% {$ `2 ~2 X* `
( z; G7 y2 n/ G% y; Q7 F$ h- I
& e. l' i, O/ @+ Y, ~2. 引脚连接. I$ Y( b0 k$ q/ d4 _- C4 {
! `6 X( A( m$ x) b
* [+ [1 ?& E" z8 I0 a. y
( l6 B- c8 F& d; D' Y) _% W& X
二、SPI 底层驱动" g7 c' x+ m- X) z% c
SPI.c
) t% G0 L+ p+ X( u) f0 s7 b! I- #include "SPI.h"' A% }. k5 L6 } I) k/ P2 j
+ G* X$ l% t. H- /*/ _* l- i: c5 Q
- SPI引脚初始化配置
5 ~ M$ H; n6 z0 n - **PA4------CS
& B5 ?6 \2 o- A& d" _ - **PA5------SCLK
+ |- ?' u1 c- ^ - **PA6------MISO7 y' `6 n4 v. |1 p- n
- **PA7------MOSI1 O2 T. l" R/ ~0 h2 I
- */& Z6 |3 N3 G: V `& s
~/ s+ Z# i$ s# L6 E- static void SPI1_GPIO_Config(void) # I, H$ O- l( | R
- {
' v' x \$ N8 U+ t" r; ~( B/ M - GPIO_InitTypeDef GPIO_InitStructure;' i8 F3 U& s+ F, M6 n# `* ]
- ; s# P* h% B8 \+ y1 U
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );//PORTA时钟使能 $ C5 E" ^9 f$ |
0 _; K, ]4 _/ ?* { f) G- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5| GPIO_Pin_6|GPIO_Pin_7;
; W \# V, [' s( Q0 V, v! g: V - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PA5/6/7复用推挽输出
3 H; t1 M7 l5 c9 K" ] - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; I# l; g3 O7 u
- GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA
' _! Y7 Z; r; h" V" j0 r -
5 M J4 k- H8 k6 p" s E - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
3 Y. U( V$ ~% L3 b8 G8 t4 D( V9 W - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //PA4推挽输出$ q# `; G0 W. Y l* Z8 o" M
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
H3 I! a. I+ j2 I' O. I - GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA; {/ T9 {4 F! O( Z! F: G5 u" e* }
- 7 P2 m3 g9 {1 N4 A2 v0 y
- GPIO_SetBits(GPIOA,GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7); //初始上拉输出
, E/ M) ?; x) Q, a5 u& X7 |! r - 3 W' r. {8 H4 K% L8 D" @8 y
- }" m/ L4 ^. w8 c# s9 s' L
- M7 w _+ o+ p$ C/ P+ |- //SPI1初始化函数
& r1 ~9 P; s" I7 H1 E - void SPI1_Init(void)
' B- d6 N4 w* G - {9 t, V. ]7 P. N* x, C0 a3 y- e
- SPI1_GPIO_Config();//SPI引脚初始化配置 8 h5 J7 x, R! F: m
0 ?: m( q2 ^: e3 Q- SPI_InitTypeDef SPI_InitStructure;% v1 D6 t! R+ G( s6 s$ ~4 t
-
5 v4 P; n$ V" v9 Q - RCC_APB2PeriphClockCmd( RCC_APB2Periph_SPI1, ENABLE );//SPI1时钟使能
6 x& c K. g( D1 G3 f4 N$ F - 4 M- z( [* X2 N
- SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工1 b6 c, f7 z$ R' {9 k! T4 R0 L/ a
- SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI
?2 g- ^, r# E3 ?" m9 m* u } [" G - SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构5 w) d* O% \# k6 k2 e2 t
- SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行同步时钟的空闲状态为高电平
& \/ R1 N( V m5 L. B$ J$ O5 j - SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
6 C L, V/ r8 O& e - SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制" W3 |* g: u5 }/ T! }7 y
- SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定义波特率预分频的值:波特率预分频值为256+ E6 c+ v ^; O6 u7 V
- SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
* x3 J( z2 W% ~8 }3 C1 i! h - SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式- f6 M8 i B9 `, R; Y
- SPI_Init(SPI1, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器6 T! _) e3 a7 J. t4 _* @$ M7 @
- 3 c0 C* H2 O- k
- SPI_Cmd(SPI1, ENABLE); //使能SPI外设
4 w. {7 k5 K9 s0 M - SPI1_ReadWriteByte(0xFF);//启动传输) Y# k3 \5 D8 O
- } 0 A6 N! `% [4 \% _
- }$ d* J! ?9 `4 @. ?% u- /*
, D- M7 s/ d# a) X/ S2 g - SPI的读写操作
( Q3 a' u3 ^/ c9 r/ R1 i5 _7 Y - **TxData:要写入的字节
- G0 R" {5 w5 c% t/ e( i - **返回值:读取到的字节: a: V; C( y6 o/ J3 U$ m
- */4 o- N$ J$ Y0 H h+ J$ P
- uint8_t SPI1_ReadWriteByte(uint8_t TxData)
0 }; i2 c6 Z% p# ^- k8 e7 E$ W - {
2 z( C6 v; p# Y( l" z - while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
$ q: [! s+ X0 M4 N3 L - {9 p& {# U4 [7 t# @7 ^
- //等待发送完成
% ^1 v1 P) N1 @) S( f4 j+ ^1 Y" F - } , v7 R5 ?; z/ _
- SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个数据( t" _. p; _8 ?" o5 m% B% S
- while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受缓存非空标志位( g7 g" [; _( S9 {/ T3 ~( j1 T
- {/ R& S! l1 O. C; c0 o- g8 q3 \
- //等待接收完成
0 I) V5 J. D1 v/ C* v - } / Q" q7 |. Z! ?. W2 y
- return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据 % M5 u8 O$ n8 E9 @
- }
复制代码 / ]2 K* c! O' A d3 k6 r- E$ _
SPI.h6 r* y+ L# P& O! n- f3 j7 b8 q+ ^
- #ifndef __SPI_H5 U, d. [' S- n3 r: t9 n
- #define __SPI_H: E& l7 p% ?( o! M* O
( H. Q* y$ U( O: r8 [- #include "stm32f10x.h"
* H% W J0 [8 |% a" { - ; M& _/ W. _ k, D$ I
- #define SPI_CS(a) if (a) \
( D# E! w, _4 v9 p - GPIO_SetBits(GPIOA,GPIO_Pin_4);\
! }' J" }7 @1 n! K - else \
( t+ E3 l, M$ v* ]5 K3 s6 K - GPIO_ResetBits(GPIOA,GPIO_Pin_4)
7 t/ |+ T, o2 F$ y" g - , T8 \# E# ]' i% w- P: W7 T$ z
- void SPI1_Init(void);//SPI1初始化函数
3 I! S0 M( S: U1 z9 u9 ]/ v - uint8_t SPI1_ReadWriteByte(uint8_t TxData);//SPI1读写函数8 i) [" `7 T5 S6 w( ~
- ; w0 P- O3 Q" B$ g7 H# h5 e1 u
- #endif
复制代码
# I7 |- k* a7 d* J( S) L三、读取 W25Q128 设备 ID% M, v5 F/ S4 H& S) z p
本次实验是:读取 W25Q128 设备 ID,然后通过串口1打印出来。( X, F( u& h# y
通过简单的实验来调试,有利于发现问题。3 H& f% V4 C: ~+ |& f( G
通过W25Q128的数据手册可知其设备ID如下图所示:8 {6 p1 f! C1 b( G: I
1 A* g9 Z" S' a! ?: s9 c6 N2 W
* |% F6 \# Z4 B0 a- O1 t! Q# X: h3 S
W25Q128.c2 C+ w2 k" o7 u" t3 J- o! G
- #include "W25Q128.h"
. q2 p! U4 ~' [6 ?" C - #include "SPI.h"
$ \3 Y5 s/ q1 U+ v: E; L
4 a r; p8 d5 [2 d7 u# J- 7 S; @( z( y2 V3 o* n8 `
- uint8_t W25Q128_ReadWriteByte(uint8_t TxData)//函数包装一下+ u% w% _2 h2 A9 ~- O: p$ y
- { 9 j, q5 l4 Z& ]4 E3 Z- l! T
- return SPI1_ReadWriteByte(TxData);
) v# z$ D" B9 s" X - }* s2 K, \; b. p5 \2 B* v
- 5 Q# ^9 e& o5 f# h* K# V
- uint16_t W25Q128_ReadID(void)//读取芯片ID' y( f" X# x# ]( c
- {
8 R1 A- j2 \0 y' V; I7 s7 S - uint16_t Temp = 0; 1 ^, ]0 X- p0 K, R# E0 ^
- W25Q128_CS(0); * V1 Z9 j- S" c( \- E5 _
- W25Q128_ReadWriteByte(W25X_ManufactDeviceID);//发送读取ID命令 / ?( }3 V3 U% U- {2 d' Z
- W25Q128_ReadWriteByte(0x00); - e& R" h# c. w2 ^6 m" \$ h
- W25Q128_ReadWriteByte(0x00);
0 `/ _9 o8 J0 k. r - W25Q128_ReadWriteByte(0x00); % D1 H7 h, X. m1 [! |) b5 p$ P
- Temp|=W25Q128_ReadWriteByte(0xFF)<<8;
& E! Z/ S: G: R c - Temp|=W25Q128_ReadWriteByte(0xFF); # }' v. M- u+ q/ D
- W25Q128_CS(1);
* i L* E2 Y2 F- b4 ^& j - return Temp;
: J1 s8 @% P! G' A+ p1 L, g7 y! w - }
复制代码 9 f% I2 }* n# r
W25Q128.h
/ ^' I: a; f2 ?- #ifndef __W25Q128_H+ s$ T, h( [; E$ c, s7 m' ~
- #define __W25Q128_H2 H; m- T2 ^% a0 M9 e
r2 v+ _3 k5 E4 }; O, b& S; C- #include "stm32f10x.h"
- o( i/ D9 A: E2 _ Q$ A5 k! J
$ v% K; Y1 ?4 v. v9 @' @$ }# G$ m/ r- //操作指令表
! C- o. k ^' F2 N - #define W25X_ManufactDeviceID 0x90 //制造商+设备ID, J* e# x/ Q" I
6 H3 S4 i: L8 r& N ^+ Q6 O- #define W25Q128_CS(a) SPI_CS(a)
o8 [2 u, L# I8 `6 d# J
# O1 C) L3 U: A6 E$ j7 f- uint8_t W25Q128_ReadWriteByte(uint8_t TxData);//函数包装一下4 p( q, C$ v# K0 U, Z4 l
- uint16_t W25Q128_ReadID(void);//读取芯片ID n4 F& i# ?1 j& H$ }
- 3 i1 K2 i% D' C
- #endif
复制代码 . x' ^- p# Q+ B/ e: S( ~
main.c
" ]% D; g9 E7 I, ~- #include <stdio.h> i6 o' ]) F/ T* J/ S
- #include "Uart1.h"
8 c8 `& B5 A g A - #include "delay.h", m$ K5 S, R" D+ w
- #include "SPI.h"
0 }5 C8 g5 U5 m P: I1 ? - #include "W25Q128.h": \3 T7 `7 B! j& [! R
- 1 m/ C( Z5 q+ ?+ }3 C- n
- uint16_t W25Q128_ID=0;
1 _ k6 Y! a! S* ^4 M
. G7 A( S& E0 a- int main (void)
' h1 v( ~' Q/ @( h( T: k& P) ~ - {
: v, X* L/ t; ] - Uart1_init();
! ]) o3 P- {% N3 u - SPI1_Init();
* U6 R" A4 R) v -
3 o7 u0 J: h8 o, n+ y/ B. ] - W25Q128_ID = W25Q128_ReadID();
" w/ U; \: D4 v0 d, u- M - while(1)- ^8 J; ] q; B$ Y8 w
- {& Y G l5 X `% y2 z8 e
- printf("\nW25Q128_ID=0x%X\n",W25Q128_ID);
}3 A, a7 R( J - delay_ms(500);9 l8 J, ?. X6 A
- }5 W6 p! Z2 ~$ Z3 G6 O- T
- }
复制代码
3 s- h2 T! w0 p" X/ Y) Q: Q实验结果 G, c2 T0 C9 _
, S7 v. b, G0 F' s
- H2 X2 I# b! M$ T9 T& E; Y
接收到 W25Q128 设备ID 为0xEF17) X9 @, i- ^8 F; s' a( Q. [
$ A9 P" c# [# N |5 e
四、读写 W25Q128 外部 Flash
- A5 l) X& o% a3 [W25Q128.c9 V0 {1 a1 e1 o: M" X
- #include "W25Q128.h". t: Q( M- H! {4 r% @
- #include "SPI.h"
5 I3 ~0 I; p( @. W! P3 q. e
. }1 {7 Q% v( T( [: M$ y
7 o& H- [ N$ ^: y- o6 }- uint8_t W25Q128_ReadWriteByte(uint8_t TxData)//函数包装一下. t4 C0 A% q4 R3 b
- { 8 D! Y3 k! N( v# f, V7 J0 W1 y$ l
- return SPI1_ReadWriteByte(TxData); 9 Z7 c2 F. _/ S1 V
- }. H3 k5 h' g% P" N- E
, R! P I3 N/ C/ k: [& P- - p3 j' d- j4 g4 [( l# W
- uint16_t W25Q128_ReadID(void)//读取芯片ID
) J$ i7 r8 i" f6 b B" } - {
R# ]1 \5 ?+ |9 P" w1 K, q. z' S - uint16_t Temp = 0;
8 y1 m2 i3 ?$ G/ n# q - W25Q128_CS(0);
/ P( W# c9 ^3 d. T: Z- O - W25Q128_ReadWriteByte(W25X_ManufactDeviceID);//发送读取ID命令 # C) m/ c! \# E! n) y/ t, l" }
- W25Q128_ReadWriteByte(0x00); 0 T$ v" A8 E; e' ~9 X" U# \! ]! Q) a
- W25Q128_ReadWriteByte(0x00);
2 i3 u& b+ C+ V C% X0 \ - W25Q128_ReadWriteByte(0x00);
2 Q! ?5 M' z- s; w1 ~( T- v - Temp|=W25Q128_ReadWriteByte(0xFF)<<8; 0 A9 x/ |5 ]% a/ { l
- Temp|=W25Q128_ReadWriteByte(0xFF);
. ~9 R! z8 u# R - W25Q128_CS(1);
P, }* A6 A( n/ L% L5 x - return Temp;( _) ` W# D5 X- V$ q6 i0 Y2 y& m: N
- }* p6 F' d( N1 \7 V9 T' S* Q
7 x6 h% R/ T7 V( w" E- //读取W25Q128的状态寄存器
: ?$ r5 r% ?7 S8 c- v0 _( l, g' f6 E - //BIT7 6 5 4 3 2 1 0
' y: N! e7 b, ]' a' t, U - //SPR RV TB BP2 BP1 BP0 WEL BUSY
. y& r$ R( s! a/ f; y3 a - //SPR:默认0,状态寄存器保护位,配合WP使用1 x O7 x" z4 Z: } O0 n
- //TB,BP2,BP1,BP0:FLASH区域写保护设置
1 }) z& ]# ^- F% t+ `- O* m# k - //WEL:写使能锁定& `& t% w0 u6 Z( ?- \/ T
- //BUSY:忙标记位(1,忙;0,空闲)
2 c% ~& W0 D/ ~0 w/ y4 X+ t- Q - //默认:0x000 p" m7 S6 o% t/ _7 p4 G) x
- uint8_t W25Q128_ReadSR(void)//读取状态寄存器
; D; ?- Z1 \$ R6 c - {
- S$ U S5 @0 m1 o e" _/ ~" j' G - uint8_t byte=0;2 z" H$ ~; h P. M( g- E/ T L
- W25Q128_CS(0); //使能器件, x: s* u6 A3 U5 z2 f
- W25Q128_ReadWriteByte(W25X_ReadStatusReg1); //发送读取状态寄存器命令0 R1 D3 e& e" Y& p7 ?% O0 t
- byte=W25Q128_ReadWriteByte(0Xff); //读取一个字节
% f. E3 D4 V1 m3 B - W25Q128_CS(1); //取消片选; I5 K+ d$ i5 z; R7 `
- return byte;4 R5 a7 k7 z. T7 E2 n
- } w/ {5 ~7 l4 K
- 3 i' `+ M9 B4 N
- //写W25Q128状态寄存器2 Q7 U% k3 p5 z: ]+ d* \
- //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
( G/ Z& |8 o2 n" O8 a3 B8 Y - void W25Q128_WriteSR(uint8_t sr)//写状态寄存器3 X8 Z: f4 t& p+ R+ D
- {
! l* z4 f+ i/ i* y4 O7 J: ~% ^ - W25Q128_CS(0); //使能器件
. J& b: m% I7 p: u) o1 y3 M - W25Q128_ReadWriteByte(W25X_WriteStatusReg1); //发送写取状态寄存器命令1 W, j. |2 e) Z/ u# Z- M2 \
- W25Q128_ReadWriteByte(sr); //写入一个字节
0 e3 t, N! s$ G6 m3 p - W25Q128_CS(1); //取消片选
) K0 u4 }, x5 r: u! h' b3 y* |7 I - }
# V R- h2 ?; n* m - + @5 l% g5 _0 e
- void W25Q128_Write_Enable(void) //写使能
& @. |; c ]1 C; i1 W - {
2 @6 q* M$ E' f, r - W25Q128_CS(0);
) x. D# Q9 {' U: q6 M( L - W25Q128_ReadWriteByte(W25X_WriteEnable);
) F. d0 q. q, U - W25Q128_CS(1); / ?- v" R9 I6 ]* Z( s
- }
6 j5 M1 @$ p2 s$ ~' J% [+ j3 e - * W, y: Z3 Z0 U( [: b4 a$ E
- void W25Q128_Write_Disable(void) //禁止写入
1 O1 W5 |0 n& r& G - {& w9 B8 I; z/ O0 k" Q/ ^" v
- W25Q128_CS(0);
9 C! Y/ f! U; O/ H5 d% F2 E9 D - W25Q128_ReadWriteByte(W25X_WriteDisable);
- F U5 u3 w. `8 T' P* V* n - W25Q128_CS(1); 7 a1 }! L7 z! P4 p9 `
- }9 G4 t& w+ A8 U; R/ {& y% g$ I
- ( d8 x1 R% l r1 Z( Q* X
- void W25Q128_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead) 8 X9 c. j9 ?) Y$ A
- { , D2 t/ u1 I+ A" ]$ }
- W25Q128_CS(0); //使能器件 * v$ T$ T) U. p& y
- W25Q128_ReadWriteByte(W25X_ReadData); //发送读取命令
2 h5 u0 u& W% t2 T1 [ - W25Q128_ReadWriteByte((uint8_t)((ReadAddr)>>16)); //发送24bit地址 x/ s$ J* [! @1 m- ]; X
- W25Q128_ReadWriteByte((uint8_t)((ReadAddr)>>8));
0 e& s' ~4 B4 M$ n( c9 H# Y9 a - W25Q128_ReadWriteByte((uint8_t)ReadAddr); 4 u' U6 a6 v: ?0 ]6 j3 t2 s
- for(uint16_t i=0;i<NumByteToRead;i++)6 E: t2 u5 I. u( g( u* l
- {
& O+ u6 ]5 L; K k2 Z - pBuffer<i>=W25Q128_ReadWriteByte(0XFF); //循环读数 3 [# t/ R& M, M5 J7 H+ R* N' Y
- }
+ n) \" z/ M3 I1 \1 \; P - W25Q128_CS(1);
1 r& R- q5 v8 B: a( W5 G) f - } " Q; f1 E+ D& y2 m
- $ E q$ L( Y( o: y+ s/ V6 x1 W$ j9 R, n
- / O0 v y. j8 u4 |# M+ K
- void W25Q128_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)% U# o% ~5 O; D
- {
+ K! q* t$ M" M2 |6 ]) \ - * c1 }( _+ j9 X5 K' r H1 P/ j
- W25Q128_Write_Enable(); //SET WEL
: A6 m8 o; ~1 t: C - W25Q128_CS(0); //使能器件 % _6 h( b+ |# J. m+ Y, H @5 E
- W25Q128_ReadWriteByte(W25X_PageProgram); //发送写页命令
& P! i, A- U9 g, j" O - / W* Z' G+ X, G
- W25Q128_ReadWriteByte((uint8_t)((WriteAddr)>>16)); //发送24bit地址 9 K. T% T' B7 s% x
- W25Q128_ReadWriteByte((uint8_t)((WriteAddr)>>8));
2 N ^2 H5 I5 B& f d- a, w - W25Q128_ReadWriteByte((uint8_t)WriteAddr);
# z8 a$ W" m! y: F l& Y - for(uint16_t i=0;i<NumByteToWrite;i++)! ?* v P" ?- p+ \: d4 I
- {
$ ^, L( K. R7 T. Q+ R" N6 O - W25Q128_ReadWriteByte(pBuffer<i>);//循环写数 . ]% Y- g. h+ ]! ^' E# \
- }
& o& _0 v- ~ n+ s - W25Q128_CS(1); //取消片选
, b7 J% c5 R4 F" l8 F2 n* J - W25Q128_Wait_Busy(); //等待写入结束
/ U5 W& x0 W" ?+ c4 H% v - } ' t2 o' I( _- Q3 C. U0 N
2 g" s' w; R# G( i0 _- //无检验写SPI FLASH
. F+ s! J# s- H- L! P- `" W - //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!- Z* k" f' E" Z, J# E4 ^0 ~
- //具有自动换页功能
: c7 e& F; j- S! ? - void W25Q128_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
/ }& z1 i9 b; w* _ - {
6 x2 D8 b, v: i3 e/ ? - uint16_t pageremain=256-WriteAddr%256; //单页剩余的字节数 [# W7 P j. S- @+ P6 p; N3 y7 x
- if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节) S) r& o9 e2 u) ~
- while(1)
9 Q$ s# o8 M0 O: U - {
" Q; n) ~4 ?7 ^' g% e; b" i - W25Q128_Write_Page(pBuffer,WriteAddr,pageremain);% l) \$ l0 J0 c& U1 _! t$ S7 X& v/ X
- if(NumByteToWrite==pageremain) break;//写入结束了
a+ o2 O( R6 @* O U - else
1 x5 `1 ~$ J$ d" X* Z; X- d% ?) \ - {
# U3 v; ]5 s: ] - pBuffer+=pageremain;/ `7 _# J/ x- x4 q* o
- WriteAddr+=pageremain; ( o& U$ K, K9 m3 @
- NumByteToWrite-=pageremain; //减去已经写入了的字节数9 i! E8 k' `% C" j! o3 f
- if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
7 K+ u6 {- {% T! ~2 o& T - else pageremain=NumByteToWrite; //不够256个字节了
) R% f0 Z; C" H. q# ^7 I - }5 [5 Z' j! h2 d" q1 ~
- }
- M! a: c, C7 ` - }
" _, `# ]% ~; B1 }% E - C+ {# U" t A. p4 p
- //写SPI FLASH
" ?7 j( r, d; m - //在指定地址开始写入指定长度的数据" C, Y( t1 B: E2 t3 I- c( O
- //该函数带擦除操作!
1 w+ V- T/ \' V" z! O - //pBuffer:数据存储区; }4 K c! P/ [9 y7 |
- //WriteAddr:开始写入的地址(24bit)
7 f, \' j0 j( V% ^: H - //NumByteToWrite:要写入的字节数(最大65535) , z, V6 ] p3 u" U, \' s+ @+ e
- uint8_t W25Q128_BUFFER[4096];
9 w- L1 n3 V! O1 c+ u1 V - void W25Q128_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
/ }. V2 h2 r: ~+ V) t4 L - {
' w, b# J% L+ N+ o/ k; O( h - uint16_t i;
1 P' h! k/ q: R - uint8_t * W25Q128_BUF;
$ b- e. e+ ^5 Q. \8 I ]! f - W25Q128_BUF=W25Q128_BUFFER;
2 B) r8 c1 `7 d6 t- O' H - uint32_t secpos = WriteAddr/4096;//扇区地址 * F/ r/ j( K5 R
- uint16_t secoff = WriteAddr%4096;//在扇区内的偏移" g. V9 c3 X7 c( V
- uint16_t secremain = 4096-secoff;//扇区剩余空间大小
6 ~! O% _) q! X% L
& V: f& I1 o# D2 K( K- O- if(NumByteToWrite<=secremain) secremain=NumByteToWrite;//不大于4096个字节
0 e; s: y) E: [9 \; n3 `, o& w - while(1) 7 _; g* X; C. x& b; U2 D+ z
- { 8 I; [4 d( G1 z& W# U
- W25Q128_Read(W25Q128_BUF,secpos*4096,4096);//读出整个扇区的内容
+ s* g+ D. P- u - for(i=0;i<secremain;i++)//校验数据
) O" N6 ]' N- B8 {% c# U1 _' Z) A' g2 g - {+ q3 x+ U1 m( ]7 N
- if(W25Q128_BUF[secoff+i]!=0XFF) break;//需要擦除 0 ^2 ?$ g T$ _9 ?) b
- }
6 I5 n& A- t5 J) j4 d - if(i<secremain)//需要擦除
! v) e1 b0 v& @2 g - {
* }8 u* ]+ R7 q ]! k - W25Q128_Erase_Sector(secpos*4096);//擦除这个扇区
" w; Z$ W) @2 u. ?# y - for(i=0;i<secremain;i++) //复制
- A' u# w; ^# P: z - {
* P+ c- W5 }& D# i+ a1 D1 f3 j - W25Q128_BUF[i+secoff]=pBuffer<i>; 3 I( P1 s$ `7 h g
- }% }+ S2 d, u# ?& }+ q) r
- W25Q128_Write_NoCheck(W25Q128_BUF,secpos*4096,4096);//写入整个扇区
8 c) }7 ~# a% ^0 Q. v! \
/ h( F% I2 ^+ h, A( _7 }# N$ Z% @. ]- }else W25Q128_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间. ; v6 Q) T6 Q/ E, E- l* i3 B
- if(NumByteToWrite==secremain) break;//写入结束了, @" Z4 d0 p, o4 J& _. A- F
- else//写入未结束6 D. Q& b- S) I1 T* ^6 I
- {. b- Z S5 h7 R5 \, k4 G
- secpos++;//扇区地址增1
8 l' F. {$ h: \2 T# H - secoff=0;//偏移位置为0 ; s* W* q2 R: L# I5 W
- 6 f7 b$ \0 ^" Z& o) @
- pBuffer+=secremain; //指针偏移
4 Y) V1 `) ?; K - WriteAddr+=secremain;//写地址偏移
. w. N: o- e. \! z' M, f - NumByteToWrite-=secremain; //字节数递减4 h/ j6 W- K% G
- if(NumByteToWrite>4096) secremain=4096; //下一个扇区还是写不完
! D% q8 [" v) Y4 M9 }. F - else secremain=NumByteToWrite; //下一个扇区可以写完了9 s- H% {1 D0 D2 T" w2 H' g4 t
- } $ c! p) q6 M; L& H8 X
- } % h+ U* d3 R* H4 v6 I
- }
7 X( v$ S5 D9 k" L
2 ~- l5 O3 b7 Z2 {4 D9 r4 U- }- //擦除一个扇区+ M2 I1 \9 m8 G1 z V! [: f A
- //Dst_Addr:扇区地址 根据实际容量设置
4 G# @, y1 j9 v8 z, M$ ` - //擦除一个扇区的最少时间:150ms+ c8 L w4 K6 P( g H5 w
0 g' H! l5 f& { S& J- void W25Q128_Erase_Sector(uint32_t Dst_Addr)
: J% c! B! h; I3 u" C. ?2 G s* j - {
& ]4 m1 ^" \( |2 \3 C - W25Q128_Write_Enable(); //SET WEL
" P9 ^: W0 G0 v - W25Q128_Wait_Busy();
: A2 G; k7 e1 L2 Q* X3 k0 l - W25Q128_CS(0); //使能器件 5 \( J; J0 F9 ?! T3 n4 h( K& }
- W25Q128_ReadWriteByte(W25X_SectorErase); //发送扇区擦除指令 0 h; \4 h7 p8 f' |" H- T8 A# t
- W25Q128_ReadWriteByte((uint8_t)((Dst_Addr)>>16)); //发送24bit地址
* I& a' I3 L" o0 n$ r" j - W25Q128_ReadWriteByte((uint8_t)((Dst_Addr)>>8));
8 `; J! `& q3 \2 o% m - W25Q128_ReadWriteByte((uint8_t)Dst_Addr);
- _4 f, z1 F" z. ?3 J" P - W25Q128_CS(1); //取消片选
4 U3 ^, d: a$ H - W25Q128_Wait_Busy(); //等待擦除完成
7 P4 A4 ~) F$ E9 L& r. E/ s - }
/ l6 a# s# D# c" {/ i) u - 8 ?0 Y; n$ A' i, | g
- //擦除整个芯片 0 c5 v9 j9 A) ~! A& F
- //等待时间超长...
! A/ ~ \2 F" d - void W25Q128_Erase_Chip(void) ' w( ^7 a0 w( \5 Q }
- {
$ \' D" H# E( X# Q& B - W25Q128_Write_Enable(); //SET WEL 2 R ]8 S t. U6 N5 @
- W25Q128_Wait_Busy();
1 p j6 E& B" y$ e2 | - W25Q128_CS(0); //使能器件
- |7 |2 a' `$ V; K- Q, R8 e - W25Q128_ReadWriteByte(W25X_ChipErase); //发送片擦除命令
! I" z, l/ }- B9 t8 Q1 \: v - W25Q128_CS(1); //取消片选
0 J1 a5 j) [) O" O* |3 V; s - W25Q128_Wait_Busy(); //等待芯片擦除结束# N$ |4 H/ _( {+ R2 Z( }) G
- }" l5 E8 Z4 v4 l" d2 w4 r: M' [
/ b# F+ q' k+ R7 M5 x2 d! L7 `" d- & l0 t. P1 S3 R8 n
- //等待空闲
7 l8 o- }6 N5 f$ J - void W25Q128_Wait_Busy(void)
- u# m/ z% {& n1 W - { 7 C0 m6 U8 e j0 d
- while((W25Q128_ReadSR()&0x01)==0x01); // 等待BUSY位清空) g3 `2 r+ @, \; ^* o
- }
' R; g5 a t2 B& e, } `& l5 { - % M t- G) O. j: n0 F
- //进入掉电模式
' D) e* u7 w& _# M. A - void W25Q128_PowerDown(void) 8 I4 a5 H @/ f5 u( y' @
- {
" S' \! c# n+ [ - W25Q128_CS(0); //使能器件 ) `- u) h) ^/ {+ k- C# ], P. A& P
- W25Q128_ReadWriteByte(W25X_PowerDown); //发送掉电命令 9 J( o' L9 k! j( X; A
- W25Q128_CS(1); //取消片选
. ^+ k7 z1 e' V) H - } $ V3 R4 L2 ?% u; I4 A ?: E
- //掉电唤醒5 {# A9 H, v0 L9 B
- void W25Q128_WAKEUP(void) + d) y7 @$ o6 X/ J6 H9 l& I. y: E7 o
- { - y9 W4 k7 Y4 H1 ^. u
- W25Q128_CS(0); //使能器件
0 [7 ^* v+ d% S0 q5 h& f - W25Q128_ReadWriteByte(W25X_ReleasePowerDown);
; Q; V, l! W8 b) O4 H - W25Q128_CS(1); //取消片选
2 _, F+ Z4 y3 ?9 P. }, o - } </i></i></i>
复制代码 0 t) \/ ?6 Z6 { y
W25Q128.h
0 b6 e7 Q- P& \$ I: Z#ifndef __W25Q128_H+ C$ X/ ?, w4 f" r5 g
#define __W25Q128_H
0 [% b' {& |# m! K5 W. Y" H/ i9 |- U# r# F# C/ o0 ^
#include "stm32f10x.h"1 y; [% Y2 l2 }
! T( ]: R7 K7 N
//操作指令表+ k/ `" r% f, ?) C
#define W25X_WriteEnable 0x06 //写使能; [7 o& c/ X" J: T
#define W25X_WriteDisable 0x04 //写禁止
. Q% F4 k; d& p5 N#define W25X_ReadStatusReg1 0x05 //读状态寄存器10 Y" ~+ @: y# Y) y$ D
#define W25X_ReadStatusReg2 0x35 //读状态寄存器2
0 ~) Y0 a! e2 F#define W25X_ReadStatusReg3 0x15 //读状态寄存器3! n# u0 W$ C$ J! t. o
#define W25X_WriteStatusReg1 0x01 //写状态寄存器1
X5 h. G5 H- v8 g# q#define W25X_WriteStatusReg2 0x31 //写状态寄存器2
8 ~$ o' q0 O7 _8 ~% P9 Y#define W25X_WriteStatusReg3 0x11 //写状态寄存器3- |( l3 _. {) X# ?. _1 K4 I
#define W25X_ReadData 0x03 //读数据
+ w2 W6 `7 }, {) C' K z#define W25X_FastReadData 0x0B //快读
. K, |: S, S( g6 r; ^, ~% u#define W25X_FastReadDual 0x3B //双输出快读; D2 Q1 V. `* k% G; O/ ^4 C: E
#define W25X_PageProgram 0x02 //页编程
4 a* t/ }; j r* M, T* k- C#define W25X_BlockErase 0xD8 //块擦除(64K)5 o5 c- P/ r9 n4 Z0 O: Y& U4 n- o
#define W25X_SectorErase 0x20 //扇区擦除(4K)) o9 i! n) C& L1 E
#define W25X_ChipErase 0xC7 //芯片擦除
& ^5 I2 Z! N, c9 K#define W25X_PowerDown 0xB9 //掉电
* e3 ~% P# @. W1 i* i* ]#define W25X_ReleasePowerDown 0xAB //释放掉电; H( E `, c2 _5 ?2 ?2 f5 d
#define W25X_DeviceID 0xAB //器件ID2 R& y, z) m4 \* Y C( D n
#define W25X_ManufactDeviceID 0x90 //制造商+设备ID% S1 l: s& y+ T% C' b- I' Z
#define W25X_JedecDeviceID 0x9F //电子元件ID
5 q, z( p$ Y" a. Y, r& I8 e) f( }
5 ]# |" T# U0 x+ u0 ?3 O# a& w i8 `! H4 B. k) r! @
#define W25Q128_CS(a) SPI_CS(a) - e% t5 @! Z' R+ j J0 U+ T0 v: M3 _
) f, @4 r A' S- g! I" K( a( S7 W' Q) N1 h. p
uint8_t W25Q128_ReadWriteByte(uint8_t TxData);//函数包装一下
: s& z8 _5 i/ g4 J6 Q( z. C/ k$ \/ n# C$ N2 w' b g
uint16_t W25Q128_ReadID(void);//读取芯片ID
# E/ n3 d' w/ A: g8 X, v8 b$ e4 o
8 A) C0 L2 {# [* a; @. Zuint8_t W25Q128_ReadSR(void);//读取状态寄存器
+ _1 B+ ~6 C5 w$ v/ c6 Tvoid W25Q128_WriteSR(uint8_t sr);//写状态寄存器
" Y0 V, ^& }& G2 g; o3 B( v; {9 Z. H5 o+ Y% s& O3 B
void W25Q128_Write_Enable(void);//写使能) d+ w, K! ?" u; o& n- H
void W25Q128_Write_Disable(void);//禁止写入 $ [+ ?( [: b" {6 s9 ]0 G2 L/ O/ P
7 v* `& G$ z* m2 G* v
void W25Q128_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead); //读取数据
# b9 b' i; W4 J* ^) Ovoid W25Q128_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);//页写" L0 F6 p) l) a, W" I
void W25Q128_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);//无检验写数据,可自动翻页% Q! h# y$ C8 I. V' K% n
void W25Q128_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);//写入数据,带擦写功能
: @; T k6 V! N' j) C" y( K9 Y: x2 m7 \4 ^
void W25Q128_Erase_Sector(uint32_t Dst_Addr);//擦除扇区
( }% ^. Y f- }void W25Q128_Erase_Chip(void);//擦除整个芯片
! M) |( q; r; n7 a" e: L c7 ^
' H0 G( [' ~# H+ C7 N9 J# Tvoid W25Q128_Wait_Busy(void);//等待空闲
% g! q) P) g+ [* {0 F; E+ P& p& Svoid W25Q128_PowerDown(void); //进入掉电模式- |3 j/ q8 `+ c. J2 C! z; {$ p
void W25Q128_WAKEUP(void);//掉电唤醒
5 L2 Y% b4 J7 H+ L* H
& C- e6 h. [" B; M" T$ D% H. i: s#endif
& z) M+ N( |7 t4 S$ u, n$ q) z+ n) X) v7 f& d
1
`8 R7 ^) R- W( t8 V2
H+ c+ _: r: u! d! o3# M# q: ]2 ]6 `6 O1 ^! b% E7 d
4
2 R+ l; B$ Y0 ?$ F& a/ h/ b5
& ~: {$ b2 `' N* ~1 s9 {% ~66 y! Q: _: ]) f& I O
7 `) ~$ J. Q: ?/ J4 v
8
6 X9 X3 I8 c0 {. Z4 q7 D8 c9
2 t3 z R6 \3 d10
! N& ] z& Y* o( p% E11
% Q2 k' s/ C# _+ l& t8 N123 O9 u. v( J0 {2 S4 q- Y" }1 |. o
136 f) K | j3 B1 J
14* ^$ v( n E! ?+ z
15
; j6 u7 K- N, q2 G% R: y; f$ p16
% J, m# p) Q* ]" w) n( ^. H17) ^' J' K' _, v6 a0 V+ b: W( c! P
18
- F3 ^$ J: z8 u8 n; K19. N0 |5 i, l( S' y& a( f5 c
201 f, F/ ~2 M& o2 D, ~# Z9 E
21: e( X7 }) s7 L- z' l. ?
22
( z. i8 |& U7 H' l23
9 b$ r, Z; a: I f: l, O24
# J" T% b' M& X' m" s# a25
" _; ?0 x* B- N& w9 e26
9 {+ v+ q' k! l( Y273 w. f# e' O. L4 f7 o6 q) I3 f2 A
28
9 d2 I; o8 @- v29/ H! f3 m# H/ d
30; N7 L' F0 O9 z2 f. C+ b
31: O5 q! p n9 x) Y
32
( C" J/ B1 `6 n/ }& b. `337 B, \' f! i3 o: i, D; h' Q7 \
348 d5 x) c: h5 E
35
$ v( e7 t" x1 \36% U2 X9 p7 z; Z' Z$ k: m1 Z+ U" v4 _5 b
379 M# U2 e/ \# X) ?
38
. Q9 ^' r( F3 G# M) A39
/ O. G1 x* x( ~4 G40- G! ^: e. d u! I, h
41
. g1 H; u6 f$ ~42
8 m* M1 ?- R+ z2 Q; ~43: Y G9 \9 ?; n2 R4 l
44
+ _/ E. P5 n F: z& M45: Y. ]! u# M. K2 m0 A& G# s
46; z! R- t5 Z5 p# X# N4 z
47
# v. m5 X. }- M48
, A+ |% t7 C. d$ U! f49
. _2 R- a# [6 y4 ]0 @& v z50 l3 s! W( _6 G9 p+ ]
51+ u( P2 {3 E, j
52
# C( F s u% j& w$ G* j53" k; U2 k" T7 c* D
54
5 U1 @1 i! k$ \ }3 T% smain.c
8 }7 [" V) s' }: \: j3 I8 J#include <stdio.h>
* F' j* r, N8 Z0 R#include "Uart1.h"4 Z" z2 @% c R- l" C) f
#include "delay.h"% ^0 F/ y+ f7 B0 n1 k8 }. E: O, h+ O
#include "SPI.h"+ F7 [# P- c0 z! m( x
#include "W25Q128.h"' b9 C+ d8 ^. Q: y( `# W
7 P, A" [5 g( b, |/ ^
uint16_t W25Q128_ID=0;) }5 q/ G h9 @8 N" A9 o. u
uint8_t Write_data[20]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
& c8 F- K$ |9 }1 \uint8_t Read_data[100];$ x, P! o& h! I
0 n9 i! K w4 a
int main (void)( B2 ?3 F' F" j
{4 I6 r8 c0 I8 M5 p7 v U" s
Uart1_init();5 I: o s/ f; a
SPI1_Init();0 |# A* y& n7 N( `4 |7 \/ i9 b
6 P' D1 d, i& J, h0 y) U
W25Q128_ID=W25Q128_ReadID();0 P$ p8 M: J& U, S2 X# s
W25Q128_Write_Page(Write_data,0x01,20);
# {! v& B: }" ]" v
8 @) [4 S1 s" `. r: ] while(1)
! ?: Z# q& v" H7 O* P- @ {. q, Q/ o) t- H% d! y
W25Q128_Read(Read_data,0x00,100);
% x0 C/ J+ ]* g" ?4 m9 y7 Q for(int i=0;i<100;i++)/ ^* t/ I' Q6 M! w3 F. ~: _
{
4 \+ |% Q, k( @: i- S9 W printf(" 0x%X ",Read_data);
0 x) Y5 n ~8 o9 }$ }: L" B# p }
; D7 G9 S- m" `* Y2 u9 ?: Q& l K, m printf("\nW25Q128_ID=0x%X\n",W25Q128_ID);+ {8 m2 S) s1 X, _! J% y
delay_ms(500);# \& u9 @0 }- A, W
}
( {# g3 |4 R5 c$ D, r! a0 d* X}
1 n: Z' n' r: r, _) S# x3 ~$ N$ @ V9 _: w+ b
1
- b( D) I% w3 o& `% U% Z( c2
+ T' ~6 ~1 e# Z, ?) T3 i* y3 i0 N' _3
# F0 L' U) s$ {) O# T4
4 v1 H: F1 q& S) S: L- P* d5! N6 `3 x9 G3 }. Q# a/ Y3 f. x" U) i
6, z: `! _6 N" r& l+ j; H7 G' Z' u
7
9 A& V; e7 {& g# w. Z! g) Q( E8
& l1 L, W" c9 w% l/ t' f9: m* h7 f8 W( f! n* C& N+ Q" F( p
10
. S1 }7 T# U6 B1 ]" k! d5 o, B11
- [2 s2 A8 m. v12* c8 g9 R$ I" p8 M: S2 P# {) B, }: O
13* G8 h) S, K0 D& E
143 |* J2 y. f- Q5 l h6 ]$ U
15' K+ W- Z) n' @+ |* U/ X" ]% c6 O
167 Z& ~' l Y* q
17
. u7 J k. {" w8 e, j" L. i18
( `* z4 R' Y v7 X/ k% u- d: D19% ]8 J, [4 X) J* R' I
20% h+ K C# [1 X+ P' K; \, w: ~
21
5 A, @/ L3 J0 m' T22
. I: I- E5 L& l; m4 u+ A23) o% z6 R% j* [! ?$ _/ S
245 z. M4 J4 L- \9 R0 ~
257 A3 n: v5 P3 v5 p
26) e' a! I: ?* J# D6 w
27
4 N! m+ `5 y( H28 f; C! i. @4 N+ A
29
+ j# Q1 |! Q- I" E实验结果
$ {. |/ v; N1 b$ x! a* c( l O" B0 [( |
————————————————
8 ?& i o* ?. F6 Z. T, y版权声明:根号五
$ }$ L1 a! O( h/ [& s# R6 c) x: m) \! B+ |5 l/ H4 @
5 f0 P9 Z8 r4 @' p% U+ q! v6 E |