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