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

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

[复制链接]
STMCU小助手 发布时间:2022-5-16 11:53
一、SPI介绍# q, S1 I7 h3 [8 a
SPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。
2 o+ N; x. ]: W' B& B! C
7 _% G4 j$ S- y6 u' b2 [正是简单易用的特性,如NRF24L01、VS1053、SD卡等皆集成了这种通信协议: D$ E% E4 a* j3 r9 E

& i: S$ R- _+ c' p _A_6DRLZV3W0~DW}CEZQ7{O.png & R) K( I0 y6 ?+ D
9 m) V( Y6 S5 Y' z' @2 i4 s; m9 q
二、SPI接口框图
' N/ E$ O: m, V8 b; q% m$ j! ^( r5 D  u8 c9 n/ J' L
@OFY~~NQ}EZ}HLQL{EB%0ND.png
* d  D/ H) U- C# S
: I8 k8 a9 d. L1 ?4 p6 e+ d: n9 z. B三、SPI优缺点
4 u4 ~/ A4 x$ C, [
SPI接口是在CPU和外围低速器件之间进行同步串行数据传输,在主器件的移位脉冲下,数据按位传输,低位在前,高位在后,为全双工通信,数据传输速度总体来说比I2C总线要快,速度可达到几Mbps。
% v1 C( c. I% W6 g; P$ G9 l
) @. J) D( ]/ p2 f8 S  W信号线少,协议简单,相对数据速率高。4 x" W; p) J) D0 o

3 M/ Q5 |& O% J, `, O缺点:没有指定的流控制,没有应答机制确认是否接收到数据; i  P2 m% y7 d) \. a1 e  Z1 g  y

2 p7 Y: ]3 R+ e$ x5 V四、SPI工作原理总结& S; V: f; Z8 Z7 _8 o0 K3 D. ^  D
硬件上为4根线。. Z2 r% K! d+ {/ O$ g
主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。- o) v- F! u6 `$ ?1 ~( J
串行移位寄存器通过MOSI信号线将字节传送给从机,从机也将自己的串行移位寄存器中的内容通过MISO信号线返回给主机。这样,两个移位寄存器中的内容就被交换。0 b3 j' ?, b4 a1 z6 h6 @
外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。5 V2 ^+ b1 T) U  H$ `- h
多个设备使用SPI的应用举例$ D% k6 n1 W5 a# u
9 Q' X* w) m3 |& y6 G0 b
(_8GH(KLZ4`~[RSWOUL_B{Y.png & \) B: y2 q& {1 y6 D2 U. ?
* t/ H# {9 m6 I
五、时序图8 _) e+ m7 N1 h4 I
时序为 SPI_CR1 寄存器中的 LSBFIRST 位复位时的时序
3 w3 }+ |7 s3 d: N3 I8 @' e, B
' N, o9 u! z: ]1 S' Y/ Z; v& v* ASPI_CPHA的值将会影响SPI_CPOL的值
9 A- S8 }$ H9 A' T
# R2 N" }! \& K+ Z0 { 4R1]OS0}$R`DITZTCDX~T~D.png
% g6 P  `6 o' ~7 q7 a1 k; Q# A' f3 O
六、SPI程序编写过程
4 D9 K& c' r5 |" Q: d5 F  }# f
  1. //①使能SPIx和IO口时钟9 F$ K5 l: j6 y8 m* F- A- ^  |! Q
  2. RCC_AHBxPeriphClockCmd() / RCC_APBxPeriphClockCmd();- t, B# u$ s, N; \

  3. - j" T* S8 T3 t# Q3 B
  4. //②初始化IO口为复用功能. y* ]  y, `/ f& u8 d- s, s% j
  5. void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);0 b+ h& F! f- t, X" p
  6. 3 M; }% _4 c" c5 F" P* B
  7. //③设置引脚复用映射:; O, f% l1 X% q7 `. Y
  8. GPIO_PinAFConfig();
    8 ?( N/ |; G9 ~9 S, U, z; r
  9. * l! o/ j* V, f& d, S5 B. f
  10. //②初始化SPIx,设置SPIx工作模式
    ( G1 H$ U5 P, W
  11. void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);! T- P. x% N2 f3 A' T3 [
  12. 8 n( Z4 M! F% b6 e: {0 [9 j
  13. //③使能SPIx: q3 X3 L& n2 O* s0 e1 r/ d
  14. void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);! D; h( u; A! ?* H$ m
  15. + D3 Q2 I5 V! T  p  r
  16. //④SPI传输数据
    2 j8 K6 Q/ |* h7 q/ \
  17. void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
    " T$ \4 N9 I. X% {! }
  18. uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;. U9 Q$ Y0 S, z5 c5 r' n

  19. . K% ?3 Z5 o! Z! y
  20. //⑦查看SPI传输状态
    ; x! ?, @9 C% A3 ^+ s, H9 `: w3 m9 X
  21. SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);
复制代码
  1. SPI.c:) b) k5 E/ I. D% a$ f5 e
  2. #include "spi.h"# c6 w: {* h! I3 A( ?& e' [" o
  3. 7 ~. p0 I( W" a& A" t6 i) q
  4. //以下是SPI模块的初始化代码,配置成主机模式                                                   . E! x0 H- I, c1 T- ^. t
  5. //SPI口初始化' V- @% {( w* O0 P7 Y
  6. //这里针是对SPI1的初始化
    1 [$ C: s2 f/ [' \! W
  7. void SPI1_Init(void)
    / m* |+ l4 V8 P5 V8 S" g" Y5 a
  8. {         
    9 a' V3 ~& @% F& L6 L3 a
  9.   GPIO_InitTypeDef  GPIO_InitStructure;% O- m  r! U! W& p
  10.   SPI_InitTypeDef  SPI_InitStructure;
    9 W7 l2 [1 N. c( I2 u$ t3 T
  11.         6 b$ t$ \; b1 J" U& d5 N
  12.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟3 `$ Y8 b* _' ~6 B9 Q- o, S6 U) y
  13.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟
    ' a+ g1 c  o4 `
  14. ( |( K" y( s$ O% J, H
  15.   //GPIOFB3,4,5初始化设置' @, ?5 b( b3 \( q
  16.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出        % G* {' U4 L7 z1 t% H/ e
  17.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能) g  l5 l1 v7 j" l$ {0 o6 q
  18.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出* H7 q  e. n: W. U( ^
  19.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    * d; ^# k% H' C4 _8 C% T
  20.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
    / L8 \. V/ i! J. q5 `/ Q% c
  21.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化0 G( b# B; U8 [! _, r; B1 ]
  22.         , Z& J" D# ~: k( F
  23.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为 SPI1
    5 F2 ^$ C/ A3 y; c4 U
  24.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为 SPI1
      ]: _1 s8 o2 U$ j
  25.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为 SPI1/ Y1 H. f, f* ?; w4 A- \( P: s- h

  26. 0 n) ^+ O  i5 ^% h3 {$ c  q) t
  27.         //这里只针对SPI口初始化8 _. ?* r" H* n" \! O% e* W' r: R
  28.         RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI1
    " z. j0 K  G; J: N4 Y; b
  29.         RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1+ D. A: C  V' A0 Y3 @2 t

  30. * R" ^1 Z& h. r7 I
  31.         SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
    . r  P) H0 y; c' t4 _3 C  I; v+ l
  32.         SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                //设置SPI工作模式:设置为主SPI( C6 r4 {+ E3 J6 w) l; `. }: s
  33.         SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                //设置SPI的数据大小:SPI发送接收8位帧结构; H( n; Y9 p5 `
  34.         SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                //串行同步时钟的空闲状态为高电平
    9 V' a" x/ d: [! y
  35.         SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;        //串行同步时钟的第二个跳变沿(上升或下降)数据被采样3 |" M5 v8 ~9 l& p" i
  36.         SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制( p$ P. H- f( g. s
  37.         SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;                //定义波特率预分频的值:波特率预分频值为256, m! u3 U. z! }
  38.         SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;        //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
    * R0 D/ j# b3 ^  }% G
  39.         SPI_InitStructure.SPI_CRCPolynomial = 7;        //CRC值计算的多项式+ Z3 w! _5 C6 E; j( d
  40.         SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
    5 M7 J0 n+ B" j$ z6 h
  41. ' ]# |2 c5 l( R6 K( c
  42.         SPI_Cmd(SPI1, ENABLE); //使能SPI外设
    & B4 X; x/ u; u1 H9 W% C2 N+ @
  43. % I- ]7 Q& R* a+ \, M, |
  44.         SPI1_ReadWriteByte(0xff);//启动传输                 
    3 {, M3 ]3 i, \% m4 @' R
  45. }   7 f8 E( l/ G$ w& [
  46. //SPI1速度设置函数& G/ o, D, ]/ C5 V7 }3 l3 X
  47. //SPI速度=fAPB2/分频系数$ J+ X  x/ d1 D+ |+ c9 w  ^! C
  48. //@ref SPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256  
    0 o" b; V6 N. T% H9 d$ Q+ E
  49. //fAPB2时钟一般为84Mhz:( J6 A5 k& a' \/ [
  50. void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)6 T3 [# t8 N+ M  i9 B
  51. {3 W; y7 m8 R; C  b1 @$ k
  52.   assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性3 v: y" G5 A3 F7 {. H* t
  53.         SPI1->CR1&=0XFFC7;//位3-5清零,用来设置波特率
    * I' X/ N4 M/ o, Q$ M; ~
  54.         SPI1->CR1|=SPI_BaudRatePrescaler;        //设置SPI1速度
    * ]' o" Q/ C- D) P+ o
  55.         SPI_Cmd(SPI1,ENABLE); //使能SPI1
    4 J% b% f# i9 L
  56. } . G( u" n* `3 J7 u" J
  57. //SPI1 读写一个字节
    ' N& {) x3 o( X4 u* J
  58. //TxData:要写入的字节
    % I. u4 w2 P" T
  59. //返回值:读取到的字节6 A* e8 i1 H4 z
  60. u8 SPI1_ReadWriteByte(u8 TxData)
    ( }+ \! l$ B; @' V
  61. {                                          5 k# b% @) B. J9 w

  62. 4 S, l* w3 c8 d8 E9 P5 e
  63.   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空  
    ' b7 ~! x  j6 E3 ~
  64.         
    4 g; g% }( r+ _
  65.         SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte  数据
    " P2 b: R2 O! J6 }* A
  66.                 # R! P. I! w+ U. Q: B+ A1 x
  67.   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一个byte  
    ! P9 J# W) J) h2 H  s
  68. 6 x8 x) D1 `4 _9 m
  69.         return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据        
    5 E6 r% }5 v: ^' T5 s
  70.                      % E, r2 l1 U$ n1 p; S
  71. }4 c6 D6 Y" O$ o" b$ M( P
  72. $ N5 G* h( o- @+ ]( l7 l9 h
复制代码

8 O3 Z& U" g6 l' `5 a七、W25Q12xx的原理及应用* }9 M# i0 _5 q- U$ g' @, U7 b$ @
W25Q128将16M的容量分为256个块(Block),每个块大小为64K字节,每个块又分为16个扇区(Sector),每个扇区4K个字节。W25Q128的最小擦除单位为一个扇区,也就是每次必须擦除4K个字节。这样我们需要给W25Q128开辟一个至少4K的缓存区,这样对SRAM要求比较高,要求芯片必须有4K以上SRAM才能很好的操作。! M- h" y& z8 o) }* G; V/ q* K

) Y8 o) K$ I7 ^' QW25Q128的擦写周期多达10W次,具有20年的数据保存期限,支持电压为2.7~3.6V,W25Q128支持标准的SPI,还支持双输出/四输出的SPI,最大SPI时钟可以到80Mhz(双输出时相当于160Mhz,四输出时相当于320M),更多的W25Q128的介绍,请参考W25Q128的DATASHEET。
! m( O3 i0 ^  w
) p$ F5 M. n3 P3 j, H D%)W27U]]MDR`PRB[FT4~Z1.png
8 v, B! h) U$ }6 ?1 V
8 h; T* f5 f% j4 R* C9 xW25Q12xx可根据原理图查看,使用的是SPI总线通信协议
3 \$ d( B* @6 X& w& _( B3 K( t- u8 Q/ e) b( P4 c& c4 S2 l9 o# s
比如原子的原理图
+ I: u$ I5 c9 j5 s# y
4 P& R6 `- a/ }$ ], l: D: V7 A" F' Y 20190611221654679.png
; L1 u. P9 X8 v7 p) i6 U4 n
/ y( N" p; Z" H- X7.1 分析W25Q128指令8 h5 A5 |0 D" q+ O
可参考W25Qxx的数据手册,这里列出W25Q128部分指令:0 o, E* j7 B- w+ J  w
$ y* I/ \) ~  Y3 z( L# Y
2019091611364032.png 1 i7 h3 y/ @% n4 ~& I' L  K; O$ @
' Y8 W$ t: W3 m. V
比如读取设备的ID的指令:0x90000000
, e! {) a6 F% }) `3 ~5 u: q' G+ v  F% P( z7 L0 p# Z( r
20190916113704620.png
2 N8 J! y. V1 n- A
, E  {( A7 r, M4 l K$HWO9ZGFGE$CVLD47Q]_4G.png
+ X1 n- J8 V& U
/ F+ t4 r' c) u) \/ u& j. ]- a WLLL(({{_EVAN[005V791UU.png * @" V2 k( H$ Z  O/ |6 g
) R  k  k1 C$ ?0 w
7.2 擦除扇区:
4 D3 H$ K- t5 t1 w6 K
( y9 Y8 L* D9 \( B
NZ$~TU098T[KO5)GTQW){UQ.png - O7 }% s0 @  d2 ]9 W3 P

) x* `& h) u. \" N; A; i9 C* m7.3 部分常用设备读取指令:
& q5 A  Z* L. V" a
  T+ _% N7 h0 R- b3 |
Q`V(R2ZUHAZ8SAS2[A}1H%2.png
. N8 b' s# y# X. R3 e( B9 y; e9 r/ J8 a6 F
每次操作前要使能写操作,且给一个低电平。结束时要给其为高电平,再失能写操作。也就是通过操作片选引脚来确定是否使能或失能。$ v1 W. D% g1 J) s0 ^! V
* E0 n/ x, g, ]6 Q
LR6P1TX%{R][_M{CX4]K.png 6 Z& `: W+ L5 j- K( w0 [5 z

. f- S7 e7 z# ?! q" i- d
  1. W25Q12xx.c:
    3 T: m! X. m1 H5 @9 v' T  S
  2. #include "w25qxx.h" , C7 H  [* Q8 |$ [# v3 A
  3. #include "spi.h"/ i5 P8 d$ {. I
  4. #include "delay.h"           
    . B) z$ r; Y, k/ Y$ y
  5. #include "usart.h"        : {$ A" }  C" d; e: i7 d) A

  6. 1 F8 z% @+ H: F4 B) h. O
  7. u16 W25QXX_TYPE=W25Q128;        //默认是W25Q128
    ( e) m& p# T8 v) P( X( N

  8. ( I, _; C3 k1 J" h; U" X" t
  9. //4Kbytes为一个Sector) H/ Y5 _1 r6 b" ]4 P
  10. //16个扇区为1个Block
    5 X% t/ y! h" z# X7 J; C; Z$ O) T
  11. //W25Q128
    ( t8 l& V: m" a! r; d
  12. //容量为16M字节,共有128个Block,4096个Sector ) r4 h9 m) g, n3 Z
  13.                                                                                                          5 o9 \2 ^# j0 ~
  14. //初始化SPI FLASH的IO口
    9 g' h+ S) k0 p) }2 A
  15. void W25QXX_Init(void)4 T; {( d" U  q* G  b; @
  16. { 3 R1 a. U! _9 y% s  D1 c
  17.   GPIO_InitTypeDef  GPIO_InitStructure;
    % Z: G0 V  ~' R
  18. 8 O2 M# k# `1 F4 Q
  19.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
    ! v  n. A. k0 i" m: M. H" V
  20.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);//使能GPIOG时钟6 Z& a+ f% F: z1 q8 j% H& Q

  21. 0 s2 t' v# {/ b! A$ b0 ^
  22.           //GPIOB14
    2 Y, h+ r0 Q9 v
  23.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;//PB148 \; E0 t% [3 c
  24.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出6 o7 [: V! d8 n! \) }
  25.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出2 D+ h! r) S2 `" R+ c: S% e
  26.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    - _. V) \8 G" l) ~+ m
  27.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉$ Y( R; P* Z: \* s' J0 b: M4 }
  28.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化+ n# g: h2 S1 E9 j; t
  29. ; ]: p( v* p% S/ D
  30.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//PG7
    ! R0 k5 {# Z5 I
  31.   GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化  I+ G8 ?4 h* Y/ \, q3 g; R. d

  32. 6 l+ g+ w* p  e
  33.         GPIO_SetBits(GPIOG,GPIO_Pin_7);//PG7输出1,防止NRF干扰SPI FLASH的通信
    $ O* m4 f: P" W, f) j9 H+ m- h
  34.         W25QXX_CS=1;                        //SPI FLASH不选中
    # T" c4 n' k- s2 d1 v
  35.         SPI1_Init();                                           //初始化SPI
    : H  j+ Q! U# I; b
  36.         SPI1_SetSpeed(SPI_BaudRatePrescaler_2);                //设置为42M时钟,高速模式
      q& M% K, A9 O: G' G+ s
  37.         W25QXX_TYPE=W25QXX_ReadID();        //读取FLASH ID.8 T9 `# Q" d: q  X6 F1 r; u6 h) K
  38. }  
    7 p# }/ c# D2 }7 [9 j
  39. " I% |' y9 L2 h( J7 p+ ?3 i4 b+ `
  40. //读取W25QXX的状态寄存器
    7 l9 u7 ]+ I1 F8 w2 w6 U, J
  41. //BIT7  6   5   4   3   2   1   03 `, o& a% \  z& o6 Y( ?6 H. E
  42. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY+ F9 b+ j/ t- y0 r
  43. //SPR:默认0,状态寄存器保护位,配合WP使用
    , {* z% ^" x. b
  44. //TB,BP2,BP1,BP0:FLASH区域写保护设置
    ; U. K  H% |7 d
  45. //WEL:写使能锁定
    + r9 K! C5 \2 f
  46. //BUSY:忙标记位(1,忙;0,空闲), z/ z) W9 I, B7 P) d: ^' }
  47. //默认:0x00
    , V6 [) L$ d) F  ?( W- h- @/ f) S) T
  48. u8 W25QXX_ReadSR(void)   + U2 E4 y! i4 Q" W
  49. {  
    0 `1 k5 {) x+ |- R" x$ k& P4 c
  50.         u8 byte=0;   + g4 B8 ~1 B# s% o
  51.         W25QXX_CS=0;                            //使能器件   * I. y( S) r7 R' S2 i! E
  52.         SPI1_ReadWriteByte(W25X_ReadStatusReg);    //发送读取状态寄存器命令   
    1 w. s) L& T6 d1 ~, R" I5 [
  53.         byte=SPI1_ReadWriteByte(0Xff);             //读取一个字节  " X  _- @+ ~. N7 ~& w/ e
  54.         W25QXX_CS=1;                            //取消片选     
    " |3 [, n/ [, u0 Z: f1 Y
  55.         return byte;   4 k8 x/ Z. |  P4 A  k# y
  56. }
    1 B. {( I0 h" p
  57. //写W25QXX状态寄存器7 j( D  V, ^! @7 p
  58. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
    0 @0 z' u1 `) c2 b) r' P
  59. void W25QXX_Write_SR(u8 sr)   
    ( q3 p& a& S! Q9 r5 {
  60. {   
    3 }: T/ O. m8 h# f+ B
  61.         W25QXX_CS=0;                            //使能器件   ; ?6 ~. e+ b$ @" L. n. ^' b7 [
  62.         SPI1_ReadWriteByte(W25X_WriteStatusReg);   //发送写取状态寄存器命令   
    2 I; [+ s& T2 @% d3 l0 s
  63.         SPI1_ReadWriteByte(sr);               //写入一个字节  1 M( j" p2 `9 r( D' e
  64.         W25QXX_CS=1;                            //取消片选                  
    % M! q, M' d! p: |& g( C& N2 j
  65. }   
    - K' c, q6 ^* s2 h. u
  66. //W25QXX写使能        
    ! }9 Z+ K- V" r# v: P, \
  67. //将WEL置位   : i4 x4 A; e& k
  68. void W25QXX_Write_Enable(void)   % f+ H8 _- @- ^) G
  69. {# \# X8 s4 W/ ?. r5 f% C, \5 f
  70.         W25QXX_CS=0;                            //使能器件   
    : Y0 [* x4 L# r# ^8 ]
  71.     SPI1_ReadWriteByte(W25X_WriteEnable);      //发送写使能  + y3 ?# F! V) L8 L
  72.         W25QXX_CS=1;                            //取消片选                   % t6 w6 K' n8 H$ ?: U# o; A
  73. }
    : }8 l8 X+ L* w
  74. //W25QXX写禁止        # r+ T. m7 _# T  d$ g  J
  75. //将WEL清零  . m% x8 c( ~3 S. c1 k
  76. void W25QXX_Write_Disable(void)   2 c5 F) N1 v; j6 P* U. p) C
  77. {  - C4 _- n7 L9 i  ]; `7 g' g5 h6 o
  78.         W25QXX_CS=0;                            //使能器件   
    8 f; ?; T% _& R
  79.     SPI1_ReadWriteByte(W25X_WriteDisable);     //发送写禁止指令    5 M+ O  D# k# K( B+ v% a
  80.         W25QXX_CS=1;                            //取消片选                   8 M6 ]- j4 @- T+ M% M, T- {
  81. }                 1 B0 h! X) ~* Q5 r( s
  82. //读取芯片ID
    5 j) y3 G& r0 f5 b% B* L7 w% {
  83. //返回值如下:                                   
    & o8 [1 @* l4 G8 Q1 Q- y$ H
  84. //0XEF13,表示芯片型号为W25Q80  
    ! w! P  T" n2 ~7 z% _2 j3 s
  85. //0XEF14,表示芯片型号为W25Q16   
    % v$ P% T  ^! a) N0 h
  86. //0XEF15,表示芯片型号为W25Q32  
    $ p- k0 |: M1 k- w* N$ C* d8 [
  87. //0XEF16,表示芯片型号为W25Q64 / n, X+ T/ \+ D5 Z: D5 I
  88. //0XEF17,表示芯片型号为W25Q128           % V1 A6 @" e$ D+ h+ Q$ N9 B0 i5 D  M
  89. u16 W25QXX_ReadID(void)4 ~0 g+ s; y8 F, E6 Q# L7 j0 V
  90. {
    0 A3 P( [6 `8 u  ?& \+ f
  91.         u16 Temp = 0;         
    ( ~, J% w8 l( r" t1 ?: ~0 c. f
  92.         W25QXX_CS=0;                                    
    3 q- _' i* q8 Q1 y% }
  93.         SPI1_ReadWriteByte(0x90);//发送读取ID命令            
    % F0 y4 Q7 O$ I; x  ?& ^" B7 t
  94.         SPI1_ReadWriteByte(0x00);            
    : K0 u! d, _6 ~9 ]( F1 i* I3 h: @
  95.         SPI1_ReadWriteByte(0x00);             ( U& H, K) J' M& V6 o9 s
  96.         SPI1_ReadWriteByte(0x00);                                    
    : t3 ~3 v: b0 D0 F) p2 x4 ?9 ^" w
  97.         Temp|=SPI1_ReadWriteByte(0xFF)<<8;  , {  {5 s! k! S4 n, s* \, b
  98.         Temp|=SPI1_ReadWriteByte(0xFF);         $ t4 M4 v' d, Q6 h+ A
  99.         W25QXX_CS=1;                                    
    ( C8 E/ j' ?# T8 R" C2 t, W2 t
  100.         return Temp;( |! w2 ]1 n- [2 l
  101. }                       
    7 Y. ]/ G0 n6 f9 B/ N
  102. //读取SPI FLASH  
    $ F( a) B! ]' G. w
  103. //在指定地址开始读取指定长度的数据
    % I3 T& E, m4 z. H* G  z5 s2 J3 q
  104. //pBuffer:数据存储区
    6 L; J4 H: C, p, l, n$ l4 A9 {3 P6 e& x
  105. //ReadAddr:开始读取的地址(24bit)
    / I. n3 T1 K9 j1 ^% t  ]8 U
  106. //NumByteToRead:要读取的字节数(最大65535)
    & I% j2 T4 e! ]' [1 D) X
  107. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   ( s0 x/ s! |1 D
  108. {
    6 P# G* I1 n0 ^3 Z
  109.          u16 i;                                                                                       
    ; J: U  |5 K$ `0 C) d# A* Y7 J3 c
  110.         W25QXX_CS=0;                            //使能器件   9 i5 H8 @" x. Y% |& g* i
  111.     SPI1_ReadWriteByte(W25X_ReadData);         //发送读取命令   
    5 i: m2 k" @" Q/ x1 f( e3 k) t- j0 N1 _
  112.     SPI1_ReadWriteByte((u8)((ReadAddr)>>16));  //发送24bit地址    ! u. e; U6 L4 q3 k8 Y/ j
  113.     SPI1_ReadWriteByte((u8)((ReadAddr)>>8));   + n7 P6 m* ?& ~  F* ]& z* h
  114.     SPI1_ReadWriteByte((u8)ReadAddr);   & y7 m- I% E7 m+ d+ {6 X
  115.     for(i=0;i<NumByteToRead;i++)' ?! W& i6 f7 O' T1 U
  116.         {
    " ~5 M' ~' J  G- o; |; u+ M
  117.         pBuffer<i>=SPI1_ReadWriteByte(0XFF);   //循环读数  ) N" a4 g8 S7 N! M4 P
  118.     }
    ' e3 U0 R+ x6 S( H5 A
  119.         W25QXX_CS=1;                                                    9 ^4 F3 ]6 L$ M( r* k; x- O. A
  120. }  # ]+ f. z0 c- k" J7 x' b9 G/ ~
  121. //SPI在一页(0~65535)内写入少于256个字节的数据& F! T0 J2 V) b2 p* q) R
  122. //在指定地址开始写入最大256字节的数据5 d" @; Q+ d( v" @/ {
  123. //pBuffer:数据存储区
    5 y+ M9 f. o; z2 h% r* z) u
  124. //WriteAddr:开始写入的地址(24bit)8 X5 ]' C' _" s9 l" [  ^. x
  125. //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!         ! ~! n* T- p' q& H7 ~+ d
  126. void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)0 s/ U+ k( s2 b& _7 c
  127. {
    9 J4 e/ J2 ]1 I- }& h; t
  128.          u16 i;  : W! ]# l  I, @1 x  Q5 w$ a
  129.     W25QXX_Write_Enable();                  //SET WEL
    9 l8 @, G, {1 S/ k7 d! t
  130.         W25QXX_CS=0;                            //使能器件   
    ) T" e4 y. U, I6 m- x, @: F; k
  131.     SPI1_ReadWriteByte(W25X_PageProgram);      //发送写页命令   - B6 F, F2 \) R! [: T
  132.     SPI1_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址    4 j: F$ E4 u! ~- C' @8 ~
  133.     SPI1_ReadWriteByte((u8)((WriteAddr)>>8));   6 ?- @- X/ F% @8 o- m3 Y. K+ ]
  134.     SPI1_ReadWriteByte((u8)WriteAddr);   * l  l$ D  s6 d( _+ ^. `
  135.     for(i=0;i<NumByteToWrite;i++)SPI1_ReadWriteByte(pBuffer<i>);//循环写数  
    4 n0 m/ K, B8 ~5 e$ h5 z
  136.         W25QXX_CS=1;                            //取消片选 3 _; o7 I+ B# U. I" J# `
  137.         W25QXX_Wait_Busy();                                           //等待写入结束: y( _  n, V7 D+ w' n3 u
  138. }
    & G# i7 }; D7 L/ e0 u
  139. //无检验写SPI FLASH   {4 ^8 E8 W# t: s8 u6 D
  140. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!* ~% x* ?/ J8 B
  141. //具有自动换页功能
    # l0 u  f- F* L( `
  142. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!! p2 K9 y3 E+ k# J; L! n7 t& \7 s( J
  143. //pBuffer:数据存储区
    3 I+ x+ j1 e9 I! `; F" f  g/ Y
  144. //WriteAddr:开始写入的地址(24bit)7 k: ?: g- _3 n1 @, ^4 X6 h4 x
  145. //NumByteToWrite:要写入的字节数(最大65535)
      p8 o3 i/ n) `2 \: s3 I) m
  146. //CHECK OK# m" _4 i0 N2 ]# c' \
  147. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   # e7 p! |" G0 N, m7 a
  148. {                                           & v* Z( V) [% G/ o8 r$ I& Q
  149.         u16 pageremain;           6 L5 Y0 w: L! W* E0 n' W% `
  150.         pageremain=256-WriteAddr%256; //单页剩余的字节数                             . o& g3 n& ]6 ]( ^
  151.         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节+ v5 d( S9 P, d) {3 g5 j2 t
  152.         while(1)
    % K( Q. B+ b- T6 O/ [5 u0 W
  153.         {           
    8 w; P8 E% }& @$ H
  154.                 W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
    ! V- v+ N0 A7 W
  155.                 if(NumByteToWrite==pageremain)break;//写入结束了* c; f7 u  {7 r4 R6 u# e
  156.                  else //NumByteToWrite>pageremain
    4 Y0 s1 K7 ]$ j
  157.                 {, d* t& D8 ]2 M3 j8 g
  158.                         pBuffer+=pageremain;
    - f  E" ^, i1 ~8 Z% Z8 y
  159.                         WriteAddr+=pageremain;        
    ! U4 N  l" n. B: s" X

  160. % K4 v, y: P8 V5 T6 d
  161.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数
    " r- i- O& w1 e1 b6 R
  162.                         if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节+ I' _# {6 Q- s. r9 z: k
  163.                         else pageremain=NumByteToWrite;           //不够256个字节了% H+ g+ f( `& Y+ o7 `- g3 G$ d9 r
  164.                 }# v  J6 ]# C) d, u$ h% T6 j
  165.         };            3 o- `1 K) u" \+ V9 }( F$ J
  166. } + l, P1 ?3 }* L& n
  167. //写SPI FLASH  
    : w, O6 L: F: t
  168. //在指定地址开始写入指定长度的数据
    - |% P( ?+ a. }7 i% ]9 @9 a
  169. //该函数带擦除操作!/ U/ x: B" A8 p
  170. //pBuffer:数据存储区
    * a; n4 v1 C" E, Q
  171. //WriteAddr:开始写入的地址(24bit)                                                
    7 E0 \6 `/ b$ d1 C* v
  172. //NumByteToWrite:要写入的字节数(最大65535)   
      ^! E( S/ q6 s1 y0 f5 y$ s
  173. u8 W25QXX_BUFFER[4096];                 ! `  m3 d+ ^5 ]1 a
  174. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   5 C4 X9 S) _6 Y6 t7 L; @
  175. { - E. p; b7 Z1 G/ \: G+ }( r) k2 `
  176.         u32 secpos;& ]- o+ x! x8 S0 M% |
  177.         u16 secoff;
    , O$ A# f' U+ G3 w
  178.         u16 secremain;           
    * _/ q+ Q: z+ q
  179.          u16 i;    " g0 e2 M: I( V& P. p: c0 x/ d
  180.         u8 * W25QXX_BUF;          2 R- q. Y) @$ S3 ?
  181.            W25QXX_BUF=W25QXX_BUFFER;            
      a+ c% @: Q7 D, M, o1 v
  182.          secpos=WriteAddr/4096;//扇区地址  * ]% o$ z1 u5 o4 t& m$ x
  183.         secoff=WriteAddr%4096;//在扇区内的偏移
    0 M3 {1 y& u0 M$ p8 M8 F' b
  184.         secremain=4096-secoff;//扇区剩余空间大小   
    0 U8 T& h3 U8 j: K" H! f4 i
  185.          //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
    ! h1 \  n( a2 K0 f/ J! G' E0 e% C
  186.          if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节. V# @8 w2 V0 }& e9 Z
  187.         while(1)
    8 V) c, r. }. R$ ]* ~
  188.         {        5 _: _; V# Y1 M% P
  189.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
    ' V; B, |  {# z: _2 t
  190.                 for(i=0;i<secremain;i++)//校验数据+ p2 q3 a2 p0 V' s0 Y, b. j
  191.                 {. H" b% ^: e* Z6 e8 r
  192.                         if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除            . t0 S" e0 Z1 F/ U2 v; c
  193.                 }0 J$ y! {5 {2 |* j. x! q. B
  194.                 if(i<secremain)//需要擦除! K. ~% L, @) k8 ?3 U
  195.                 {
    , l4 G- J7 F- A7 J
  196.                         W25QXX_Erase_Sector(secpos);//擦除这个扇区
    0 H& s+ _) ?4 E% B
  197.                         for(i=0;i<secremain;i++)           //复制$ O% p4 O2 z: {( Z
  198.                         {
      R+ l4 l4 m- z: x" C  r
  199.                                 W25QXX_BUF[i+secoff]=pBuffer<i>;         
    * A2 b5 {# e( T% u  r1 w! O) d$ \) s
  200.                         }! `& P6 m7 s" C" \4 k
  201.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  ! I7 ]. J$ s& r* i9 x

  202. 9 @( m, m. S, ^' @- q6 b
  203.                 }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    , p+ U1 V% L# z) I2 ?
  204.                 if(NumByteToWrite==secremain)break;//写入结束了+ V* J3 a$ s8 P' @# p$ l4 Q
  205.                 else//写入未结束1 X' |$ W/ X8 X, }
  206.                 {) B/ X9 z% B! w& P
  207.                         secpos++;//扇区地址增1
    ! A( h" D4 a- x. i
  208.                         secoff=0;//偏移位置为0         
    ! V: f6 e, {/ K, N2 m) y

  209. % W# _5 w: m7 a% ?4 M9 _: C
  210.                            pBuffer+=secremain;  //指针偏移1 [0 v, f. j8 J, i
  211.                         WriteAddr+=secremain;//写地址偏移           , l8 P; i3 u7 K. ]8 j# X6 ]
  212.                            NumByteToWrite-=secremain;                                //字节数递减
    9 s, W8 e, `8 _; T- r
  213.                         if(NumByteToWrite>4096)secremain=4096;        //下一个扇区还是写不完
    ' M! b9 y5 X$ H& O4 B7 M5 j
  214.                         else secremain=NumByteToWrite;                        //下一个扇区可以写完了" [9 k1 l" [2 y7 ^5 p) i1 [9 M
  215.                 }         
    $ Q  i0 @- z. O* E
  216.         };         9 R& \& p( [* M# Z2 ?8 F- Y7 A
  217. }% b( A9 N4 C  D+ ]1 A; I
  218. //擦除整个芯片                  
    - L( L9 G# X4 h+ I7 K' ~5 f
  219. //等待时间超长...# |6 i( x' X5 i# q: Z) X) m5 r- j
  220. void W25QXX_Erase_Chip(void)     @/ u/ z. ]0 Q% C; c
  221. {                                   
    % ]0 S6 v) y9 L1 R4 x# U" O' Y
  222.     W25QXX_Write_Enable();                  //SET WEL
    ) T; P( n, H4 D+ ^
  223.     W25QXX_Wait_Busy();   # A! c, e$ d: s6 g' m- \4 k
  224.           W25QXX_CS=0;                            //使能器件   
    ; [7 P" h" d; [9 t/ m, B2 Z$ ^
  225.     SPI1_ReadWriteByte(W25X_ChipErase);        //发送片擦除命令  , J0 D9 N& k# B: K7 Q2 k& A  l
  226.         W25QXX_CS=1;                            //取消片选                   + u8 U8 z2 D  \  A1 X
  227.         W25QXX_Wait_Busy();                                      //等待芯片擦除结束
    6 x# u6 B3 X" ?9 f
  228. }   / X  x+ a) {9 |# N0 C6 O
  229. //擦除一个扇区  _7 r: S& I- {9 U4 j5 r" b
  230. //Dst_Addr:扇区地址 根据实际容量设置
    ) n+ u8 r" D3 X
  231. //擦除一个山区的最少时间:150ms. ^! y  `2 Y' J0 K( u
  232. void W25QXX_Erase_Sector(u32 Dst_Addr)   ( l0 h$ @9 F; h& I+ L
  233. {  
    & I3 n/ ]' S3 L+ B0 I; c
  234.         //监视falsh擦除情况,测试用   + s5 t$ j, h9 w% \0 s( I% A
  235.          printf("fe:%x\r\n",Dst_Addr);          3 P/ m$ |, E5 I" y/ Q" F* T2 R' J
  236.          Dst_Addr*=4096;  ^. ]$ E- j9 M, D
  237.     W25QXX_Write_Enable();                  //SET WEL          9 S$ H0 d# w" k4 e& Y$ v& [" {
  238.     W25QXX_Wait_Busy();   
    1 t- t. @* k) [
  239.           W25QXX_CS=0;                            //使能器件   : e9 l9 v+ G% Z$ ]
  240.     SPI1_ReadWriteByte(W25X_SectorErase);      //发送扇区擦除指令 2 T5 J9 e/ d( F$ q. K' c; Y( M
  241.     SPI1_ReadWriteByte((u8)((Dst_Addr)>>16));  //发送24bit地址    & v* D6 U8 x: v
  242.     SPI1_ReadWriteByte((u8)((Dst_Addr)>>8));   
    3 s4 _- U$ ]& S) A. W
  243.     SPI1_ReadWriteByte((u8)Dst_Addr);  
    0 X5 r  X( Y7 V5 O+ X# F$ s
  244.         W25QXX_CS=1;                            //取消片选                  
    2 @3 K; ?+ R3 r
  245.     W25QXX_Wait_Busy();                                      //等待擦除完成
    - Y4 n7 F+ k  M( e! N
  246. }  * `3 ]5 j% o0 z; s( l- s# o
  247. //等待空闲9 _& I3 [- R9 z  g) r
  248. void W25QXX_Wait_Busy(void)   2 q% r  C1 f8 X$ ~
  249. {   5 a7 k4 J' C( _4 [
  250.         while((W25QXX_ReadSR()&0x01)==0x01);   // 等待BUSY位清空
    ! E& p& d9 L: T" A/ d
  251. }  ; E+ g' G2 E9 f! T
  252. //进入掉电模式. X! {; k! R6 L! {
  253. void W25QXX_PowerDown(void)   ) M7 }5 L" G( P2 Y% ]
  254. { : i7 i8 q" m9 Q. k! B5 t4 l
  255.           W25QXX_CS=0;                            //使能器件   
    - |: ~) R7 H6 J" `
  256.     SPI1_ReadWriteByte(W25X_PowerDown);        //发送掉电命令  9 }1 j, x7 r0 ?- L- x$ }- W: l) z$ p
  257.         W25QXX_CS=1;                            //取消片选                   : g% _6 l/ t  `+ n8 P4 ]  O/ B
  258.     delay_us(3);                               //等待TPD  
    " b8 J. A) S( r  h  a
  259. }   
    * ^! w, f! K7 Q
  260. //唤醒
    / \# d( d" g+ q9 m. D
  261. void W25QXX_WAKEUP(void)   
    ) e/ _) X0 s4 _; X2 x
  262. {  ( X' Z% x9 h0 ^* H, n& ]5 V- r
  263.           W25QXX_CS=0;                            //使能器件   
    % @' J+ B: U5 d6 I( G
  264.     SPI1_ReadWriteByte(W25X_ReleasePowerDown);   //  send W25X_PowerDown command 0xAB   
    # f+ Z8 c8 Q  T/ S8 V
  265.         W25QXX_CS=1;                            //取消片选                   1 c( h# m  Z$ m& ~* {
  266.     delay_us(3);                               //等待TRES1
    4 Y- e; R. Y; [6 z& C% K, ?$ |! [
  267. }   </i></i></i>
复制代码
  1. 3 u6 ~5 x) O' I& \- N* w
  2. W25Q12xx.h:
    + |% H( k) e2 ~% N: k2 H+ I1 k" Q
  3. 3 S9 ]9 y( H$ I  D0 b3 J! ?) G% P
  4. #ifndef __W25QXX_H
    ' ^: r' V% h* K4 A7 k2 \
  5. #define __W25QXX_H                           
    - O3 c# U0 y/ B. i9 [9 f$ H0 ^# `/ b
  6. #include "sys.h"  
    5 v# o: D- j0 D% w
  7. + n& ?* ~2 d9 x" S: M+ n. G$ j
  8. //W25X系列/Q系列芯片列表           
    ( C) O) d: W6 P7 B  @
  9. //W25Q80  ID  0XEF13# m% X# f: ~, M; D7 m0 `# u
  10. //W25Q16  ID  0XEF146 d, J" S# B% E% Y* H$ I( n
  11. //W25Q32  ID  0XEF15
    - V$ m2 v& t. q0 O* p
  12. //W25Q64  ID  0XEF16        : R8 l/ \; r1 x" z# V
  13. //W25Q128 ID  0XEF17        
    & n, r. Y1 J' S  V
  14. #define W25Q80         0XEF13         
    & T0 _9 A% h+ y" c
  15. #define W25Q16         0XEF143 `- M" r. N+ d; J  t/ Z
  16. #define W25Q32         0XEF15$ D# A. C* D2 c/ k. C. z+ k
  17. #define W25Q64         0XEF162 Z$ o( r+ r/ e9 N
  18. #define W25Q128        0XEF17
    * w0 s( b3 K# L/ f% A
  19. 6 }, _% ?+ e' O- Q0 o: s8 t6 C
  20. extern u16 W25QXX_TYPE;                                        //定义W25QXX芯片型号                   ( ~* a$ k9 Y' z% Y7 `
  21. % M* J, j6 D( ?  \. u
  22. #define        W25QXX_CS                 PBout(14)                  //W25QXX的片选信号
    + S! }0 \. Z  Y) I5 q
  23. //
    . N9 Y+ x: h2 z$ P6 N7 s0 x$ a: D" [
  24. //指令表, j# x7 |4 _- M& `* L
  25. #define W25X_WriteEnable                0x06
    7 D; f. ?: H9 I8 @& c5 x
  26. #define W25X_WriteDisable                0x04
    ( s8 Y2 @3 o  |; O/ O
  27. #define W25X_ReadStatusReg                0x05
    ' e2 q9 H  w. M# L8 m
  28. #define W25X_WriteStatusReg                0x01 + {: }' y' f9 @) u1 L5 s, n1 X. O
  29. #define W25X_ReadData                        0x03
    % a! e' X7 l$ e) S
  30. #define W25X_FastReadData                0x0B & i6 z9 ?+ x! y7 K! D
  31. #define W25X_FastReadDual                0x3B
    9 Q" L4 x, ]( z9 r7 J( _3 F
  32. #define W25X_PageProgram                0x02
    0 l  j) e+ C- ]9 C9 Q" X
  33. #define W25X_BlockErase                        0xD8
    ' _/ X' i4 _  ?3 t8 k& \1 \
  34. #define W25X_SectorErase                0x20 & @, m) ]2 v& t2 J1 |
  35. #define W25X_ChipErase                        0xC7 4 o( b8 z) r: M/ T* b! N
  36. #define W25X_PowerDown                        0xB9 ' k! ]* ^& P9 V' C; ]% Z  `' J
  37. #define W25X_ReleasePowerDown        0xAB
      p/ W/ y% `8 H5 U4 E! |0 V
  38. #define W25X_DeviceID                        0xAB + @( W. X, M) h, H/ e5 X
  39. #define W25X_ManufactDeviceID        0x90
    ; X) K4 c% T  Y" E; c# Z/ P, [
  40. #define W25X_JedecDeviceID                0x9F
    1 I4 f1 D: _2 j  j3 g

  41. % J* p5 w3 f1 Q3 k9 c; x
  42. void W25QXX_Init(void);
    " Q& @5 T. C; A* M6 ~* D
  43. u16  W25QXX_ReadID(void);                              //读取FLASH ID
    0 t1 J: S3 X1 X( V6 s) S
  44. u8         W25QXX_ReadSR(void);                        //读取状态寄存器
    , U6 X4 y- A3 o
  45. void W25QXX_Write_SR(u8 sr);                          //写状态寄存器% I" M9 F# w3 D
  46. void W25QXX_Write_Enable(void);                  //写使能
    8 O, ~6 H( k3 y: E, ^7 \
  47. void W25QXX_Write_Disable(void);                //写保护
    $ V7 n2 G3 N( f3 K* n) k. |
  48. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);% n2 K4 p" s. n% o( U. |
  49. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash9 j% d0 ?: t" v! V  Y, e- n. c
  50. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash' T. I6 S# t" [7 `' m
  51. void W25QXX_Erase_Chip(void);                      //整片擦除4 M& R6 T4 d% ]+ `( _+ Z
  52. void W25QXX_Erase_Sector(u32 Dst_Addr);        //扇区擦除  f8 \. f* f, t0 g$ b7 w
  53. void W25QXX_Wait_Busy(void);                   //等待空闲& f% q2 A  b& F6 `% N
  54. void W25QXX_PowerDown(void);                //进入掉电模式
    & U. U( ?1 [  v% l- \0 Y' z
  55. void W25QXX_WAKEUP(void);                                //唤醒
    ; |! S3 V  g  M1 Y4 n) X
  56. / c) W  U$ m4 z+ v; j1 z) }
  57. #endif
    3 w9 c' \$ T6 a3 t9 y' B) b
复制代码
% @% ]' A4 I4 k5 s2 f
main.c:8 c. S' m3 F  k
) ?! ^4 z. K  i# R& o* L- k5 w
  1. main.c:: _( K& H4 q3 \: B" G2 s* |
  2. #include "sys.h"& G% N, ~7 J% S2 w3 A
  3. #include "delay.h"
    " q- _4 d: o' L/ z
  4. #include "usart.h"
    - C. s8 \2 g; S$ g( L, i/ ~/ e1 h. d
  5. #include "led.h"# Y: Q" ?9 j) S
  6. #include "lcd.h") I' \4 d1 q: {
  7. #include "spi.h"- C$ ~/ D+ o. N0 j2 [+ C
  8. #include "w25qxx.h"  A3 z( P  g! n' L: v; c. ?# c
  9. #include "key.h"   
    ( e4 C7 [0 \( f

  10. 0 ]1 E$ L; B. I
  11. //要写入到W25Q16的字符串数组
    : U& i2 i/ }, C: A1 D7 I- c
  12. const u8 TEXT_Buffer[]={"Explorer STM32F4 SPI TEST"};& _5 w9 z4 F5 E$ M+ X
  13. #define SIZE sizeof(TEXT_Buffer)         
      m# a! F" n- o
  14.         
    / T/ p/ \- q4 E3 A7 [! ~
  15. int main(void)
    ' ^- S- S$ l6 C
  16. { " R" G! \! X- I+ T: n1 {
  17.         u8 key;
    2 B) k/ [* o* ?/ f7 P$ R
  18.         u16 i=0;
    : i) j* J# J: U) P2 d; G- Q
  19.         u8 datatemp[SIZE];        $ c( {+ B) b# B* `1 ~# k$ ?+ v
  20.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
    ) Q$ n3 }4 D( l2 X7 }
  21.         delay_init(168);    //初始化延时函数
    5 [( G' O* p" [
  22.         uart_init(115200);        //初始化串口波特率为115200
    / b) q! g. I; h$ L8 C
  23.         LED_Init();                                        //初始化LED ( k! Y; _2 w# i+ B
  24.         KEY_Init();                                 //按键初始化  
    ) s, |' w/ F' J7 Z) T% P
  25.         W25QXX_Init();                        //W25QXX初始化 ; E* m# V$ q' a7 t( ~
  26.                         
    ) G" G9 F! ]. a# k
  27.          while(W25QXX_ReadID()!=W25Q128)        //检测不到W25Q128* ~! C% C- q: f; y
  28.         {
    3 u1 I3 F; v0 I
  29.                 printf("W25Q128 Check Failed!");9 J. v) P" |# ^; g8 ]& C2 H
  30.                 delay_ms(500);
    0 o. z" w/ j3 P/ O( y4 P4 ]7 C9 B
  31.                 printf("Please Check!      ");
    - ^- Q. b8 q1 E5 k
  32.                 delay_ms(500);5 k3 M* p0 {! m' L
  33.                 LED0=!LED0;                //DS0闪烁
    5 H9 Z* K' Q1 M- a" Q4 g* V
  34.         }7 [& S5 C2 t0 C" R* I, P2 z
  35.         : w# L; z3 h. T0 ~
  36.         while(1)! z2 G, N! j2 w9 L
  37.         {+ ?) z2 P- K' s" |
  38.                 key=KEY_Scan(0);) H0 r3 y3 e; S  x1 Q7 B7 s0 z) B
  39.                 if(key==KEY1_PRES)//KEY1按下,写入24C023 @8 S1 f% Y/ Q  T; @- H* k
  40.                 {
    ' d% e) k/ _3 b, H, S
  41.                         printf("Start Write W25Q128...."); 0 _' _1 x/ y$ w+ i
  42.                         //从倒数第100个地址处开始,写入SIZE长度的数据  4 ]; w$ s1 r0 K$ @: v, h8 d) e
  43.                         W25QXX_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);
    5 S5 O2 @. I) ?8 c
  44.                         printf("W25Q128 Write Finished!");  //提示传送完成
    / B6 k* H1 @, O% C# V6 w% f$ Z6 b1 u% m2 _
  45.                 }4 U9 A" {2 h; P9 I/ v& a" @" i
  46.                 if(key==KEY0_PRES)//KEY0按下,读取字符串并显示
    % b% s2 a' M5 g% l
  47.                 {$ j' ~1 U- ?# u
  48.                         printf("Start Read W25Q128.... ");
    1 s  a, y, [" g
  49.                         //从倒数第100个地址处开始,读出SIZE个字节" [, |: s' i  ~3 A3 \4 h# r
  50.                         W25QXX_Read(datatemp,FLASH_SIZE-100,SIZE);                                       
    ! U0 v) ^9 ^  V" C5 [3 V5 Y# r
  51.                         printf("The Data Readed Is: \r\n ");//提示传送完成
    9 `: z' T3 j/ @3 t6 D8 S6 n9 h
  52.                         printf("%s\r\n ",datatemp);
    $ L/ {! D+ R1 _3 {7 W& V5 D9 |
  53.                 }           
    $ v2 D* e) Y$ `/ o
  54.         }             ' ~+ k. x( X7 ~% U' v
  55. }
    4 M& L5 E8 M% R
  56. ! W: h; h( u4 \
复制代码
8 r2 |8 T: s9 ~; Z
  }$ n. s, C# r6 d7 ?4 y
* z4 x, e$ C* h" n
A71_MM@Q(@D3Z`FQYIISTMD.png
1 收藏 1 评论0 发布时间:2022-5-16 11:53

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版