你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32的SPI的原理与使用(W25Q128附代码)

[复制链接]
STMCU小助手 发布时间:2022-5-16 11:53
一、SPI介绍
. N! [' Z# P0 `2 ]5 a$ t* ?  cSPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。/ I3 C; |& v; C5 q% U5 U2 f
) k9 |) ]( U) E; |3 n8 z; y
正是简单易用的特性,如NRF24L01、VS1053、SD卡等皆集成了这种通信协议7 h  c  O% f$ W) k' P# E) S8 \  W

: Q) U+ L4 E+ G* _4 j8 s/ q7 L- z _A_6DRLZV3W0~DW}CEZQ7{O.png 6 M$ I- V3 `+ f: b3 h
# K  x6 r8 `* k% ~- Y5 s
二、SPI接口框图& U' D: q/ S. w1 {3 b1 t0 J
) }3 l0 J1 l- ^" y' _; E& ^
@OFY~~NQ}EZ}HLQL{EB%0ND.png # K- a  P' X) N  L! ^9 v
* g5 T( b% X% S8 i& i0 W7 L- j
三、SPI优缺点
  N; ]) _1 q) s3 D. S* q$ E# D: X
SPI接口是在CPU和外围低速器件之间进行同步串行数据传输,在主器件的移位脉冲下,数据按位传输,低位在前,高位在后,为全双工通信,数据传输速度总体来说比I2C总线要快,速度可达到几Mbps。
- ^, u5 j: m: B; M1 t
/ V% i5 A6 @# T' y信号线少,协议简单,相对数据速率高。( _) ]3 @* B8 O" B: c6 ~2 _, x

+ L; b$ g9 @5 m4 q, G缺点:没有指定的流控制,没有应答机制确认是否接收到数据9 c7 [5 }5 ~- F  G3 W; u# {: Q
* i: N4 y5 E: B$ [! l9 n
四、SPI工作原理总结
1 O$ c  }3 S0 r& o. p硬件上为4根线。8 Z" A' R2 ~, N, P, {+ X
主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。
/ x& T% R4 F9 f& N; l串行移位寄存器通过MOSI信号线将字节传送给从机,从机也将自己的串行移位寄存器中的内容通过MISO信号线返回给主机。这样,两个移位寄存器中的内容就被交换。) M% D0 e: n7 N4 t5 h
外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。
* P" G, W6 Z6 \$ `! f3 H多个设备使用SPI的应用举例$ z% f8 Q( P' w( E. V  z! E

9 f, t  |" E8 g: H0 E) S (_8GH(KLZ4`~[RSWOUL_B{Y.png
. `* Z1 C' A3 u7 g( \+ Q. ^8 H% d+ N+ T; a2 d9 m1 c
五、时序图
+ N6 N) e9 g) g. b6 P! |0 Y时序为 SPI_CR1 寄存器中的 LSBFIRST 位复位时的时序
" N7 W5 S5 t! Q- l! ~  |% _
( B0 f( Y6 f9 t* K, u3 o  v  GSPI_CPHA的值将会影响SPI_CPOL的值
* Z, I  e. ?( j- t+ ?1 T; p+ q; u0 ^' U- M4 z9 r7 I! q/ F
4R1]OS0}$R`DITZTCDX~T~D.png . L& t+ F% K$ K, q. M$ Q0 |' b
# P6 Y- [) n8 u) V
六、SPI程序编写过程$ o; W% b$ I- W. x( E- `8 M& s0 Y
  1. //①使能SPIx和IO口时钟- I/ k0 L, v# c7 ^
  2. RCC_AHBxPeriphClockCmd() / RCC_APBxPeriphClockCmd();
    9 V; o3 D& y4 v) K3 Q: K2 ?3 _

  3. % [' h# M( Y/ m2 I- h
  4. //②初始化IO口为复用功能+ E. X: O1 F+ r
  5. void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);4 v8 c2 Y: [  t9 c

  6. 3 E7 ^7 Z5 _" I) B. {0 F& l1 F& m
  7. //③设置引脚复用映射:% Q% t. y( w7 t8 F* S
  8. GPIO_PinAFConfig();# ]5 N" e$ U& U
  9. 9 C1 K" j* c7 E; E- z
  10. //②初始化SPIx,设置SPIx工作模式) E1 h2 I% e+ Z0 ]6 }' z8 c5 p
  11. void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
    9 U" M$ s7 A/ b

  12. - S- |! }  S% ]1 _/ P  ?. T
  13. //③使能SPIx' e1 b- ]* b; n' l. k7 a3 _
  14. void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);# W0 w2 R2 c! }  B6 b! N! K
  15. 0 d8 w' m( z7 w
  16. //④SPI传输数据
    0 Z3 T. K1 R3 H, `9 K. i
  17. void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);# ]+ q/ c" J  x1 j- V
  18. uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;% F! P: }1 h, b7 ~  t
  19. ! x( ~( z- H  r. _; G
  20. //⑦查看SPI传输状态) a. E. q% J6 t
  21. SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);
复制代码
  1. SPI.c:
    9 c) {) m9 L1 m" G  c
  2. #include "spi.h"
    ' f! ~: g- {% K; u

  3. 1 j; |  h7 q' W5 b# |) r
  4. //以下是SPI模块的初始化代码,配置成主机模式                                                     \/ f. H0 P7 H3 A- o6 k& `# w1 W
  5. //SPI口初始化+ c8 C$ ]0 z( W) o7 e
  6. //这里针是对SPI1的初始化
    % m  ^) k8 ^) _8 `; n
  7. void SPI1_Init(void)
    9 u1 T# R& ?1 ^
  8. {           T2 S2 z) J2 m' J& T
  9.   GPIO_InitTypeDef  GPIO_InitStructure;
    8 ~8 U4 {# [% {( Z# Y
  10.   SPI_InitTypeDef  SPI_InitStructure;
    ' R, z2 O) X" y
  11.         
    ! {, I1 {; i2 D6 G- N0 a5 W* }
  12.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
    3 `% N! j! [0 y9 M* B) ~- g* E
  13.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟/ A8 }1 c! Y+ {1 }7 R

  14. ) e& v( h1 I! }: L
  15.   //GPIOFB3,4,5初始化设置1 t$ f# m- p3 B6 s! }0 ^, Y1 Z
  16.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出        8 D' _# J( N, @6 H
  17.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能0 r% e: A4 m$ b2 b3 t8 v
  18.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
    : ]0 ~8 E0 |' z- A! V
  19.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    . e/ O/ F. K8 Y6 B" i8 t. A
  20.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉" M/ e! n+ q, a- `3 k
  21.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
    - _: e, `$ e/ j# D! Z
  22.         
    + b$ o7 ]& s8 ^8 O" H
  23.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为 SPI1
    4 W0 c. R2 O; T" {  G- v
  24.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为 SPI1% s% x4 z. f( u
  25.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为 SPI1
    : j! A% o) [, t9 h6 U) D
  26. 0 Z7 H; }' f9 S' k  ]) F: x- ?
  27.         //这里只针对SPI口初始化( f) Z$ U. g8 i% N* n- o
  28.         RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI1
    ; K1 y  d) D% t4 M, ~& T9 ^; O
  29.         RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI11 c2 K5 f8 w5 y2 j6 V7 Y
  30. - u4 ~+ A. F8 b+ e4 Q
  31.         SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工9 s% D0 p0 }" z' z3 j) O; d: T
  32.         SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                //设置SPI工作模式:设置为主SPI
    / P3 j- _  W) x1 d* ?9 W/ j
  33.         SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                //设置SPI的数据大小:SPI发送接收8位帧结构
    : j, j7 \% {* {0 r1 w
  34.         SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                //串行同步时钟的空闲状态为高电平
    , n3 ^" ^  M# ]( J8 p
  35.         SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;        //串行同步时钟的第二个跳变沿(上升或下降)数据被采样% J0 P3 c8 P  `, Z3 T: ^! h  V$ a" [  k
  36.         SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
    1 B, r) N8 V9 K$ ?! A
  37.         SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;                //定义波特率预分频的值:波特率预分频值为256" s0 Y8 T* `" L5 o  r  R
  38.         SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;        //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
      B/ [% z  U% V% h4 p" _5 o
  39.         SPI_InitStructure.SPI_CRCPolynomial = 7;        //CRC值计算的多项式. R) O1 w3 [, V7 b" z& ]
  40.         SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器7 w( ~  o3 o2 K$ R; i5 t( U: j8 ~
  41. ( U! U8 ?0 r0 F1 K! d! {
  42.         SPI_Cmd(SPI1, ENABLE); //使能SPI外设
    # Y' a( a1 S: _4 Y! _# w. W& _
  43. # b/ x* }  l. P; K! H. U. W
  44.         SPI1_ReadWriteByte(0xff);//启动传输                 . S# t1 C. ^' _1 N* h
  45. }   
    & v" c# @1 @, L
  46. //SPI1速度设置函数1 C3 z; F$ b1 l! r
  47. //SPI速度=fAPB2/分频系数
    # m  u$ Z0 L8 u' N
  48. //@ref SPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256  
    6 \+ A5 o3 F  ?1 p& w9 o) ^
  49. //fAPB2时钟一般为84Mhz:* m0 j2 ^5 g3 P( [' L8 V* ^! t
  50. void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler). |) Y  H, u4 D0 y
  51. {/ J& j4 h' |* f) U
  52.   assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性) m6 X9 q' E* Y8 v5 L
  53.         SPI1->CR1&=0XFFC7;//位3-5清零,用来设置波特率1 O' A. {# b% l, r3 U/ h3 y
  54.         SPI1->CR1|=SPI_BaudRatePrescaler;        //设置SPI1速度 $ w/ [3 h* D. d4 U
  55.         SPI_Cmd(SPI1,ENABLE); //使能SPI1' G5 ]7 ~! h$ c; Z/ B4 E
  56. } ( _" L. _) D; M# C
  57. //SPI1 读写一个字节# F* ^6 ?. N5 O6 Y( g
  58. //TxData:要写入的字节
    1 k0 i2 B& S3 u$ u! I& @
  59. //返回值:读取到的字节
    ) K: D  f5 E9 k; G9 @+ J: r
  60. u8 SPI1_ReadWriteByte(u8 TxData)
    , n- x) v6 n: r8 n# ~' i
  61. {                                          - K, ?9 a) u/ e( f0 M

  62. ! W5 x3 r! P& S4 K
  63.   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空  $ N6 I! |8 L8 t# h6 v* i0 V
  64.         2 F( @8 D9 N" s' w4 l) F( Y" ~
  65.         SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte  数据
    ; w. e, }. p( j; \
  66.                
    + H+ l4 L- |$ I, {
  67.   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一个byte  9 G1 z4 M4 b% \) s7 C" c" e

  68. % a8 g" j& r6 H( a; i; N" @
  69.         return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据        
    $ a7 V9 g: m% y
  70.                      5 U7 R2 K) ~0 E6 W' q6 ?) H
  71. }
    & H. r) v9 r$ L
  72. . T! x) z' g6 B6 I
复制代码
$ G: h' `! P* \+ i* w% G' ]9 ?
七、W25Q12xx的原理及应用0 \) }  h/ D7 Q$ V, E
W25Q128将16M的容量分为256个块(Block),每个块大小为64K字节,每个块又分为16个扇区(Sector),每个扇区4K个字节。W25Q128的最小擦除单位为一个扇区,也就是每次必须擦除4K个字节。这样我们需要给W25Q128开辟一个至少4K的缓存区,这样对SRAM要求比较高,要求芯片必须有4K以上SRAM才能很好的操作。1 v8 j4 a5 e, e& y+ ]& _* _

/ x/ ~  |* O: R9 G5 O/ P* l& c9 H- }W25Q128的擦写周期多达10W次,具有20年的数据保存期限,支持电压为2.7~3.6V,W25Q128支持标准的SPI,还支持双输出/四输出的SPI,最大SPI时钟可以到80Mhz(双输出时相当于160Mhz,四输出时相当于320M),更多的W25Q128的介绍,请参考W25Q128的DATASHEET。; K$ C# U; f% w/ _4 q" P
; h+ f% i2 p* W
D%)W27U]]MDR`PRB[FT4~Z1.png ) S( R' i. `8 r) M9 K, }+ q$ K2 N
9 c; n4 ?! ]$ W" d; I$ b0 X
W25Q12xx可根据原理图查看,使用的是SPI总线通信协议
; u( K% Y! o5 t, I2 Y- O' C* y- d& I% }" x
比如原子的原理图
; D7 D4 w/ z2 J2 {$ M  P
4 K' a: p% l. K" [: k 20190611221654679.png
& F6 s5 K$ [  j7 L6 s' X1 Q/ d7 O( r# V/ c# u7 T5 ^
7.1 分析W25Q128指令' F2 e. l, N" i* B. ?& x
可参考W25Qxx的数据手册,这里列出W25Q128部分指令:
2 g. H& A" f1 C) V3 n9 ^7 ~
: M0 W$ Z9 U) J/ L3 ^& Q 2019091611364032.png - a5 {) X! o; g1 T

, H3 P/ m/ [2 ~, Y% A5 i比如读取设备的ID的指令:0x90000000! c8 o/ {& d0 a4 e+ t$ i/ ^/ j9 p: [

4 E& G: a& {: C; ` 20190916113704620.png ; i( i) h2 Y& I% a
1 X4 r6 @4 w2 s( X
K$HWO9ZGFGE$CVLD47Q]_4G.png * l7 {! h1 ~1 b. |, D* W$ C

% ?0 h+ u/ d- E/ R WLLL(({{_EVAN[005V791UU.png
) e0 A, @$ {3 n8 |, ]
* f, V1 _; b% r3 N) k7.2 擦除扇区:
2 a# U2 z# Q+ i/ f( [& C( e# g1 m- H+ ]6 t& J4 ^& Q6 P1 \4 q# Q
NZ$~TU098T[KO5)GTQW){UQ.png
* Z! I- w/ j% f. r! D# @9 g2 k5 j
: ~+ P) {  C3 M" n" |) T0 ?7.3 部分常用设备读取指令:
) ^5 i: H+ t* }
8 b; b+ i5 N( M4 J5 L- r
Q`V(R2ZUHAZ8SAS2[A}1H%2.png ' u5 g( s% M! i" e
. [. p' u9 ~) s' w8 k7 p% O/ X$ N' I6 ?
每次操作前要使能写操作,且给一个低电平。结束时要给其为高电平,再失能写操作。也就是通过操作片选引脚来确定是否使能或失能。' A  ^# b* C) q& |9 X0 C+ h! z8 _6 C: A
' {8 O7 A7 X8 O
LR6P1TX%{R][_M{CX4]K.png
2 ]* T5 |. S* B* e; E' e, k; [, V" N  P) E
  1. W25Q12xx.c:8 ~6 K* u4 ]" ]) A/ ?0 c0 E
  2. #include "w25qxx.h"
    3 e' ]+ f4 H' v( X
  3. #include "spi.h"' y& w, `& Z8 K5 b& P4 H0 U% O
  4. #include "delay.h"           # s$ U+ `  m6 q3 E
  5. #include "usart.h"        ; t  _  k+ C$ L6 ]. x7 e
  6. - G, J; s" N" z+ Z% ]" }' v. _$ O
  7. u16 W25QXX_TYPE=W25Q128;        //默认是W25Q128
    4 T) P1 K( i, u$ W3 W

  8.   c! Y9 n0 D7 r- p4 T+ e
  9. //4Kbytes为一个Sector  w/ P3 D& q: l2 Z- ~
  10. //16个扇区为1个Block
    - |" G9 h( F7 r4 R$ `& e: M: E
  11. //W25Q128: O! ]. F7 g9 H5 G; B( N$ y
  12. //容量为16M字节,共有128个Block,4096个Sector ( \7 w% g# D+ E+ ?3 _
  13.                                                                                                          ; @1 F3 q3 g4 V9 S/ P- D6 C6 S
  14. //初始化SPI FLASH的IO口
    3 y/ f) e. L/ f1 ^# ~2 d
  15. void W25QXX_Init(void)
    7 u# Y6 O1 a8 V
  16. { + U' q- q' Z  d! ]3 I: E
  17.   GPIO_InitTypeDef  GPIO_InitStructure;
    ' Z/ G& Z7 K- t! V  T+ X  P! Z
  18. 9 Y0 k6 `4 ^0 P: e9 G* k! n; L$ T
  19.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
    + u! H; Y  f8 n  r( a& R3 \/ P% G
  20.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);//使能GPIOG时钟" J% r# [3 ]1 }7 a
  21. . H) H+ i2 v% h* a
  22.           //GPIOB14
    6 s2 w- c9 n3 e) W6 ]& q7 N* F
  23.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;//PB14
    $ M8 s" Y: h$ D. o' r
  24.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出
    3 I# }' v& L3 g6 c+ {
  25.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出, E4 [1 r0 x9 g
  26.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    6 C# ~5 Q) _* S
  27.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
    ; L" f- V9 s$ _# A7 s" ^/ \
  28.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
    , ]+ B1 I! q! {

  29. ( r; V8 L( k  x  i
  30.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//PG7; k! N( U. F2 h3 N, i
  31.   GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化9 z1 X( I- I% u+ }6 G+ m4 h
  32. 5 N. G& a+ |( m% b: r5 Q% E( [3 f
  33.         GPIO_SetBits(GPIOG,GPIO_Pin_7);//PG7输出1,防止NRF干扰SPI FLASH的通信 . r* k" H) E0 S
  34.         W25QXX_CS=1;                        //SPI FLASH不选中
    ) z- K8 w  ~; f* ]. d3 W$ @7 K7 P; B) R! }: I
  35.         SPI1_Init();                                           //初始化SPI& C% N6 E0 h8 B/ f" J
  36.         SPI1_SetSpeed(SPI_BaudRatePrescaler_2);                //设置为42M时钟,高速模式
    , }7 Y8 B( f; |+ p1 ~. z, J7 j
  37.         W25QXX_TYPE=W25QXX_ReadID();        //读取FLASH ID.9 Y8 f' Q* ~0 {, @8 _+ c
  38. }  
    ' c0 O0 c1 W; x2 p5 c6 G

  39. * A! d+ X+ d/ k/ K9 t0 S
  40. //读取W25QXX的状态寄存器$ s$ y5 O7 X+ [% Q% ]
  41. //BIT7  6   5   4   3   2   1   0# q9 d3 h8 d3 T& u- h1 q
  42. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY
    6 ^2 {; N* z; S' J
  43. //SPR:默认0,状态寄存器保护位,配合WP使用
    + ~" @8 G8 ^- u
  44. //TB,BP2,BP1,BP0:FLASH区域写保护设置
    * p0 J9 i8 h" D
  45. //WEL:写使能锁定& Y* a& T% ^- i2 \' c
  46. //BUSY:忙标记位(1,忙;0,空闲)& h; w7 x% }" j% S+ g/ Q
  47. //默认:0x00
    * O) v! x8 |& u$ W, Z
  48. u8 W25QXX_ReadSR(void)   
    1 t9 }) r* R% q# O- J# W, h2 k
  49. {  
    % M0 H) t0 P% G* I
  50.         u8 byte=0;   
    7 S8 R; u2 J6 s! I
  51.         W25QXX_CS=0;                            //使能器件   
    - v- t+ }: K7 W2 E+ i
  52.         SPI1_ReadWriteByte(W25X_ReadStatusReg);    //发送读取状态寄存器命令      j  K3 O, l1 b6 ?3 v
  53.         byte=SPI1_ReadWriteByte(0Xff);             //读取一个字节  
    + S; L3 w9 F2 ]* f" o, P2 [
  54.         W25QXX_CS=1;                            //取消片选     8 `6 [: `2 ?4 i$ n
  55.         return byte;   ; m& z$ r+ k: z* ]
  56. }
    4 G4 N- F6 ^# ~) X  A% g6 K2 e
  57. //写W25QXX状态寄存器
    / K1 n; M: I0 l
  58. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!1 `5 g6 f$ H+ l6 ^) C  D& f0 d8 m
  59. void W25QXX_Write_SR(u8 sr)   
    # x, n; c( V6 n6 e1 z; V  H
  60. {   ( P& `% R- f/ o0 r. T1 N1 J
  61.         W25QXX_CS=0;                            //使能器件   
    . J4 }' G# T7 L
  62.         SPI1_ReadWriteByte(W25X_WriteStatusReg);   //发送写取状态寄存器命令    # ^. R) t5 i/ k8 J! w7 N, l, y
  63.         SPI1_ReadWriteByte(sr);               //写入一个字节  3 Y" V4 r2 _7 U+ i  y. s  j
  64.         W25QXX_CS=1;                            //取消片选                  
    1 @! j1 s' s2 Z4 j/ {
  65. }   6 ]9 s' H' e; f5 i( G! H8 T7 A7 G
  66. //W25QXX写使能        & o6 v, P/ _9 ?- F0 p' k
  67. //将WEL置位   ; I- h; t( w* y8 s' K
  68. void W25QXX_Write_Enable(void)   2 ^* i& K7 o9 z) H+ L2 `& {
  69. {; d0 m) w' }6 g2 C5 c
  70.         W25QXX_CS=0;                            //使能器件   
    ; c# {5 F  w# x' o  a1 j
  71.     SPI1_ReadWriteByte(W25X_WriteEnable);      //发送写使能  
    , Y0 V/ G7 k2 n' a1 K  r
  72.         W25QXX_CS=1;                            //取消片选                   6 p0 x, k/ @7 |" S( y+ h2 f2 t/ m
  73. }
    4 X9 h4 ^$ W! |- U, z
  74. //W25QXX写禁止        ; |& Q0 t$ [- L! {! H3 N* m" ^
  75. //将WEL清零  7 {/ b+ n  X. |/ Q5 p
  76. void W25QXX_Write_Disable(void)   
    ) O0 ^# X3 v0 z1 j2 U) |$ E! w4 A
  77. {  $ a( i- P. A0 ^1 ^+ D
  78.         W25QXX_CS=0;                            //使能器件   - }  v# [& _, K. K) u
  79.     SPI1_ReadWriteByte(W25X_WriteDisable);     //发送写禁止指令   
    7 Z' [. _6 q' a# Q1 ?
  80.         W25QXX_CS=1;                            //取消片选                   + @3 f4 A6 C) h3 z! t
  81. }                 
    & k6 N1 Q+ @+ m5 _- ]& B) F' h* t: H
  82. //读取芯片ID4 C: z0 ~# f2 N+ r# v0 x
  83. //返回值如下:                                   7 `* M+ O6 {) v) s, h
  84. //0XEF13,表示芯片型号为W25Q80  
    , L& X$ N% H' o6 @& e
  85. //0XEF14,表示芯片型号为W25Q16   
    4 z# U4 w- Z' }" n  [% X6 I0 C+ D
  86. //0XEF15,表示芯片型号为W25Q32  
    . S$ `8 s9 _) R  O9 E2 V8 [' y
  87. //0XEF16,表示芯片型号为W25Q64
    % H$ n8 x  T# V0 o( h, u- u
  88. //0XEF17,表示芯片型号为W25Q128           & B" J& C, E% T
  89. u16 W25QXX_ReadID(void)
    ' x# j8 u$ {0 y! k6 Q
  90. {
    4 q: [: O9 L- q* D3 v0 B7 D# z
  91.         u16 Temp = 0;         
    . Y) Z: P: X- }3 T- {
  92.         W25QXX_CS=0;                                    . e. @1 t9 V; n# \3 u2 Y5 b
  93.         SPI1_ReadWriteByte(0x90);//发送读取ID命令            - o& I& b* T( J
  94.         SPI1_ReadWriteByte(0x00);            
    & H& ~( e" _. q; V3 O! n
  95.         SPI1_ReadWriteByte(0x00);            
    " U0 B6 B0 ]: |( Z9 T  y$ h0 K
  96.         SPI1_ReadWriteByte(0x00);                                     # S, H. \" _8 o* D( U9 }
  97.         Temp|=SPI1_ReadWriteByte(0xFF)<<8;  8 V( j' ]2 h" x0 q
  98.         Temp|=SPI1_ReadWriteByte(0xFF);         + _/ Y, E- z: Y0 f. i/ z$ `# C
  99.         W25QXX_CS=1;                                    
    * q* W+ B5 u1 N8 s5 E& f
  100.         return Temp;
    7 j2 U5 {9 A) Q8 Z' B- f8 F1 x7 o
  101. }                       
    9 \0 {4 }/ T+ W  F7 {/ @
  102. //读取SPI FLASH  
    1 C) R; Z3 W% y  {+ U  ]( F6 c
  103. //在指定地址开始读取指定长度的数据7 v& c( W; ~- I; g& k+ i. ]$ [6 ]
  104. //pBuffer:数据存储区
    & M. A/ S8 G4 V" |5 z, u
  105. //ReadAddr:开始读取的地址(24bit)
    5 ^( \$ K2 L. }+ `& t+ @
  106. //NumByteToRead:要读取的字节数(最大65535)6 D6 X& a3 ^# }1 l/ i( v8 t; l
  107. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   4 R4 w- K& V9 {* G& d( ^8 s
  108. { - E( w/ R( m+ V+ h1 k6 N( k+ g
  109.          u16 i;                                                                                       
    4 O0 T" h" a8 Q
  110.         W25QXX_CS=0;                            //使能器件   3 k. l8 a- f! j- B, H
  111.     SPI1_ReadWriteByte(W25X_ReadData);         //发送读取命令   
    ) y- n2 |' y0 N
  112.     SPI1_ReadWriteByte((u8)((ReadAddr)>>16));  //发送24bit地址   
    2 c. h1 d* `/ K% `: k
  113.     SPI1_ReadWriteByte((u8)((ReadAddr)>>8));   8 p' y5 H- Q% \# `9 K
  114.     SPI1_ReadWriteByte((u8)ReadAddr);   
    ) M) O4 K$ k. W+ q
  115.     for(i=0;i<NumByteToRead;i++)
    % m# U' G% T) \6 W% V7 n
  116.         { ; m8 X  K+ k5 B& T6 K6 C* X. S
  117.         pBuffer<i>=SPI1_ReadWriteByte(0XFF);   //循环读数  
    + F) B4 [6 d% [! F/ X3 o) z1 E/ w
  118.     }
    + h: i' \! J2 m- ?. O% G, d
  119.         W25QXX_CS=1;                                                   
    / [0 ^( O7 M; m/ h5 `0 M2 ?. D
  120. }  1 M. a% l; j/ X- s0 Q6 A  ?
  121. //SPI在一页(0~65535)内写入少于256个字节的数据
    7 \( |7 i1 n3 v
  122. //在指定地址开始写入最大256字节的数据9 o9 C6 B. x% D1 J# {7 T" T( ~
  123. //pBuffer:数据存储区" [: i; D- j* W
  124. //WriteAddr:开始写入的地址(24bit)
    * P/ r8 \( ?* D' [
  125. //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!         
    ( s" J) b! n& k" a! t' ?
  126. void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
    ) x5 e6 f3 Z+ J- h0 k
  127. {
    $ p9 G! Y; O* q
  128.          u16 i;  
    4 L% n& P7 ~4 ]& D/ V
  129.     W25QXX_Write_Enable();                  //SET WEL
    5 h$ m, H4 O2 E% }7 Z/ s- F
  130.         W25QXX_CS=0;                            //使能器件   
    3 t5 S; h: h0 e# P) T( i. J3 S
  131.     SPI1_ReadWriteByte(W25X_PageProgram);      //发送写页命令   
    9 @7 v  N( o7 O. Y
  132.     SPI1_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址   
    / [6 D$ k9 z8 \5 z% x, c
  133.     SPI1_ReadWriteByte((u8)((WriteAddr)>>8));   % l; {6 a4 D/ P# V0 x4 _9 r8 ?
  134.     SPI1_ReadWriteByte((u8)WriteAddr);   
    4 T5 G' }3 @3 l
  135.     for(i=0;i<NumByteToWrite;i++)SPI1_ReadWriteByte(pBuffer<i>);//循环写数  ( H3 S/ R7 S4 Q6 O8 o9 S9 }5 g
  136.         W25QXX_CS=1;                            //取消片选 3 O) r5 H5 N7 J  V: w1 s
  137.         W25QXX_Wait_Busy();                                           //等待写入结束
    % ~! P# Q( W2 C; ]. ]) ]& z
  138. } 7 d* v) w/ |! V* N
  139. //无检验写SPI FLASH
    & J: x% M7 X& H# n
  140. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!% K. d" ]9 T# t0 K" T+ w
  141. //具有自动换页功能
    : f! c* C( A. I8 P6 j4 R9 V
  142. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!
    . ~4 g  Z9 W" `' y& `7 b6 W
  143. //pBuffer:数据存储区
    7 E% R+ Y$ t9 G) c; [) ]
  144. //WriteAddr:开始写入的地址(24bit)
    ; o8 p; Y; H. q$ p$ c3 w5 j- ?, K' ^
  145. //NumByteToWrite:要写入的字节数(最大65535). T- a" h- c- m% P7 D
  146. //CHECK OK% @/ Y9 h6 d8 _, K, r6 a
  147. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
      D  N0 C# H# C7 \. D0 A/ t
  148. {                                          
    . [+ g) \. f' W- g( U, }) D
  149.         u16 pageremain;           4 E4 t; I, |' d" K& i+ R* N6 H
  150.         pageremain=256-WriteAddr%256; //单页剩余的字节数                             
    , V: O- [2 @: N- o9 s; _( E" z: r
  151.         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节3 U) w: k: h! ?% F! Q* h
  152.         while(1)
    & |, h+ w; G' ]! U8 s# o
  153.         {           
    2 R6 Y" O6 [' C9 Z6 ~6 @  r' n
  154.                 W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
    2 ]% a: Y  [2 P9 x0 ~9 I
  155.                 if(NumByteToWrite==pageremain)break;//写入结束了8 r, x0 O6 c$ }9 e" ]) ]- [  x/ P
  156.                  else //NumByteToWrite>pageremain
    # O, A1 T6 Z4 e$ P$ j4 N
  157.                 {: r& d9 p0 G$ W
  158.                         pBuffer+=pageremain;; ~* `" _& [/ v0 I! O8 U9 k7 o, H1 V
  159.                         WriteAddr+=pageremain;        
    2 O6 y2 v# m# k% J3 C* e7 c
  160. $ P6 z& e( D, F! Y
  161.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数
    1 I7 G" H9 h+ X. }5 O0 o$ v
  162.                         if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
    1 _% A/ u* F# L# d# M. w+ }
  163.                         else pageremain=NumByteToWrite;           //不够256个字节了
    3 ^+ Y: O8 _# ]* W# d3 N6 b' t
  164.                 }
    ( g+ d: n& O& U. _7 `3 y: }7 ]  q
  165.         };            0 M0 v# d, F) K, j2 Q/ B* S
  166. }
    : t4 n% g/ A& z8 C! l5 p7 q1 F
  167. //写SPI FLASH  5 \+ H1 f  {. G3 J3 N
  168. //在指定地址开始写入指定长度的数据9 u9 e& o6 R' \, @+ d% n9 M( C
  169. //该函数带擦除操作!+ I0 c0 k& o5 ^" s" P# d. v6 U) j
  170. //pBuffer:数据存储区" K" y3 k4 u2 [. h7 o
  171. //WriteAddr:开始写入的地址(24bit)                                                
    4 l2 E2 X  o# r# U3 C* v* b
  172. //NumByteToWrite:要写入的字节数(最大65535)   ; M: M$ \- d! D# M7 ^$ _
  173. u8 W25QXX_BUFFER[4096];                 : t8 ?; V# |  G1 S! s0 T" s" }4 h
  174. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
    1 V$ E( F' f) W5 A4 M5 L
  175. {
    . ]0 A7 r% x: I" B" I
  176.         u32 secpos;0 t  a* k8 y5 X7 w1 B) u
  177.         u16 secoff;- j+ L0 c' n$ _3 D$ e3 L
  178.         u16 secremain;           
    " U$ v6 G, C# Y
  179.          u16 i;   
    $ _- U% O) q7 J5 D
  180.         u8 * W25QXX_BUF;          0 N* ?$ g: K1 B8 g# g
  181.            W25QXX_BUF=W25QXX_BUFFER;            
    ! {# \( r% S8 i$ r
  182.          secpos=WriteAddr/4096;//扇区地址  5 @) w2 \, _3 p" I' }/ ]2 u6 ?2 R
  183.         secoff=WriteAddr%4096;//在扇区内的偏移
    . r* F* n; t2 P
  184.         secremain=4096-secoff;//扇区剩余空间大小   
    ; |4 ~$ y3 u+ G4 }4 O. ?7 Q
  185.          //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用$ @  A5 h2 p& v, j7 I5 K$ {* D6 ]7 Q
  186.          if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节  B# ^- `( w5 O6 T/ m
  187.         while(1)
    1 }7 A. n" x+ G& W. H& A! s
  188.         {        
    & m/ f9 ^, L5 H: S) K
  189.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
    " r; l8 H! ~! `2 m# e
  190.                 for(i=0;i<secremain;i++)//校验数据
    5 v/ k/ g1 C, E
  191.                 {8 T! ~. Q# x' K2 w4 o4 O0 ]$ T- x) C- l
  192.                         if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除            4 O9 w0 `, e. R3 A  z8 A, p% A
  193.                 }
    , U" X! s( K. O( j1 D
  194.                 if(i<secremain)//需要擦除
    ; L: T2 }' e( P4 k$ M0 e
  195.                 {
    - t+ Q, Y' k7 E. {$ G
  196.                         W25QXX_Erase_Sector(secpos);//擦除这个扇区: s! `( h. A( ]( v5 t
  197.                         for(i=0;i<secremain;i++)           //复制% ~# V6 R# x/ k2 O) N5 L: C* Q
  198.                         {2 E; }  C$ C3 R& F3 r% l/ V
  199.                                 W25QXX_BUF[i+secoff]=pBuffer<i>;          * |* [  e3 H3 g& B0 Y- B* U# N
  200.                         }
    1 `' m; e* |4 P' v+ l/ f% f
  201.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  ( ]* Z/ h" I" Q3 Z) j
  202. 8 f! x/ r7 j) I* L% _
  203.                 }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                      _. Z% H. G4 Q- i) N/ ?8 g! d+ c0 M  I
  204.                 if(NumByteToWrite==secremain)break;//写入结束了
    % ~6 F  i- |7 F3 ]" ]3 V
  205.                 else//写入未结束
    # f6 r) ^0 s" \( p( [
  206.                 {5 |8 W- }7 Y5 M- H- E5 k
  207.                         secpos++;//扇区地址增1! Z9 }: z) X7 E0 ?* S
  208.                         secoff=0;//偏移位置为0          : ?  {" V$ L  K1 q% ~/ k4 h
  209. % t+ R) c. i1 q# B
  210.                            pBuffer+=secremain;  //指针偏移
    3 N  U" d6 G- ~! C, m
  211.                         WriteAddr+=secremain;//写地址偏移           6 c$ r- f3 z/ N* }( f4 j: Q; [
  212.                            NumByteToWrite-=secremain;                                //字节数递减
    + W4 h* P5 Y& |" i8 p6 @0 E3 l4 x0 B
  213.                         if(NumByteToWrite>4096)secremain=4096;        //下一个扇区还是写不完& j$ {' z( s+ ?$ G! \
  214.                         else secremain=NumByteToWrite;                        //下一个扇区可以写完了
    3 z! |1 Q" X6 d1 {+ l8 f$ u9 C6 h9 ^
  215.                 }         $ a, D& c, n, h, Y0 R3 [' o5 q
  216.         };         2 `+ ?3 k: F2 {; v! U
  217. }# |% \2 Z! Z) b/ I  K
  218. //擦除整个芯片                  , F3 X) {& s8 z
  219. //等待时间超长...5 j( I* I! c. B) O( h4 M* ?
  220. void W25QXX_Erase_Chip(void)   5 d2 j0 g% B  U$ A' h) |* A" s; `9 F
  221. {                                   4 W9 O, ?* T+ i9 v2 u: F/ g
  222.     W25QXX_Write_Enable();                  //SET WEL & C+ Q2 L, n, L
  223.     W25QXX_Wait_Busy();   & D4 h6 X# ]! r$ b4 \" }* Z
  224.           W25QXX_CS=0;                            //使能器件   ) K8 Y0 t( Z8 o! L4 S1 H0 N
  225.     SPI1_ReadWriteByte(W25X_ChipErase);        //发送片擦除命令  . ]; l8 f% G" Z, e. z6 q: \% a
  226.         W25QXX_CS=1;                            //取消片选                  
    * P$ ^0 \' Y; ?7 j" f
  227.         W25QXX_Wait_Busy();                                      //等待芯片擦除结束" f& K! {( S8 n" _
  228. }   
    5 ~1 Y/ B* |- F" `* t
  229. //擦除一个扇区
      o  e/ M, V$ [6 U! A
  230. //Dst_Addr:扇区地址 根据实际容量设置9 k; U% t1 L( g, V2 ?
  231. //擦除一个山区的最少时间:150ms; o( }5 a! b6 F; P5 [4 t2 k
  232. void W25QXX_Erase_Sector(u32 Dst_Addr)   
    ! z0 _1 w% ?6 X6 `+ K1 o7 E
  233. {  ! q8 s5 F$ ?# o7 Z5 A' Q
  234.         //监视falsh擦除情况,测试用   
    5 Y0 c; f! \0 t' w+ {; r, f4 `( I
  235.          printf("fe:%x\r\n",Dst_Addr);         
    + C1 J$ ^) ?+ a% `
  236.          Dst_Addr*=4096;
    8 n- [$ V, M& g; |9 c
  237.     W25QXX_Write_Enable();                  //SET WEL          + K. @% a, O) D1 E% v/ G7 K, W
  238.     W25QXX_Wait_Busy();   ' H" m! j8 e, p7 E
  239.           W25QXX_CS=0;                            //使能器件   
    4 f" q$ x4 a7 s" ~. D  I
  240.     SPI1_ReadWriteByte(W25X_SectorErase);      //发送扇区擦除指令 : J" O& P# M- t# y
  241.     SPI1_ReadWriteByte((u8)((Dst_Addr)>>16));  //发送24bit地址    " d4 y2 Y  w( d( m6 M6 q, ^" s
  242.     SPI1_ReadWriteByte((u8)((Dst_Addr)>>8));   ) i. M2 H# F9 s8 o) n
  243.     SPI1_ReadWriteByte((u8)Dst_Addr);  ) k* M. O1 z& r' V- h& e9 [
  244.         W25QXX_CS=1;                            //取消片选                  
    * ^  o2 C6 G' P' h9 b
  245.     W25QXX_Wait_Busy();                                      //等待擦除完成3 J# j( r. t5 O4 b0 }- h, @
  246. }  : _) h7 w7 \2 M
  247. //等待空闲+ B$ J. C0 e( ?" F8 Q
  248. void W25QXX_Wait_Busy(void)   
    8 ~0 R7 v: U& K$ _' y
  249. {   , P$ z2 @$ T, ?3 \0 L1 n
  250.         while((W25QXX_ReadSR()&0x01)==0x01);   // 等待BUSY位清空: @) m9 P+ D1 d0 {1 [; Q: K
  251. }  # U5 t+ c  p1 B: U5 A
  252. //进入掉电模式3 y3 H, U' d( V8 i2 M9 f3 Z
  253. void W25QXX_PowerDown(void)     y0 e" Y$ S+ {, C7 K9 p
  254. { , E& `4 P" o% ]  \, h6 Y
  255.           W25QXX_CS=0;                            //使能器件   
    # h2 ?, \, N, M6 U) G4 g+ Z
  256.     SPI1_ReadWriteByte(W25X_PowerDown);        //发送掉电命令  $ U% y7 Y; U* e, ~, ~$ v6 S/ X( V
  257.         W25QXX_CS=1;                            //取消片选                  
    ; P* L- p  v% c& N$ X. K
  258.     delay_us(3);                               //等待TPD  
    ( B8 ~2 {( ]6 [2 @
  259. }   + M7 W5 e* c/ [# U
  260. //唤醒5 ]8 i, {! _4 p9 `" @# ]* D. V3 q
  261. void W25QXX_WAKEUP(void)   ' S* W, F1 v' Z' S; m' @; i
  262. {  - n, N/ C8 ]# u+ ?+ r$ m- [: }* Z
  263.           W25QXX_CS=0;                            //使能器件   % q- _8 ~! N7 U8 x$ Q" i
  264.     SPI1_ReadWriteByte(W25X_ReleasePowerDown);   //  send W25X_PowerDown command 0xAB   
    7 f# e+ k( {% L
  265.         W25QXX_CS=1;                            //取消片选                   5 T& ~* ]5 b* q. D- D* h, L
  266.     delay_us(3);                               //等待TRES1
    $ B. ^, v4 k! T
  267. }   </i></i></i>
复制代码
  1. 7 T6 `1 c% y- I
  2. W25Q12xx.h:
    " t2 P, v; Z7 N. r$ h/ q1 w& ^
  3. ' L& m2 q; w$ F8 a$ [
  4. #ifndef __W25QXX_H5 q: x3 f5 C$ y3 u
  5. #define __W25QXX_H                           
    9 l% t* N, @' d( x
  6. #include "sys.h"  
    " p# h- f: C$ M; i+ g! y3 S6 X4 q
  7. 9 ?) d, C$ O9 J
  8. //W25X系列/Q系列芯片列表           6 o5 [! n6 c/ K- y9 U$ |
  9. //W25Q80  ID  0XEF13
    : N0 R+ t! E! V0 h& R
  10. //W25Q16  ID  0XEF145 e4 n' V" [: T3 D) r# p
  11. //W25Q32  ID  0XEF15
    0 j% a, @" q% Y9 o
  12. //W25Q64  ID  0XEF16        
    4 H, |; t, i) h/ Y8 @- y2 H
  13. //W25Q128 ID  0XEF17        / ^& K7 b( r1 w1 l
  14. #define W25Q80         0XEF13         
    ) `% N6 x, ^' r7 O6 f0 A8 c9 S
  15. #define W25Q16         0XEF14/ w5 ?  Z2 `7 {( Z2 {* Y
  16. #define W25Q32         0XEF15
    . o" t3 L3 l6 c  n
  17. #define W25Q64         0XEF16( B( d( e, L1 t3 Q5 r1 V. ]5 g/ K6 F  U
  18. #define W25Q128        0XEF17+ L& X+ Y$ `0 ]( b

  19. 1 }4 ]! }7 \+ a6 Y$ e; L
  20. extern u16 W25QXX_TYPE;                                        //定义W25QXX芯片型号                  
    8 C  V1 Y' G2 H5 D

  21. ( g& n7 u8 O( r
  22. #define        W25QXX_CS                 PBout(14)                  //W25QXX的片选信号
    9 \  V$ |' q+ C7 a. J, {
  23. //
    $ D: w! I9 F, _' [$ r7 m7 ]
  24. //指令表
    7 z1 E/ c" G9 o
  25. #define W25X_WriteEnable                0x06 $ |1 |' H5 B7 d' l
  26. #define W25X_WriteDisable                0x04 * J, B" @+ x! H9 e& d0 b
  27. #define W25X_ReadStatusReg                0x05
    : _& y4 ^! Q$ f; r. v! V0 f% Y
  28. #define W25X_WriteStatusReg                0x01
    7 X6 }2 M! @* s8 c! f  u' n3 N
  29. #define W25X_ReadData                        0x03 * c) L, R- w5 d
  30. #define W25X_FastReadData                0x0B ; h& c! g) ^: L( N8 g, H3 ]% d8 o
  31. #define W25X_FastReadDual                0x3B
    . l+ `$ b0 }; U2 v  D8 C0 X
  32. #define W25X_PageProgram                0x02 ! v1 z1 B* [$ m! t8 J
  33. #define W25X_BlockErase                        0xD8
    0 T, m, N  @4 l; f5 a
  34. #define W25X_SectorErase                0x20 8 R# X/ B- q8 M) i
  35. #define W25X_ChipErase                        0xC7
    , F( t6 u/ {. L( w" |0 C
  36. #define W25X_PowerDown                        0xB9
    2 l5 X5 M3 h" k* @
  37. #define W25X_ReleasePowerDown        0xAB 0 L) X2 P* \6 s* G# j3 b
  38. #define W25X_DeviceID                        0xAB
      ?3 G; u% r3 }6 u' S" Z4 M  i' M
  39. #define W25X_ManufactDeviceID        0x90
    4 ?5 W3 q& y. c6 w; T
  40. #define W25X_JedecDeviceID                0x9F 3 i$ f+ t; }7 _. k+ _+ [" d* ~0 l

  41. 1 e+ ~: M$ N4 n% p$ i; a* h
  42. void W25QXX_Init(void);( K4 O! v; j7 J# x7 \  V9 A* ?
  43. u16  W25QXX_ReadID(void);                              //读取FLASH ID
    / c: {- @. O+ y4 s( {' O
  44. u8         W25QXX_ReadSR(void);                        //读取状态寄存器
    ; A7 Q4 C+ T6 z3 e' r3 i
  45. void W25QXX_Write_SR(u8 sr);                          //写状态寄存器' I7 C" b1 E  A+ N1 I
  46. void W25QXX_Write_Enable(void);                  //写使能
    5 x- _4 Q8 Y- H# d1 c
  47. void W25QXX_Write_Disable(void);                //写保护
    2 E1 K0 O  H9 j1 U! Y/ B2 _
  48. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
    , t' `. G7 O: ?
  49. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash
    8 l% l9 s, L2 ]7 z0 n
  50. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash
    / C; ?4 O# ]$ [; r# A/ E8 u
  51. void W25QXX_Erase_Chip(void);                      //整片擦除
    ; K+ `, l) g/ @  E' ^' @
  52. void W25QXX_Erase_Sector(u32 Dst_Addr);        //扇区擦除, ]+ T6 @/ i: q; ^" c. f# O
  53. void W25QXX_Wait_Busy(void);                   //等待空闲; [+ a$ P9 X/ f/ S
  54. void W25QXX_PowerDown(void);                //进入掉电模式
    # g; L) A) ?2 r9 }7 r1 s1 ^  j( d1 X
  55. void W25QXX_WAKEUP(void);                                //唤醒1 a. B' c' p% Z1 ^/ [
  56. 0 T9 V2 K, ?7 X" D" x' x2 a
  57. #endif/ j5 \8 W- ^8 x0 t: c7 V4 U( k
复制代码

3 ]6 x. q7 J' G. Imain.c:
1 R: d5 o9 J# w# y) o5 @8 V/ @9 T$ Z' D+ m" [( R- T
  1. main.c:; |2 X! P5 }1 J
  2. #include "sys.h"
    + {( ]" _  c1 P7 ~7 B( R% v
  3. #include "delay.h"
    $ I, |* c9 O" s. }; {+ |/ w
  4. #include "usart.h"
    4 ]3 Y" F! k- b2 _" V) X, p
  5. #include "led.h"- a& U; m* O- i# v% v$ s
  6. #include "lcd.h"$ \3 x+ y1 J5 ~  }3 a+ Q
  7. #include "spi.h"
    " r& Q6 E; Y0 i! r( V
  8. #include "w25qxx.h"
    # X2 s: p8 ]# P3 v4 F! ^1 l( k' a
  9. #include "key.h"    5 b: o, x! ~8 Z. N" C1 h# N2 i
  10. 8 P( ^- @. p2 ?  h
  11. //要写入到W25Q16的字符串数组
    ; N' t4 S6 U. ~' I# K% o
  12. const u8 TEXT_Buffer[]={"Explorer STM32F4 SPI TEST"};. M; z+ C, h. r6 K
  13. #define SIZE sizeof(TEXT_Buffer)         
    . u" b. k1 Q( e- O9 K# e6 Q
  14.         5 ?. N  a8 l+ D( e
  15. int main(void)
    ' _: [) ~3 [+ P+ L7 g% x% `+ f6 n4 C
  16. { 4 I+ [2 S* _" C
  17.         u8 key;3 I) {4 i4 v6 T+ n8 ~3 M* M
  18.         u16 i=0;4 y; j& w. z% G2 o9 ]
  19.         u8 datatemp[SIZE];          a' z& A9 d1 |' m" @
  20.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
    4 |- h# ?' m. L
  21.         delay_init(168);    //初始化延时函数& P1 Y5 A3 J% G( t
  22.         uart_init(115200);        //初始化串口波特率为115200
    , [1 C. `. Z) u2 T3 X
  23.         LED_Init();                                        //初始化LED
    6 ~( F+ d+ z- e9 z! b& B
  24.         KEY_Init();                                 //按键初始化  
    ( C5 @7 x2 `- u. D% j" _
  25.         W25QXX_Init();                        //W25QXX初始化 % R& S3 w+ B4 h7 y
  26.                          & O3 Q9 [. `( M* m2 W
  27.          while(W25QXX_ReadID()!=W25Q128)        //检测不到W25Q128
    # o" ]) w& C# d: q
  28.         {
    ! |# M+ ~7 l! y" o# g* j- w
  29.                 printf("W25Q128 Check Failed!");* j) Z, n; c2 M1 b, w
  30.                 delay_ms(500);$ p5 o0 l$ x4 p" z6 f
  31.                 printf("Please Check!      ");
    + D1 j+ z; M2 A
  32.                 delay_ms(500);, q  ]' l: x& Z: O( i
  33.                 LED0=!LED0;                //DS0闪烁
    / E/ ?( c$ \+ F7 x+ Y
  34.         }
    & B  Q: f1 x* {; r% S% r5 U
  35.         " M" ^: Z  [, ~) h1 X  L
  36.         while(1)
    0 f6 X% z3 H) O5 H
  37.         {4 U& w% @) l7 r( T4 m. j8 l
  38.                 key=KEY_Scan(0);
    1 j! c/ o! f) S/ V  |
  39.                 if(key==KEY1_PRES)//KEY1按下,写入24C02
    6 L+ r; p1 C$ i. W0 X
  40.                 {
    + c" Q, r5 f" U) c- q0 Q* e; l
  41.                         printf("Start Write W25Q128....");
    : q$ F" s% y/ A, r
  42.                         //从倒数第100个地址处开始,写入SIZE长度的数据  
    ) B5 p; S1 e, p+ j
  43.                         W25QXX_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);
    0 E4 c- g9 m- B' |! B
  44.                         printf("W25Q128 Write Finished!");  //提示传送完成( Z. C3 K6 O: S: j5 ^# E% D
  45.                 }7 ], q% g8 |" o( V
  46.                 if(key==KEY0_PRES)//KEY0按下,读取字符串并显示. f; ]4 e, m0 F4 o& h
  47.                 {$ l5 e; n) X. M9 Q3 s$ N( b
  48.                         printf("Start Read W25Q128.... ");
    & o+ L: k: N0 `* H/ N7 M
  49.                         //从倒数第100个地址处开始,读出SIZE个字节3 ^3 o/ @- A) J
  50.                         W25QXX_Read(datatemp,FLASH_SIZE-100,SIZE);                                       
    1 O* G7 |+ x( _, i( I# M
  51.                         printf("The Data Readed Is: \r\n ");//提示传送完成' H* @  g/ D% V
  52.                         printf("%s\r\n ",datatemp);4 h+ o% s" b. S, N, v  E
  53.                 }           . O7 d( {$ @- Q* i8 D  I3 [
  54.         }             , V) a5 ]% B. I) S/ Q
  55. }/ W$ j. o8 Z: }6 a

  56. ( g3 O! @# l7 I7 H6 ?
复制代码
4 F4 _8 p- k( N5 W9 h! u
0 S+ u; B% ^2 j/ p# B! g

0 ~7 r9 o" D! W
A71_MM@Q(@D3Z`FQYIISTMD.png
1 收藏 1 评论0 发布时间:2022-5-16 11:53

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版