一、实验前期准备
. y7 ^2 j& X$ h, Y+ n本次实验的 MCU 是 STM32F103C8T6 芯片,通过 SPI 通信实现 W25Q128 的读写操作。
6 w/ y+ s0 J) w8 L9 `
, |9 h* Q) V6 ~' i) I1. 原理图, Y, F. Q: E/ [# w
; B6 J& `# {7 a& f. h3 `3 F9 d
8 e) ?2 }2 D/ s# ?$ |! S: D
4 l5 e; b6 c* @ n3 U* A x, X
2. 引脚连接
3 w. F& T" p5 ]- l; @7 o( s+ p* N1 f8 D
0 r ]: c5 ?- A! Z- p
M3 S7 ^3 z6 u二、SPI 底层驱动
, S# [- j% i. M# D# x# s JSPI.c
' L( _, t s- Y- #include "SPI.h"
+ v3 N4 W2 q* Y' U; x3 S - ; l. T+ p2 C$ J7 y2 p
- /*1 \% o* e; @0 v% f* T4 U) w- `
- SPI引脚初始化配置8 A! e5 ?6 _+ x
- **PA4------CS' X! [8 X$ w1 P& v3 y( S
- **PA5------SCLK
" W* [7 F/ c2 P( l! Z. S" ?: l - **PA6------MISO0 }/ k. d" K4 @' x# w( P4 B' ?& b
- **PA7------MOSI8 t6 M- [) V- o! a& E8 T2 ^5 X
- */
! y/ m8 b/ O% S+ W" Q
) J$ [! {8 K! n) `$ r- A- static void SPI1_GPIO_Config(void)
& G' `& K1 T2 m) X - {; ~; D6 d" [3 ^. N G5 d
- GPIO_InitTypeDef GPIO_InitStructure;- T8 y5 i: P0 v3 [6 o) W
- / X. l' o* o* h
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );//PORTA时钟使能 k/ c. T2 X/ d; D0 j$ w: F) m) A
- & ]2 I- G4 d4 G3 _
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5| GPIO_Pin_6|GPIO_Pin_7;% W) W% T3 @6 s2 l" a/ }! `
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PA5/6/7复用推挽输出 , Y$ Z6 d1 L, n: F" m8 k
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;4 }% T1 A }$ }/ o% K% V
- GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA
5 P. ?6 O# R' i -
' q; O5 l9 i9 X7 b( d - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;7 n* r% c: [! P7 }
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //PA4推挽输出4 _/ D2 R2 t& F; U8 D r
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;* H$ ^; ~4 m) {2 T; a
- GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA# E* [. b/ ^& h+ O
# [; C Z, ^, g. f. U- GPIO_SetBits(GPIOA,GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7); //初始上拉输出& u* J7 u! |4 c( m' j6 j/ E
: E! P/ s' F" Z+ X( |. i- }
- u& C3 P- e- L9 M! c6 t, l - . `9 ?$ E$ `7 Z2 ?0 V. E; u
- //SPI1初始化函数
2 [4 q9 G4 P, _ - void SPI1_Init(void): i% J4 n7 z1 t1 v3 w9 ]
- {- q# T* @, z+ S( g
- SPI1_GPIO_Config();//SPI引脚初始化配置
. O2 ?( I$ e$ q - 2 @% d% S9 [; I8 l
- SPI_InitTypeDef SPI_InitStructure;$ P7 x. y. J, d( C+ Z' S
- 2 y5 y" R8 f. Q# p
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_SPI1, ENABLE );//SPI1时钟使能 % d0 ]( l1 \/ A2 `; i9 ?! E
- 5 o! p4 q( t! |+ a: h, Z" d
- SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工4 I. L; M9 \$ n
- SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI
, M7 Y, O2 ]. \: S. B - SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构, [# Q- B! w6 u+ ~& g; ~
- SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行同步时钟的空闲状态为高电平7 L3 W- t7 B3 O, i& P1 n- C8 T* @
- SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样6 h! ~: r H7 } P2 t
- SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制+ O5 d$ A: h0 i( @- ^1 b1 Y# g+ k
- SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定义波特率预分频的值:波特率预分频值为2567 V+ _6 ^/ x9 D U
- SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始5 X( R7 e# E( P, k/ z2 w
- SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
0 h: M# g1 q1 x0 }0 C - SPI_Init(SPI1, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器' t9 ~' O$ h8 D0 P' w
-
7 X1 z2 i/ T( g, \( E6 d - SPI_Cmd(SPI1, ENABLE); //使能SPI外设' C, N6 m& g- U) Q
- SPI1_ReadWriteByte(0xFF);//启动传输) w2 x" ]" \# {, u- [& d7 @
- }
/ B- B4 [9 r, E2 X( h! r" U' d
" C+ g% s) H" a! h+ k; L- /*
' E9 c- I' U; C( _* f - SPI的读写操作* |5 T- E% l4 {9 t$ K
- **TxData:要写入的字节* N: j' g# f7 c3 j& O
- **返回值:读取到的字节
4 i& a' E3 F: S/ s! | x, q! H - */
4 W0 t! D, A- i& d+ f% i - uint8_t SPI1_ReadWriteByte(uint8_t TxData)
9 B$ t/ g# a& T0 k( E) H& r - { % q" E$ B3 F3 k8 y* I; N1 H3 A3 V
- while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位; \3 X- c+ M$ S. ?& {- E2 v
- {. G9 W% x: F1 }" |6 Z; p
- //等待发送完成
# d+ E9 M; y/ P5 ? e' L - }
: j" o. c+ @& U& k! G - SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个数据: ]: Y. N0 X. a' z [
- while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受缓存非空标志位
$ t/ [ e) q; V$ L5 `* Q2 T1 O - {
* c c0 {( O) T6 R - //等待接收完成8 i; E0 x4 q Y5 ]- ~
- }
. m2 b ]+ @+ H0 `% ^) n: t - return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据
" `: L6 I; X3 H$ | - }
复制代码 9 Y. Y+ k3 J7 f, r
SPI.h# @ H; }# V; c! k: u
- #ifndef __SPI_H7 X6 K. ^' r" r6 K
- #define __SPI_H5 T/ Y' C7 Z1 [, T; I9 v2 o
- / B/ o9 o7 O% ~
- #include "stm32f10x.h"* d. C$ u' R. i; |' h( B X# O( r
- : s; S) p- y/ d) X. B: W% D
- #define SPI_CS(a) if (a) \& G% {) \6 [2 k; h
- GPIO_SetBits(GPIOA,GPIO_Pin_4);\
/ x( c3 _- S1 R" } ~/ t5 V6 J: F - else \
7 m$ L$ T2 I0 ]2 T - GPIO_ResetBits(GPIOA,GPIO_Pin_4): y* S+ Q9 q- `. d+ W2 f, C! X" T$ D
- # L" M( L) J& O: j6 p' E' y0 h7 ?
- void SPI1_Init(void);//SPI1初始化函数8 Z$ x1 L- A6 D! o
- uint8_t SPI1_ReadWriteByte(uint8_t TxData);//SPI1读写函数+ l, ^3 N8 I- z" A. p* e5 z ~/ M, k
3 q9 y J- T/ n* y" W- #endif
复制代码
; b8 N# H4 N2 z% m7 l三、读取 W25Q128 设备 ID
+ n2 f9 N/ v4 H+ |; z, C本次实验是:读取 W25Q128 设备 ID,然后通过串口1打印出来。- m9 c, u2 P6 K+ S! @
通过简单的实验来调试,有利于发现问题。
! _& @* }0 s0 _% C7 T. N1 r通过W25Q128的数据手册可知其设备ID如下图所示:0 }* P# n" Q7 _3 w
% T- {% ~& T& Y4 W1 \
) T5 {; y. d! z% S
& b" n/ r9 l+ c+ k
W25Q128.c/ l/ Z, v* r3 @' X
- #include "W25Q128.h"
2 s# e' I9 C& p8 K& I; l( a - #include "SPI.h"
$ V& C: C6 D R - ; M# n! a& M5 U2 ]
- # G% f: G, }" R* e
- uint8_t W25Q128_ReadWriteByte(uint8_t TxData)//函数包装一下
" W0 i1 L( k6 J) V! L - { 7 e5 @0 e: ?/ V% V% ]9 Z
- return SPI1_ReadWriteByte(TxData);
6 [1 b, v" D) }9 v* w5 w( a - }* d0 e: x# f/ b% E
- ) l5 S$ t" q! r$ R! H7 d
- uint16_t W25Q128_ReadID(void)//读取芯片ID
& y' C% w: }* q, M - {8 h9 K: g6 K$ ?1 Y! c* ^7 c
- uint16_t Temp = 0;
) v- c* ^1 K2 S; b& e - W25Q128_CS(0); " V5 j. J4 }. z: H6 ~: p. k( _
- W25Q128_ReadWriteByte(W25X_ManufactDeviceID);//发送读取ID命令 9 x0 @# {+ ~0 i- p! ?
- W25Q128_ReadWriteByte(0x00); 0 u* m0 D0 j3 z$ v
- W25Q128_ReadWriteByte(0x00);
7 K9 Z/ M; O+ v# H: u - W25Q128_ReadWriteByte(0x00); 4 Y" }0 d5 P" n' o6 k
- Temp|=W25Q128_ReadWriteByte(0xFF)<<8;
& N! f1 m% L1 V T( g - Temp|=W25Q128_ReadWriteByte(0xFF);
- G, L' h' [1 u) e. i - W25Q128_CS(1); / @+ Q3 ]$ x" C
- return Temp;& H4 R8 E! S/ T. ]& r* f6 E8 u
- }
复制代码 3 z; T/ \3 u/ O- `) r/ |
W25Q128.h" h+ H" d$ l' T1 J
- #ifndef __W25Q128_H
/ ~# S+ D5 ~9 u# C* K4 e - #define __W25Q128_H
2 R$ V4 |4 |* N6 g
* M1 C3 d- d$ }+ L# O- #include "stm32f10x.h"9 w& H* W$ D7 f" D
# O: Z6 o' u) K8 T q ^- //操作指令表
6 w- l3 {. X& }+ e/ P$ y* Z) R - #define W25X_ManufactDeviceID 0x90 //制造商+设备ID, o# c1 z, H+ q
- O2 ^1 r d- g0 ~) P3 _4 y- #define W25Q128_CS(a) SPI_CS(a) ) i8 Q7 j! k) T
- 9 c, Z$ p: H3 [- G/ \$ x% z
- uint8_t W25Q128_ReadWriteByte(uint8_t TxData);//函数包装一下- G1 j ]$ P; Q" z; ~+ r" F
- uint16_t W25Q128_ReadID(void);//读取芯片ID9 [) v u: @, U' o9 O% N0 ?
- % M/ X, N- a+ m w4 w, w; u
- #endif
复制代码 ) X) U( D b9 y M
main.c
) E! O5 u# c- G$ {- #include <stdio.h>% }/ M" o( Y2 D0 i! K1 |
- #include "Uart1.h"
% h! r" B( S2 l5 B3 f - #include "delay.h"9 e1 e$ p1 |" D9 z) O
- #include "SPI.h"! T. O* y- q0 c& A5 N7 `
- #include "W25Q128.h"
2 ]8 H- i! e4 k) ^1 y5 A$ ?
( S% R. F) Z( O& U* H; s- uint16_t W25Q128_ID=0;
9 V: V# \$ f. y, I# o+ ] - 6 E' U9 [+ |7 r3 ^ B5 C
- int main (void)) y; G6 G- k5 G& Q) X, e O
- {
0 U7 w; a$ G* C# \* u - Uart1_init();1 Q! N9 l* |* b7 D4 w" V
- SPI1_Init();
' q5 Q) Y5 y- o -
/ j; n3 [+ D) m2 i - W25Q128_ID = W25Q128_ReadID();
! k& |% T7 g4 K: r) g# T - while(1)
; W+ W+ w" f% V p. M! C6 O) C - {
4 F# k6 u- q; S* P1 ?4 {. F. _2 | - printf("\nW25Q128_ID=0x%X\n",W25Q128_ID);
7 I/ U" C& r7 Z - delay_ms(500);
7 `2 W4 e; Y# n( f) b8 O& [+ r& b' g - }
3 g7 F2 a% d& g. ]4 g; R - }
复制代码
7 J8 a2 n: S- D* f4 @5 U实验结果. \! H. Q; b) e
9 Q; V& A2 _5 r, Y' v
* v' G3 E4 m, e4 \) b接收到 W25Q128 设备ID 为0xEF17
, i& a3 S0 O% X
# l b5 x' r3 ]9 L/ M& C四、读写 W25Q128 外部 Flash
$ C( U ~2 d( j3 [W25Q128.c
: \) j& W/ p1 J9 N5 R1 k3 ~- #include "W25Q128.h"
+ g/ a: `; X, N' B - #include "SPI.h"6 e, V6 U3 m X
- & ^8 u1 N! @2 R2 L9 M: k
- % b5 o: t0 Z d4 i/ _7 Q6 P
- uint8_t W25Q128_ReadWriteByte(uint8_t TxData)//函数包装一下
+ r2 M" v" ~6 K9 C% N3 W - { 1 \! D' @; l3 S3 o
- return SPI1_ReadWriteByte(TxData); . k1 ^6 z" n" |% ]
- }; j* y+ R0 M6 ]5 ^
; O6 ^9 m9 w. E: ~5 d% L
) q7 m# j z6 ?" T# N/ M- uint16_t W25Q128_ReadID(void)//读取芯片ID) Y; r% _4 x1 Y0 X% w9 y7 o8 y
- {: W/ E, j1 R& @1 h$ W
- uint16_t Temp = 0;
: n0 r2 g+ n* K4 G! Z8 ]7 o4 c - W25Q128_CS(0);
& I4 u2 Q$ y: j& w- n( U - W25Q128_ReadWriteByte(W25X_ManufactDeviceID);//发送读取ID命令
" o: A4 Z4 J8 l) k8 g9 A4 i# W - W25Q128_ReadWriteByte(0x00); , ~; i X/ B/ z5 V
- W25Q128_ReadWriteByte(0x00); % K8 D! [! f3 l7 B9 \' c# ~
- W25Q128_ReadWriteByte(0x00);
( ^! M& i$ h+ |0 t1 G - Temp|=W25Q128_ReadWriteByte(0xFF)<<8; 3 p0 [( H8 G. L1 j$ ^( F
- Temp|=W25Q128_ReadWriteByte(0xFF);
# P( c( g# K) a. M; B) W - W25Q128_CS(1);
) \, a* _' A3 B' z: q - return Temp;; C" X H( z0 A
- }
1 w; s5 m B# D1 ?4 s1 g( _
, Z7 q* W- J2 a- A0 R8 \- //读取W25Q128的状态寄存器
$ H- d+ l3 b( N0 Q8 s: J6 t1 i - //BIT7 6 5 4 3 2 1 0, k& D& n0 e9 @3 x! U
- //SPR RV TB BP2 BP1 BP0 WEL BUSY
B& b4 Q3 F: [. P - //SPR:默认0,状态寄存器保护位,配合WP使用! z* ?% o2 V6 k5 c P, c
- //TB,BP2,BP1,BP0:FLASH区域写保护设置
2 P l! z5 l" H$ m1 Y f - //WEL:写使能锁定
. K" r% Y7 e' v - //BUSY:忙标记位(1,忙;0,空闲)5 i# n; U i; R% F; J
- //默认:0x00
0 B; v! @3 F- i; I" K, { F1 M* X. X - uint8_t W25Q128_ReadSR(void)//读取状态寄存器
k$ ]/ Y* x. f$ G0 D1 A: M7 m - {, f+ Z" b+ |1 c5 `* E; a
- uint8_t byte=0;- P' Y: X3 Y( q
- W25Q128_CS(0); //使能器件* b8 o5 `4 v% f1 y
- W25Q128_ReadWriteByte(W25X_ReadStatusReg1); //发送读取状态寄存器命令
$ k x2 u# ]/ |3 ] - byte=W25Q128_ReadWriteByte(0Xff); //读取一个字节
: e* R) ~, x% N: k* g' [ - W25Q128_CS(1); //取消片选
* L5 K2 e+ ~( ^5 P3 \* o - return byte;
) W! c1 B: Q3 ]1 f - }4 I) k3 `& _. c: l# ?
9 F1 S. s8 `. y1 m) \! b- //写W25Q128状态寄存器6 u7 U4 w! V4 J* h4 I3 l
- //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
+ m/ d. u; m9 ?. Y' N - void W25Q128_WriteSR(uint8_t sr)//写状态寄存器
# o$ N- j: F t( m. G - {
% s5 L) ?6 X! l# J - W25Q128_CS(0); //使能器件
) L$ C( n' X% w - W25Q128_ReadWriteByte(W25X_WriteStatusReg1); //发送写取状态寄存器命令
. c* j! i/ D$ b" o8 A9 m; w4 K - W25Q128_ReadWriteByte(sr); //写入一个字节
8 j2 ]5 t$ X7 ]! A7 q! L7 [/ K - W25Q128_CS(1); //取消片选
' k; j8 Y& Q* S2 q; B9 Z6 a* e+ c - }
0 g0 z" l8 u! } - 8 l5 E$ @3 s6 {4 c H* S4 Z, v$ r
- void W25Q128_Write_Enable(void) //写使能
: W9 {3 H5 F: h/ H- P6 b6 Q - {
( e1 X+ m' p' X5 f- X - W25Q128_CS(0); & Q) J! X3 G4 U9 \
- W25Q128_ReadWriteByte(W25X_WriteEnable);; J% w: I& v+ h- r
- W25Q128_CS(1);
' E5 x8 l" ?0 ~- u8 b/ ^' p - }, ^' R7 Y0 X/ h
: T* S( f; n, y7 I% L. }- void W25Q128_Write_Disable(void) //禁止写入 5 a( ?9 b8 J# d3 g- Y
- {
% P1 _: Q$ { A+ ^ B, K - W25Q128_CS(0);
4 t- f$ _# f" z; J. ^ - W25Q128_ReadWriteByte(W25X_WriteDisable);
8 g. s/ I* B/ ^3 Q/ b6 u& A( S - W25Q128_CS(1);
% y4 G6 j! M4 K1 \ - }+ a8 N. U# O& l8 j3 ]
- ; p* H) ~0 @) {* a; N
- void W25Q128_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)
2 o3 M* S) r8 a' [ - { , K' O, }$ J! `. k8 v( {) j
- W25Q128_CS(0); //使能器件
; v$ Y' B6 c7 q; K: Y" q; v - W25Q128_ReadWriteByte(W25X_ReadData); //发送读取命令
0 t1 v& ]/ M$ H" Y# N' n. p - W25Q128_ReadWriteByte((uint8_t)((ReadAddr)>>16)); //发送24bit地址
5 g' t. c, _% x% k, q - W25Q128_ReadWriteByte((uint8_t)((ReadAddr)>>8));
4 e/ y( }+ S# k: T - W25Q128_ReadWriteByte((uint8_t)ReadAddr);
: k# x( }- @- q7 P& U - for(uint16_t i=0;i<NumByteToRead;i++)# f4 V5 I* _+ S# x4 }4 e
- { 1 c6 G9 a/ l* ?- `) \
- pBuffer<i>=W25Q128_ReadWriteByte(0XFF); //循环读数
( f: A. T6 M4 Y1 \1 O% J9 \( Z - } K. Z3 |) }! {2 @
- W25Q128_CS(1); : A$ s5 j0 G! l4 }! |( a
- }
9 {/ n1 f/ c U1 G- Z- E( k+ W( W - 7 ?$ ^3 A7 x3 U$ u
6 f$ t$ P) I; I/ p- void W25Q128_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
' v9 {4 b u( X5 G2 G$ m& C - {
6 `) p) ]/ v; p - * S3 g7 y2 M# _; a6 S: a/ h
- W25Q128_Write_Enable(); //SET WEL / r! ]0 ~0 i3 @2 q, @
- W25Q128_CS(0); //使能器件
% {4 K" O4 r, H - W25Q128_ReadWriteByte(W25X_PageProgram); //发送写页命令 - T. a) Q9 v. X) X# g1 ?" W
; s* d# S; X0 f* |- W25Q128_ReadWriteByte((uint8_t)((WriteAddr)>>16)); //发送24bit地址 / s( d: ^- C* E1 v% V, x
- W25Q128_ReadWriteByte((uint8_t)((WriteAddr)>>8));
S3 l$ Y/ k& [4 q* K+ e# v; | - W25Q128_ReadWriteByte((uint8_t)WriteAddr);
8 D* d+ O6 |, Q. I" P+ p* f - for(uint16_t i=0;i<NumByteToWrite;i++)# O5 w$ w0 C6 d! q9 |' p
- {
, u. G3 Z* R9 |2 ^ - W25Q128_ReadWriteByte(pBuffer<i>);//循环写数 , S1 z7 j6 Z J
- }
- J+ n% z5 p7 L1 ` - W25Q128_CS(1); //取消片选 6 L( E5 Z4 q* Q- q$ A: z# J
- W25Q128_Wait_Busy(); //等待写入结束; V/ E: i# m9 ~1 Y, [0 C
- } 7 y- }5 F C) K: p3 u- K8 d
' u! f) S4 [) ]& g- //无检验写SPI FLASH
3 s+ J) ?8 }& a+ M1 \1 ]+ [! Q - //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!. n9 b! ~+ ^5 r% y+ Q& g$ S
- //具有自动换页功能
" G3 {" j* m) d x& Y, d - void W25Q128_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite) 3 {& c L. O! g9 L8 E
- {
( E" z3 r* A& @ o G$ I3 C - uint16_t pageremain=256-WriteAddr%256; //单页剩余的字节数
; B& L) m' q6 J - if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节" {" w# O9 a+ Q) D, k
- while(1)
7 h8 r- ]% c; T/ c# S8 v - { 4 _; x. z5 M' A8 j
- W25Q128_Write_Page(pBuffer,WriteAddr,pageremain);
6 `4 H% c0 O) { - if(NumByteToWrite==pageremain) break;//写入结束了2 e3 G1 q) w) T0 e& `
- else - u: r1 E! a V& H' r& c
- {
5 {. Q6 g: z2 p& L! t& z" @+ @. v - pBuffer+=pageremain;8 u8 O; k1 j5 [2 [" y% i
- WriteAddr+=pageremain; - @# P! a$ X1 `4 K, g/ R
- NumByteToWrite-=pageremain; //减去已经写入了的字节数9 R m& v- `% ~$ o, ^
- if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节 y! `. P* X; {. Z3 Q [* Y
- else pageremain=NumByteToWrite; //不够256个字节了
9 B7 h8 S$ |5 A( [# r+ u - }
$ v; g- k9 k z% `! ^1 K - } 9 `6 t7 b( g' J- ~, i
- }
- D' \2 ]: `3 s' c
1 A1 s9 I6 C' b- v5 Q- //写SPI FLASH 7 |- y! t O* K! g5 w3 D9 {
- //在指定地址开始写入指定长度的数据
# c1 ~" t! p7 f+ A1 G0 P) N5 r8 o - //该函数带擦除操作!
% K' M; T% p# x. N- u - //pBuffer:数据存储区
, V% C/ `+ ?+ q% F& |" \* h0 v - //WriteAddr:开始写入的地址(24bit) ! t T1 z! l. P" k) l: m
- //NumByteToWrite:要写入的字节数(最大65535) 0 J$ ?3 W( o( c5 w5 y
- uint8_t W25Q128_BUFFER[4096]; - X+ [" t" n3 Q, P4 @) u7 \. E
- void W25Q128_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
8 b; Q/ j- f9 \" s - { ' {* {* ? S* A
- uint16_t i; ; k9 }/ p' G( S7 q0 I* l
- uint8_t * W25Q128_BUF;
: l& w, y% W& x - W25Q128_BUF=W25Q128_BUFFER; 1 f9 D9 _+ `( W! B- ^% \' f: Q* y6 {) L
- uint32_t secpos = WriteAddr/4096;//扇区地址 ' o p- V: `# V% i5 R
- uint16_t secoff = WriteAddr%4096;//在扇区内的偏移( f/ ^2 W, p! G- s5 Y7 N
- uint16_t secremain = 4096-secoff;//扇区剩余空间大小
W0 ]' h u, l: m0 z
3 w1 b2 X; o4 }# i j* b, L- if(NumByteToWrite<=secremain) secremain=NumByteToWrite;//不大于4096个字节
0 c; i3 D- P0 b g0 W - while(1) $ }$ D( \2 f! c* z$ e
- {
+ H9 V8 B# ]- U( `1 O& B - W25Q128_Read(W25Q128_BUF,secpos*4096,4096);//读出整个扇区的内容
U6 _$ y$ r3 M! l3 { - for(i=0;i<secremain;i++)//校验数据
* a3 ]6 C7 G5 X" t6 g - {
, [. i3 G3 r \' b; z - if(W25Q128_BUF[secoff+i]!=0XFF) break;//需要擦除 - b: a( n+ [) q
- }, }3 s7 g4 |: G& B5 p' F- C9 V' Y
- if(i<secremain)//需要擦除
+ h# ?' i& R" m - {
) X; N0 ?2 |0 K2 Z0 N9 B - W25Q128_Erase_Sector(secpos*4096);//擦除这个扇区3 ]% R2 o# w; T/ Z- Q
- for(i=0;i<secremain;i++) //复制6 `! d% I/ y; j9 q
- {
/ F/ `# I* k! v {( _( r4 m - W25Q128_BUF[i+secoff]=pBuffer<i>; 9 A" { X6 o5 R% p1 D$ r+ F* S$ S
- }" `' n! B; u9 [3 [7 z9 i; N* s1 r
- W25Q128_Write_NoCheck(W25Q128_BUF,secpos*4096,4096);//写入整个扇区 ( X6 y9 o U4 o' ~; t
- ! T! u8 u0 M7 V0 r4 h( X' z
- }else W25Q128_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间. 6 p+ l/ m- M) y1 Q) Y$ C" H
- if(NumByteToWrite==secremain) break;//写入结束了; @" m- y+ V# e" Q' j4 T
- else//写入未结束
( D5 W! _8 H: s1 x - {& \& f2 `8 G; F2 k: ~4 [# U6 i
- secpos++;//扇区地址增1
4 _. W C5 Q8 E1 X9 @ - secoff=0;//偏移位置为0 + q/ i) v# H' P$ ]+ d' A
- 3 ^1 B Q) h5 m& c8 N4 z2 I
- pBuffer+=secremain; //指针偏移2 f- v6 G. ~; k7 h0 O* ^
- WriteAddr+=secremain;//写地址偏移
( M4 [* t$ | ~ - NumByteToWrite-=secremain; //字节数递减8 S2 H" s \$ A9 y7 p" W
- if(NumByteToWrite>4096) secremain=4096; //下一个扇区还是写不完3 Y% c' Q; Y% e4 u/ L% L
- else secremain=NumByteToWrite; //下一个扇区可以写完了
5 N; f1 t% c: A$ b: l! W - }
+ y* I6 x( B W8 H' T' K: C$ O - }
5 n( {/ R' V1 D3 u' Y - }) z8 k& l0 f# ` S# S
0 N$ t% u4 ]2 i+ r2 A7 b- //擦除一个扇区5 Z8 X5 F" I9 a3 z; m' u- s
- //Dst_Addr:扇区地址 根据实际容量设置
( N9 A# `4 {! N( |8 \5 w& ?& y& M - //擦除一个扇区的最少时间:150ms" K8 A1 ]4 [( w/ S9 X h: l
! J; U& g8 H1 P6 V- void W25Q128_Erase_Sector(uint32_t Dst_Addr)
4 t/ P* V# I! {# z9 l - {
( k$ K) V* l7 Q& q5 C. y+ o, A% g - W25Q128_Write_Enable(); //SET WEL
W! ~2 L( x$ q. }2 `2 v+ ]- B/ b% T - W25Q128_Wait_Busy();
( T+ a1 @! Y5 Y9 P - W25Q128_CS(0); //使能器件
7 K& p8 u: A n# t7 J - W25Q128_ReadWriteByte(W25X_SectorErase); //发送扇区擦除指令
& N* f5 x: B, K; O - W25Q128_ReadWriteByte((uint8_t)((Dst_Addr)>>16)); //发送24bit地址 7 y1 u) n" w5 U$ M4 X+ G
- W25Q128_ReadWriteByte((uint8_t)((Dst_Addr)>>8)); L, `1 ?0 |' h, C+ A
- W25Q128_ReadWriteByte((uint8_t)Dst_Addr);
* L- _& }, i7 O! T( w6 g2 g( W# H - W25Q128_CS(1); //取消片选
: Z+ m: z5 s1 Q# I% |; h. |* V - W25Q128_Wait_Busy(); //等待擦除完成
. A4 j8 Q$ m6 W2 A% Y - }
p8 } Q. w' W8 O& l @ - " M# \4 ~" \" }! v( ~. i8 O
- //擦除整个芯片
( p0 B8 X% h8 W& Q - //等待时间超长...2 [1 q. t4 ]% r8 f
- void W25Q128_Erase_Chip(void)
( G& f9 P" r, u - { % F6 z0 D$ o ^: y% L
- W25Q128_Write_Enable(); //SET WEL
" q, M& p8 A, x. |8 ~ - W25Q128_Wait_Busy();
0 `9 w# Q0 O; f* x9 s" W - W25Q128_CS(0); //使能器件 $ k5 w& U! f2 r2 S, r
- W25Q128_ReadWriteByte(W25X_ChipErase); //发送片擦除命令
/ v2 Y! e0 T9 \9 } ~ - W25Q128_CS(1); //取消片选
) t2 h$ v2 ~" D( s - W25Q128_Wait_Busy(); //等待芯片擦除结束5 R' K A( n# `# t
- }* A6 u8 R& C/ q# \. ^
0 w$ f+ G# t4 [& t
. \! H2 v; u: G7 O' e. @ l+ u- //等待空闲
* }) ?. t& ?' @ - void W25Q128_Wait_Busy(void) - J' F& ]8 V6 _0 k% X3 M: {
- { i% g. ]2 a' n: B
- while((W25Q128_ReadSR()&0x01)==0x01); // 等待BUSY位清空/ g0 M" [# h7 E. \( o3 \# m
- }
5 Y, G( V4 |' ]* t, q1 P
' `5 K1 m- d* K& C( Y: |- //进入掉电模式9 G7 Y. W8 u) M7 q) L6 Z. d
- void W25Q128_PowerDown(void) " B' m6 K9 {/ [/ n, O) U
- {
8 w" I7 ?% b7 } C; ^; v" p - W25Q128_CS(0); //使能器件 / c0 b+ Z& ]8 ?2 B* m
- W25Q128_ReadWriteByte(W25X_PowerDown); //发送掉电命令
0 v! M2 e3 {/ J; ?; | - W25Q128_CS(1); //取消片选
! E l: \# m& _+ s - }
; g6 ]+ H% ~# m, t) q4 t& C5 S - //掉电唤醒4 {; W2 n: a& W& V. o5 H
- void W25Q128_WAKEUP(void)
0 c, S! {1 y+ g/ d& I - { 7 X" k7 T, H* G4 B3 X4 J
- W25Q128_CS(0); //使能器件
( C0 H( K7 e- U7 X$ f - W25Q128_ReadWriteByte(W25X_ReleasePowerDown); ) u* L- n# z/ ^4 M, I: Z
- W25Q128_CS(1); //取消片选
9 o# \! R: \: t, s; ] - } </i></i></i>
复制代码
1 {: l# e: ?$ i9 ?5 eW25Q128.h
& h, R8 Y$ P9 Z1 t2 n% [#ifndef __W25Q128_H" p T, Y3 E) S4 V& [" M
#define __W25Q128_H
E8 H: i9 D' Y1 Z8 W* g. p* r# }- D
#include "stm32f10x.h"
- n& |0 R' h& P5 I# L/ U4 Q. q, T- O8 q" X/ a0 t* F# D( D
//操作指令表* ]) R$ f# _5 }4 f8 N- j
#define W25X_WriteEnable 0x06 //写使能
% \. p+ m5 p( R6 C. |& d#define W25X_WriteDisable 0x04 //写禁止 L; e1 j, }4 z
#define W25X_ReadStatusReg1 0x05 //读状态寄存器1( g6 M, Z4 m( i
#define W25X_ReadStatusReg2 0x35 //读状态寄存器2
% Y+ p# ?6 Y. B4 O/ L#define W25X_ReadStatusReg3 0x15 //读状态寄存器34 M- Q9 l; X. b, M% ~
#define W25X_WriteStatusReg1 0x01 //写状态寄存器1! d% F- j) C' }9 E' G( E1 r- K4 p& H$ Q
#define W25X_WriteStatusReg2 0x31 //写状态寄存器2" o$ F2 O% d2 R7 i
#define W25X_WriteStatusReg3 0x11 //写状态寄存器3
1 l" E% }9 z& C#define W25X_ReadData 0x03 //读数据
( d( V o; k8 U1 _#define W25X_FastReadData 0x0B //快读
9 R* t; ` k) n! w: n8 P) n2 J" ?#define W25X_FastReadDual 0x3B //双输出快读! }, Q M, h2 s! z3 L( v
#define W25X_PageProgram 0x02 //页编程
, G j5 M# w+ k3 a: U8 z$ n) a#define W25X_BlockErase 0xD8 //块擦除(64K)
" k% X! n( Q0 h+ S#define W25X_SectorErase 0x20 //扇区擦除(4K)
+ i$ o8 Y9 X4 e+ S" w2 D' ^" F#define W25X_ChipErase 0xC7 //芯片擦除! u1 K4 L4 R2 [# T" K+ B Z8 e/ u9 c
#define W25X_PowerDown 0xB9 //掉电
8 ]$ q1 i) |& G" @#define W25X_ReleasePowerDown 0xAB //释放掉电
4 J' N% J8 O8 q& H5 j7 F+ j#define W25X_DeviceID 0xAB //器件ID) ]- O6 E; l, k5 t
#define W25X_ManufactDeviceID 0x90 //制造商+设备ID2 ]& _3 j$ y* \4 R% Y
#define W25X_JedecDeviceID 0x9F //电子元件ID
1 r0 n# n( M/ R: K
0 Q1 |6 Z( G. g1 c
) j! j, }7 F4 m) O6 S1 _% u) f- K6 G#define W25Q128_CS(a) SPI_CS(a)
; |! s; C' T+ g* B8 \( r4 w$ E- [4 e( _' U% I$ h. ?
" n; f3 ]' `5 T% s; r
uint8_t W25Q128_ReadWriteByte(uint8_t TxData);//函数包装一下
% R) g+ B/ a. B( t! W# H5 X0 v, J! g2 W1 P2 O" E8 ~+ W: g
uint16_t W25Q128_ReadID(void);//读取芯片ID7 t+ w) f/ b: l6 u4 C
4 }8 }% x2 @0 n' F. z
uint8_t W25Q128_ReadSR(void);//读取状态寄存器# l8 S% f2 V4 t! b% r- c: h
void W25Q128_WriteSR(uint8_t sr);//写状态寄存器8 d% p( T6 Z9 X d5 S
4 Y) l5 m1 w4 {( a. K7 {void W25Q128_Write_Enable(void);//写使能
/ @- g' J# k$ [; O7 O) Zvoid W25Q128_Write_Disable(void);//禁止写入 3 H, g7 ~1 `- [- z, e
' Z3 i* M6 l" W; I t! P. cvoid W25Q128_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead); //读取数据
+ r' Z/ r% k& J) u+ W% o; h3 X+ Zvoid W25Q128_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);//页写! ~, X* \" ~1 r! ^; E
void W25Q128_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);//无检验写数据,可自动翻页- [) K3 I, x! t
void W25Q128_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);//写入数据,带擦写功能
" K+ h9 U6 e, g
, s/ O# L, X$ J) ^4 Y3 C: Tvoid W25Q128_Erase_Sector(uint32_t Dst_Addr);//擦除扇区' F8 E, n$ w# R0 D0 n* Q6 ^
void W25Q128_Erase_Chip(void);//擦除整个芯片
' b# M+ o8 y$ a8 j& E. y7 Q, q# K$ k$ y2 n
void W25Q128_Wait_Busy(void);//等待空闲+ R- d& B" ]& ~, D. H
void W25Q128_PowerDown(void); //进入掉电模式
* S2 x9 F8 B6 r7 cvoid W25Q128_WAKEUP(void);//掉电唤醒
: v+ i ]0 x1 w$ `9 e5 K1 ^
; R% M n6 h7 W9 C' z1 m5 M9 `# [#endif 3 ]( ?% o# N/ W5 C3 n; x1 V- T% C
+ y; H% v" M. k2 b4 r |12 ^+ y$ s/ p! D6 w
2
# J* {. {6 V3 G* S: r3
2 N5 L" K3 v! @' c; e, [# W6 b4, F4 @* q( F4 L* Y7 s* H, i
55 i, K5 ^% ^! R. v: ~- q% _
6
* g! Y7 Y8 k/ B& j2 D7 @, E" h: Q9 ^
8/ z+ Q: ]: c0 [4 s* X
9
: Z* L3 H# I% N% L) M. s2 N1 B2 m106 `. b! F: }9 G! D1 ?
11
+ S* d# K7 X" J7 ~( o12
U9 j6 L; P0 {8 i+ G# m13, \1 h& f0 L$ m6 z, D+ Q
14
$ Q' k* m7 W6 @) G- S/ n* |15% ~1 _: j( V/ r/ \- B! I# ]2 z* {/ K
16
" Y/ C/ p9 L( n- m( @1 u! m170 q1 Q4 C7 }9 f( j$ v( V
18
. j$ S5 d' T2 D# B& w% i19, Y5 l: W5 R! M3 [& P
20 f8 L; |* j6 j8 J4 u5 F! L- ^4 s
218 g1 m5 I/ r* I2 n& t
22! K3 _1 [# i/ R3 h$ L, a% d
236 p( {( x. J1 J) A' q) s
249 x2 J S! L# p# g( P# e
25) K6 D8 x l* D: J- O& n& X
26* m8 T6 c4 }+ I! k: v7 }
273 E+ N/ N* b! Y; t" \' m/ \
287 n u c( B7 f
29; }. C/ B+ `$ h4 H# D7 R
30# J" j' g1 K* h) V
31, {: m3 r! m# }1 ?9 y8 _. R& ` \& T
32" M4 a; R" ]+ d7 N. L, `9 K4 L
33
" g% @, ?* ~& ]$ M- R$ C341 z1 y+ P: ^- a9 F/ {" S
35
9 `$ w( E9 z( C3 | Z' }! U36
3 H+ D- s/ u$ H1 S% H/ X* {372 u1 }7 ^/ F( \3 e- K B- H
382 K) o7 z# D3 p( F- G9 h" y2 [
39$ F2 |; \6 G5 _
40* G5 H ?3 d3 Y$ U
41: H, q Q! x& X5 D9 x% K$ ?5 B
42
( R: a2 i& a) j% E5 M3 H43
( e- G( W: \' ^# R5 j3 j5 O- u k: C44
% P6 |" Z( X: |% v454 X) @4 @! ~- \/ u/ g% l: g/ a
46% I S. F; }: E; r* I% x- p
47
U! _% p0 \: |6 ?48
" x: {9 L" ]; ?49, f& ^$ I) m/ E6 P" V0 o
501 g# S2 I( l3 A4 w1 t5 Y3 ~! `- b- p
51
# _, y& g1 Z+ o# |52
$ _! M" F' p. B53# ] g2 o& R- i) [$ F5 p8 U
54
- B6 X; T) j+ i R! o$ z, ~main.c
. B( W- s. w3 ?/ m- q* w8 |/ \#include <stdio.h>$ t. t! v8 D* A( D: H8 G* \- C: Q( _
#include "Uart1.h"
$ r, Q1 X8 A) f: ]#include "delay.h"
% Q7 P l8 Z5 j: K#include "SPI.h"8 z( |# a _# k2 K
#include "W25Q128.h"
( f9 z$ R& h) a2 K( I v5 H8 `; Y" ?" f* T; D
uint16_t W25Q128_ID=0;% L; E8 I. N8 C# j: R! ]
uint8_t Write_data[20]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
7 B) |+ b& U E! \uint8_t Read_data[100];
2 w8 I( S1 a+ \7 v; d/ t' y8 v4 n7 N. T% b, Y5 b
int main (void)
$ j9 s" |; j1 |4 n7 p L{
& e! i) E( s7 s* L Uart1_init();6 q w$ p+ p; U& A5 C
SPI1_Init();
5 } r( Z& _4 j4 E& r2 f + v. ]3 ^; _2 D* Y. M+ ?
W25Q128_ID=W25Q128_ReadID();$ j5 W5 P* h1 O# m, Y, u" j* Y& m) Q
W25Q128_Write_Page(Write_data,0x01,20);
6 e `$ c+ B5 H! ]' r) J % Y* Y( W! Y! q8 o2 R# B$ `. ?% Y: Y
while(1)
5 ]% S) f& S$ @$ h {- p0 r% ^$ K3 d3 m Y9 T
W25Q128_Read(Read_data,0x00,100);6 E- n# @1 l$ H# N
for(int i=0;i<100;i++)
% I% L6 e3 l: B) u' ~ {
: j ^3 Z$ j6 f0 x printf(" 0x%X ",Read_data);3 [, F; O, x( l C) U3 a. i
}
4 v; h% @3 a4 m; M printf("\nW25Q128_ID=0x%X\n",W25Q128_ID);$ W: H( x1 _ D8 R# O; T" X( d+ b) A
delay_ms(500);
5 o2 y: R. e- d1 b. ] }
2 u7 Y% W6 R5 m9 E* `}7 d( ]9 L6 D% @2 p2 k. ~* ^
/ X* v1 M! S0 v) R0 [" @6 |& d
1
8 W8 N) v) V/ J2
0 U0 m4 K1 X- V5 k& \3
! Z# v! R0 Q' Q' E4
$ G2 O5 D0 s- w9 |- D( A# J5
) w0 o+ y8 S/ N; Z6 h; V% `2 x! n6
4 E& }1 E4 l! U* ~! Y7; E$ _/ F5 k- z$ u: Z! v
8
% L* t) }" ]8 n* Q& J9
! |' I. b) V/ y# n10. d. s0 f. [% U9 w( c& K
11
+ b: J$ Y8 c: ~3 q$ ?12
) R! j2 S3 C( ~! K# D: {13
/ ~( a% W7 c* p5 z9 _14( t4 V) ]" v& w! v$ o
15
+ `7 g5 W- d& b7 h1 R; w/ h16- |6 f! a- A& n* l, d
17
3 \# A X) j* ~, {18& G2 y x1 y7 w
19
8 l5 ]6 P& ?0 R R+ d1 j1 E `20
& C4 @7 j, j9 u2 a1 e$ }" U+ N% J21
7 w5 E8 A. W+ W/ H* [' `' v; u9 s. H22
% y' J& r! |' F23
7 m* T* h: K+ S& w2 [24
; A/ t$ D1 S; w25
$ M# ?3 }8 C; n0 X3 I4 c2 z26
% z- R H7 V2 }6 m/ q: f27
5 w G: Y; I) x: W+ u8 Y28
) a+ o1 Z6 W% \$ t. w* Y" n) w29' H8 j1 L8 E& d; y e% p
实验结果
a$ S9 z3 ]" l6 ?9 V" |6 H6 K$ j1 ~7 ~) n& `! T0 s
————————————————9 b6 G! s$ ]) f/ H6 [
版权声明:根号五2 @- o/ u4 b& N6 D1 W6 q j
4 U6 t* e+ w S; L' y- `/ A
7 |- p6 ]2 U7 @) q) X, E
|