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