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