一、SPI介绍6 B& ^; c5 H: s5 ~; I) y% K
SPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。/ r) g! `- \. C+ {! C, g
6 N2 u% C& d( u1 h6 b
正是简单易用的特性,如NRF24L01、VS1053、SD卡等皆集成了这种通信协议/ h2 x: j2 y R& x% V+ H7 O+ | |
) ]' e. y" p8 c E9 a- k
; Y* F3 Q8 g( Q: a/ E J3 j
2 x5 F1 x- F f二、SPI接口框图
& Z4 N- `- E6 K8 ^7 K7 g' O5 \; e* H
# C* \ v! ^* W( z$ i
: K, l2 L) |1 A/ m4 V三、SPI优缺点
" `. j+ C/ D3 F: pSPI接口是在CPU和外围低速器件之间进行同步串行数据传输,在主器件的移位脉冲下,数据按位传输,低位在前,高位在后,为全双工通信,数据传输速度总体来说比I2C总线要快,速度可达到几Mbps。
) X* f5 M) ?5 x. J" E7 A. [. D+ ~# H( H
信号线少,协议简单,相对数据速率高。$ V% h6 N- g" p! A
U9 J& R d- Z0 I3 N4 |缺点:没有指定的流控制,没有应答机制确认是否接收到数据
$ J8 U# Z3 P0 f# l, f- t
$ k6 v# J, n' I四、SPI工作原理总结
5 m' P B( \3 @4 @+ e硬件上为4根线。3 s5 M4 ]6 Q# g0 _; e9 N( r
主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。
! D/ M% m$ E) j* B2 x( w1 a. `串行移位寄存器通过MOSI信号线将字节传送给从机,从机也将自己的串行移位寄存器中的内容通过MISO信号线返回给主机。这样,两个移位寄存器中的内容就被交换。
& |& x, E, c$ H8 D0 \6 `外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。
" l: V! U3 g* g! t0 Y) a! N多个设备使用SPI的应用举例6 _; v! r+ a$ k+ E
w) i/ F# X% ?0 Q& C1 o s( l
) ?8 y: O7 h4 e
& E( u- M5 Q5 M3 {* R& S五、时序图4 r0 d3 x3 T4 t# x+ Q5 f
时序为 SPI_CR1 寄存器中的 LSBFIRST 位复位时的时序
2 Q! Y6 ]6 m3 F) z1 E9 u# B6 u+ o0 @% Q/ y
SPI_CPHA的值将会影响SPI_CPOL的值, j2 Q" M9 g9 O2 D; a
. |1 K4 _7 w: \0 {
* b4 g' F2 N% S$ S' N( I
+ Q* ]/ Q4 I4 _" ] o9 a8 d六、SPI程序编写过程
6 ]. v& K% r- r* t) m4 @- //①使能SPIx和IO口时钟
+ k; [2 M* O! a - RCC_AHBxPeriphClockCmd() / RCC_APBxPeriphClockCmd();7 a% r: ?% c# w" B+ r& Y
- , @# _( H1 z# b( ]' d3 k0 P
- //②初始化IO口为复用功能$ D7 p& Q/ x) v8 h S* a# z2 X
- void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);1 D$ L$ m2 |0 n$ T( R5 v4 x- r* y
4 p+ g/ x, o- s) F; \6 R% ]" a- //③设置引脚复用映射:
7 {! H$ {( @- m - GPIO_PinAFConfig();
) V n! k1 p' W3 G - - P* K: G5 n, U. b. q1 g5 D
- //②初始化SPIx,设置SPIx工作模式
`- ^4 G8 } b; D$ Y8 Q - void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);! ]/ `# N4 o; t. n3 H; I. S% t
- ' l# W6 q! t1 a( ^
- //③使能SPIx! x9 S6 W5 }8 Q6 M0 x' u
- void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);4 z7 t( Y* Z6 K$ D! O) }
- - N2 i# R5 j, q. m
- //④SPI传输数据
/ j* H; n' c5 `5 K8 f# c) b - void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
, y* ?; P2 w/ x$ B8 l1 f; T: i - uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;
1 M# a7 h: ?: y [/ K - 0 ], l+ ]: Y" f9 u* _
- //⑦查看SPI传输状态
) R3 Q% l r4 P! w2 ^8 o) T - SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);
复制代码- SPI.c:
) B1 t, `& `) U9 y4 p - #include "spi.h"
2 z8 R' w4 o& B: [
9 r6 h" M* g# m- //以下是SPI模块的初始化代码,配置成主机模式
8 ?7 }, Y) G7 r, b2 h- a5 \8 m/ K4 t( k - //SPI口初始化
' P' l4 q/ ?; K/ I2 B - //这里针是对SPI1的初始化" n4 C$ B, `( i- d; S
- void SPI1_Init(void)- R" R* y- _9 v& F3 R0 a
- { ( c: K0 j! f' u. g/ E
- GPIO_InitTypeDef GPIO_InitStructure;
& G+ u* _& P0 t - SPI_InitTypeDef SPI_InitStructure;
9 f6 Y5 }: G2 A# x+ t- f# z/ P r - 0 N% F4 \3 y) _' ?! v0 F" ?
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟- N6 i: r$ M" \) [. [ E+ u$ o
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟
' \6 E2 l% J5 m0 U - * v' [; s& y5 Z( V3 C' c3 F
- //GPIOFB3,4,5初始化设置
4 J- K9 O5 K* w+ t* p, A - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出
/ H0 J+ r2 j: A4 ^ - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
! M7 v! T8 M' j/ Q6 i( e - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
5 J6 {: X* s1 ^ ]6 |+ u+ | - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz0 Y; |' y2 V& ]& @, x8 c- o
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
! \: ]8 K) S+ a# k) W - GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
9 E1 p. r. ^4 ~ -
) N9 F- x9 Y9 N - GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为 SPI1
. A: T& E! U/ N! Y" l+ E - GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为 SPI1
: R# }/ ?& m9 j/ d- Y) y9 P, G - GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为 SPI1
% o% t6 s& F" T& Z+ \: f
8 ~4 L9 w; r3 O& j; ?4 `- //这里只针对SPI口初始化" e; `+ I; \% k5 J
- RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI1
; E7 R$ B" I" u C8 K- E/ a - RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1
) q5 F% C f: ]" V- @9 W; n
9 X" J* E& U( w, N- SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
6 U$ r' H& ~' ]. _; u$ t - SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI4 w7 D2 k5 e9 i0 s/ u! [. o
- SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构; C% Y" x: h7 M7 ]5 X' D
- SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行同步时钟的空闲状态为高电平
+ N7 C5 U D4 ^ - SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样* j. q5 T8 I" u
- SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制8 }' l' q9 q" |
- SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定义波特率预分频的值:波特率预分频值为256+ j$ q% ^6 L" t% V3 w, o# T
- SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
6 r- a+ [, @2 a) }4 Y - SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式4 |1 H# ^3 S V% B0 w6 I H
- SPI_Init(SPI1, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
. \1 E3 {4 H. e/ H4 |( ^. E
5 \. Q5 q$ O( A% D- SPI_Cmd(SPI1, ENABLE); //使能SPI外设
2 N, j. R& d8 _ - ( _4 A6 E$ K0 t: k# `- _/ A
- SPI1_ReadWriteByte(0xff);//启动传输
4 ?5 C; Y" j6 Y6 ?0 x! j% r - }
' J. Y# W9 w2 V1 @3 ^: A - //SPI1速度设置函数" N* P0 f0 A* W- r& z+ @# o* ]
- //SPI速度=fAPB2/分频系数( V- l) y& |2 G/ h; F3 |& c
- //@ref SPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256
3 r' u# h' l5 A - //fAPB2时钟一般为84Mhz:% t3 `- |5 T3 a' C$ u1 J! D) S( z
- void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)+ L! k/ G$ p$ ]/ y4 F
- {
2 t( R: J; F. @' X - assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性0 l- P3 @+ X% N
- SPI1->CR1&=0XFFC7;//位3-5清零,用来设置波特率+ M- m+ t: T, m* a9 b2 H
- SPI1->CR1|=SPI_BaudRatePrescaler; //设置SPI1速度
; ]- H% N7 N F6 p& a R - SPI_Cmd(SPI1,ENABLE); //使能SPI1
, N: r& [/ G, p0 q! e - }
, A6 _# G3 H2 z: F: h - //SPI1 读写一个字节
. b/ R" m% j3 |: ] - //TxData:要写入的字节
% A* W1 M$ n/ p3 Z7 T) N - //返回值:读取到的字节" M, B( x% t, }9 g+ a/ E- W
- u8 SPI1_ReadWriteByte(u8 TxData)$ s$ M4 q# P' O) g9 `
- {
2 p* j2 R5 a5 J0 O0 f
& h `3 Q4 k. f% P- while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空 . }2 Q! R) P$ `) g) C5 w. O# ?
-
! d% L9 `7 e p) }$ L; c. L% A' S - SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte 数据; }0 [# g7 ?( [7 g
- 4 s% h' ^5 l- c7 J: U: T1 i
- while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一个byte ' v. q0 v0 s( [& x
) c% ]: j$ \6 ?4 f+ H% {3 Z. A- return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据 2 }8 R5 i% A* }; X4 j9 w e% d" J
- & Q& D3 W% G9 F# R) C1 F# m8 C# N
- }
" _ n8 S- J2 r, b2 v. E1 }) n - + {( v1 q) L' B: l4 u: q
复制代码 . o9 y- N B! A0 ?
七、W25Q12xx的原理及应用
* a5 B" a( W! ?" RW25Q128将16M的容量分为256个块(Block),每个块大小为64K字节,每个块又分为16个扇区(Sector),每个扇区4K个字节。W25Q128的最小擦除单位为一个扇区,也就是每次必须擦除4K个字节。这样我们需要给W25Q128开辟一个至少4K的缓存区,这样对SRAM要求比较高,要求芯片必须有4K以上SRAM才能很好的操作。
|+ A1 V( Y+ }; z- }! A. D1 r7 G! [/ G0 X
W25Q128的擦写周期多达10W次,具有20年的数据保存期限,支持电压为2.7~3.6V,W25Q128支持标准的SPI,还支持双输出/四输出的SPI,最大SPI时钟可以到80Mhz(双输出时相当于160Mhz,四输出时相当于320M),更多的W25Q128的介绍,请参考W25Q128的DATASHEET。
) v G# O# }6 p* D5 D s! L, J% Y( Z# A% W' i& @- @
; B8 w: O+ N/ r1 m$ P+ {
: U7 o9 m+ b1 `# r& D
W25Q12xx可根据原理图查看,使用的是SPI总线通信协议5 v3 v0 h0 Y2 s& l/ ~- g, i) ?; f3 d, {
) v* a( g- r0 }, J- R比如原子的原理图, c$ e; k4 H) N* `- r
/ M$ v/ F6 L3 i5 k! T: x5 e h9 x4 s
& T" z$ B! X) {6 [5 ^" F6 f* @
$ V) [. |) ^$ A7.1 分析W25Q128指令
9 H( G- L4 q7 ~: G3 p# F( B" t可参考W25Qxx的数据手册,这里列出W25Q128部分指令:
6 B) j$ e9 f( T; {. w3 ]$ l4 P$ b
$ Z% a7 ^1 z; U/ t: F% \9 v9 u3 t' a1 Y
) w; E8 E: V' [/ R% Y$ k" C+ V
比如读取设备的ID的指令:0x90000000
v0 i& Y2 N" G3 G4 G6 r( w+ K2 ]8 s; X& @0 A- D) S& H K
2 x/ A+ ]7 F6 X+ o
6 f8 F# r3 v" n7 P# X2 g* t3 J
, O6 X! j0 Y8 E( T( E" j
% g0 ` f: o E
0 i' R4 |% Q7 Z# A7 V; |1 R, G$ V, g a
7.2 擦除扇区:
2 m0 N8 f" W# j. J1 B
- @: \1 Y! H7 d }
! b2 H: \6 J |2 i/ ~0 L9 N: |- W
" _0 p- F: P# z; S6 C: M
7.3 部分常用设备读取指令:! R: ~- L' ^$ l9 m
\; a* \/ \- j1 D6 c
9 A! r, [) t- T5 B: k; t. ^1 e2 \3 X. p& { K( I
每次操作前要使能写操作,且给一个低电平。结束时要给其为高电平,再失能写操作。也就是通过操作片选引脚来确定是否使能或失能。
% p- n W" s2 H8 K5 B# g2 M e$ Q: A0 Y7 O
1 x' A8 c; j ~* W- B$ c
) o z. z% x0 D' c* v8 H5 Q- W25Q12xx.c:; R% D6 k/ I: `; D" n
- #include "w25qxx.h" - E( z- C( P( {' }* u# a
- #include "spi.h"
z. t% N: k! v! C" g - #include "delay.h"
X* \; Q% z; f ]. C - #include "usart.h" : W; b9 v1 x# N+ V, M' {, {
0 _$ @8 Y4 z/ J: C% d) w' R- u16 W25QXX_TYPE=W25Q128; //默认是W25Q128
/ [' s0 a: \. K- M - " r5 B) K$ c g0 ^* o
- //4Kbytes为一个Sector& B& C; C& N/ x. Y! |' x
- //16个扇区为1个Block6 O0 x, B; ~" j3 v" b f
- //W25Q128
$ n( t$ V7 k& i7 b - //容量为16M字节,共有128个Block,4096个Sector , u/ V& L1 f7 v0 |6 y8 n
- $ L( n1 g) k, I P# t& p
- //初始化SPI FLASH的IO口
3 i3 ? z5 d7 N: r y - void W25QXX_Init(void)7 Q2 \8 W: w5 H1 W6 z
- {
2 a! E/ A+ u3 Y - GPIO_InitTypeDef GPIO_InitStructure;' D* W0 B) a! o
( S- k8 u; R9 F( j& w# g- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
" s8 I' a; ~: ` - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);//使能GPIOG时钟
7 {; ~! Z1 |6 S$ Y& r* |+ P; s! v
5 F# e( o# D6 b% J9 ?0 b K- //GPIOB14
) X8 w1 h9 M. I: K# A# z - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;//PB149 u+ C' R* I* Y9 A
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出
@* Z* |# Z( Z1 |* b! K6 k' P - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出0 R8 @ B/ ?6 p3 `1 r- B
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
3 D- d1 b$ s, A9 [ - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
; y q/ T" B7 @ - GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
2 w# f2 S4 Q: }) I( ? - 2 V, {9 O7 |. \0 N9 `
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//PG7
6 S `! N' h6 D" J4 Y2 N - GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化( W! H: p/ }% r- p* V% a
" ~, N0 C/ U) O- B6 W) |- GPIO_SetBits(GPIOG,GPIO_Pin_7);//PG7输出1,防止NRF干扰SPI FLASH的通信
. W4 _+ t- v5 L5 @- Q0 f8 a6 X3 c - W25QXX_CS=1; //SPI FLASH不选中
' W; U- t" s/ ~ - SPI1_Init(); //初始化SPI c( M$ l8 w2 z; }5 Q
- SPI1_SetSpeed(SPI_BaudRatePrescaler_2); //设置为42M时钟,高速模式 - S! O! Y: `1 t: h# j
- W25QXX_TYPE=W25QXX_ReadID(); //读取FLASH ID.3 M O( o- B! R
- } . w% q5 z5 x. e) y
]9 I- \2 Y, l& @. s- //读取W25QXX的状态寄存器) F Y: R( z5 f1 u. D8 I
- //BIT7 6 5 4 3 2 1 09 ~& h9 Z1 T' B$ d4 v
- //SPR RV TB BP2 BP1 BP0 WEL BUSY! P7 ~1 G; p2 ?6 y8 m" n
- //SPR:默认0,状态寄存器保护位,配合WP使用
, ?* i3 T& U8 J( }% G - //TB,BP2,BP1,BP0:FLASH区域写保护设置
+ r* I- G% r& S6 Z, ]( A7 z4 v0 b - //WEL:写使能锁定3 R L/ c; g1 ^* G2 L4 p
- //BUSY:忙标记位(1,忙;0,空闲)5 |" q8 A4 A( ?% j, ]5 r7 Y
- //默认:0x00
! I% ~2 K/ q5 } - u8 W25QXX_ReadSR(void)
1 |0 |" s, [! W! M6 J - { 3 j( u! \5 s" r( R% {
- u8 byte=0;
' c/ x- G; t3 b# \( r& N7 d - W25QXX_CS=0; //使能器件
1 B, N* X- q& v+ A- d" D. Y - SPI1_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令 ( N0 Y6 A( t2 Y" n: j
- byte=SPI1_ReadWriteByte(0Xff); //读取一个字节
+ F& i7 x) a! s) B: s - W25QXX_CS=1; //取消片选
/ u6 U# z, n5 H! ]; s2 _ - return byte; ; u, e. N6 R# ], b6 _# a% a* A" E
- } . W& C g3 g& X3 {8 T
- //写W25QXX状态寄存器
5 p9 H C' f8 n1 A3 i* X, @ - //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
, N( Q5 K# w; T, g; P - void W25QXX_Write_SR(u8 sr) # C q/ O% F+ G& u
- {
, y$ G# q! j- V- ` - W25QXX_CS=0; //使能器件
7 ?1 {5 V u N- D - SPI1_ReadWriteByte(W25X_WriteStatusReg); //发送写取状态寄存器命令
7 `6 N$ o' ?* C9 _ - SPI1_ReadWriteByte(sr); //写入一个字节
2 @ O# J) o$ F L' R% K - W25QXX_CS=1; //取消片选 ' P' s* E' C0 Y' E1 c
- } 1 U6 ?8 d% P2 J
- //W25QXX写使能 ! |9 G6 L* {: D) X) N4 O, h+ P
- //将WEL置位
! x# v2 k/ t0 o; f5 z - void W25QXX_Write_Enable(void) 8 c4 q: E. Z& r6 f) a
- {3 \' I0 b0 B$ d" f; j# ]* i; Z' x
- W25QXX_CS=0; //使能器件
/ U* I" q9 G' {% d- ? - SPI1_ReadWriteByte(W25X_WriteEnable); //发送写使能 - c: ?4 j: @1 Q6 F
- W25QXX_CS=1; //取消片选 " g0 J5 N- l4 U9 ^6 }
- }
) C6 `& x& p8 y1 H - //W25QXX写禁止 ! ?9 g0 n! N9 H- @2 Y! S# |
- //将WEL清零 ' a/ Y9 P7 R0 { U3 B# O
- void W25QXX_Write_Disable(void)
, {) Z, T5 p+ g& v& j - {
$ \! j; Q! u2 ~5 b5 g5 \ - W25QXX_CS=0; //使能器件 1 a" _3 \& J( W9 L2 |
- SPI1_ReadWriteByte(W25X_WriteDisable); //发送写禁止指令 7 x' P3 z0 R$ l1 [
- W25QXX_CS=1; //取消片选 9 j$ ]1 O3 T, V Q: ]4 ~
- }
) ]5 q8 a; r7 V' h; X - //读取芯片ID. ]) s8 x1 g; |& ], j
- //返回值如下: 0 X7 Z4 o$ O, Q5 t6 W# o
- //0XEF13,表示芯片型号为W25Q80
$ w1 B) |8 Y# ?& R5 w - //0XEF14,表示芯片型号为W25Q16
; x9 p) L# x) J - //0XEF15,表示芯片型号为W25Q32
% s, X o/ ]% [3 T - //0XEF16,表示芯片型号为W25Q64 , P# r3 f' U; i" \
- //0XEF17,表示芯片型号为W25Q128
8 D# p k. g9 n- |4 k1 S( p# o - u16 W25QXX_ReadID(void)" L; F9 p! T" E1 S
- {
; W: O8 b1 m4 H. ~- W - u16 Temp = 0;
0 k2 Y9 j5 T! A' u/ J - W25QXX_CS=0; 1 J) s7 i: A7 `
- SPI1_ReadWriteByte(0x90);//发送读取ID命令 # B5 y9 @, s6 d+ S
- SPI1_ReadWriteByte(0x00);
& f% i9 y1 B: I7 c - SPI1_ReadWriteByte(0x00);
* I. g, P- G; g* P M; k6 E - SPI1_ReadWriteByte(0x00); . b4 ?9 \1 h9 u, f0 L7 A+ \
- Temp|=SPI1_ReadWriteByte(0xFF)<<8; ; o q; X. G! j8 c- F
- Temp|=SPI1_ReadWriteByte(0xFF); 5 X+ J% T6 j% E7 @( v
- W25QXX_CS=1; 7 ~3 R) U- e) h* R. K6 ]4 F
- return Temp;
! {' N+ S0 \: z% Q/ C - } ) p' k/ A& C6 _2 E" W
- //读取SPI FLASH
& E; O* V6 ?$ ^! r - //在指定地址开始读取指定长度的数据1 k* v" |$ K, t; \! H) @
- //pBuffer:数据存储区
9 n# E% b% d+ p3 L5 K5 E, N - //ReadAddr:开始读取的地址(24bit)
! @0 |, i: J% e" p# B9 y8 L$ Y& B - //NumByteToRead:要读取的字节数(最大65535)# P: Y8 ?" b1 z& c
- void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead) " ?* M' V' L4 T5 J8 G2 n3 g7 P8 C
- { 5 u! j" }# [$ i) ]5 p
- u16 i; + y$ l5 K- B% V0 |4 c( l; {
- W25QXX_CS=0; //使能器件
# f5 M1 j3 z" D - SPI1_ReadWriteByte(W25X_ReadData); //发送读取命令
& o5 d; L+ i4 I - SPI1_ReadWriteByte((u8)((ReadAddr)>>16)); //发送24bit地址
' ?% l9 i; m" d- ]0 K+ c5 I" p - SPI1_ReadWriteByte((u8)((ReadAddr)>>8));
6 a8 r- S; |" O5 f - SPI1_ReadWriteByte((u8)ReadAddr); 9 j2 A2 q5 P% P+ ^) `
- for(i=0;i<NumByteToRead;i++)2 i0 B. _! `1 [" z+ u/ g6 H
- {
+ K: Q: K0 r: |9 D% Q+ T% o! K5 E - pBuffer<i>=SPI1_ReadWriteByte(0XFF); //循环读数 1 E/ D5 R2 c1 p
- }/ N K4 B1 y. E( K6 S" h \) ^# x
- W25QXX_CS=1;
$ t! p! c4 n8 W: ?% N - } 9 m, p5 ] i. ]6 X4 S5 X
- //SPI在一页(0~65535)内写入少于256个字节的数据7 @' a! ], x- P+ o
- //在指定地址开始写入最大256字节的数据1 K& @. `# ~& q/ L' z6 P
- //pBuffer:数据存储区
/ O& |* r0 s+ U# U( R0 u& c9 A3 F - //WriteAddr:开始写入的地址(24bit)
5 P( @3 @5 }$ {2 p l* p" J! ] - //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!! " b$ A# n+ ]# M8 I5 S
- void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)& F4 _) [( M$ x# P& o4 v% w5 y
- {5 A# k. J/ A( ]. f9 D; T4 L8 K c
- u16 i; . z5 V: d# L9 n: |, O
- W25QXX_Write_Enable(); //SET WEL
% p! _* N) G8 s; D% H5 U - W25QXX_CS=0; //使能器件 , I6 E8 b z( `, V
- SPI1_ReadWriteByte(W25X_PageProgram); //发送写页命令
7 Z" r: O& R9 m3 p: Z" T Y' Q - SPI1_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址
! X9 ^, u: U+ T5 F! } - SPI1_ReadWriteByte((u8)((WriteAddr)>>8)); / C& X( J0 \, C& ^; z! J
- SPI1_ReadWriteByte((u8)WriteAddr); 6 ~! o5 m6 V8 ]$ K& k8 h- U$ @
- for(i=0;i<NumByteToWrite;i++)SPI1_ReadWriteByte(pBuffer<i>);//循环写数
# M/ L/ [7 K; H - W25QXX_CS=1; //取消片选 + Z5 F3 F- q4 }! ]% [# o+ b
- W25QXX_Wait_Busy(); //等待写入结束 |, |) D: N7 s& X# S& p
- } 3 m$ a4 S# F; `3 |$ m2 {* E+ U
- //无检验写SPI FLASH e- m; ^8 d4 q6 p' c" D; W) q5 q) o
- //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!0 ^4 s& K% I J# g
- //具有自动换页功能
7 E9 k7 B& Q" `. \' U - //在指定地址开始写入指定长度的数据,但是要确保地址不越界!+ E* o3 c" q; m! @( N3 S. z, J
- //pBuffer:数据存储区0 m9 E; ]/ K* F' u
- //WriteAddr:开始写入的地址(24bit)
a: g4 l' ~( H8 |3 `7 r - //NumByteToWrite:要写入的字节数(最大65535): h( S' \' i$ W1 M# x( E
- //CHECK OK
7 L* o6 s& p/ I* w' P3 D; Y; h4 j - void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
$ c; ?( b: Y. N7 u9 e! v) Z - { 4 E f/ Z |3 O% U$ r7 _; U2 a5 S/ l
- u16 pageremain; / U [ l2 H m! p. u6 L+ ]
- pageremain=256-WriteAddr%256; //单页剩余的字节数
- R$ E6 ]4 J2 ~7 j1 H# H - if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
6 d. x, H4 S, h# ]9 P' Q- B# o+ X - while(1)
4 p, z5 N u9 x) s - { 3 X7 v: }; F+ G0 I z/ U1 O
- W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);( x9 c O, [$ I N+ l
- if(NumByteToWrite==pageremain)break;//写入结束了+ {! Z4 b6 Y- s# v7 {! R2 l$ B
- else //NumByteToWrite>pageremain
' v( y/ L1 \- _" B, X3 n$ v2 q& y - {7 e9 x6 j. V- e. B ]7 Y' i# a
- pBuffer+=pageremain;
- r7 k" M W2 S$ L, _# P - WriteAddr+=pageremain; 1 T& T& h% q, f8 ?% L# X
# u5 S7 M# t( D$ N- NumByteToWrite-=pageremain; //减去已经写入了的字节数
5 d& a5 p1 k% q% Z& C) Q+ f6 E% E. O - if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节) I0 E& m3 k* x
- else pageremain=NumByteToWrite; //不够256个字节了0 J# W8 U: }% [0 B* R8 C3 L7 @: X/ G
- }6 A$ t, G1 G: `3 K
- };
% h0 a6 L. x" g: O - } 9 t% s8 ]. L3 O7 P8 ~' c
- //写SPI FLASH $ @ ^: @- k! W' `6 o9 Q
- //在指定地址开始写入指定长度的数据" Z# {4 V9 s7 I* z
- //该函数带擦除操作!* T! S, d r: J! o: M2 D [! [( o( h
- //pBuffer:数据存储区. w# e* t# }( {
- //WriteAddr:开始写入的地址(24bit)
- s# c% m+ ?' H - //NumByteToWrite:要写入的字节数(最大65535) 1 T6 u( b$ L4 Y5 J
- u8 W25QXX_BUFFER[4096];
2 z& \, k( B. _- N3 g - void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
4 V. S6 z# x. y; I2 I - { ' f2 [4 V% E5 h/ v
- u32 secpos;: A0 [" Q& x) _7 l1 l/ P
- u16 secoff;
6 ?$ v1 z: }* e - u16 secremain;
5 p( P" p7 p$ } - u16 i; 5 { D' B" D: h% |+ d( v' D
- u8 * W25QXX_BUF;
& t+ c/ v* A) J: |+ v/ m - W25QXX_BUF=W25QXX_BUFFER; " e5 b# {/ ]% V4 m# h, q
- secpos=WriteAddr/4096;//扇区地址 1 N( v/ Y$ ~ w Y
- secoff=WriteAddr%4096;//在扇区内的偏移
) a; L* Z e2 ]5 m" z3 O0 q - secremain=4096-secoff;//扇区剩余空间大小 6 Y$ |* r3 Z" m. j& d" l( a
- //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
( ^( B+ e: u+ S, ~- ~5 ^% T - if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节
g" i; E7 X7 ^# A) e' H( h. i J1 y - while(1)
0 o0 M5 ?2 x, l# w4 q# b' k3 x# {- {3 n4 U - {
1 C5 v4 m3 M3 L% Y. E7 o7 `2 ? - W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容% \0 o9 x( ^, p, P4 B6 Y
- for(i=0;i<secremain;i++)//校验数据
9 P5 N0 p/ I$ Z" P" g) z8 s - {2 @ P4 [0 ?' S3 \
- if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除 8 u/ d* l" c8 `$ u3 @# H
- }/ }7 x& H) i6 L
- if(i<secremain)//需要擦除
0 Z3 J6 l) I# z9 E; T d8 @ - {
8 p2 z y5 e4 s/ g - W25QXX_Erase_Sector(secpos);//擦除这个扇区
9 h* j/ f7 T) `0 j9 l% j, R - for(i=0;i<secremain;i++) //复制6 s8 v- I) }( D) p- [
- {
; C8 e8 `0 K/ a$ R/ b - W25QXX_BUF[i+secoff]=pBuffer<i>; 5 i' K% s/ c2 H: f. x2 d
- }
! r5 k5 K! T5 r - W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区 ' | d/ y# p5 j8 @
' |. s K9 l$ H& V1 Y9 w' T' r- }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.
) J w& k7 u4 R- m( k7 z2 ^9 q7 K - if(NumByteToWrite==secremain)break;//写入结束了3 _- n4 i2 r2 S5 V- S
- else//写入未结束
9 D- h* k* Y4 J) X5 M# {' ~ - {
: b/ \/ U- f+ O3 ] - secpos++;//扇区地址增19 n5 O5 y! m$ e/ P$ F% Y
- secoff=0;//偏移位置为0
`7 c C( H4 A) {8 k9 W - , h! @3 z+ u0 s: [2 m! v) r5 u
- pBuffer+=secremain; //指针偏移' w0 m" b! l" b7 P6 E# X0 k
- WriteAddr+=secremain;//写地址偏移
) i1 a9 k& L) T/ x# i- L9 q - NumByteToWrite-=secremain; //字节数递减
+ \9 U9 G2 {0 h: n2 }) p - if(NumByteToWrite>4096)secremain=4096; //下一个扇区还是写不完
" g h8 x4 c; k8 T- E& J - else secremain=NumByteToWrite; //下一个扇区可以写完了
[4 H9 }7 ?0 e( K% |6 _3 |5 u5 K - } $ p3 u8 Y/ N( H. v. W2 s' g. T
- }; 2 m( d! L" M: @% _
- }! V" h* V4 {! M
- //擦除整个芯片 * L, k" y1 b! S' `- k# {3 a* H" N3 H
- //等待时间超长...
$ }$ s, U& n4 O8 f0 S& u% H' j - void W25QXX_Erase_Chip(void)
; i7 X5 P; `& t# j6 C1 D/ g b - { : z1 s2 T3 |- K0 `$ Y
- W25QXX_Write_Enable(); //SET WEL 5 n$ V7 R. r9 s0 Q
- W25QXX_Wait_Busy();
1 e+ N% v. H9 \6 F: `# h8 C" B% Z - W25QXX_CS=0; //使能器件 . M* Y# c; J: f0 o. v$ b0 H' |
- SPI1_ReadWriteByte(W25X_ChipErase); //发送片擦除命令 8 h+ T5 P& a0 J4 ~8 v% Z
- W25QXX_CS=1; //取消片选 ! y7 ] D# W \5 K7 Y% d, J+ m
- W25QXX_Wait_Busy(); //等待芯片擦除结束3 n3 B+ G7 Y \/ C) t8 b
- } 7 J3 N% Z5 Y% R( K% P
- //擦除一个扇区# {7 B: z9 j/ o' q+ [: J8 b
- //Dst_Addr:扇区地址 根据实际容量设置
7 X7 i g s0 ^- U' g9 { - //擦除一个山区的最少时间:150ms
0 r: @" t4 X% k5 t - void W25QXX_Erase_Sector(u32 Dst_Addr)
5 s' ~! D1 y9 o' Q; ?1 j. ] - { . G" B H' V/ I3 u) d- a( g$ N* I
- //监视falsh擦除情况,测试用
\$ _5 H* @5 H& l - printf("fe:%x\r\n",Dst_Addr);
! q7 T; q$ A! H2 j+ D - Dst_Addr*=4096;
B9 w# ~* c# `7 P - W25QXX_Write_Enable(); //SET WEL , ?( n. `+ X4 [8 p6 |6 J, K% [6 [# a
- W25QXX_Wait_Busy(); 9 n3 J5 g( I/ F5 ^
- W25QXX_CS=0; //使能器件 6 ]/ k1 y3 { M/ O
- SPI1_ReadWriteByte(W25X_SectorErase); //发送扇区擦除指令 ' t. u( Z2 |- _) Q/ ?) R
- SPI1_ReadWriteByte((u8)((Dst_Addr)>>16)); //发送24bit地址 / G$ V. c( M$ d7 {; W$ f; \3 n$ r
- SPI1_ReadWriteByte((u8)((Dst_Addr)>>8)); 2 P0 X' |( A; z; d O
- SPI1_ReadWriteByte((u8)Dst_Addr);
7 w: R Z1 Z8 ]" H; p - W25QXX_CS=1; //取消片选 3 l! Y/ y3 g$ c% x0 L; S8 }
- W25QXX_Wait_Busy(); //等待擦除完成2 H3 e% i$ M" k* D' T" E
- } 0 T/ y/ w" m' f3 _
- //等待空闲
: a% z0 Q, ]/ G' \6 Z5 m - void W25QXX_Wait_Busy(void)
. \; T$ a7 A2 q7 g - {
9 P1 F) }- X7 n( F$ n9 q( @: G - while((W25QXX_ReadSR()&0x01)==0x01); // 等待BUSY位清空
' H; ]1 V. k% Q# s0 Q$ ~ - } # H2 ~. G. a5 S7 s+ }' x6 e- T
- //进入掉电模式4 v1 g& C7 w* g2 g# \1 e
- void W25QXX_PowerDown(void)
, V/ `" E: {/ ?. U! B. v - {
* b# ^ u- a" t& \8 _/ S1 J# p y - W25QXX_CS=0; //使能器件 & }: }; y e7 u0 `+ U2 S N
- SPI1_ReadWriteByte(W25X_PowerDown); //发送掉电命令
X8 \) J& \9 g - W25QXX_CS=1; //取消片选
1 c: I P/ J. C" `- d! b - delay_us(3); //等待TPD 9 {$ Z. ?5 L; v) ^" I m3 G
- }
- E1 L/ M9 U3 _& j' U2 A& `1 ]; t - //唤醒
: C' a1 U( I, _$ y - void W25QXX_WAKEUP(void) ) w( r. u* E3 c- B1 V
- { 7 r7 o. {- }/ n, f1 r$ w- c5 j3 K
- W25QXX_CS=0; //使能器件
* {+ O) C; N1 h2 ] - SPI1_ReadWriteByte(W25X_ReleasePowerDown); // send W25X_PowerDown command 0xAB
/ P1 w7 k9 i8 U - W25QXX_CS=1; //取消片选 8 A! ^7 @9 \- N
- delay_us(3); //等待TRES1
# n% |% v" w2 M. v - } </i></i></i>
复制代码- 7 q6 V( r+ ?% u+ o7 u. |* J3 l
- W25Q12xx.h:
' p$ c/ M5 N* I8 x q - " Y5 V& N( u; o
- #ifndef __W25QXX_H
* F z- s7 v* B9 L/ v- O3 X/ K - #define __W25QXX_H - ?& G1 l8 [0 V
- #include "sys.h" ) y1 R3 Z7 ]/ ~) q' I
- 6 [. b1 l2 Y7 p5 q. {! e
- //W25X系列/Q系列芯片列表 * d$ ^; X0 R0 h0 }. W/ w2 j6 R
- //W25Q80 ID 0XEF13
( h1 W1 a/ l$ s9 P: K; a - //W25Q16 ID 0XEF14
/ J2 \. c3 H+ ] - //W25Q32 ID 0XEF15
0 [9 f8 L# b M- d* L" K+ Q) T - //W25Q64 ID 0XEF16
: ^6 [ \! h! c; s* ^8 V - //W25Q128 ID 0XEF17
2 ~# {8 \! e9 Z* u5 ~$ `9 z. M; t - #define W25Q80 0XEF13 / l, r2 k7 z3 @3 H* [! x0 K
- #define W25Q16 0XEF14
& q/ u" Y& E9 h" E7 c$ Z3 Q( R - #define W25Q32 0XEF15
4 Z8 M( o1 n, _& M% C - #define W25Q64 0XEF16
/ N1 H6 j8 p) B - #define W25Q128 0XEF175 ~4 L! U5 ^( i5 Y0 x$ e
) E1 p$ z2 E) Y1 B+ W- extern u16 W25QXX_TYPE; //定义W25QXX芯片型号
% U, O$ g2 ~4 {, _2 U - ! j) n& m% H t* I* m
- #define W25QXX_CS PBout(14) //W25QXX的片选信号0 |. K3 `' X4 {2 a) V
- // 2 D# P* B% ^& n& h
- //指令表
, S+ c$ |& S3 u, \) M! u N - #define W25X_WriteEnable 0x06 - n* h/ ~% W1 ]7 m* c
- #define W25X_WriteDisable 0x04 % M7 J, c8 q1 l5 {
- #define W25X_ReadStatusReg 0x05
, F* q4 P7 L7 x; P( @ - #define W25X_WriteStatusReg 0x01 6 t# _! t" x7 p7 W
- #define W25X_ReadData 0x03
# }, F! u7 a. U# E! V+ I' k6 v - #define W25X_FastReadData 0x0B 0 h: B( K; L4 m0 \) t
- #define W25X_FastReadDual 0x3B 3 a* \% G# w& M% Y# B2 c5 P- m7 H' ]
- #define W25X_PageProgram 0x02 # @' d: ~: [8 f* ]# _1 @
- #define W25X_BlockErase 0xD8
5 Z1 v# n# B' ~3 B - #define W25X_SectorErase 0x20
, M& X- P+ k4 g - #define W25X_ChipErase 0xC7 # u1 _! W" x4 u" @ d. F
- #define W25X_PowerDown 0xB9
, B9 a8 I) p4 s. J0 c/ y - #define W25X_ReleasePowerDown 0xAB
1 W& O" m: s- l( E - #define W25X_DeviceID 0xAB
7 J3 W2 s3 G/ S - #define W25X_ManufactDeviceID 0x90
# O: }7 c. {$ j: H) L - #define W25X_JedecDeviceID 0x9F
8 o* ~/ Q& c7 x. L' q+ l$ x+ t - 9 j$ [) l! A8 h
- void W25QXX_Init(void);8 ~; L$ ]" p" t/ d
- u16 W25QXX_ReadID(void); //读取FLASH ID5 h. [' ^9 w' P7 I4 w
- u8 W25QXX_ReadSR(void); //读取状态寄存器
" O+ d1 @* m& N8 ~* z - void W25QXX_Write_SR(u8 sr); //写状态寄存器
; n$ S0 r3 ^$ [! G0 V7 X - void W25QXX_Write_Enable(void); //写使能
9 n. H! i, u+ Q& R- F2 p- T - void W25QXX_Write_Disable(void); //写保护! P. B: m) b" a- k" |
- void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);6 M7 g; D3 g7 p% p
- void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead); //读取flash5 l" t3 g- t+ y" e3 Z4 _( h
- void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash
5 n, a: b1 t/ _- z - void W25QXX_Erase_Chip(void); //整片擦除& R5 H# B/ k1 `7 B: I5 k4 @' Q9 [
- void W25QXX_Erase_Sector(u32 Dst_Addr); //扇区擦除9 Y8 q6 d# d; Y+ o& |
- void W25QXX_Wait_Busy(void); //等待空闲3 B2 p+ g+ L+ z7 J8 B# O
- void W25QXX_PowerDown(void); //进入掉电模式
" r, q% H5 \5 \ - void W25QXX_WAKEUP(void); //唤醒2 a9 `& A3 V$ |) }3 U9 u5 v
- q3 Q Z& ~$ \2 m d( n
- #endif
4 ^/ |: l, x& Y: q7 S2 x+ m
复制代码 ( {8 U+ k a0 O$ f# n$ W; ?$ R0 L
main.c:
' ]2 m" ~' u& C/ T& k
; J2 o4 H7 i: Y" Y: }& ~) v- main.c:
3 b; {7 h2 q- P5 J6 z; D! p - #include "sys.h"
% H1 O/ W5 N/ [) y4 U+ g+ h1 ~ - #include "delay.h"
4 H9 G: U9 e# _) f( R - #include "usart.h"0 E0 W, s. J* S8 {5 y+ R
- #include "led.h"
7 i$ Q5 H+ {, Q- T! f4 Q - #include "lcd.h"* N1 J$ F5 p3 O1 l, X& R) H0 P
- #include "spi.h"
" j. n" H/ D3 l5 O0 d8 X% Q" h' v - #include "w25qxx.h"
, m U+ l: W* c. y; I! N - #include "key.h" & U; K* f" t: C. J
- 8 b% U M6 |3 y
- //要写入到W25Q16的字符串数组
; q, Y# j9 s9 V4 P9 l - const u8 TEXT_Buffer[]={"Explorer STM32F4 SPI TEST"};- k1 |4 k% ?8 b5 ~ s
- #define SIZE sizeof(TEXT_Buffer)
, _5 {* D0 n+ ~8 b0 }5 F2 `6 _3 k5 x - / Y/ J% _8 b$ x. K( y U9 g
- int main(void)
, T+ Q. v! F x9 L* o: N" t - {
# p' T; v% t. q& h6 y# ?" T6 a - u8 key;$ V$ k5 f2 u& x$ t
- u16 i=0;
* r. K1 o* C4 T+ z4 t3 x, t/ [ - u8 datatemp[SIZE]; * ^& U! h) G; U! A) B3 u5 v6 c, A
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
) g$ J ~' p" ]* C- x1 E% b - delay_init(168); //初始化延时函数+ y& X: l) c- x- u- r) o* p# m
- uart_init(115200); //初始化串口波特率为115200
/ q2 O$ A( j# y5 _ L3 g - LED_Init(); //初始化LED + e5 B+ M4 Q* c! O! l
- KEY_Init(); //按键初始化
5 @- o" _( u+ N% W, T- M - W25QXX_Init(); //W25QXX初始化 ; u/ K3 J7 F) Q% F7 u C
-
7 x9 }) t+ e: ]# U - while(W25QXX_ReadID()!=W25Q128) //检测不到W25Q128
1 B5 i& u( }+ {2 f1 P; ~ - {: v6 \8 }4 ~7 v
- printf("W25Q128 Check Failed!");+ W( X& I! V% Z% R$ F, v
- delay_ms(500);( t$ g/ }. }, J" \
- printf("Please Check! ");
, e u6 Y; g' ~' l - delay_ms(500);
$ V% t: C7 I% M# l7 O* e1 K+ z - LED0=!LED0; //DS0闪烁! L% p2 N8 Q- Y0 j$ |
- }
% O. |7 J" u3 y -
1 ]2 x$ r# X- i3 l+ } - while(1)
& i4 K9 \6 v# n# w - {
2 D8 {- d5 m4 [3 y - key=KEY_Scan(0);0 j1 O3 U- y0 A- M* M
- if(key==KEY1_PRES)//KEY1按下,写入24C022 o8 ~# n @5 e- v* j9 L1 Y
- {6 E$ H0 C; L, G+ ]# d+ c+ w
- printf("Start Write W25Q128....");
/ G7 q/ D8 r2 A" u* ~& L - //从倒数第100个地址处开始,写入SIZE长度的数据 . f* V( v5 R" a8 ~; n
- W25QXX_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);
8 y- Y9 _! m, s k - printf("W25Q128 Write Finished!"); //提示传送完成
* T5 G6 C( Q) Q7 F( z5 z6 C4 P: _$ G - }
0 E1 \; A' j- N q: P/ G, [$ c - if(key==KEY0_PRES)//KEY0按下,读取字符串并显示4 @3 |# Q7 R+ z- C
- {
( `3 i3 p5 `1 P0 S. E - printf("Start Read W25Q128.... ");3 r# e5 Y; N0 m# l! n
- //从倒数第100个地址处开始,读出SIZE个字节; m3 ~0 b$ `1 ]
- W25QXX_Read(datatemp,FLASH_SIZE-100,SIZE); ! X, ?, [) h* Y: h! d6 ]
- printf("The Data Readed Is: \r\n ");//提示传送完成
- b3 Q! |" o) I4 s5 e( ^ - printf("%s\r\n ",datatemp);. K+ H _( W. b4 q# C0 b0 w
- }
+ O/ n) v3 F" u3 R% I) M0 { - } 3 @: S& l1 K- F
- }
5 D' w- ]+ s5 m# e6 _ - 3 s% b* y- a+ s% j4 k, @7 }2 S9 @
复制代码
* l3 j8 G0 ]. B9 J6 ^% c) Q2 ?# F" F) `: r" B/ P
% a9 {# G) w4 q0 R! c2 z8 @ S2 ^ |