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