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