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