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