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

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

[复制链接]
STMCU小助手 发布时间:2022-5-16 11:53
一、SPI介绍
( O, h9 j. l. x% j/ r- ]( lSPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。
' U! q8 N  O- N" Q0 \
6 ~0 A" z& _5 S0 w# p正是简单易用的特性,如NRF24L01、VS1053、SD卡等皆集成了这种通信协议
7 S& _. H& h4 {% X$ l! D% P1 w$ m& d) L9 b) C& d4 K& g
_A_6DRLZV3W0~DW}CEZQ7{O.png 0 A7 \& I, U1 b! S+ o  [

& }% _( j& c  J; L7 r; k2 E4 I二、SPI接口框图7 i2 I/ B0 [; `$ t4 L

) e$ P: o) I/ Y1 I
@OFY~~NQ}EZ}HLQL{EB%0ND.png + v$ }+ }4 b' N, x2 t5 S$ P9 `
1 K  }) v. v6 U- ^1 J2 |% G7 _' P
三、SPI优缺点

4 u+ w6 ~: n. t6 Q5 R4 m  fSPI接口是在CPU和外围低速器件之间进行同步串行数据传输,在主器件的移位脉冲下,数据按位传输,低位在前,高位在后,为全双工通信,数据传输速度总体来说比I2C总线要快,速度可达到几Mbps。
0 I) E) F' x: M; _# K
( L6 ~# g( w/ H9 r信号线少,协议简单,相对数据速率高。& J1 v+ X4 I4 U' R$ W/ s

6 i* ~  d+ S( O$ b# a2 I缺点:没有指定的流控制,没有应答机制确认是否接收到数据; }6 G) O0 r8 X# q' P9 y( @3 `$ ?0 R
* \, P& h3 s( I! y
四、SPI工作原理总结
) R! r' }1 w; Y% F" W8 p6 r硬件上为4根线。* `) W/ w* Z7 V9 C4 B7 R* `/ b! g
主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。
- z& v% k& e; e8 Y+ P串行移位寄存器通过MOSI信号线将字节传送给从机,从机也将自己的串行移位寄存器中的内容通过MISO信号线返回给主机。这样,两个移位寄存器中的内容就被交换。* |: H1 r& ^  K" T# v
外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。+ _4 [, \. F2 V( Q5 k" h
多个设备使用SPI的应用举例* R0 e7 H* U7 d  C& j( P
' Y4 T2 S) k6 S; N7 d; @: l" Y! L
(_8GH(KLZ4`~[RSWOUL_B{Y.png % U8 N8 V4 G) B7 p! l2 x
) W, r7 M7 h$ J/ A% m( |/ a
五、时序图3 Y( }( R8 [; }! ]8 B3 C6 _" Y9 ^& t
时序为 SPI_CR1 寄存器中的 LSBFIRST 位复位时的时序
) Z/ [0 ]( e7 U; @
9 d- C/ ?0 D6 |3 }; D. @, ISPI_CPHA的值将会影响SPI_CPOL的值
1 s: A5 E$ s( r5 W" x! A0 k: u6 `% n7 R' z8 u, D
4R1]OS0}$R`DITZTCDX~T~D.png
4 j$ F8 s5 O0 Z" S$ H( U  i- L0 j8 C
六、SPI程序编写过程
7 g0 S5 b1 C+ j9 N2 X+ d+ `
  1. //①使能SPIx和IO口时钟
    8 o$ D0 `. J8 ~0 K( j
  2. RCC_AHBxPeriphClockCmd() / RCC_APBxPeriphClockCmd();
    . f/ }- |: Q- T) z( M
  3. 0 F+ R5 l( X& `( ?( l# T
  4. //②初始化IO口为复用功能; l3 {* E% I6 O$ h. p
  5. void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
    / ?" ?7 g9 H" J/ x

  6. 3 Z( O9 J( P4 ?8 X3 Y( ~8 z2 w
  7. //③设置引脚复用映射:
    9 g" @  }) \2 {: j5 @2 j. b
  8. GPIO_PinAFConfig();
    & q4 c4 k4 G/ ]( I# a# H6 ]7 o
  9. : S+ r3 @0 t, |5 E' D
  10. //②初始化SPIx,设置SPIx工作模式
    0 N" P% {$ U, c- N
  11. void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
    4 K: E8 A( a" z# G

  12. 3 O! r: ?; f8 o# l8 [! _* B
  13. //③使能SPIx
    / t$ G; C: R% u( X. @
  14. void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
    , h2 c# g% C* A3 G
  15. , T! s, |+ T3 p4 z4 R
  16. //④SPI传输数据
    ( h" _# i% G% G: q0 K, f
  17. void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
    , A* r1 P! J9 ?( i8 T! B: U
  18. uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;
      y* q6 d4 |  L( L
  19. ' R2 B. V' I/ a1 c$ C
  20. //⑦查看SPI传输状态2 n: _# Z' N5 `: X- T
  21. SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);
复制代码
  1. SPI.c:) l! ]7 m" c$ ?+ i; ^
  2. #include "spi.h"8 b5 [6 b! s; ^2 m& b2 L

  3.   ]" u* ~6 y5 {5 p, |
  4. //以下是SPI模块的初始化代码,配置成主机模式                                                   4 k: T" n% }7 g% ~/ r
  5. //SPI口初始化
    5 s: g7 T7 G3 C. I6 s- D
  6. //这里针是对SPI1的初始化, j5 e8 n8 C& G3 b. M1 h* c
  7. void SPI1_Init(void)
    + p7 K  v+ h& P3 X/ {" C
  8. {         ' v9 G# J, e3 }$ U  A4 n' O# s
  9.   GPIO_InitTypeDef  GPIO_InitStructure;5 p8 N2 d: C# B: i. `) t
  10.   SPI_InitTypeDef  SPI_InitStructure;% c4 W( e/ _' D4 _: p
  11.         
    ' s# ^& ]/ ^+ ?* z0 _9 e9 u$ o
  12.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
    9 O, a/ g. ?! n5 X
  13.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟
    7 w% Z& f9 s0 Y% H

  14.   |" y) c) k) H3 ]
  15.   //GPIOFB3,4,5初始化设置
    5 L% }6 Z9 I9 y: Q* N
  16.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出        * P9 ~7 O6 p' p
  17.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
    8 f9 q! n. q$ Q! G5 c  [) ^- F
  18.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出9 q, K( [$ S" w  s9 b3 `( R% f* T
  19.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    7 c6 F; \2 o3 W0 f8 t9 Y0 F2 x
  20.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
    & |; Z8 \8 ~$ t# t! |4 L' a% T
  21.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化/ u! ?: x" F6 N+ J9 c. Z% X
  22.         6 Y6 ]3 c% m( o/ `! C
  23.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为 SPI1
    " F9 R1 T/ o; g. I1 V+ E+ W
  24.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为 SPI1
    $ Y& {' Q+ x, m: j* K
  25.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为 SPI1# o/ E4 X6 B% K( I! F3 W
  26. * i# v  T9 _0 }# a! p) V6 Q
  27.         //这里只针对SPI口初始化, k0 [2 _) J& \( q! k9 [
  28.         RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI19 U! z0 ~  O  \9 Y7 m
  29.         RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1$ D6 ]! K8 B7 K% f3 \( T

  30. 6 U) S) j! l  Y% l' l- j
  31.         SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
    $ K$ @: D9 i! e4 I' a* G9 S0 ]$ A
  32.         SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                //设置SPI工作模式:设置为主SPI3 p) h9 k  N1 z9 R
  33.         SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                //设置SPI的数据大小:SPI发送接收8位帧结构
    ! o/ }! q! I, F1 b, t
  34.         SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                //串行同步时钟的空闲状态为高电平* b6 M: {! X( B/ b( q
  35.         SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;        //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
    # a: M1 s9 p' X' B: R, V/ [% |+ P9 d. t
  36.         SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制2 R" d) ^+ c9 u# F
  37.         SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;                //定义波特率预分频的值:波特率预分频值为2566 N* m9 Y# ~  V$ z' S( x
  38.         SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;        //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始, @2 J$ b9 n9 h2 `- {
  39.         SPI_InitStructure.SPI_CRCPolynomial = 7;        //CRC值计算的多项式
    * l- u2 B7 e2 b: Z( f0 S! |# M
  40.         SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
    9 c! f* @  Q" Y

  41. 4 z  |8 e4 O6 b7 C3 w
  42.         SPI_Cmd(SPI1, ENABLE); //使能SPI外设- l) d. ^8 U; c3 _7 n

  43. 1 l! \3 o% M/ d  _0 o
  44.         SPI1_ReadWriteByte(0xff);//启动传输                 
    6 h. Z6 ~. ~- _3 i( o  G
  45. }   8 e: T* m+ {8 y  h  k7 l! {
  46. //SPI1速度设置函数
    0 i1 C, Y1 e, S0 s& x7 u: y8 h7 h
  47. //SPI速度=fAPB2/分频系数
    & N  v5 U; V1 Q
  48. //@ref SPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256  3 `" }2 V! P6 t/ i0 t2 @  W9 R. }
  49. //fAPB2时钟一般为84Mhz:
    2 h8 g7 I* g4 c+ ~
  50. void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)2 x9 A8 h1 P% E6 S; d: g5 r
  51. {2 R: |  c- k' H3 K
  52.   assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性" t" n1 N  m: d/ p
  53.         SPI1->CR1&=0XFFC7;//位3-5清零,用来设置波特率
    ( M  Q# Z& B2 ^/ A- g
  54.         SPI1->CR1|=SPI_BaudRatePrescaler;        //设置SPI1速度 & x) I( X3 E/ Q# r4 X* \! K
  55.         SPI_Cmd(SPI1,ENABLE); //使能SPI1
    1 ?* y7 Z9 @- X+ T! m5 f
  56. }
    ( ?3 C* @4 p% i! {$ s# f8 K% P
  57. //SPI1 读写一个字节
    ( [4 S8 |0 i7 [& R
  58. //TxData:要写入的字节
    " g5 d' E9 H0 p& P6 W9 k, B, r
  59. //返回值:读取到的字节
    6 R, `# M& l0 c6 r$ s# L
  60. u8 SPI1_ReadWriteByte(u8 TxData)! U, a' }2 }: Q3 }- B; K" g
  61. {                                          4 a& S4 D8 Q4 I: y, d
  62. ; u% E# l) Z5 ?: o
  63.   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空  
    0 \7 y# P# g  q' Q* ~* \
  64.         
    , ~$ h2 T. z: z, \' Z# R, I7 N
  65.         SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte  数据
    1 e' q! V5 \. p+ G9 w' V
  66.                
    : |$ x$ Q: a( V
  67.   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一个byte  2 ~5 h3 S1 G, e
  68. / e: H# l% C# ]/ {# c
  69.         return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据          V& l4 a4 [) v: O/ W" w2 j2 q
  70.                      + x! g9 h: ]6 u7 n8 m$ g9 L* I2 ^$ {
  71. }  x  z& }% o% f( B+ C

  72. - t9 [4 H! B9 U' b3 {
复制代码

2 e! S, Y/ E$ }七、W25Q12xx的原理及应用& j$ P5 \7 X- O& z7 a+ |# g
W25Q128将16M的容量分为256个块(Block),每个块大小为64K字节,每个块又分为16个扇区(Sector),每个扇区4K个字节。W25Q128的最小擦除单位为一个扇区,也就是每次必须擦除4K个字节。这样我们需要给W25Q128开辟一个至少4K的缓存区,这样对SRAM要求比较高,要求芯片必须有4K以上SRAM才能很好的操作。2 `+ M( m/ n5 y' S
" r% p* |5 b% D" F( r0 f4 z
W25Q128的擦写周期多达10W次,具有20年的数据保存期限,支持电压为2.7~3.6V,W25Q128支持标准的SPI,还支持双输出/四输出的SPI,最大SPI时钟可以到80Mhz(双输出时相当于160Mhz,四输出时相当于320M),更多的W25Q128的介绍,请参考W25Q128的DATASHEET。9 c. q) c9 e" |: ~" }6 @1 [

  J# C3 |" L. D+ L D%)W27U]]MDR`PRB[FT4~Z1.png
( |: W! W: C! {& [6 Y
+ X7 o5 a. q1 D$ CW25Q12xx可根据原理图查看,使用的是SPI总线通信协议
0 L3 p' v/ [  i  y7 n
* M0 e1 `+ }) x( O% b比如原子的原理图
0 j1 G# }' i: D- u
9 c7 V, @% ^- ^, w$ p 20190611221654679.png + u: b1 U% h8 [- [  |6 F" s

! g5 G* ]7 v9 z) O9 v7.1 分析W25Q128指令, }4 x8 {5 P- {) n" d& Q/ Y
可参考W25Qxx的数据手册,这里列出W25Q128部分指令:
2 S; M) U. E& O7 s1 J+ ~0 m
3 A% @1 X" ^3 c+ S; T9 P 2019091611364032.png
# M# p5 d: S9 p. G7 b2 l, r: D% P9 b& d  ]! t0 e
比如读取设备的ID的指令:0x90000000. a. S# P  Q1 _
9 R! r; c; N4 t
20190916113704620.png
3 p2 K% [$ e2 w1 {( A" |9 U/ a3 R1 r5 }7 [1 ~' v* N; e
K$HWO9ZGFGE$CVLD47Q]_4G.png - H% ^% ^- |3 Q) g7 i& C3 R9 _

1 c) [4 |3 U& \3 a# R# ~$ q WLLL(({{_EVAN[005V791UU.png
" w* D  s8 l3 e& c  e  y% f; p$ k; H* ~& q) V
7.2 擦除扇区:
, }3 [: [  N  w% n: q% t- x5 L+ E) _, m9 O
NZ$~TU098T[KO5)GTQW){UQ.png
& U% N' g0 ]1 B* G! V4 m) y5 {% _$ I( S& L7 Y9 K0 g
7.3 部分常用设备读取指令:

% D. o# i% i# P- @& ]# u) g5 _6 F1 v; `6 R2 s
Q`V(R2ZUHAZ8SAS2[A}1H%2.png
) C9 p  u) W! k7 J7 q& n+ r  f& a/ y) s
每次操作前要使能写操作,且给一个低电平。结束时要给其为高电平,再失能写操作。也就是通过操作片选引脚来确定是否使能或失能。4 Z1 H4 o7 w' p& |
, G# g$ _. \, i
LR6P1TX%{R][_M{CX4]K.png $ f2 e+ @8 g$ A, W
# |7 |/ d- X; K- P8 J' F1 M% r; ]9 ^
  1. W25Q12xx.c:7 q3 n7 ^5 z5 x2 n* w( J
  2. #include "w25qxx.h" 4 q# Z! X4 s! u3 y5 a7 P
  3. #include "spi.h"; \4 U: Z& s! y' U3 Y- i
  4. #include "delay.h"           / U5 i, U5 f8 |: r. F0 n+ J
  5. #include "usart.h"        
    & v2 O& g. T1 |2 Y

  6. * H3 |. B/ h+ w% O8 Y2 [
  7. u16 W25QXX_TYPE=W25Q128;        //默认是W25Q128
    1 [1 d, Z* y! V: T

  8. 2 j' k5 h, x" _+ S2 ~) g' u
  9. //4Kbytes为一个Sector/ v) n. |+ S3 }1 L  u$ [8 e% G
  10. //16个扇区为1个Block+ z! f% ]# G( }3 r" e" y0 n% v# \
  11. //W25Q128
    ' [: {8 i9 a- Y  j
  12. //容量为16M字节,共有128个Block,4096个Sector . j, T: s0 q; B5 @5 {6 ~
  13.                                                                                                          6 s6 ^7 q* U0 W
  14. //初始化SPI FLASH的IO口/ V+ o% K& i+ n, M- k( q
  15. void W25QXX_Init(void)
    ; z. o- \& k% A7 D; Q
  16. { " X, R8 A- ?, ^  [' Q
  17.   GPIO_InitTypeDef  GPIO_InitStructure;( N' H3 Y' U4 F/ Q6 r' U

  18. + Z4 y5 o( P3 F5 V4 g8 g* b
  19.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
    ; k# K; R4 @. F( k1 E2 t, f
  20.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);//使能GPIOG时钟
    7 z- _+ ?/ f: t' w  ]9 \" Q% @

  21. # s0 y7 |4 q7 O) V2 A0 e! b2 u5 d
  22.           //GPIOB14" v) r, p$ U" V2 Z, i# J  {
  23.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;//PB14" z' x, D1 g. W' ^/ P& ^/ B
  24.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出. e5 F' k8 w  m) c( h) Q$ l& i9 n7 z
  25.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
    ! C. Z( d/ p. l% ~
  26.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    + @, q  j* |9 g$ v- v% y4 m$ a
  27.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
    1 P6 z" w2 T# l$ b0 @
  28.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化) Q4 Z! @5 n; G

  29. : [8 g2 ~6 X: ]
  30.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//PG7
    ) H6 R5 y  x' X- _1 T0 e
  31.   GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化+ u9 y  }/ w# P2 I& V( n& D0 I

  32.   T$ V4 ~0 W2 z$ k6 s. v
  33.         GPIO_SetBits(GPIOG,GPIO_Pin_7);//PG7输出1,防止NRF干扰SPI FLASH的通信 + e$ S0 Z7 f, }) b
  34.         W25QXX_CS=1;                        //SPI FLASH不选中/ x1 c( w. |6 Y+ N5 O. o
  35.         SPI1_Init();                                           //初始化SPI7 T7 b. b; W  X$ W# @' X9 G: N
  36.         SPI1_SetSpeed(SPI_BaudRatePrescaler_2);                //设置为42M时钟,高速模式 ' \7 K# V4 [' ^# a1 y6 g" Q/ q
  37.         W25QXX_TYPE=W25QXX_ReadID();        //读取FLASH ID.$ W4 ?; G- _$ Z
  38. }  
    9 J* N4 R# e- |4 `- Z
  39. 9 v+ a1 s6 |1 V* n3 a3 Z# z
  40. //读取W25QXX的状态寄存器
    / B* h& V1 o# w
  41. //BIT7  6   5   4   3   2   1   0* Y; Z1 _9 ]' n( l; ?9 A
  42. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY# C4 k, X9 ]7 ]) e7 w
  43. //SPR:默认0,状态寄存器保护位,配合WP使用
    1 f/ J# }- T+ a6 w  R# w( g
  44. //TB,BP2,BP1,BP0:FLASH区域写保护设置
    + K9 s/ c2 R) m( W* W
  45. //WEL:写使能锁定, W9 e9 V2 D7 ~  E$ @) e' L3 I7 q$ [
  46. //BUSY:忙标记位(1,忙;0,空闲)$ w4 L$ U/ D2 C
  47. //默认:0x00
    % L7 Z0 @2 F  y! g
  48. u8 W25QXX_ReadSR(void)   2 m2 b4 |2 I. A2 l; M
  49. {  - h- V& E  z4 q+ W3 X9 a4 ?
  50.         u8 byte=0;   
    8 b* ~; D  @9 |/ i/ Y" p6 V
  51.         W25QXX_CS=0;                            //使能器件   
    ; P) r! p- ^% z# f5 Y
  52.         SPI1_ReadWriteByte(W25X_ReadStatusReg);    //发送读取状态寄存器命令    / m# T: U3 [. E, ?
  53.         byte=SPI1_ReadWriteByte(0Xff);             //读取一个字节  - G% |3 z8 B9 s4 P3 ?
  54.         W25QXX_CS=1;                            //取消片选     1 h* {3 D+ I  h6 K8 D" B$ R
  55.         return byte;   : U& c. M: n2 S. E; `! X
  56. }
    . y$ x+ d; H5 q. g! e
  57. //写W25QXX状态寄存器- X( z  @( [' U; J; i( l- l9 f
  58. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!% ?7 g. n- e6 R
  59. void W25QXX_Write_SR(u8 sr)   ) l2 L3 L$ o' H6 V4 P1 S7 I
  60. {   ; E' E  F, H; L9 ^' a
  61.         W25QXX_CS=0;                            //使能器件   6 U- _/ \, G1 }
  62.         SPI1_ReadWriteByte(W25X_WriteStatusReg);   //发送写取状态寄存器命令    3 R9 d, ]) ~5 Z) l8 b+ f: R) Q
  63.         SPI1_ReadWriteByte(sr);               //写入一个字节  # _" m* Z4 _/ }
  64.         W25QXX_CS=1;                            //取消片选                   * s& A; q/ W- Q1 Q
  65. }   1 P1 [+ n* L0 V# z& B
  66. //W25QXX写使能        . N3 P! f4 A. I) K# N/ ~' Y( K6 y
  67. //将WEL置位   
    + \) t3 x8 L7 Y/ l( g6 P! F
  68. void W25QXX_Write_Enable(void)   " t4 l; r0 N' u, w, {( t, J
  69. {& }% ]8 E7 b7 o) l+ w/ ?
  70.         W25QXX_CS=0;                            //使能器件   
    ; r  r3 E' Q  r* u
  71.     SPI1_ReadWriteByte(W25X_WriteEnable);      //发送写使能  
    ; o% m4 x" F1 R! q
  72.         W25QXX_CS=1;                            //取消片选                  
    ; {* d) e; N( H* x2 \# C9 }
  73. } 1 V( r) H8 G& s- J7 j
  74. //W25QXX写禁止        # D( U& ^# m6 S9 [0 |
  75. //将WEL清零  
    ( ~" Q' A2 g; s) \1 I
  76. void W25QXX_Write_Disable(void)   : \; o2 a3 P# T& ?
  77. {  
    ' v# {$ j7 B' V' X5 b& f3 r8 L6 Y+ {
  78.         W25QXX_CS=0;                            //使能器件   5 m- p: \* z" Q+ i* P
  79.     SPI1_ReadWriteByte(W25X_WriteDisable);     //发送写禁止指令      ?% c1 s, w7 k6 Z
  80.         W25QXX_CS=1;                            //取消片选                   % @+ J% c# C" ~; G. f
  81. }                 
    . w: p3 B* q2 `- ^& x
  82. //读取芯片ID
    7 k0 v5 X1 ?- x/ w% b$ ?8 w( A% G( ?0 E! O
  83. //返回值如下:                                   
    ( I  U1 ], Y$ ~3 b$ ]+ Q
  84. //0XEF13,表示芯片型号为W25Q80  " l0 X+ e8 G3 |- U
  85. //0XEF14,表示芯片型号为W25Q16    , \3 e' x- }( ?2 d. `$ X+ S+ _# R2 Q
  86. //0XEF15,表示芯片型号为W25Q32  
    , ]5 g  C8 s  j
  87. //0XEF16,表示芯片型号为W25Q64 # C0 Y% L( E! q0 k; B
  88. //0XEF17,表示芯片型号为W25Q128           # u" P( d3 L' q. Z/ c
  89. u16 W25QXX_ReadID(void)3 Q9 l- I. U  t* ?9 f8 \
  90. {) t% P6 q2 t% g: J- j) g7 K0 j
  91.         u16 Temp = 0;         
    % r; g2 t% n& ]/ f+ ]  h
  92.         W25QXX_CS=0;                                    1 D2 p% ^$ b. D3 E$ K/ E3 f
  93.         SPI1_ReadWriteByte(0x90);//发送读取ID命令              S  u3 Y7 e2 r
  94.         SPI1_ReadWriteByte(0x00);             - C$ x, G) W! D6 \$ ?
  95.         SPI1_ReadWriteByte(0x00);             , ]9 K7 d) f4 E
  96.         SPI1_ReadWriteByte(0x00);                                    
    , h$ U8 x- i& Q: Q( B3 d0 `! W
  97.         Temp|=SPI1_ReadWriteByte(0xFF)<<8;  9 o3 C1 ]' H8 v# h/ }0 B+ _
  98.         Temp|=SPI1_ReadWriteByte(0xFF);         
    " E7 G& C8 M/ x* M' F3 {
  99.         W25QXX_CS=1;                                    
    9 l& S1 r% P6 {( Z8 j% n/ V3 ?# \
  100.         return Temp;
    & ]2 u, _! G% ~4 C
  101. }                       0 K8 C6 O3 e; @$ v! I
  102. //读取SPI FLASH  % p' H0 F: \+ ?* {1 |
  103. //在指定地址开始读取指定长度的数据
    9 I1 W7 ~  h$ o4 B0 Q5 ]
  104. //pBuffer:数据存储区! l+ }" e$ O2 Y
  105. //ReadAddr:开始读取的地址(24bit)
    8 h  {+ y' a3 I8 [5 {
  106. //NumByteToRead:要读取的字节数(最大65535)1 p. S( H! X) g( J4 C7 ^  q8 D
  107. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   2 F+ W! x+ W( S4 b) Y' h+ _& [! O6 k
  108. { 8 `7 Z( I, T, c4 Z, I$ X5 H
  109.          u16 i;                                                                                       
    % }$ @! r. e3 u( p4 n2 W$ m" h! I- X; a
  110.         W25QXX_CS=0;                            //使能器件   2 i( V  X( G3 n+ q3 E; C. D
  111.     SPI1_ReadWriteByte(W25X_ReadData);         //发送读取命令   " a7 `4 b: p5 ?% j6 k+ {3 ~
  112.     SPI1_ReadWriteByte((u8)((ReadAddr)>>16));  //发送24bit地址    5 s) E1 ^; G" O5 f
  113.     SPI1_ReadWriteByte((u8)((ReadAddr)>>8));   : a5 e- R; ?/ n$ t) b; T
  114.     SPI1_ReadWriteByte((u8)ReadAddr);   * I% |4 h# a! s
  115.     for(i=0;i<NumByteToRead;i++). {/ t: [  O' \! y/ y# J3 q
  116.         { ) A( ^6 x/ Z! v1 o6 ^0 m5 K% }& B
  117.         pBuffer<i>=SPI1_ReadWriteByte(0XFF);   //循环读数  ! P8 I6 }1 Z& X
  118.     }
    / l% p2 p3 }; l1 b. M4 Y
  119.         W25QXX_CS=1;                                                   
      h* i2 ?6 S* ]' E/ ^0 @
  120. }  
    3 \0 E5 J4 y+ T+ h0 }! X( z
  121. //SPI在一页(0~65535)内写入少于256个字节的数据. N" P0 p0 h9 z4 \' u' n$ F1 D
  122. //在指定地址开始写入最大256字节的数据2 @2 ^8 X8 I5 F1 Q) K7 T9 C
  123. //pBuffer:数据存储区
    9 ]0 L" I+ w; x9 S
  124. //WriteAddr:开始写入的地址(24bit)" S# a# z$ W# Q. y' P
  125. //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!         - A, K1 x1 O3 }3 a! `7 y- y: R
  126. void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)) N8 I2 S. Q6 c: [1 Y9 X
  127. {
    8 L3 C! s9 N. _5 h0 y- ?$ o# i
  128.          u16 i;  
    0 q* [4 x1 \" \( u0 n8 [
  129.     W25QXX_Write_Enable();                  //SET WEL
    6 v$ ?3 l, N% j1 o+ C" S% @9 T
  130.         W25QXX_CS=0;                            //使能器件   
    & v& ^8 U; p6 Z
  131.     SPI1_ReadWriteByte(W25X_PageProgram);      //发送写页命令     a2 f8 O: l5 s  \" ~) h
  132.     SPI1_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址    , G  C% W' f% q! \2 N) @
  133.     SPI1_ReadWriteByte((u8)((WriteAddr)>>8));   0 y9 e2 ^$ U& g& h- c# k
  134.     SPI1_ReadWriteByte((u8)WriteAddr);   
    ) k, n- a* F# A" S' l$ \' \
  135.     for(i=0;i<NumByteToWrite;i++)SPI1_ReadWriteByte(pBuffer<i>);//循环写数  , C- O' H; o/ A# P6 h. e
  136.         W25QXX_CS=1;                            //取消片选   a" t7 s# k. I- P4 g
  137.         W25QXX_Wait_Busy();                                           //等待写入结束
    + P6 F& ~! A, P6 M' i2 n
  138. }
    ' S1 w; L8 o' t# q
  139. //无检验写SPI FLASH 3 @( C% s" v6 j0 }- `
  140. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!* E1 B4 t9 Q9 f! [5 M/ w
  141. //具有自动换页功能
    8 _- W7 S  H1 t/ [1 Z' J
  142. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!
    2 f  W. ~- C9 `$ m
  143. //pBuffer:数据存储区
    4 Y$ i5 h' h: I( a! P
  144. //WriteAddr:开始写入的地址(24bit)  ^2 @/ I2 g/ D
  145. //NumByteToWrite:要写入的字节数(最大65535)1 E8 _1 n% [  {; y1 n
  146. //CHECK OK
    1 R8 t% d" Z, C# u% z' D" D" r5 [
  147. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
    - t/ E& D$ E, l, K3 \) T
  148. {                                          
    8 _7 Y, L  p- o2 e- R& B6 C
  149.         u16 pageremain;           
    4 A6 ]. e& Y' n% @
  150.         pageremain=256-WriteAddr%256; //单页剩余的字节数                             ) D; Q* i; e! `2 R7 t
  151.         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
    4 G' i: d4 c5 L" W' L8 S
  152.         while(1)% \3 E9 L5 o2 P7 k  ?& @8 K. @
  153.         {           2 q0 ^/ U* s' U2 u) Y
  154.                 W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
    # \2 b" k! Z4 k! x. V; @+ p" H& f
  155.                 if(NumByteToWrite==pageremain)break;//写入结束了' l+ x, T0 K  M/ F
  156.                  else //NumByteToWrite>pageremain
    " y' p4 v/ ]5 o  r/ ~& h2 _5 Y
  157.                 {
    # T  a& F" d3 [( b7 l7 |
  158.                         pBuffer+=pageremain;* \6 J; F. Z, c; w1 D
  159.                         WriteAddr+=pageremain;        
    9 L! C$ ~, P# |4 ?+ S- M" g7 K& O

  160. ' [8 m8 {- X; E$ z1 R! Q
  161.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数  X, _5 M. e' \* D, [. }
  162.                         if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节! k: |" e. v4 w0 v; U
  163.                         else pageremain=NumByteToWrite;           //不够256个字节了% n" q% B  O6 `3 y% l  r" w. j
  164.                 }
    : \3 d* g2 Y! q% R/ t3 X
  165.         };            
    8 O4 y0 G$ j! n! {& z$ R
  166. } : N0 T) {# Y. b
  167. //写SPI FLASH  
    $ W1 F) g' }3 c& A# k
  168. //在指定地址开始写入指定长度的数据, _* u7 G9 f, ], U. _
  169. //该函数带擦除操作!
    # B7 p5 d! x) F+ m) ~8 l
  170. //pBuffer:数据存储区( S  {4 b1 j, A# F4 s, D
  171. //WriteAddr:开始写入的地址(24bit)                                                ' D2 u# N( Y' y) D3 a9 W
  172. //NumByteToWrite:要写入的字节数(最大65535)   
    4 T* b) A/ o* ?
  173. u8 W25QXX_BUFFER[4096];                 
    ( P- a8 Q, Z) w* r! s- {
  174. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   9 E3 L' G$ U" p% G
  175. {
    7 e5 y& _. a! R: |, r( O
  176.         u32 secpos;
    7 R- u# A4 `8 Y- k6 Y1 o& g, V
  177.         u16 secoff;
    ) s6 {3 B6 Q& c/ g5 x
  178.         u16 secremain;           
    % Q( U5 O+ o5 N
  179.          u16 i;   
    4 h2 O( h' J* A' K" o$ a
  180.         u8 * W25QXX_BUF;          ; T' V3 D0 z, a& U, C" q
  181.            W25QXX_BUF=W25QXX_BUFFER;            
    , B+ U% ~. ]( d
  182.          secpos=WriteAddr/4096;//扇区地址  8 X7 m, d6 ?! Y; N, @# \
  183.         secoff=WriteAddr%4096;//在扇区内的偏移8 Z" [' h* a3 R3 X: Y3 O
  184.         secremain=4096-secoff;//扇区剩余空间大小   0 A8 z- ?! p( e
  185.          //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用/ g; e3 P4 H9 _0 V- I' [' l: P$ D
  186.          if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节
    ' N$ F$ j) N7 w$ O$ ?' S5 U* T4 q* s
  187.         while(1)
    , v" A2 Z  ~$ D2 u# [9 D9 Z, F
  188.         {        $ q7 _1 R+ x! E) }
  189.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
    0 h/ Y) r+ S6 N% ?% o; Q, \4 d
  190.                 for(i=0;i<secremain;i++)//校验数据
    $ a- y; d* @/ j0 n
  191.                 {
    4 q$ }/ V: N2 W  ?1 V
  192.                         if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除            $ ^! b8 M1 e& S, O& J
  193.                 }9 _+ j% E; E3 J' c% X2 J' g
  194.                 if(i<secremain)//需要擦除$ K& A! t& Q! k9 T. w
  195.                 {" A  z5 m% n+ T# w' w0 E$ Y
  196.                         W25QXX_Erase_Sector(secpos);//擦除这个扇区3 X# H, g. I) j, C9 L% Q" k3 T
  197.                         for(i=0;i<secremain;i++)           //复制
    $ u8 d4 z: k4 V2 o5 ~' r$ Z( {! k
  198.                         {) Q' L* T4 {! J, H* e* C
  199.                                 W25QXX_BUF[i+secoff]=pBuffer<i>;         
    " H5 O3 \& p/ J& A, p
  200.                         }7 c$ ~  Z8 }0 K: E! b, Y
  201.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  
    8 S3 P2 L2 x# z
  202.   u7 H2 N2 c/ F5 w( I0 b0 R4 ?
  203.                 }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    2 f0 k& v; ]6 \
  204.                 if(NumByteToWrite==secremain)break;//写入结束了
    2 x; X3 q$ R2 j, U
  205.                 else//写入未结束" k: W- @4 `" _; {
  206.                 {, v: x3 h2 a+ M. c  ~
  207.                         secpos++;//扇区地址增10 N: h# N/ S" _9 t1 [. Y
  208.                         secoff=0;//偏移位置为0          " [+ {( ?: x* B( H
  209. 5 E! J! x! v3 a. u" |
  210.                            pBuffer+=secremain;  //指针偏移
    / i8 `& N  R: M! m  s
  211.                         WriteAddr+=secremain;//写地址偏移           
    $ |* X4 L) K) ]
  212.                            NumByteToWrite-=secremain;                                //字节数递减
    - a+ `5 r8 e/ j1 i
  213.                         if(NumByteToWrite>4096)secremain=4096;        //下一个扇区还是写不完2 i) C+ h( G+ c. ?. L
  214.                         else secremain=NumByteToWrite;                        //下一个扇区可以写完了
    / Q. j2 v2 T- Z) {
  215.                 }         ' m0 L  N1 n1 C/ x
  216.         };         
    , D. ]# z/ ^% D4 g0 |4 B9 W0 t
  217. }4 r8 e1 b1 c! u
  218. //擦除整个芯片                  ; N( t0 ^1 B9 A* c  |1 N
  219. //等待时间超长...
    . t3 M- V9 w. p6 i8 h0 ]  Z
  220. void W25QXX_Erase_Chip(void)   
    " k& S  y9 y4 o7 A% X
  221. {                                   
    + P7 D) Y$ y2 m
  222.     W25QXX_Write_Enable();                  //SET WEL
    0 G! l' h/ @0 I% e2 Q) v
  223.     W25QXX_Wait_Busy();   / u- x& P3 _# s
  224.           W25QXX_CS=0;                            //使能器件   / [6 ^5 u( ~8 w1 ?8 A2 {  {
  225.     SPI1_ReadWriteByte(W25X_ChipErase);        //发送片擦除命令  
    2 f) ?' C  K6 l% m, k$ |! E
  226.         W25QXX_CS=1;                            //取消片选                   2 D" {! `6 b1 ]$ S. b6 r
  227.         W25QXX_Wait_Busy();                                      //等待芯片擦除结束
      q$ P4 }$ A% y; Z% s+ y$ r
  228. }   ; r+ n! o2 ]1 \
  229. //擦除一个扇区; k3 v) e4 s' |: I! g& H
  230. //Dst_Addr:扇区地址 根据实际容量设置
    # U% s2 s+ N0 E! C, r. z" B1 u
  231. //擦除一个山区的最少时间:150ms$ f- Y" e8 O4 f
  232. void W25QXX_Erase_Sector(u32 Dst_Addr)   
    & V! j$ a0 Q3 b' g
  233. {  * t, m. @. f8 F' i6 a5 H; w
  234.         //监视falsh擦除情况,测试用   - E; m' C" l7 u" V( t2 {
  235.          printf("fe:%x\r\n",Dst_Addr);         
    8 [& {- p6 c8 Q5 m1 K+ F* |
  236.          Dst_Addr*=4096;- U" H- R- C3 T8 s, q
  237.     W25QXX_Write_Enable();                  //SET WEL          , s0 X7 W3 C$ S) l$ Q9 i* m: g
  238.     W25QXX_Wait_Busy();   
    ; ^/ o) t$ K& B9 ~8 T/ w! w# ]
  239.           W25QXX_CS=0;                            //使能器件   
    6 P  i6 T7 u$ I0 P
  240.     SPI1_ReadWriteByte(W25X_SectorErase);      //发送扇区擦除指令
    2 @0 u: h8 [! R/ O0 V1 t
  241.     SPI1_ReadWriteByte((u8)((Dst_Addr)>>16));  //发送24bit地址   
    + R* Z+ Y" v8 L! X3 H) R) u
  242.     SPI1_ReadWriteByte((u8)((Dst_Addr)>>8));   
    ; h  W8 I3 W( x7 H- g# }4 J* |8 @
  243.     SPI1_ReadWriteByte((u8)Dst_Addr);  / U' b  e' E2 g2 ]4 g
  244.         W25QXX_CS=1;                            //取消片选                   6 y9 p& g  K7 E/ ?, c! T' H$ o3 @
  245.     W25QXX_Wait_Busy();                                      //等待擦除完成1 ]  r% E4 \7 I$ l5 J2 I3 G$ n
  246. }  
    6 T% @! o  V2 e$ \3 ?+ ^
  247. //等待空闲
    & J4 L0 N3 t6 v
  248. void W25QXX_Wait_Busy(void)   : Y7 ]- q7 G  a
  249. {   
    - Q2 q5 y7 N. R$ I/ q) J
  250.         while((W25QXX_ReadSR()&0x01)==0x01);   // 等待BUSY位清空1 a/ p, o1 ^& R% _% X) v
  251. }  5 p2 X. `* ~/ V" O3 @2 F/ A' t" W- w
  252. //进入掉电模式
    6 n( B' U0 w' h7 V) c6 L+ u+ ]
  253. void W25QXX_PowerDown(void)   . T8 c3 l, m. Q1 `
  254. { . V; l% J) a. U8 C. [2 m
  255.           W25QXX_CS=0;                            //使能器件   
    . k7 c, i' J; ^& `
  256.     SPI1_ReadWriteByte(W25X_PowerDown);        //发送掉电命令  
    * K  L+ ~* \% l
  257.         W25QXX_CS=1;                            //取消片选                   - N$ c* A* N! a6 J
  258.     delay_us(3);                               //等待TPD  ; E9 g! J7 w' O7 v; J
  259. }   
    " r" K/ i! F4 `( m6 S0 N+ V+ Z% u, [
  260. //唤醒
    + T6 X( Z0 N* }; I  W; L  {8 L  `
  261. void W25QXX_WAKEUP(void)   
    1 y8 j! H) @8 b2 }( v0 H3 T
  262. {  . V" l" E' G+ b- R/ Z
  263.           W25QXX_CS=0;                            //使能器件   
    7 U1 p3 U) H5 X' V3 S5 r3 ^
  264.     SPI1_ReadWriteByte(W25X_ReleasePowerDown);   //  send W25X_PowerDown command 0xAB    + m8 x7 M8 D( j* i9 c
  265.         W25QXX_CS=1;                            //取消片选                  
    * y6 R7 b! _- J# E9 C. [
  266.     delay_us(3);                               //等待TRES1( H6 L. X& F9 M' @
  267. }   </i></i></i>
复制代码
  1. $ `9 w9 c: ]" u# I0 i
  2. W25Q12xx.h:; ?! B7 p( v# i( ~' V& |# _

  3. 8 C% u- F: N- x) m# ~- W
  4. #ifndef __W25QXX_H) M+ I& j+ M# \& n
  5. #define __W25QXX_H                           
    + @1 l9 i# c" O* F4 o
  6. #include "sys.h"  
    , Z8 v$ a% p9 K

  7. 5 k4 Q. H5 k1 ^* @
  8. //W25X系列/Q系列芯片列表           % M: c8 U# B4 C/ ?% |* U1 g# g
  9. //W25Q80  ID  0XEF13
    6 S4 l7 q/ P4 u2 Y& t* X
  10. //W25Q16  ID  0XEF14
    4 \; J/ a, W" e! x$ Q
  11. //W25Q32  ID  0XEF15
    5 o% H; X9 Y6 N/ h: ]* U' i
  12. //W25Q64  ID  0XEF16        2 N, S" W2 v3 L$ S& n  {9 [* h
  13. //W25Q128 ID  0XEF17        
    . o4 u# ^9 i" d0 ^
  14. #define W25Q80         0XEF13         2 F# y& w( ?3 Y
  15. #define W25Q16         0XEF14" ~$ [) y1 J! T6 @$ Q4 n4 Z
  16. #define W25Q32         0XEF15
      Z" ~' s9 P) n9 c  {: }
  17. #define W25Q64         0XEF16; R+ h) o& K* G" @
  18. #define W25Q128        0XEF17: U" P: l0 \* j
  19. 7 h: a5 c, X  l# f" f4 ~
  20. extern u16 W25QXX_TYPE;                                        //定义W25QXX芯片型号                  
    6 z0 V/ N3 X, @+ I5 U

  21. 7 L2 y7 n. }5 c
  22. #define        W25QXX_CS                 PBout(14)                  //W25QXX的片选信号
    # e+ h' Z; g6 o
  23. // ) E; q: P$ Y5 T( Z6 ^; Y/ Q
  24. //指令表7 Q3 T) h  I0 _6 U) m. f2 j6 q: b1 N" j
  25. #define W25X_WriteEnable                0x06
    + e  U7 t5 [$ O) E% G- w
  26. #define W25X_WriteDisable                0x04
    / K/ T) k6 `: o6 J0 Y
  27. #define W25X_ReadStatusReg                0x05 $ T  Z, d3 K( ~) U
  28. #define W25X_WriteStatusReg                0x01 / ^+ b+ D% M/ V7 I# l+ E8 L
  29. #define W25X_ReadData                        0x03 ) _" y! ^- X6 j% @+ d  R
  30. #define W25X_FastReadData                0x0B
    3 ^& z" I- H% z. ]* ^
  31. #define W25X_FastReadDual                0x3B
    $ k! `: V0 z. l+ @3 ~
  32. #define W25X_PageProgram                0x02
    ; ]. x( q6 d) s" z3 w$ S6 F
  33. #define W25X_BlockErase                        0xD8
    0 l: s. r' K2 }# j
  34. #define W25X_SectorErase                0x20
    . d" r. V4 v4 W8 v
  35. #define W25X_ChipErase                        0xC7 , N, `% G5 E$ f* a
  36. #define W25X_PowerDown                        0xB9
    " P; E0 u. T( _: P
  37. #define W25X_ReleasePowerDown        0xAB ' A* k- S+ F! d( u! E- m1 T- ?
  38. #define W25X_DeviceID                        0xAB
    / R& e! Y; f3 s% N; }
  39. #define W25X_ManufactDeviceID        0x90
    ' r, B" K" t" C5 ^7 ~
  40. #define W25X_JedecDeviceID                0x9F
    " E# c! U7 h" g

  41. : J- h0 [( Z- }% X# G
  42. void W25QXX_Init(void);3 k( L( o- e5 s# Q4 ~6 V, \9 Y
  43. u16  W25QXX_ReadID(void);                              //读取FLASH ID" n$ i& _  O  F9 l0 s; p- g" P
  44. u8         W25QXX_ReadSR(void);                        //读取状态寄存器
    0 [# T( ~1 N) c* \# Q1 H6 G, g
  45. void W25QXX_Write_SR(u8 sr);                          //写状态寄存器
    * s7 X; ~% N: E, P5 e
  46. void W25QXX_Write_Enable(void);                  //写使能 / k+ j' x0 K: ~7 [9 K( e
  47. void W25QXX_Write_Disable(void);                //写保护
    ( r' T3 h* l: u/ v% ~) `" J
  48. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
    9 t$ J- M/ G9 G( n
  49. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash3 h  j/ K) @0 z
  50. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash' F9 V' M, Q0 G
  51. void W25QXX_Erase_Chip(void);                      //整片擦除
    2 @3 w. n6 v, I! t9 i
  52. void W25QXX_Erase_Sector(u32 Dst_Addr);        //扇区擦除
    6 ^- e3 t8 E& Q# W7 i0 d
  53. void W25QXX_Wait_Busy(void);                   //等待空闲
    , C& K1 N/ a. l  d
  54. void W25QXX_PowerDown(void);                //进入掉电模式
    9 |5 s; d' M: Z+ n
  55. void W25QXX_WAKEUP(void);                                //唤醒6 \) N, k' c( y0 O' |7 N

  56. ( v2 q8 _. G' M
  57. #endif
    7 s. I/ j7 x  h$ G
复制代码
. I2 Q  Z3 [( G* A: W3 A
main.c:
$ A1 p, g& B( P  y, R( A, P9 n" d) y% x5 N( m$ x
  1. main.c:* @" j' b, G. N1 @
  2. #include "sys.h"& I0 X6 @/ [& C' S+ A: y1 {5 F/ e
  3. #include "delay.h"
    ) s" u: A* O& @# O' v
  4. #include "usart.h"2 V! Z+ G+ d5 Z, O% z% o; H6 Z/ _/ {
  5. #include "led.h"+ x/ l& `! h2 Y' T9 ~. T
  6. #include "lcd.h"% f0 W* _8 R' ?
  7. #include "spi.h"
    & l( r6 Z* w4 p4 [) p7 p
  8. #include "w25qxx.h"
    , P4 `) Z5 K4 e# L, r% {* q8 V
  9. #include "key.h"    ) s' }% \, C& r* L) G% H
  10. % k) a+ v8 m: r+ i$ c
  11. //要写入到W25Q16的字符串数组
    . B/ V8 }) r% t, o# a3 l
  12. const u8 TEXT_Buffer[]={"Explorer STM32F4 SPI TEST"};
    . V1 c- V3 u7 \' d" P5 n$ B* {! a
  13. #define SIZE sizeof(TEXT_Buffer)         4 E) B% m5 m, ?# H; X6 _
  14.         
    6 [  x( p# i) s: Q8 [9 a* ~% @3 Y8 t3 ]
  15. int main(void)
    8 }4 _) Q" Q* R. T
  16. { # A- _8 l: E& y5 V
  17.         u8 key;5 N1 W7 A- M. R* G2 ?9 ~. C& I
  18.         u16 i=0;* d9 z) H" r0 @5 S* z+ [
  19.         u8 datatemp[SIZE];        
    : E  q$ r4 h9 x. v
  20.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组27 f* A% V, C5 w- C+ S
  21.         delay_init(168);    //初始化延时函数  M, k3 g% l5 n4 s3 \! d
  22.         uart_init(115200);        //初始化串口波特率为115200  {5 N" Z7 n5 E! K/ x# T/ M: E
  23.         LED_Init();                                        //初始化LED
    % U7 U/ }6 o+ |  S7 j  X
  24.         KEY_Init();                                 //按键初始化  % ?! l; }- j  @, a8 }6 y
  25.         W25QXX_Init();                        //W25QXX初始化 . Q: s# m1 c- m. _% s4 T5 }5 q1 o
  26.                          3 `4 s3 e3 @$ M5 l& ^
  27.          while(W25QXX_ReadID()!=W25Q128)        //检测不到W25Q128
      r( x9 a. B. w4 d. ~. r
  28.         {: O( `5 F! }. |% N' M8 j: |* [
  29.                 printf("W25Q128 Check Failed!");
    ' N" m3 i0 i, |  b
  30.                 delay_ms(500);1 B1 V, G) c7 g' n/ D7 X
  31.                 printf("Please Check!      ");- i+ [- _5 P0 ~  S8 I: b
  32.                 delay_ms(500);
    6 U, o- D. H1 V  D
  33.                 LED0=!LED0;                //DS0闪烁9 V3 H' ~* g* ^
  34.         }! j* {* p# i  \7 ~8 f
  35.         " F" {1 a* P  v: D3 `2 l& F& g0 c
  36.         while(1)6 d& ]9 f  s) K! X' s0 B
  37.         {
    ; T8 O2 d! K2 O9 @+ r
  38.                 key=KEY_Scan(0);- U7 n- M2 m7 @% U! _2 u
  39.                 if(key==KEY1_PRES)//KEY1按下,写入24C02+ F. t- S* T, q  U2 p" w9 q4 N( q' A
  40.                 {/ ^  i7 R" ~: s5 |( O6 C
  41.                         printf("Start Write W25Q128....");
    5 W& s6 T4 p4 W  X# e4 h8 X: H
  42.                         //从倒数第100个地址处开始,写入SIZE长度的数据  
    ) z. e* L( q3 x1 E$ `
  43.                         W25QXX_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);
    4 J- E( n4 m" B: \1 J& @
  44.                         printf("W25Q128 Write Finished!");  //提示传送完成7 [7 F1 n- o$ G# B0 H/ G
  45.                 }( a0 a  {- n0 l% _5 ?- ?) i
  46.                 if(key==KEY0_PRES)//KEY0按下,读取字符串并显示' n; U' s2 i: x/ j1 ~4 h9 b
  47.                 {
    , ?4 X9 D  x. \: D' H  A
  48.                         printf("Start Read W25Q128.... ");% k# [5 i- l4 j
  49.                         //从倒数第100个地址处开始,读出SIZE个字节7 N4 W6 K, M3 U, U0 {
  50.                         W25QXX_Read(datatemp,FLASH_SIZE-100,SIZE);                                       
    + D) C  Z1 I& f, j3 w1 j, e/ L
  51.                         printf("The Data Readed Is: \r\n ");//提示传送完成
    % d5 v- A/ V2 a8 Z! h9 i3 d! \
  52.                         printf("%s\r\n ",datatemp);/ @/ v: {  }% T0 Z
  53.                 }           
    * W1 L" O. z1 X. j8 E8 V( p& R4 |" y
  54.         }             : |6 z4 ]0 ?5 q+ p  u# ^3 s7 n# k
  55. }% P3 y/ J& _" s9 K1 H
  56. ( Y+ X' \; o  W3 v9 T9 H
复制代码
6 ?+ i1 z3 ?" ^6 V

5 A+ `7 G; p* ]

) V# J! I+ P! I0 L
A71_MM@Q(@D3Z`FQYIISTMD.png
收藏 评论0 发布时间:2022-5-16 11:53

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版