01. SPI简介
9 F% z! [( n+ B/ @" c9 a: \9 mSPI 是英语 Serial Peripheral interface 的缩写,顾名思义就是串行外围设备接口。是 Motorola首先在其 MC68HCXX 系列处理器上定义的。SPI 接口主要应用在 EEPROM,FLASH,实时时钟,AD 转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为 PCB 的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,STM32F4 也有 SPI 接口。
/ P7 Y/ c( z" H& o8 I" l( T- U: Q" J( M# d
02. 功能描述8 `9 w7 p$ w8 U" G
使用 STM32F4 自带的 SPI来实现对外部 FLASH(W25Q128)的读写,并将结果显示在 TFTLCD 模块上。* `; \) D6 z" s/ h1 U# ?& d
2 A6 P; ^' f7 G3 c' D3 C# n' g: T# [03. 硬件模块1 q5 p1 i. U) Y1 t& Z
开机的时候先检测 W25Q128 是否存在,然后在主循环里面检测两个按键,其中 1 个按键(KEY1)用来执行写入 W25Q128 的操作,另外一个按键(KEY0)用来执行读出操作,在 TFTLCD 模块上显示相关信息。同时用 DS0 提示程序正在运行。* C0 f4 _9 B5 A, i
所要用到的硬件资源如下:
0 R7 U5 I& h/ p0 N1 W2 u9 I1) 指示灯 DS0; x9 ^4 N) L$ l3 y/ R8 m
2) KEY_UP 和 KEY1 按键
$ `& F1 C- l! b7 c: c" ]1 M! n6 C3) TFTLCD 模块2 S, @& t2 ^" [8 a$ i
4) SPI8 Z3 k7 a; b- _" E0 w1 R( z
5) W25Q128
! F$ b, y1 `* S3 S u
* x7 s/ `3 S; r9 p) K! O8 j& p
0 U! P+ T( T8 e! J$ h5 C; ~' V! j( u0 j5 k, M& V
04. 软件设计
+ v. U( j6 }& {4 ?, {! f* w. lspi.h
1 G5 i4 f" |+ Q- #ifndef __SPI_H8 Z# N! L. x+ p3 d {( i
- #define __SPI_H- _/ w3 J/ S5 k+ g7 U" X
- #include "sys.h"
1 J3 b+ _: q: c* g' L* D+ H7 g -
" a' p" h9 B* w1 L) ~& J# G - void SPI1_Init(void); //初始化SPI1口. l; F5 S, K8 N; p$ M9 }2 L5 P
- void SPI1_SetSpeed(u8 SpeedSet); //设置SPI1速度
% r+ g4 y& W/ O; V - u8 SPI1_ReadWriteByte(u8 TxData);//SPI1总线读写一个字节
7 c4 c+ i6 u% @ _# g - ( _$ @$ ]8 J7 U3 O. G
- #endif
复制代码 . {) ]8 ?7 W% b
spi.c
! e6 W9 ]' Z5 `! X- g4 p& b9 n* G/ i$ ^" `- S9 U
- #include "spi.h"
: x. J# `9 K% h/ S - 6 c: D9 j6 }9 c3 j" e2 C
- 1 l# X$ K; k1 R+ R) T+ ^4 P; R
- //以下是SPI模块的初始化代码,配置成主机模式 4 C4 w1 c3 e% @" l" y- t1 g
- //SPI口初始化& C% l6 b. O& M' ]( l/ n" W
- //这里针是对SPI1的初始化
% h2 D) t- [5 s# [( R - void SPI1_Init(void)
f7 \6 \/ E' P* H+ p - {
. b# ~" w7 M/ ^, H: G6 { - GPIO_InitTypeDef GPIO_InitStructure;
! G& U, H+ ~3 b4 i - SPI_InitTypeDef SPI_InitStructure;
7 H' I3 ^0 s. Z8 S6 T -
' w7 ~* r+ G7 S4 B( W - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
+ h9 ^& x6 B# r2 p - RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟
8 B" ~: }' S5 {/ z
% ?' P: ~' O- \$ ^1 M2 v# ~- //GPIOFB3,4,5初始化设置
+ @; i$ o7 V! N3 p/ k: F - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出
+ Y2 q5 ^7 I' p4 i& X - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能- H* f! p1 x6 J; d; N. p( T: B2 A) i
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出* z0 e; H# M) p
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz) D) s& Y4 n, t( K
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉! `; ~5 T' B/ ^: V; A2 S
- GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
' ], @! f5 \* ]6 t -
) r m2 n6 S& G! e: K - GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为 SPI1$ \3 M2 u# U$ f5 [. I9 f5 p
- GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为 SPI16 e! U9 `% d3 g& m4 ?3 M) [$ I7 c
- GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为 SPI1
: q1 U% l$ U8 ?+ U6 F) b3 v% T1 s9 e& l
$ ]$ N$ a( q; T+ Q. K# _% Q- //这里只针对SPI口初始化
$ v& z7 K" u. }, ~ - RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI1& u1 R- P; {; B# t4 b& Y5 p( ~
- RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1 i* ]( O: k6 e
0 R! ~3 g2 T( ]! {+ R" c6 M% i) g/ i- SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工 ]1 N# L2 O5 t0 U7 k+ S
- SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI
) |1 k* a+ Y$ Y/ s" s5 a* [ - SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构
6 t6 v$ y e k5 L2 J - SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行同步时钟的空闲状态为高电平0 [) N' O% t; e/ o; U
- SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样, J) J; l3 s* D1 p6 m; {
- SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制* Z! _6 J9 j7 v4 l" \6 b9 o# e8 z: \/ V
- SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定义波特率预分频的值:波特率预分频值为2568 l5 u$ x0 E8 _6 a6 U
- SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始7 w( O% Y9 h' x4 Z0 F, h
- SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式) P1 W% b, c4 h. Y" p9 {
- SPI_Init(SPI1, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
2 ?. ]- y( @6 \1 H# V. z( F! j - / j5 s/ R N/ }8 _0 I4 Q
- SPI_Cmd(SPI1, ENABLE); //使能SPI外设, A* D- k; Z( r
- 2 D) N5 ?. s0 b- ?! I$ G
- SPI1_ReadWriteByte(0xff);//启动传输
. @ U- S O% t1 h - } / e' _0 z* ^; r0 d1 t: u
- //SPI1速度设置函数
0 \5 Y2 q5 F- D1 F. b% ^* c" c2 b/ f* ~ - //SPI速度=fAPB2/分频系数
( p u( n) d3 c1 b% u - //@ref SPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256 2 ]: j7 v, S+ R
- //fAPB2时钟一般为84Mhz:& g. @2 p* Z- B2 v0 ?, F
- void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)3 d, ?% j8 z9 K; }; W7 ?5 j
- {
/ v$ m8 g! Z( o2 ] - assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性8 k' _' Y* B9 a; |- V: \
- SPI1->CR1&=0XFFC7;//位3-5清零,用来设置波特率, d6 W/ S6 [0 M' [( D, q
- SPI1->CR1|=SPI_BaudRatePrescaler; //设置SPI1速度
: o* u( J. A/ l5 E: K' X. m5 T7 X - SPI_Cmd(SPI1,ENABLE); //使能SPI1
, z- a% H) L0 a6 X F - } ) u( i* l# l4 G3 A
- //SPI1 读写一个字节
h1 ^( @# B3 V5 q/ [6 C - //TxData:要写入的字节
+ e5 M E. c9 O1 j. o# Q - //返回值:读取到的字节
. s- Y1 i5 r" a+ [ P$ J/ k, [5 L. | - u8 SPI1_ReadWriteByte(u8 TxData)4 _2 b, e d e& V% D+ M- S
- {
' S! k6 O- K1 @( I1 }
( w6 U. v& `& G- while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空
' \2 @. S& h* Z2 k- {% E# G. ~ -
3 g2 I3 f7 v+ q, W" u# l - SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte 数据
' _) c; ^! C; @- Q2 I5 P -
$ ~5 d4 z; ~4 W; n* g& y - while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一个byte
) X7 F5 C9 h4 |( J3 n( t0 c - 8 c. A3 m$ V Q0 y" I
- return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据
4 j: w" v% F5 d* U" U& c0 A - ! h- `* R) M4 @" S2 }( b* E
- }$ x) A' t0 ^% h% L8 G( y0 K
复制代码
+ f5 H& |$ n. B1 m. m8 `2 ~w25qxx.h; k$ X2 @$ i! l& w
6 u; M; Y) y9 d& D- \- B. a( U$ p
- #ifndef __W25QXX_H7 A) \7 b) s* j# I
- #define __W25QXX_H
/ Y6 C! e( p1 U: _ - #include "sys.h" : t9 N! c$ [# P* E
- ) A' B8 P- V3 Q# [8 q2 p1 ^ @
- //W25X系列/Q系列芯片列表
+ L5 i' C3 O" T% O - //W25Q80 ID 0XEF13
1 M7 E0 I7 y) A n1 ? - //W25Q16 ID 0XEF14
) a9 U! z* L3 v) ]; d - //W25Q32 ID 0XEF15: @/ E7 ?, [. n) Z' G$ Z
- //W25Q64 ID 0XEF16 + I% t" q* z8 v
- //W25Q128 ID 0XEF17
: b2 ^# b2 l( c% \ - #define W25Q80 0XEF13 4 g# a$ ^1 E5 g6 h% L
- #define W25Q16 0XEF14
' N( S9 r! e5 x4 ~/ \* j8 `+ F1 Z - #define W25Q32 0XEF15, P- a4 c/ Q2 G& r* V
- #define W25Q64 0XEF16: u3 |0 q# t' K1 x$ Z$ @4 _% @+ e
- #define W25Q128 0XEF17
' V% I. E: O1 T0 V/ W+ h - 9 F" t q, H1 F7 n0 Y$ n7 \. K7 V
- extern u16 W25QXX_TYPE; //定义W25QXX芯片型号 5 X9 g, ?# q& Z3 |& C
/ @+ S+ _! x& T9 u# e* j9 Y- #define W25QXX_CS PBout(14) //W25QXX的片选信号
3 Y( h9 ^: g- r; C! }. n) W
% q/ X Y; s, K6 b u% N- //
( D. Y& W4 `$ s8 P$ o - //指令表) Z4 ^, J/ o1 S- F" |, V
- #define W25X_WriteEnable 0x06
0 C* T7 G' P) C: [" L) G - #define W25X_WriteDisable 0x04 3 t. y g+ Q' o _) C- q' M
- #define W25X_ReadStatusReg 0x05
' e. ]8 j7 R" h: B - #define W25X_WriteStatusReg 0x01 % K$ |2 a% z4 q$ `5 |
- #define W25X_ReadData 0x03
; f% l7 R* ` Y9 @% B - #define W25X_FastReadData 0x0B - a- T- ?3 G6 s- i$ t2 b$ \9 U2 U* M
- #define W25X_FastReadDual 0x3B
7 f7 g5 F1 R3 p9 F# T8 t - #define W25X_PageProgram 0x02
! n" r/ z2 t6 ^/ }7 ~! B, ` - #define W25X_BlockErase 0xD8 ! W. K# Y; |' K( y! E1 v
- #define W25X_SectorErase 0x20 , I) C4 a. w6 M% J; r; Q* h# @- x. B
- #define W25X_ChipErase 0xC7 ( V s4 r' U3 O
- #define W25X_PowerDown 0xB9
3 `# o4 B. P2 Q" Y, R - #define W25X_ReleasePowerDown 0xAB / v- J4 F \ p; H5 x, b
- #define W25X_DeviceID 0xAB 8 o$ K/ V; ^ b$ s
- #define W25X_ManufactDeviceID 0x90
% o# t" H6 I- u6 y1 W - #define W25X_JedecDeviceID 0x9F
7 a5 Y# z+ l- x9 }- c" Y - ! o3 f4 \, w2 A
- void W25QXX_Init(void);
$ D. E5 A+ G* A5 ?; J - u16 W25QXX_ReadID(void); //读取FLASH ID
* _/ Z% f, u$ j* u - u8 W25QXX_ReadSR(void); //读取状态寄存器 ) a+ `: _4 c* a& b8 Y# T
- void W25QXX_Write_SR(u8 sr); //写状态寄存器0 Z# J. N) y8 ^/ V
- void W25QXX_Write_Enable(void); //写使能 1 e4 I/ m5 ^; l8 u* @
- void W25QXX_Write_Disable(void); //写保护
8 j/ r7 u# q! d/ l( a+ z - void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);+ h% D/ }6 ~! v& X* N
- void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead); //读取flash9 y8 z3 Y9 d4 m
- void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash! n2 U8 m P* z
- void W25QXX_Erase_Chip(void); //整片擦除
5 m; b+ c7 V8 [ - void W25QXX_Erase_Sector(u32 Dst_Addr); //扇区擦除3 u& z: r+ n" F
- void W25QXX_Wait_Busy(void); //等待空闲# r# X$ y8 w( q9 _8 n* s
- void W25QXX_PowerDown(void); //进入掉电模式6 B% M W( r( u3 F+ d
- void W25QXX_WAKEUP(void); //唤醒# Q3 t6 z! S, d" f* b4 b& l) R1 x
- #endif
复制代码
0 G$ V# g4 ]& d1 L# {w25qxx.c2 P( I- u! x! F9 w+ y. \: C
4 {# Y$ |4 H0 g* `- R: J6 m
- #include "w25qxx.h"
* f: ?) c! T0 |3 K8 P0 X: ]+ p - #include "spi.h"
@$ M7 N7 W0 w, }0 A9 J - #include "delay.h" * w" Y) o/ h- I8 E; w
- #include "usart.h" 7 N3 |2 {: J& x, V0 D/ b, O. p
" T* n5 y7 }2 k; c4 K- % B: ?& V6 X5 U
- u16 W25QXX_TYPE=W25Q128; //默认是W25Q128
; `9 ^ x I- [; T2 t' l0 p2 H - 3 R8 U# u, a4 A; f4 a
- //4Kbytes为一个Sector8 Q; S+ T0 b# p2 D7 x5 p
- //16个扇区为1个Block
) p( S5 T: Z7 Z - //W25Q1283 ]2 D$ J& p6 l0 p
- //容量为16M字节,共有128个Block,4096个Sector
; D! R( I% L J1 T0 S" K' {5 i - " _% d: U' w) _& R& N" M
- //初始化SPI FLASH的IO口
3 V: m. r9 b- z' B - void W25QXX_Init(void)1 j' [2 z$ L: A; W- }3 y3 J/ `4 F
- {
# ^9 M; p; s' v* H' [, \ - GPIO_InitTypeDef GPIO_InitStructure;
5 A8 u4 ^. ?. y+ j" D1 T' }7 H - " i# y" U# X# T% Y) L* n
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟+ e; M4 ?7 G. C
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);//使能GPIOG时钟- o( I6 \6 O6 c5 V
- % j- X4 E9 H' m( d( H
- //GPIOB142 L; _+ Z6 ^1 U7 g; `! P
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;//PB14& `" Y: a4 s/ k6 T' A. W" f/ g
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出! m4 q9 p" k6 {- e8 @, G4 p/ F
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
0 v- o$ i# `9 m3 [8 P& Z - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz/ C5 Z" l& h- q7 K7 ~3 |
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
+ k+ ?' |, M# w# }9 r) N/ Z - GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化. w. S* t! z$ l( @; z4 n% X
- & M0 [! F8 @# |% ^7 d3 ~8 W
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//PG7
+ l; d, C' i6 i& C, k( z* l - GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化
8 a( ~! S6 U+ ~9 }
* i9 Q; I( f! U. d6 C8 M( L- GPIO_SetBits(GPIOG,GPIO_Pin_7);//PG7输出1,防止NRF干扰SPI FLASH的通信
& ^! i, f8 C3 q8 u - W25QXX_CS=1; //SPI FLASH不选中4 ~! t. t3 _3 e. _5 J* H6 i
- SPI1_Init(); //初始化SPI
! ^8 M) c! u$ h4 E: b. x - SPI1_SetSpeed(SPI_BaudRatePrescaler_4); //设置为21M时钟,高速模式 . ^4 v( `) i' X$ X2 ~
- W25QXX_TYPE=W25QXX_ReadID(); //读取FLASH ID.
+ z/ y6 ^/ v1 |2 ?8 A; r - }
7 x8 Z% Z2 ]( m1 Z3 M% v- J( h - $ m- A: Y& b( Y- g; D
- //读取W25QXX的状态寄存器) R, U# o2 V( b ~3 l* o" L' W
- //BIT7 6 5 4 3 2 1 05 P0 x9 B% ]& A# _7 k2 |- B; B
- //SPR RV TB BP2 BP1 BP0 WEL BUSY+ Y( Y1 d5 h( l0 |1 R n6 ~( l) A
- //SPR:默认0,状态寄存器保护位,配合WP使用
" O2 s' ` _. }5 Y4 h) ~ - //TB,BP2,BP1,BP0:FLASH区域写保护设置: M+ s% K. |1 \$ ^$ t8 v
- //WEL:写使能锁定5 W' @! {/ J5 y/ |" E9 U# q: | o/ X/ l
- //BUSY:忙标记位(1,忙;0,空闲)/ O. U W% o$ J) w2 }
- //默认:0x008 ?& \ q2 d: d; r* p
- u8 W25QXX_ReadSR(void)
; I& W% b( S [6 q, ^2 E. P - { : R T" i& H, D2 i
- u8 byte=0;
1 L6 J8 ^7 n9 | D* P/ P# } - W25QXX_CS=0; //使能器件
% G# U; O0 V6 |" f% ~/ P, N - SPI1_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令
, T0 [ P- E' o9 t" A; M2 j8 H - byte=SPI1_ReadWriteByte(0Xff); //读取一个字节 ' _ A0 S, N. L0 [( Y
- W25QXX_CS=1; //取消片选
% t3 ~2 E! ]0 s: m* k - return byte; 8 X7 @) i. Y; C& g' r; U, g
- } ( H' j& ~, N: m& q- i
- //写W25QXX状态寄存器
: R* E b- n! f/ q( b8 U - //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!! U% I k% r9 ~2 ~; n
- void W25QXX_Write_SR(u8 sr) ! i' k4 H1 Y: i ^' j, X- r8 q, u
- { " S6 Y/ ]7 i* F5 ~* k
- W25QXX_CS=0; //使能器件
2 L' P2 P) @+ m5 F2 V - SPI1_ReadWriteByte(W25X_WriteStatusReg); //发送写取状态寄存器命令
- X' J. C3 y: [5 R# {/ Z$ G( [) T - SPI1_ReadWriteByte(sr); //写入一个字节 6 z, u2 ]$ [7 E- M' J1 X
- W25QXX_CS=1; //取消片选
; Z4 C9 k1 r. n. R; e% y - } 3 S6 _4 Q5 N4 l9 Z2 o. i1 `# e5 C
- //W25QXX写使能 . U9 D; a- F& P; |5 L/ s) n
- //将WEL置位
" z6 B N: W2 |# [" q - void W25QXX_Write_Enable(void) $ u9 p3 y% [& L3 W0 ]
- {
4 n1 Z* |" _; ~2 K - W25QXX_CS=0; //使能器件 ( a5 M# _/ ^0 e" F/ J
- SPI1_ReadWriteByte(W25X_WriteEnable); //发送写使能
& ?% ^$ w9 E. h5 q7 s% } - W25QXX_CS=1; //取消片选 5 O! p1 y g3 ~% d% |, x7 m
- } 7 u3 |: o2 ]4 R
- //W25QXX写禁止 , d9 F3 r( ~9 A7 L# E; _: i# \4 s
- //将WEL清零 $ e/ @+ ~) Z6 m
- void W25QXX_Write_Disable(void)
. K5 u% z+ m4 G2 c, ]9 \ - { 1 c. K5 J4 l2 O1 x
- W25QXX_CS=0; //使能器件 & J: r, b& W% S$ o' z% i Y8 p n
- SPI1_ReadWriteByte(W25X_WriteDisable); //发送写禁止指令
: g, Y! k5 Y6 k8 l- Z - W25QXX_CS=1; //取消片选
. D0 d- ~( y; b - } 0 ]$ ?3 ~) |9 J9 M# C8 T
- //读取芯片ID
# A# [0 \6 y; [# \ - //返回值如下:
( E6 B/ t1 S4 o - //0XEF13,表示芯片型号为W25Q80 i9 \( R! G' E z' t! B# Y. k
- //0XEF14,表示芯片型号为W25Q16
+ l% b( X$ W6 x; m) m - //0XEF15,表示芯片型号为W25Q32 + C1 n' x1 F# @# Z
- //0XEF16,表示芯片型号为W25Q64 ) {4 s) q+ p0 d2 q
- //0XEF17,表示芯片型号为W25Q128
/ a$ Y3 Z, d; C - u16 W25QXX_ReadID(void)" u+ ?2 L4 B6 Y0 A: j
- {- y! ?1 r/ Q8 z( H( k7 U$ p
- u16 Temp = 0; 0 V3 ` O5 c# m# _8 N% t
- W25QXX_CS=0; - ? Z2 ?3 z) y- h# x3 M
- SPI1_ReadWriteByte(0x90);//发送读取ID命令 7 U/ u# [8 I! H6 `: |! f% D' d3 c
- SPI1_ReadWriteByte(0x00);
- C- `+ R' V- M/ T - SPI1_ReadWriteByte(0x00);
, ?! l$ f" f2 E - SPI1_ReadWriteByte(0x00); 4 U: X) f* _9 h& h. `* L
- Temp|=SPI1_ReadWriteByte(0xFF)<<8;
. c8 r) K% r8 W4 G c6 s; ~. M - Temp|=SPI1_ReadWriteByte(0xFF); * w( x# Q+ r* g' R/ t6 S1 `2 P$ e! X
- W25QXX_CS=1; $ L4 i# @' ?! s; \4 b- Q
- return Temp;+ r! T4 B/ F) ^1 w
- } - H3 e7 y. U8 `
- //读取SPI FLASH
* k2 k) @1 M! ]" g/ L - //在指定地址开始读取指定长度的数据
9 |6 g1 l6 H! {. \ - //pBuffer:数据存储区
! I3 s' \1 u* ~% Z7 J - //ReadAddr:开始读取的地址(24bit)
$ q) N7 P5 \* q5 D7 E, G) S - //NumByteToRead:要读取的字节数(最大65535)
. T/ [) @" }$ Y& Z( V; `4 \2 l - void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead) 2 h$ H% ~5 _/ ~/ z- p
- { # m7 K# o" c3 X+ W7 J: m% \0 x
- u16 i;
' N' q# p1 L( Y) P - W25QXX_CS=0; //使能器件
) h8 c a3 \) Q" D1 Y - SPI1_ReadWriteByte(W25X_ReadData); //发送读取命令
; s- E% y' i' c" H, w! ^1 u4 `: F - SPI1_ReadWriteByte((u8)((ReadAddr)>>16)); //发送24bit地址
+ `; V6 k' W6 H - SPI1_ReadWriteByte((u8)((ReadAddr)>>8));
9 ^' O! v4 a( K8 l - SPI1_ReadWriteByte((u8)ReadAddr);
+ w$ g, S# G J$ A - for(i=0;i<NumByteToRead;i++)$ N$ K# W: J8 ^* |
- {
- y+ A1 M1 O0 o2 G. C - pBuffer=SPI1_ReadWriteByte(0XFF); //循环读数 + }( |5 r9 Q2 v" L4 j5 W* z
- }
2 Q( L! [1 g& f* Y x9 I8 p) E, k - W25QXX_CS=1; 8 Y: K- C9 J, P6 b4 R' T
- } 6 |3 x4 t& }0 |0 Y
- //SPI在一页(0~65535)内写入少于256个字节的数据! @9 T) Q" x! {! z
- //在指定地址开始写入最大256字节的数据
@4 y. Z# k4 B @+ J - //pBuffer:数据存储区0 }. I) w* z# }/ D% |7 `1 C
- //WriteAddr:开始写入的地址(24bit)0 ?/ _1 J$ V( V6 @. R: c* ^
- //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!! # F$ W8 _3 [' e! \
- void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)4 I5 o# }. G: N% Q+ n' l% u
- {
, }! @8 M6 j. s2 K! A6 K - u16 i;
' g2 O6 q8 j$ c+ q+ k7 V - W25QXX_Write_Enable(); //SET WEL
1 x5 E9 C7 D6 F; H" h - W25QXX_CS=0; //使能器件
* \9 b: u7 s9 w4 m/ T( t; S - SPI1_ReadWriteByte(W25X_PageProgram); //发送写页命令 / A/ K( y1 ~ @+ m9 g
- SPI1_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址
% W2 o; V. q' H' I0 s - SPI1_ReadWriteByte((u8)((WriteAddr)>>8)); # w" h- t' j+ r: y. h2 m9 _
- SPI1_ReadWriteByte((u8)WriteAddr);
2 h( p% |- i6 a/ p' J- { - for(i=0;i<NumByteToWrite;i++)SPI1_ReadWriteByte(pBuffer);//循环写数 ) O5 e% C" X9 r0 k1 a
- W25QXX_CS=1; //取消片选
8 o- r; g: d# @9 |1 K$ H A - W25QXX_Wait_Busy(); //等待写入结束5 u: j `" G' U3 m; q- j4 {- I
- }
X9 a7 w8 ` Z: I - //无检验写SPI FLASH 9 A4 W* v+ P4 z0 `6 A" J1 X
- //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!+ }, P4 B) e0 g# X4 z8 H
- //具有自动换页功能 6 A7 n+ D/ `3 \( `: G" `& \
- //在指定地址开始写入指定长度的数据,但是要确保地址不越界!
2 r7 `# z- d _8 R - //pBuffer:数据存储区
/ `8 ?1 S- Q) x9 R4 Q - //WriteAddr:开始写入的地址(24bit)& y! P/ X& @2 l$ K" \$ {
- //NumByteToWrite:要写入的字节数(最大65535)
F! y; L4 H& M: I( j2 J6 ` - //CHECK OK
6 w& a d- S v5 p* [! g - void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
( u* P; K5 X* e$ i; v6 U - { ; \, @8 z! x0 E3 G* }' u0 h; m8 D! d' H
- u16 pageremain; ! i" V9 v& o- M; n7 p
- pageremain=256-WriteAddr%256; //单页剩余的字节数
2 N# E* u9 p$ n$ m# d - if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节 K0 j6 Z( d- r3 c
- while(1)
! C# J, k7 Y2 k5 |7 I+ | - { - n6 T1 h; d; z* S# r0 T
- W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
, D* y2 j3 r: p) c# D) t8 k5 ^/ q - if(NumByteToWrite==pageremain)break;//写入结束了
0 q2 h/ a$ z! N; t1 F; Q% V - else //NumByteToWrite>pageremain
: Y+ q# ~9 N" y6 N. o - {* x0 d2 z* Y- X0 |- x
- pBuffer+=pageremain;
* ^ p3 Y Y8 j. p - WriteAddr+=pageremain;
! ?' y' k: F8 N
; Q: W$ |& V+ m% j- NumByteToWrite-=pageremain; //减去已经写入了的字节数
/ r4 x4 C7 q) D C9 E - if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
$ p1 ~, m3 m5 k6 ~ - else pageremain=NumByteToWrite; //不够256个字节了, V& U4 v4 _2 @5 f# p2 U4 X x6 A
- }' V' g* @& Z$ o, ]
- };
6 B- F1 Q5 Q0 K; i- I3 P" K# J - }
9 N; A+ u# `9 ^& p8 M - //写SPI FLASH ; k) W- T) B2 X6 s; I2 I9 p+ r
- //在指定地址开始写入指定长度的数据
* K7 R: s/ M9 ]# @! |; h: C0 [ - //该函数带擦除操作!. ^! f3 O, A4 V
- //pBuffer:数据存储区
& p" L6 U0 H2 Y - //WriteAddr:开始写入的地址(24bit) ; { q& L4 m. [
- //NumByteToWrite:要写入的字节数(最大65535) 4 r2 l+ i( ^5 l8 X G
- u8 W25QXX_BUFFER[4096]; % d6 L3 W, n5 n9 l; h
- void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
6 K/ V( W; {5 T( L1 e: v) ~* q - {
5 g0 f9 u7 v& G1 d6 q - u32 secpos;, }: o( n3 d, i8 K9 {
- u16 secoff;
7 D+ }, [0 o: L$ Z! W - u16 secremain;
1 r6 J; ], W$ _ - u16 i;
d6 E! ? R/ \2 J. r - u8 * W25QXX_BUF; ; p: p u# S! S6 k. J9 L. |) p: V
- W25QXX_BUF=W25QXX_BUFFER; ' F! D2 C! }- i& F% q3 S
- secpos=WriteAddr/4096;//扇区地址
! o1 O& m$ m" [$ y - secoff=WriteAddr%4096;//在扇区内的偏移. \8 i: O" u- p& ]5 |
- secremain=4096-secoff;//扇区剩余空间大小 - ^' y7 m( s6 c
- //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
6 Q! M3 C. N3 @+ J9 J9 H1 ~ - if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节) j% g6 B" I5 }0 p( h, U
- while(1) ' U. B; n F" d2 @
- {
$ d' {2 ~3 P9 j/ G! y$ K* ] - W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容' F3 C' l. ~: o% j( k
- for(i=0;i<secremain;i++)//校验数据0 [. e2 c$ g% ]' q
- {
, y% B9 \: O4 {# X4 [! W - if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除
1 p8 ]+ a, W) \1 ?. }6 r7 a - }. g+ ^$ s, A6 b, u
- if(i<secremain)//需要擦除
* F$ n) D. q P5 ^ - {: o# ]; c, k' M: d: F7 n0 M
- W25QXX_Erase_Sector(secpos);//擦除这个扇区
1 W9 O2 V" r* s% T# X. q. d - for(i=0;i<secremain;i++) //复制' z& c+ d# ]" e# {, s' l6 O
- {
9 S0 e! T5 m3 _) U3 w0 p" c) i1 F; N) e X - W25QXX_BUF[i+secoff]=pBuffer;
$ q* _- z. s1 K5 m - }
$ u% A7 F5 }' m+ Y5 | - W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区 ! F% E% h9 C6 a8 x' ?
- 3 W E$ T: p0 J& ?4 i$ q
- }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间. 4 L! @+ y" @% W2 \1 {+ p7 B
- if(NumByteToWrite==secremain)break;//写入结束了* U/ n8 ^6 J* x' |& e7 @
- else//写入未结束. S# v" J2 {: ~0 T. o
- {3 Y# ^, t8 [! m/ T% i
- secpos++;//扇区地址增1
' B; h& U) ?- N- F - secoff=0;//偏移位置为0
( V# D1 P! s3 p# W1 Q2 E1 I
' X* g2 X2 O0 E) a W- pBuffer+=secremain; //指针偏移9 q% D" B B+ f6 b B. A+ |
- WriteAddr+=secremain;//写地址偏移 : h: G, P- q& u+ [; v
- NumByteToWrite-=secremain; //字节数递减
. m) c/ C7 W" }* v; W" v - if(NumByteToWrite>4096)secremain=4096; //下一个扇区还是写不完
/ ^' Z, o1 G: ^1 i5 x - else secremain=NumByteToWrite; //下一个扇区可以写完了4 w0 x2 ^/ n4 j, v( d9 [3 l
- } 3 j+ H8 O- u' ?8 L% x) m, ^
- };
2 y' u& N. a" B b8 w! C* c - }& a8 P6 i1 {" l/ s
- //擦除整个芯片 ~0 b9 y; w3 t7 k, E; e5 T! u
- //等待时间超长...) e6 l1 L4 W1 h: R+ ~# E/ Y l
- void W25QXX_Erase_Chip(void) 3 t9 M" R9 M2 }3 H, u6 }
- {
) Y& j1 f2 I; f' G0 k u - W25QXX_Write_Enable(); //SET WEL 6 U( U8 D5 T9 L A
- W25QXX_Wait_Busy(); + B0 z) c7 G" G& r+ R/ S
- W25QXX_CS=0; //使能器件
) j( U" | v; _+ k( `+ S - SPI1_ReadWriteByte(W25X_ChipErase); //发送片擦除命令 4 i; E( i) s4 S, [6 d8 S
- W25QXX_CS=1; //取消片选 6 G3 v A% I2 [
- W25QXX_Wait_Busy(); //等待芯片擦除结束
- \, o, ^6 n( e! ?- t - }
- H {+ g) H3 N/ g( D - //擦除一个扇区& P/ r3 d# j. T/ Q6 ]$ b
- //Dst_Addr:扇区地址 根据实际容量设置
4 e8 K1 i" f& m& U# N3 d - //擦除一个山区的最少时间:150ms% m* a1 s8 {/ A6 E8 r. Z
- void W25QXX_Erase_Sector(u32 Dst_Addr) . j* `6 x5 C) h v# l1 G1 r; T$ j
- { ( g& @1 P% D" ~1 F" q w# X# v
- //监视falsh擦除情况,测试用
1 c/ u0 c7 I# N" b5 m - printf("fe:%x\r\n",Dst_Addr); * K5 b# f* q$ K& v( }
- Dst_Addr*=4096;, H) y. \* V# e2 Y. v
- W25QXX_Write_Enable(); //SET WEL ) S- K+ f6 p" D$ |& Q2 v9 A1 u
- W25QXX_Wait_Busy();
l, X* `4 l3 T# w8 M- @ - W25QXX_CS=0; //使能器件 " e$ i( W; J4 B/ g9 W) C5 G( M" B r
- SPI1_ReadWriteByte(W25X_SectorErase); //发送扇区擦除指令 0 t: O1 d# j7 @2 x. T: x
- SPI1_ReadWriteByte((u8)((Dst_Addr)>>16)); //发送24bit地址
* k/ d6 k5 a* r- }+ }9 Q - SPI1_ReadWriteByte((u8)((Dst_Addr)>>8)); , n# N; z3 R4 o) U( y
- SPI1_ReadWriteByte((u8)Dst_Addr);
9 b& r7 O1 `3 b# ~; c- P" [ - W25QXX_CS=1; //取消片选 + J9 R7 o x' s
- W25QXX_Wait_Busy(); //等待擦除完成+ T$ I% I# V) y/ _" h) t
- }
4 q* j, U. v% a) ` - //等待空闲
- Y0 q+ B& G3 Z% ]9 @% I - void W25QXX_Wait_Busy(void)
i9 v! H# S$ W& l8 J; x( l - {
" U! V- m! |+ a o; d7 \. d - while((W25QXX_ReadSR()&0x01)==0x01); // 等待BUSY位清空
% [) q5 L0 d- {) @; R - } B, u, j6 e1 G. O$ E0 b! i
- //进入掉电模式3 q+ h, t1 u$ Z
- void W25QXX_PowerDown(void)
3 } u9 ], r) | - {
5 X- P) P4 k' L' U) e - W25QXX_CS=0; //使能器件
! M' q* c) `: z% X: f - SPI1_ReadWriteByte(W25X_PowerDown); //发送掉电命令 4 W( s) v+ Y O& G! n# {- l
- W25QXX_CS=1; //取消片选 + P. r; w; @! [& W& F2 l
- delay_us(3); //等待TPD 1 I% d; h. ~. j; O0 C" r; e
- }
, a; S: `/ p3 x, |- L, O - //唤醒
2 R3 R3 e H! ]# I4 g" a - void W25QXX_WAKEUP(void)
T5 Q* F2 |4 v7 G - {
- l4 `2 O2 l" u - W25QXX_CS=0; //使能器件 : j8 Q; i8 i6 ]3 Z" R. F
- SPI1_ReadWriteByte(W25X_ReleasePowerDown); // send W25X_PowerDown command 0xAB
& o; \5 X2 q5 {, F1 } - W25QXX_CS=1; //取消片选
- p# i/ [- \( f3 G/ X/ a3 u& a - delay_us(3); //等待TRES1* }' y$ o* w6 \) e$ [. V1 s
- }
复制代码 5 D! L. X1 x( r$ P1 r3 z
main.c
4 C5 J) ?4 B0 y1 s: I
+ @# R" }, r% z/ H [ K% d) j& h) ~- #include "sys.h"' E3 w! y# [/ h9 v$ S- M8 {3 s u
- #include "delay.h") m# q A+ N' e* ]* h/ N
- #include "usart.h"3 X# r( ^3 g& }6 @) g% R
- #include "led.h"4 e) A9 R% p' O
- #include "lcd.h"$ F. U- K0 e" H1 r; p1 d; ~
- #include "spi.h"& h! j6 E3 |0 ]7 J
- #include "w25qxx.h"! V* T4 C6 c2 [: F1 N
- #include "key.h"
8 S0 ]% ~0 U% W* p - % t* m3 o1 c8 S6 J( b
- % G" C8 M! H! l G* @
- //要写入到W25Q16的字符串数组: b1 d3 {) E7 `
- const u8 TEXT_Buffer[]={"Explorer STM32F4 SPI TEST"};
9 `$ {, H5 F$ C8 U8 K - #define SIZE sizeof(TEXT_Buffer) 1 D/ j5 w9 w; E2 |& K6 B
-
9 E9 Z) d( J! t) N - int main(void): {* e; a. J" H8 e( }
- {
8 u* f' I3 ~* Z: ? - u8 key;
7 G+ J5 s+ u; S - u16 i=0;
3 J7 ^- e5 |$ m) S& r - u8 datatemp[SIZE];
, [5 j2 X. }; p4 P - u32 FLASH_SIZE;. w9 M- f- O& i$ F; K: P
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组27 z; H/ [$ y) v6 k
- delay_init(168); //初始化延时函数
4 z7 W" n6 O& a* S+ R: A - uart_init(115200); //初始化串口波特率为115200
3 k+ W3 Y: X$ _8 H - LED_Init(); //初始化LED
h" y! l/ n; X" M) X - LCD_Init(); //LCD初始化
# j! H" E* X! F4 a - KEY_Init(); //按键初始化 * B& f% b5 k' Y; W% G) l' Y
- W25QXX_Init(); //W25QXX初始化
0 m+ `4 w; D1 S, D- Y* c( x4 T8 B6 ? - POINT_COLOR=RED; 8 l4 F8 w } l0 A6 k# c
- LCD_ShowString(30,50,200,16,16,"Explorer STM32F4"); * @/ f; o3 N9 J/ D. o
- LCD_ShowString(30,70,200,16,16,"SPI TEST");
4 W- j' C" |9 j$ O - LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");4 _7 y. @5 c6 [. W: d& a
- LCD_ShowString(30,110,200,16,16,"2014/5/6"); 6 N0 M; C q9 l# e9 ]
- LCD_ShowString(30,130,200,16,16,"KEY1:Write KEY0:Read"); //显示提示信息 8 y, X& V4 T" k
- while(W25QXX_ReadID()!=W25Q128) //检测不到W25Q1284 x! y9 s ]( \+ J
- {
3 V: F* V& @! r- E" z+ p - LCD_ShowString(30,150,200,16,16,"W25Q128 Check Failed!");
9 Z$ H ~8 ^9 `7 w% H9 u0 p! ~ - delay_ms(500);/ l+ e1 V) w' ?" S& u. H
- LCD_ShowString(30,150,200,16,16,"Please Check! ");+ {( q3 c3 k+ J7 Y2 G
- delay_ms(500);
; _) H% u* F/ g" M" W& t - LED1=!LED1; //DS0闪烁
1 K1 K' q$ h1 `1 L; I/ ] - }' F$ O- O" z; f& y0 a$ Z$ j7 e
- LCD_ShowString(30,150,200,16,16,"W25Q128 Ready!");
8 ?* z6 S$ P Q1 g) ~9 N - FLASH_SIZE=16*1024*1024; //FLASH 大小为16字节
; H6 O- c: T6 N% B5 V - POINT_COLOR=BLUE; //设置字体为蓝色
$ ~6 [3 @! N1 i, S; g9 R+ j - while(1)
5 q& y7 @9 \% J; g - {+ w$ j0 }+ l0 c) I8 x0 q
- key=Key_Scan();
' E+ o1 C2 V" G- b$ z3 S8 J; ] - if(key==KEY1_PRESS)//KEY1按下,写入W25Q1285 u% {& t' f. g% F2 S5 u0 v
- {! b; F& C z. j0 k
- LCD_Fill(0,170,239,319,WHITE);//清除半屏 ; b0 _; u! f3 n- Z2 ~) C, t
- LCD_ShowString(30,170,200,16,16,"Start Write W25Q128....");& P: E: F- l) i; Q
- W25QXX_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE); //从倒数第100个地址处开始,写入SIZE长度的数据
( ?; `# r, N2 g% j9 L9 W - LCD_ShowString(30,170,200,16,16,"W25Q128 Write Finished!"); //提示传送完成7 O* o9 E+ @: Z6 H; }- s
- }
Y! t* `* h( k, _ - if(key==KEY0_PRESS)//KEY0按下,读取字符串并显示9 E" c9 s% E# O2 s# \
- {
9 c; H5 v* ~6 o* A$ P X. ] - LCD_ShowString(30,170,200,16,16,"Start Read W25Q128.... ");
3 d# Z R5 j" j& J: E6 F6 w - W25QXX_Read(datatemp,FLASH_SIZE-100,SIZE); //从倒数第100个地址处开始,读出SIZE个字节; E2 B& Y1 V& f3 E; y2 w% D
- LCD_ShowString(30,170,200,16,16,"The Data Readed Is: "); //提示传送完成
9 O; ]/ h( p7 Y - LCD_ShowString(30,190,200,16,16,datatemp); //显示读到的字符串
* P- Y7 Q% e1 B; Q. }, d+ f - } * n W, J: \# b/ U/ E* k% I' w* g
- i++; m# Q& ^( k7 v! q; E: z' Z) Z
- delay_ms(10); a( U4 L. T+ A& F: R; }+ v2 X
- if(i==20)% ^7 t2 m. y. Q A5 H7 R3 k/ I
- {( G6 Y) W& _: P( h- Z7 i2 U
- LED1=!LED1;//提示系统正在运行 Q% F& b" u) W2 @5 {
- i=0;6 Y$ H$ c1 V$ R; e' A) n4 W) w
- }
" \9 s! g9 X$ N$ ? - } 7 Q' `% h9 k7 H4 i5 P! B! R
- }
复制代码 9 q$ \! ]' x) n) W3 X' @) z
05. 结果验证6 a" B5 O( G. F' l5 [# h
按 KEY1 按键写入数据,然后按 KEY0 读取数据。DS0 的不停闪烁,提示程序在运行。: v9 j- M2 x7 ^! T
4 ?0 b$ d6 t: l3 W# \
* S: W& Q0 T' y( `' G! q |