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

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

[复制链接]
STMCU小助手 发布时间:2022-5-16 11:53
一、SPI介绍: P3 ?6 o2 v6 k, A0 [1 A* j
SPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。
7 v% y7 O' ^! l. Z' g, b; s# Q+ j; I% ]& W
正是简单易用的特性,如NRF24L01、VS1053、SD卡等皆集成了这种通信协议
& G4 b) V8 u/ e1 X! l5 f- @
7 [; x6 e% z! P! I, Q0 M _A_6DRLZV3W0~DW}CEZQ7{O.png
8 J9 R6 u8 r5 _0 F6 O
; R, e2 Y4 {5 J9 O$ ]. m二、SPI接口框图
# b/ x6 b" }& ]3 S# M0 T) ?5 d7 P: G0 d* J, U6 S0 ?
@OFY~~NQ}EZ}HLQL{EB%0ND.png ) w4 x* s  `% `+ n4 n

1 E3 L9 F( X5 F8 _5 q* t' Y三、SPI优缺点
' V* ?8 |2 N, K2 ?
SPI接口是在CPU和外围低速器件之间进行同步串行数据传输,在主器件的移位脉冲下,数据按位传输,低位在前,高位在后,为全双工通信,数据传输速度总体来说比I2C总线要快,速度可达到几Mbps。
# G4 X; o2 a8 _7 |" W: \+ T1 z5 C- [0 O2 k2 B/ D1 \7 |; z. V
信号线少,协议简单,相对数据速率高。
3 D  [$ G* g5 i# T4 Q+ V
1 G9 U9 C3 l% i0 k& v5 R" u8 Q缺点:没有指定的流控制,没有应答机制确认是否接收到数据- E, ^( j8 l6 g3 h9 H: P

& H% m! l, [. o* f* X四、SPI工作原理总结
- s5 W" u5 E6 O6 B) J硬件上为4根线。
; B! k; X; P: K3 Y1 e主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。
' o- u. A- e! w4 H) H) U' X; b串行移位寄存器通过MOSI信号线将字节传送给从机,从机也将自己的串行移位寄存器中的内容通过MISO信号线返回给主机。这样,两个移位寄存器中的内容就被交换。
; n% i- ?, P( n+ }( ^% ]5 y外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。
% q: k0 Q, m& s0 L3 u: \# e5 j. d多个设备使用SPI的应用举例: R' K4 b3 @3 E% d( _

. l6 ^" T5 }- l4 q! d$ o/ j5 ] (_8GH(KLZ4`~[RSWOUL_B{Y.png
7 m  I$ f# Y  C  m$ a
) D4 c/ [9 R* v  d五、时序图
7 i( ]7 ]7 K/ `4 }7 I: @时序为 SPI_CR1 寄存器中的 LSBFIRST 位复位时的时序
# c' e& \7 v) T  Y
  U: w0 j* z1 p2 tSPI_CPHA的值将会影响SPI_CPOL的值
9 \: v, t3 ~; Y
# C, `9 X# B$ u 4R1]OS0}$R`DITZTCDX~T~D.png
3 D* a9 q/ W$ e0 b: H- d9 x" |5 |( a: B5 N# E
六、SPI程序编写过程
) }1 W3 i/ Y4 z9 W! Y
  1. //①使能SPIx和IO口时钟
    5 B& i) {5 N/ X
  2. RCC_AHBxPeriphClockCmd() / RCC_APBxPeriphClockCmd();" B; K# J/ v% Q. ~- z+ ~8 @

  3. . h9 p* Z& v# C( |/ T7 k
  4. //②初始化IO口为复用功能$ M: v2 J0 |( |- j( |( v* E! r$ b1 c
  5. void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);1 d2 J& A8 k. ~3 z; M, q$ w
  6. / u( P0 E2 y1 y' s/ e3 X- Z
  7. //③设置引脚复用映射:
    + Z0 r" K+ g2 J" V' x+ z# i
  8. GPIO_PinAFConfig();6 @+ U; P  S" |8 Z

  9. ; s, g9 c3 |+ ^7 l
  10. //②初始化SPIx,设置SPIx工作模式
    / v, Q% u: M* e4 d' j! j* k; P
  11. void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);* w+ e- R  ], B; P
  12. " z1 f3 }3 R# {# M; l$ C
  13. //③使能SPIx
    $ {. M# C0 b% ?1 }8 k, [
  14. void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);. B; T, k# P  }4 [# p" A
  15. 1 n6 v# H3 C$ ^, b' P/ y
  16. //④SPI传输数据; Q6 R0 K' }9 C: s
  17. void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
    : k  `  ]7 R  i: x
  18. uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;2 _6 R9 l) o( p" L0 u

  19. * Z! a1 Z- ?7 `9 N4 P
  20. //⑦查看SPI传输状态
    * s5 ?5 m/ j0 p! L# H, o) z
  21. SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);
复制代码
  1. SPI.c:$ T" W* g) \, ?& i
  2. #include "spi.h"
    / J; h) I+ r3 ]3 d+ u1 ~( w
  3. " ^9 y. n" o& b3 ~, _4 A8 j
  4. //以下是SPI模块的初始化代码,配置成主机模式                                                   
    , z; ?) c' a7 l+ d! K; h) J
  5. //SPI口初始化+ x) z- q( P+ P- O
  6. //这里针是对SPI1的初始化
    3 r* G, z; Y2 \
  7. void SPI1_Init(void)  n9 }5 \8 E; i, b
  8. {         
    , }; l$ ~* I+ ^" O, |
  9.   GPIO_InitTypeDef  GPIO_InitStructure;$ q9 [  F4 S# n. g$ o4 F
  10.   SPI_InitTypeDef  SPI_InitStructure;( Z6 u" k' D1 ?0 C3 A
  11.         ) {" E, J; @$ w
  12.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
    $ z7 B% q+ {' l0 Z" G
  13.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟
    . I" {$ ^  v  S/ \' v

  14. , J( z0 q" O' w! Y. J' P
  15.   //GPIOFB3,4,5初始化设置. K4 Q% L# e5 j5 ]* |. [: k" I
  16.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出        . V" X# T$ s# }6 S
  17.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
    6 A2 @# k( q0 I3 N1 W
  18.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
    - U5 ]$ I8 B4 S6 g
  19.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    $ Q. u' r+ Q. q" O
  20.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉- N3 N" W( X! `
  21.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
    ' |/ ?1 d( M8 s% H' Q
  22.         4 ^( o& t9 R, H5 |
  23.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为 SPI1
    & q$ @; s1 ?0 O5 L
  24.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为 SPI1, ]# E( N$ O1 L$ i6 O9 {) \
  25.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为 SPI1
    # U! w# r5 a, t# V! f9 a  r9 N4 P
  26. 0 r) `1 t6 n5 i1 x; N9 K& a' ]
  27.         //这里只针对SPI口初始化! g2 v2 j! `( _, n! ~+ f( e' U3 r
  28.         RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI1# f+ m- m4 d- |: \& p$ _' M
  29.         RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1. x+ @! L7 ]  R) k% F
  30. , ]) m" u# X: d- L( z) A* i
  31.         SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工: I$ ^" o' B6 e! E& V( I" r/ L
  32.         SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                //设置SPI工作模式:设置为主SPI
    2 t  {+ o4 ^& f! S+ X
  33.         SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                //设置SPI的数据大小:SPI发送接收8位帧结构
    5 _$ b3 G4 v( k) P. L' ]- T( f( \! u
  34.         SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                //串行同步时钟的空闲状态为高电平
    7 h, F& v7 H. e% K! K4 l2 k1 g1 N
  35.         SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;        //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
    / R$ `7 C# A. n  s
  36.         SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制$ z/ a# s8 W0 K0 q
  37.         SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;                //定义波特率预分频的值:波特率预分频值为256
    $ G6 E9 l! m! T  t8 m0 D6 L; X  {
  38.         SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;        //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始% v( E  l8 q2 ^4 V6 i1 K( n+ ^
  39.         SPI_InitStructure.SPI_CRCPolynomial = 7;        //CRC值计算的多项式5 Y2 V5 r9 t' _' Z8 S) ~
  40.         SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器1 ]  j9 E) Z8 P
  41. 9 g- M! m1 B4 r% Z- L
  42.         SPI_Cmd(SPI1, ENABLE); //使能SPI外设
    ! n/ r: t! k+ f  ~" ^2 E/ W
  43. ' Y3 Y  ~5 T" W7 [+ o
  44.         SPI1_ReadWriteByte(0xff);//启动传输                 7 D% {2 Z6 z0 k4 M
  45. }   
    % P8 p& X! D1 W9 N/ F% F7 _) v" U
  46. //SPI1速度设置函数
    / @* [* Q- y' P; F) S6 p
  47. //SPI速度=fAPB2/分频系数+ d3 t* r) C& p: H7 h
  48. //@ref SPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256  
    4 n* Y9 F5 B  x' l% ?/ @1 X
  49. //fAPB2时钟一般为84Mhz:
    2 ~. w2 @7 y0 U2 [0 H; U5 H: h) G
  50. void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)
    : i$ v) i; ?$ N5 |! Q
  51. {
    " i7 y1 F9 \1 v9 p5 a# B' @2 E
  52.   assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性
    / E( G8 `& E6 R4 ^4 @
  53.         SPI1->CR1&=0XFFC7;//位3-5清零,用来设置波特率" j6 k( R8 ?' x" n5 F" N
  54.         SPI1->CR1|=SPI_BaudRatePrescaler;        //设置SPI1速度
    % w# m. q. H. a) U
  55.         SPI_Cmd(SPI1,ENABLE); //使能SPI10 r% \! C: p( b! y( p  Y; N8 r
  56. }
    5 W0 ]* ]' I3 B( \: c
  57. //SPI1 读写一个字节5 H6 `. I+ Q) o7 q7 b6 C
  58. //TxData:要写入的字节  t# k# c3 r3 U( |  _9 }+ S0 U/ P
  59. //返回值:读取到的字节  B6 X' ^2 T& h6 W  {  M0 [1 q
  60. u8 SPI1_ReadWriteByte(u8 TxData)
    . V2 Z& B. n3 Z6 C# |; l; q
  61. {                                          : w- i4 F! \, k! \1 a% P

  62. . X1 x" m2 X2 W2 H6 g3 B. }
  63.   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空  
    ; n8 T4 ?' ~, J1 S* e4 O+ R) Y
  64.         
    : x! q1 p% r( L  \: b; b" v
  65.         SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte  数据
    # s4 B" R, K4 K( @3 }# N
  66.                 $ h( c  ]5 S7 K5 X6 J9 F% E$ w
  67.   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一个byte  ' l/ ?2 X7 [1 R
  68. ; @0 h2 }7 I0 Y0 V
  69.         return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据        
    " y) u- a# D/ M9 P  k5 b
  70.                      4 d5 M" c2 ~( e7 Q' W  }
  71. }% J$ [6 F% T+ c' C
  72. - N  X6 }, j* {3 D+ \
复制代码
; i& K) k% J7 g/ ~/ k- d
七、W25Q12xx的原理及应用- o  y( ]! O8 q4 ^: n- X
W25Q128将16M的容量分为256个块(Block),每个块大小为64K字节,每个块又分为16个扇区(Sector),每个扇区4K个字节。W25Q128的最小擦除单位为一个扇区,也就是每次必须擦除4K个字节。这样我们需要给W25Q128开辟一个至少4K的缓存区,这样对SRAM要求比较高,要求芯片必须有4K以上SRAM才能很好的操作。
" ^# P; Q* G/ k: |' V; h8 O+ q& A+ ~% E( M. H
W25Q128的擦写周期多达10W次,具有20年的数据保存期限,支持电压为2.7~3.6V,W25Q128支持标准的SPI,还支持双输出/四输出的SPI,最大SPI时钟可以到80Mhz(双输出时相当于160Mhz,四输出时相当于320M),更多的W25Q128的介绍,请参考W25Q128的DATASHEET。1 W: R" L6 m5 P% `, B
: h! K4 b( K7 o. O6 s
D%)W27U]]MDR`PRB[FT4~Z1.png 4 |! S: ?7 B+ z

" Z2 [: {$ c& V- gW25Q12xx可根据原理图查看,使用的是SPI总线通信协议
1 U: U/ V# w* H* c$ L' z
6 S# l8 ]5 W! I比如原子的原理图
) j9 }, u6 {. z! O$ K5 y
- _2 h8 n) r/ H5 Z5 z6 H 20190611221654679.png
6 U$ _' z6 S* k- f  _' S5 Y3 X. ]+ z  T0 S& J' b
7.1 分析W25Q128指令/ G3 ^" w* Y1 o
可参考W25Qxx的数据手册,这里列出W25Q128部分指令:' R/ E6 z; O& k; E

( [: C6 |5 u- s' a  f 2019091611364032.png
% v& v, @( O, F* B
$ ?9 A, T/ H; y) r$ O. v比如读取设备的ID的指令:0x90000000% l9 M( h: i' c2 @
) F  {' J- c& \1 t
20190916113704620.png 6 t+ R8 `5 U5 B# N+ [. Z3 r# {7 u3 T
  x& D( k5 h1 E4 J9 Z
K$HWO9ZGFGE$CVLD47Q]_4G.png : G# E* P' B' P

0 Y: l% H  O! j  P  N5 d WLLL(({{_EVAN[005V791UU.png & I7 e- c# H5 ~! Q) r6 l

9 t2 c( x0 L. v5 Y  C: U7.2 擦除扇区:1 C2 v0 E1 ]; q2 O6 _: ?% e

% V, y* }7 X1 }# q7 l, R: R; Q
NZ$~TU098T[KO5)GTQW){UQ.png
1 Y4 E5 f& n- r0 q" b) g9 N2 k" w. j4 h+ {: d( y' t; X
7.3 部分常用设备读取指令:

- q, K; w+ F6 c
( R" ^- m# r1 [1 b9 e% S Q`V(R2ZUHAZ8SAS2[A}1H%2.png
# ~9 ]1 o4 j, r# d# `2 ^: o5 U( r4 A8 Q2 B( M3 @
每次操作前要使能写操作,且给一个低电平。结束时要给其为高电平,再失能写操作。也就是通过操作片选引脚来确定是否使能或失能。) R6 t5 _; \+ f

: j1 ~3 r* @) c5 Y LR6P1TX%{R][_M{CX4]K.png ' v: @2 M4 Q) a8 k8 \+ C

- e% }6 R. k9 T' q
  1. W25Q12xx.c:
    0 I* o' A# M0 t5 F4 s5 ?
  2. #include "w25qxx.h"
    " M/ F- y) a; u1 Z* h7 g" W
  3. #include "spi.h"2 P: g4 }: D, z
  4. #include "delay.h"           9 ?  F/ C7 _7 P7 Q
  5. #include "usart.h"        2 o) R; H* R7 K+ O! R
  6. ' e! \4 V  `. k& I! P4 M
  7. u16 W25QXX_TYPE=W25Q128;        //默认是W25Q128& u  P) P5 u' j( g: Q
  8. 2 I7 ]3 ?4 J3 ]7 Z" z9 S
  9. //4Kbytes为一个Sector
    0 J* {5 b, g$ W% K+ \
  10. //16个扇区为1个Block
    1 f, }& b! @; n6 k* U( s. Y
  11. //W25Q1287 x' J( ~# y' x7 g: }0 j
  12. //容量为16M字节,共有128个Block,4096个Sector $ b) C- J# G. A1 T  n  f6 |
  13.                                                                                                          " o5 }! u5 x+ J% l( \- X
  14. //初始化SPI FLASH的IO口
    / F- Z8 K8 h- h, C
  15. void W25QXX_Init(void)5 g- W  _- ~$ D) b
  16. { , t9 @) A  p2 j  _$ b; K7 u
  17.   GPIO_InitTypeDef  GPIO_InitStructure;
      Y% |: ^2 F2 {: n
  18. $ {! A4 |  s+ [5 q* L
  19.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟8 q- s1 M; y$ S
  20.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);//使能GPIOG时钟
    : k; G+ E- y  H) `/ ^5 n5 C
  21. $ Y7 N: g1 y! v9 b+ M
  22.           //GPIOB146 N( N7 j$ q: C# F
  23.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;//PB14
      Q) `/ J- {: o; p
  24.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出
    2 n0 |& F  ?) v
  25.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出  ?# u/ ~) [/ m0 P9 S. V* H# U
  26.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    . g- e+ e" y/ H
  27.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
    2 r% S+ O# W- t
  28.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化. A6 }  @6 B/ l& ~
  29. 7 `4 O7 W! J' \; D0 c/ O% g
  30.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//PG7
    ) @  ]/ }5 g2 O4 L
  31.   GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化
    ) w  k' J) z/ c/ Y" C1 C

  32. ' \# V; i3 M  i9 w
  33.         GPIO_SetBits(GPIOG,GPIO_Pin_7);//PG7输出1,防止NRF干扰SPI FLASH的通信 - P6 x6 {6 V% h3 A1 |! J
  34.         W25QXX_CS=1;                        //SPI FLASH不选中  o+ G5 B! f; P4 [  I. [
  35.         SPI1_Init();                                           //初始化SPI* l5 a' ^0 ^6 p$ a
  36.         SPI1_SetSpeed(SPI_BaudRatePrescaler_2);                //设置为42M时钟,高速模式 $ [: c- @8 Y& j  ]% O0 a: P& f
  37.         W25QXX_TYPE=W25QXX_ReadID();        //读取FLASH ID.
      i1 n& X9 F& W6 X
  38. }  5 Y) X; }# M0 [5 p

  39. 1 l- n5 a  g4 f  U* u4 r9 G3 B
  40. //读取W25QXX的状态寄存器: `7 X. U; X; Q/ t: p$ q0 w
  41. //BIT7  6   5   4   3   2   1   07 z+ S- y7 c, n8 L) m
  42. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY) [1 H5 K! Z- j. F, a+ k
  43. //SPR:默认0,状态寄存器保护位,配合WP使用
    / r/ b' T8 }- S
  44. //TB,BP2,BP1,BP0:FLASH区域写保护设置
    4 o" h5 w2 }, r( d# ?9 b* P& j
  45. //WEL:写使能锁定! ^$ d+ F4 L% G9 n
  46. //BUSY:忙标记位(1,忙;0,空闲)
    ; u* I* I' Y5 C4 w: M
  47. //默认:0x000 ~6 [, f8 D6 M; B
  48. u8 W25QXX_ReadSR(void)   
    4 p5 Q( M* r* D  n/ K4 q# W; G# {( D
  49. {  
    2 M( b4 A$ r. q& M8 _/ I
  50.         u8 byte=0;   : o& t& Q" W* u1 m6 I9 D
  51.         W25QXX_CS=0;                            //使能器件   
    8 _. _# i+ C  W6 _7 h/ t
  52.         SPI1_ReadWriteByte(W25X_ReadStatusReg);    //发送读取状态寄存器命令    + t, N: {' H' T" E. U" t9 V/ i
  53.         byte=SPI1_ReadWriteByte(0Xff);             //读取一个字节  
    8 v& Z5 c! b  k2 A. f( O+ U0 U
  54.         W25QXX_CS=1;                            //取消片选     2 Z4 z: j. c& i9 @. o/ i' m
  55.         return byte;   # Z, o# I# u! u9 z- f* ~8 D
  56. }
    ! J9 F- o" x, _' X. F
  57. //写W25QXX状态寄存器
    2 E: {' k  i  ~% |
  58. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
    / M9 E1 r9 `8 I/ H
  59. void W25QXX_Write_SR(u8 sr)   3 G. I6 f0 S+ ~% X# s+ h
  60. {   
    6 [% y# M& Z+ B
  61.         W25QXX_CS=0;                            //使能器件   
    - u* z' v" D# O2 H
  62.         SPI1_ReadWriteByte(W25X_WriteStatusReg);   //发送写取状态寄存器命令    ' V& `3 e, ]; V( y. A
  63.         SPI1_ReadWriteByte(sr);               //写入一个字节  
    6 V0 o5 i9 d1 y0 B7 d( N
  64.         W25QXX_CS=1;                            //取消片选                  
    - \% l6 F# |; s  f- }7 _6 t
  65. }   
    ; A6 @3 Q# h6 B
  66. //W25QXX写使能        
    - Z( Y4 X/ b& _" J( X1 i
  67. //将WEL置位   " |" m* W' f) W- c7 c
  68. void W25QXX_Write_Enable(void)   
    , \* [9 h, s5 ^6 U5 K
  69. {8 ?+ h+ ?5 n3 @& N/ U: ?/ w" g
  70.         W25QXX_CS=0;                            //使能器件   0 n* n5 j) \% M: }0 l0 N7 V2 }
  71.     SPI1_ReadWriteByte(W25X_WriteEnable);      //发送写使能  
    # h2 d* c: ?4 }2 I9 [3 F+ ^
  72.         W25QXX_CS=1;                            //取消片选                  
    + ]. w* S8 ]  a4 A+ H( a7 v' F
  73. }
    1 I4 y+ z7 J) X) A; v& C
  74. //W25QXX写禁止        & Q1 k; ^2 D& d: k
  75. //将WEL清零  
    / q4 [" e- u7 x5 Q: V: d* m+ n
  76. void W25QXX_Write_Disable(void)     x% _6 {# b5 U. C5 M) b
  77. {  
    " q7 h* S! S7 V( Y' Y' v9 \
  78.         W25QXX_CS=0;                            //使能器件   2 P& Q7 z; l  z( B  }4 m
  79.     SPI1_ReadWriteByte(W25X_WriteDisable);     //发送写禁止指令   
    5 t  w7 P9 |. ]) j2 y7 e3 x
  80.         W25QXX_CS=1;                            //取消片选                  
    , \1 S3 q' r7 Z6 N! V
  81. }                 ( {9 z! o% ?/ q2 A
  82. //读取芯片ID
    ) s$ d: ]9 a' m. b5 e. d6 W
  83. //返回值如下:                                   
    * N/ f2 k' {+ `2 H' u
  84. //0XEF13,表示芯片型号为W25Q80  
    % r; b  \8 a" |7 l6 Z  b+ o) k9 R) w
  85. //0XEF14,表示芯片型号为W25Q16   
    , P( O! H' a5 D) I
  86. //0XEF15,表示芯片型号为W25Q32  
    1 V9 h4 {) O& x7 v
  87. //0XEF16,表示芯片型号为W25Q64
    ; j0 M5 \$ @. ^+ D. o) R) w, f- V% j
  88. //0XEF17,表示芯片型号为W25Q128           
    , ~# W$ Q: t7 M* F; _. j  Z
  89. u16 W25QXX_ReadID(void); ~& p- G5 l5 R( n7 q
  90. {
    : m* z3 C/ f/ b! j
  91.         u16 Temp = 0;         
    7 S7 e! s+ D& ?' }6 k# `
  92.         W25QXX_CS=0;                                    1 d5 m7 m* H! d) l
  93.         SPI1_ReadWriteByte(0x90);//发送读取ID命令            
    5 E" H$ c& S% X0 F3 U: `
  94.         SPI1_ReadWriteByte(0x00);            
    8 h: i0 t# U* e  C; }
  95.         SPI1_ReadWriteByte(0x00);            
    3 t; U+ w" _# W! t
  96.         SPI1_ReadWriteByte(0x00);                                    
    2 Y5 K9 n4 A: \9 o' r
  97.         Temp|=SPI1_ReadWriteByte(0xFF)<<8;  0 ]" F$ B6 A' U5 Z% y
  98.         Temp|=SPI1_ReadWriteByte(0xFF);         6 S2 p) \) [- X2 g  \
  99.         W25QXX_CS=1;                                    
    5 G9 N0 U5 G& ~
  100.         return Temp;
    - c! y. T! a0 R3 L
  101. }                       % T6 n" Z' f" ~- l- N
  102. //读取SPI FLASH  
    ' Y7 j* u7 c; w- p/ s. U# V7 n
  103. //在指定地址开始读取指定长度的数据/ T  U/ J1 d6 e# {+ G! _% N
  104. //pBuffer:数据存储区
    $ c" c1 I/ s) ~3 _5 `7 w
  105. //ReadAddr:开始读取的地址(24bit)
    , }5 a; Q' |  }
  106. //NumByteToRead:要读取的字节数(最大65535). T0 i6 j  y8 n, {
  107. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   
    ; ~% J$ U* W1 Y4 M
  108. {
    5 N& x5 m  ^) \0 B9 |* N
  109.          u16 i;                                                                                       
    ' d+ ~- r: Q8 O2 y( h/ d3 D: k8 R" b
  110.         W25QXX_CS=0;                            //使能器件   
    # p$ f1 n8 N9 q' y  T2 `
  111.     SPI1_ReadWriteByte(W25X_ReadData);         //发送读取命令   
    ! K# j7 B9 |5 i+ H/ j( |# X
  112.     SPI1_ReadWriteByte((u8)((ReadAddr)>>16));  //发送24bit地址    4 e. S) i* h1 E9 b& t; F9 h7 w
  113.     SPI1_ReadWriteByte((u8)((ReadAddr)>>8));   # X! q! v4 O: r! Q1 j9 |
  114.     SPI1_ReadWriteByte((u8)ReadAddr);   
    ) s! M% z1 l( x; s0 [7 V" k
  115.     for(i=0;i<NumByteToRead;i++)
    8 u. m: O: w) D; g
  116.         { " V$ g3 g; D* h- r/ @: o/ v- u+ r
  117.         pBuffer<i>=SPI1_ReadWriteByte(0XFF);   //循环读数  
    * M" C  M9 _- A* z8 P
  118.     }' H6 u2 N1 J, [: q1 m& D: I9 W  \8 A
  119.         W25QXX_CS=1;                                                   
    . _- K' J% L+ m7 b% S; k! ]
  120. }  . O+ }+ w+ ^; w- L1 b
  121. //SPI在一页(0~65535)内写入少于256个字节的数据1 u& m; x4 c9 y" s) N" K! t
  122. //在指定地址开始写入最大256字节的数据
    ; z/ n: _! B5 K# K) c
  123. //pBuffer:数据存储区
    3 G( k$ k- d, {6 @2 B7 v
  124. //WriteAddr:开始写入的地址(24bit)0 }! X$ G5 y' X7 U$ G% S  [' o
  125. //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!         
    ' K" r8 ^5 p& x. r$ z
  126. void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)4 g8 n! p& y1 T2 a5 e  T, \# O
  127. {
    . J9 c  K$ U# T/ u3 k
  128.          u16 i;  
    ! _. _. C- L8 d! L+ S
  129.     W25QXX_Write_Enable();                  //SET WEL 9 Z. u  X2 c' b; t, z9 ?
  130.         W25QXX_CS=0;                            //使能器件   
      _* v* @6 y2 i+ w: q* L/ P1 L
  131.     SPI1_ReadWriteByte(W25X_PageProgram);      //发送写页命令   
    ; M+ ?% ?2 |3 E+ E8 c/ ~1 X
  132.     SPI1_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址   
    ! S7 t1 U  B7 n
  133.     SPI1_ReadWriteByte((u8)((WriteAddr)>>8));   / E! }2 X+ v) M+ j' ]
  134.     SPI1_ReadWriteByte((u8)WriteAddr);   ! ^( n4 h- n$ C& m
  135.     for(i=0;i<NumByteToWrite;i++)SPI1_ReadWriteByte(pBuffer<i>);//循环写数  
    " \% N: r" G: n/ a3 `
  136.         W25QXX_CS=1;                            //取消片选
    8 ]& t. G6 A' @$ L: F
  137.         W25QXX_Wait_Busy();                                           //等待写入结束
    ) a9 I! W8 w& H' m, E
  138. }
    $ V% u# d$ `$ w1 T  W2 I% S' }9 {
  139. //无检验写SPI FLASH 3 v& g) _/ ^% A: {
  140. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!0 Q, t+ u$ X: q! c% |1 ^' ~5 d
  141. //具有自动换页功能
    $ u. G9 h! o! B# M! |  h, i1 g$ Q. N
  142. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!$ M+ l/ G, D3 k4 E, h
  143. //pBuffer:数据存储区7 e  e. d: {& d7 u' U8 E, i" X
  144. //WriteAddr:开始写入的地址(24bit)
    1 ^8 x+ X$ i: L5 y4 K. V2 I
  145. //NumByteToWrite:要写入的字节数(最大65535)
    8 \: C1 v1 `9 c3 e1 X7 Q' i% ]
  146. //CHECK OK( D. g" T5 l( m' Y6 M
  147. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
    ( W- n, a& R& _! [
  148. {                                          
    6 m  f" L) M. j2 {% V
  149.         u16 pageremain;           
      c/ G: W" }2 ?1 E
  150.         pageremain=256-WriteAddr%256; //单页剩余的字节数                             + k" L: b" d% W! W" s. U
  151.         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
    0 t  N; ?9 A+ l! \* C  N+ A9 Y
  152.         while(1)# N9 ~9 r  W4 @7 W* y+ ^
  153.         {           
    / ]% L# I7 H  N4 T( Y
  154.                 W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
    : K  h# C2 E: d: y9 ~' t
  155.                 if(NumByteToWrite==pageremain)break;//写入结束了! Y( r' `4 x3 |( P* h
  156.                  else //NumByteToWrite>pageremain5 R* \& [2 U7 m% h. {; `% E7 F
  157.                 {
    ' D# S4 y; _, y% k
  158.                         pBuffer+=pageremain;
    9 G: R" p9 _& v
  159.                         WriteAddr+=pageremain;        / I+ b+ ]: a) o! }8 C

  160. 9 q8 N+ ^  y; s3 ?3 T9 t2 u1 s5 V' B
  161.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数2 u1 T& n" \# L
  162.                         if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
    + U: @( |* N2 \+ N8 `/ k' v# k
  163.                         else pageremain=NumByteToWrite;           //不够256个字节了
    : |; z8 X+ U! x' I$ T
  164.                 }( C! Z4 Z" B1 s6 r% K
  165.         };            1 @- B/ g5 K* c: S3 ^+ p0 o) v
  166. }
    6 l% ?* I/ \0 ]: q8 U" E% f
  167. //写SPI FLASH  : u' j: N$ B. W  P, e
  168. //在指定地址开始写入指定长度的数据: c. [: o3 d' Q9 Z5 r" |! Z
  169. //该函数带擦除操作!
    / q( W" ^  \4 {0 E) A: |$ z: ^- }
  170. //pBuffer:数据存储区& M! |2 W- n9 R
  171. //WriteAddr:开始写入的地址(24bit)                                                , _; p- Z, k- P& U
  172. //NumByteToWrite:要写入的字节数(最大65535)   6 A. p( R$ M" Y( s$ `  H# p
  173. u8 W25QXX_BUFFER[4096];                 
    ) c2 Y9 f7 _; w6 S* e4 h  V' R! @; o
  174. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   9 }* |6 U1 M) g) L& w& e5 g
  175. {
    ! G; v( o( h: ~# }
  176.         u32 secpos;
    , ?; x5 [! f$ P- q( V  Y' d/ L6 t) D8 C+ m
  177.         u16 secoff;
    . h9 c8 n1 q+ N# I
  178.         u16 secremain;           - d# H2 G, V! h* i" e# v$ G
  179.          u16 i;    9 u( ?! b+ ^5 j  F5 [
  180.         u8 * W25QXX_BUF;         
    ( `. k4 S. ~; }: o
  181.            W25QXX_BUF=W25QXX_BUFFER;             % Y( ^4 d$ k# |' W
  182.          secpos=WriteAddr/4096;//扇区地址  
    ; r5 a! l1 P# P* R+ o
  183.         secoff=WriteAddr%4096;//在扇区内的偏移
    : _/ s, x8 R  f5 N
  184.         secremain=4096-secoff;//扇区剩余空间大小   - G7 m4 U0 u( U. m: x7 v3 X( C
  185.          //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
    : T5 L: K* l/ T' H( E
  186.          if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节
    * w/ J" d: u( X# R8 s, V
  187.         while(1)
    4 ?) u6 {9 Z+ b
  188.         {        " I: P% P9 y2 E5 Q+ ?3 z: m
  189.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
    5 x: z% ~7 h9 p: m1 v0 `6 c
  190.                 for(i=0;i<secremain;i++)//校验数据
    . G* J) t) M1 L  C) p! p! P0 }/ x
  191.                 {
    - n7 H9 ?( ^* Q0 ^- ]
  192.                         if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除            7 W2 X6 A3 O1 s# _$ v( B
  193.                 }
    9 ]$ ^* Z( Z  B: L7 {. s
  194.                 if(i<secremain)//需要擦除# U8 F8 O: n  `5 Q9 m- x# z" x
  195.                 {
    ' s' g$ l6 c+ u  w
  196.                         W25QXX_Erase_Sector(secpos);//擦除这个扇区  t3 W/ b3 j3 [* {
  197.                         for(i=0;i<secremain;i++)           //复制$ ?- z' C5 R; p# O; V1 F+ S
  198.                         {8 P% C" j- E% p: v0 U% g
  199.                                 W25QXX_BUF[i+secoff]=pBuffer<i>;         
    - K, b8 K8 [1 W' D2 U( e7 w8 w8 R7 C& u
  200.                         }
    1 h* L& Q2 G7 p) }9 R) n% }
  201.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  
    - q; m+ f+ I) R4 Q; K
  202. ! ]% J5 C- \2 a( n. b
  203.                 }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    $ E- F& A; S" x# z
  204.                 if(NumByteToWrite==secremain)break;//写入结束了
    3 f6 M& `; Y( e
  205.                 else//写入未结束2 ]$ A( ^" X; o
  206.                 {
    ! x) j2 K. `9 D! H
  207.                         secpos++;//扇区地址增1
    ( i6 ?. ~9 I2 t) f; u2 s& I$ q
  208.                         secoff=0;//偏移位置为0         
    9 H6 m6 g! n& l, d2 I
  209. # t) N' v4 z! |- ^& d* o
  210.                            pBuffer+=secremain;  //指针偏移) {6 V5 ?# ]0 R, u. y- z" S
  211.                         WriteAddr+=secremain;//写地址偏移           
    1 {3 I2 d8 l6 }9 `8 W2 t4 C
  212.                            NumByteToWrite-=secremain;                                //字节数递减& m; E( l0 v. Y2 \  m1 d
  213.                         if(NumByteToWrite>4096)secremain=4096;        //下一个扇区还是写不完
    % P8 T* L* w9 e1 _0 p+ P: \7 U
  214.                         else secremain=NumByteToWrite;                        //下一个扇区可以写完了* O+ |7 ?! k; N, ]3 q* G
  215.                 }         
    : x7 k  o6 k* d# a1 u
  216.         };         
    * F+ }. Y) c) e' a0 Z
  217. }/ }8 C/ I; R- S. c2 @
  218. //擦除整个芯片                  0 e9 [" E# _/ N( k# Y
  219. //等待时间超长...# q' t! ]% A3 J+ _7 h. b3 {5 |
  220. void W25QXX_Erase_Chip(void)   5 o& k/ I: n, S
  221. {                                   
    . _& y, r, S/ I: K, t
  222.     W25QXX_Write_Enable();                  //SET WEL % B% m" L5 D3 }" B/ ^' |$ _$ U
  223.     W25QXX_Wait_Busy();   
    - R$ t& B; N% Q9 d% H( s* M* }+ L
  224.           W25QXX_CS=0;                            //使能器件   
    7 _, g* `8 {- {' {' E
  225.     SPI1_ReadWriteByte(W25X_ChipErase);        //发送片擦除命令  ; ^  B7 |2 b8 N3 \
  226.         W25QXX_CS=1;                            //取消片选                  
    , c1 ]; Q- _- V7 `4 g
  227.         W25QXX_Wait_Busy();                                      //等待芯片擦除结束7 W% U7 D+ ?, k" W9 N
  228. }   " p& e. g/ W0 ~' {: E9 P
  229. //擦除一个扇区
    3 \6 Y1 ^4 L. L' W3 [
  230. //Dst_Addr:扇区地址 根据实际容量设置+ L6 u" a, `) F; ?; G
  231. //擦除一个山区的最少时间:150ms6 S% Q; y5 K7 K
  232. void W25QXX_Erase_Sector(u32 Dst_Addr)   6 s9 J) v1 J2 f! d8 W7 J
  233. {  ; t1 z' a6 X! t- {1 a. X0 ~
  234.         //监视falsh擦除情况,测试用   
    ( Q: L4 L: l  n2 p9 Z
  235.          printf("fe:%x\r\n",Dst_Addr);         
    7 C0 f4 }) V8 @# J, t
  236.          Dst_Addr*=4096;
    ! {& h8 j3 ?7 D: d! ~
  237.     W25QXX_Write_Enable();                  //SET WEL          + a1 M! o; g$ E4 C. n% G
  238.     W25QXX_Wait_Busy();   " v, U7 s7 k, G/ G
  239.           W25QXX_CS=0;                            //使能器件   
    % Z% d; a7 y3 V$ }
  240.     SPI1_ReadWriteByte(W25X_SectorErase);      //发送扇区擦除指令
    3 J8 T9 h& g- D# w3 a: Q' `2 e8 I
  241.     SPI1_ReadWriteByte((u8)((Dst_Addr)>>16));  //发送24bit地址    9 m& u7 A4 |( H9 b  F& ~  I  {; k
  242.     SPI1_ReadWriteByte((u8)((Dst_Addr)>>8));   % v7 Y& S5 V. k
  243.     SPI1_ReadWriteByte((u8)Dst_Addr);  7 a1 A3 Q* r" U$ O
  244.         W25QXX_CS=1;                            //取消片选                  
    2 A( e+ C! M. M4 S
  245.     W25QXX_Wait_Busy();                                      //等待擦除完成
    $ V  D9 s# z) L% y
  246. }  
    ( K# w% _7 [1 C9 h
  247. //等待空闲
    8 m4 ^8 g1 R+ e! I
  248. void W25QXX_Wait_Busy(void)   9 _2 X$ k' Q! e- w& P+ @5 ~
  249. {   
    0 x& ?/ f" p0 x9 B% \1 D, s
  250.         while((W25QXX_ReadSR()&0x01)==0x01);   // 等待BUSY位清空3 {+ ^1 Y9 w7 B
  251. }  ! m3 v2 l+ Y5 ?! @
  252. //进入掉电模式
    / V3 a& h* G% i# W
  253. void W25QXX_PowerDown(void)   
    ! y% A" q9 Y. d9 d, E& |
  254. { % H" r+ B! {% u. {2 P
  255.           W25QXX_CS=0;                            //使能器件   
    6 Y6 ?9 G  I; g/ C$ c) F0 q
  256.     SPI1_ReadWriteByte(W25X_PowerDown);        //发送掉电命令  
    2 c$ u3 z8 H5 v; j: Y3 F
  257.         W25QXX_CS=1;                            //取消片选                   # W. z% L) L! U2 R
  258.     delay_us(3);                               //等待TPD  " v2 x4 X9 }4 w7 y9 ~& ]. p  p  a
  259. }   
    9 I3 h7 _3 `& x# S% j1 Z6 ]- `9 P. A
  260. //唤醒1 |  C; K: `; @$ m& O
  261. void W25QXX_WAKEUP(void)   0 {0 [2 A3 m1 j- E8 \, s3 r' S4 G
  262. {  # B& Z9 _  c+ j) X: L& t6 R/ v
  263.           W25QXX_CS=0;                            //使能器件   
    & [/ x6 w5 _& F1 M
  264.     SPI1_ReadWriteByte(W25X_ReleasePowerDown);   //  send W25X_PowerDown command 0xAB    5 H- k) `: [5 T7 E" i& F3 h+ n
  265.         W25QXX_CS=1;                            //取消片选                  
    : R7 _: R' a  d7 z1 j
  266.     delay_us(3);                               //等待TRES1* b/ Y# o" B: l* Z
  267. }   </i></i></i>
复制代码
  1. / Z2 R5 ]" h1 ~( s) j
  2. W25Q12xx.h:' S/ |4 t% g% v  D2 E& b! e

  3. 9 O  d7 r6 q& o% s. E; Z& @9 ?9 R
  4. #ifndef __W25QXX_H' _3 _1 l% @- ]& o4 A, K( b# D
  5. #define __W25QXX_H                           
      D. u& D3 K+ h% H) Y. `  M6 t
  6. #include "sys.h"  4 x# ~0 ^# S4 e

  7. + C+ S, c/ a+ R5 @  b
  8. //W25X系列/Q系列芯片列表           
    + l0 c2 _! A- |# N4 Y& |
  9. //W25Q80  ID  0XEF13% R9 h- l' S3 f6 ^
  10. //W25Q16  ID  0XEF14: K$ U$ r3 P# ]3 Q( b1 [
  11. //W25Q32  ID  0XEF15. Y2 N8 S2 ?! `- w7 Q
  12. //W25Q64  ID  0XEF16        
    ' u- M# B& z8 Z* w1 \6 b
  13. //W25Q128 ID  0XEF17        
    ! Q/ U0 D! a4 Z8 f9 H5 K4 Z
  14. #define W25Q80         0XEF13         ! r, J8 D* s& y1 n/ c( o! y" Y! q& v
  15. #define W25Q16         0XEF14  u) L/ u6 |  q
  16. #define W25Q32         0XEF15! }! B; D6 _. k8 g1 a  H
  17. #define W25Q64         0XEF16
    ( w2 ]+ U# d3 Y+ X3 A
  18. #define W25Q128        0XEF17
    % S; p+ O* F7 p+ `  z: |

  19. $ E3 I. D: W( h+ |
  20. extern u16 W25QXX_TYPE;                                        //定义W25QXX芯片型号                   6 k( E8 w) U- ^% v

  21. 1 N' }' Z1 b1 ]6 [
  22. #define        W25QXX_CS                 PBout(14)                  //W25QXX的片选信号
    9 D- Q7 f3 `8 v
  23. //
    : {. Q  ?( V! I
  24. //指令表
    3 k/ \1 e- M" y& f9 Q5 d
  25. #define W25X_WriteEnable                0x06 1 x9 r7 Q% @) _# J9 s5 ~' X( s
  26. #define W25X_WriteDisable                0x04
    / x9 k  z! m1 c8 h% T5 W
  27. #define W25X_ReadStatusReg                0x05 0 Y$ L- D' H8 g
  28. #define W25X_WriteStatusReg                0x01 % T$ X6 X3 M+ F8 S& D
  29. #define W25X_ReadData                        0x03   ~) x; ?8 O- r( p2 G+ A3 g2 b
  30. #define W25X_FastReadData                0x0B
    : T& l9 ]- \+ i% A; R& ?9 Q
  31. #define W25X_FastReadDual                0x3B ' w8 d% a% b. t' O. q
  32. #define W25X_PageProgram                0x02
    7 y  \( m' V3 |/ M" I+ {: F
  33. #define W25X_BlockErase                        0xD8 ' @. U7 Y1 L/ l! J& e6 y
  34. #define W25X_SectorErase                0x20 , N7 x3 {5 L1 \  j1 y: e% n4 O
  35. #define W25X_ChipErase                        0xC7
    3 I( p# z7 H6 g4 B  k! b' k% v
  36. #define W25X_PowerDown                        0xB9
    $ r( |8 P# H/ v$ j
  37. #define W25X_ReleasePowerDown        0xAB
    , ^6 j6 N. X5 S  I: l
  38. #define W25X_DeviceID                        0xAB
    $ e% q0 K# w: Q4 D: D, ~) f
  39. #define W25X_ManufactDeviceID        0x90 : _% B3 A! V: v2 {
  40. #define W25X_JedecDeviceID                0x9F
    1 d( d: {/ {7 A7 }3 c

  41. - u8 j3 b! n# r+ @7 |, V" v
  42. void W25QXX_Init(void);6 s0 {. G8 q+ J$ o5 V, U# F" e
  43. u16  W25QXX_ReadID(void);                              //读取FLASH ID7 w6 K5 j& p6 w/ J, L
  44. u8         W25QXX_ReadSR(void);                        //读取状态寄存器 % |$ T/ C  Z* t8 ]' Z, J
  45. void W25QXX_Write_SR(u8 sr);                          //写状态寄存器
    0 }) m0 _2 W( I% t( F' u+ h
  46. void W25QXX_Write_Enable(void);                  //写使能
    & ~* W- M  m5 L4 \2 Q' C( r
  47. void W25QXX_Write_Disable(void);                //写保护3 R8 i6 ~. v- \' J
  48. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
    . w; g8 ?% h2 q; a+ `! G* U$ e
  49. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash$ ]/ g8 {( ]3 y& n% T+ S, b- I
  50. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash
    & g& o- t* z0 ?
  51. void W25QXX_Erase_Chip(void);                      //整片擦除
    ( l6 V: Y7 N0 H3 {- N
  52. void W25QXX_Erase_Sector(u32 Dst_Addr);        //扇区擦除( T% H6 O( F" {8 {
  53. void W25QXX_Wait_Busy(void);                   //等待空闲( o4 d) o' Z. j2 F
  54. void W25QXX_PowerDown(void);                //进入掉电模式
    " [3 x; a: s' b
  55. void W25QXX_WAKEUP(void);                                //唤醒1 _8 j2 W( N$ R: @
  56. 7 v* K2 v2 K0 ~6 B; u7 O  I6 a
  57. #endif
    9 W. f2 s% n: I
复制代码
# d6 V- `  u; \; a$ U. f2 y
main.c:& \  I! f. c5 E( V
, B" y1 N! r5 p+ m
  1. main.c:1 T5 P$ c* j4 r1 Q& E3 o
  2. #include "sys.h"
    5 G* ^5 ~$ |0 E
  3. #include "delay.h"
    % l) R+ U$ K. i! }, X
  4. #include "usart.h"
    + }2 t. _7 b( ^4 a% b. K
  5. #include "led.h"* K" g6 V7 F9 b/ U  p( |8 U) P
  6. #include "lcd.h"6 T3 d' z% i0 x- p0 Y
  7. #include "spi.h"
    8 y# T) w5 I" h# a3 h
  8. #include "w25qxx.h"- p- O6 ^; v9 E" o/ [
  9. #include "key.h"    9 T/ a4 G( N+ Y/ Z, F# ^5 N

  10. ) C) {; J$ L8 o, _1 I
  11. //要写入到W25Q16的字符串数组4 K# V' `  \5 H) q. t
  12. const u8 TEXT_Buffer[]={"Explorer STM32F4 SPI TEST"};
    . A+ A2 M* |! f) {, k) K
  13. #define SIZE sizeof(TEXT_Buffer)         
    # Z# C- M$ R$ ^8 o  ^; [* q
  14.         9 O: t# V8 \' d$ |, l
  15. int main(void)5 v2 w  ~0 k' ?0 k3 b7 q
  16. { 4 w* [: |/ Y( L+ o! g
  17.         u8 key;
    2 f. D+ ]# \. g, |7 y2 q" u% e
  18.         u16 i=0;) P2 D2 p( H) V6 B7 N
  19.         u8 datatemp[SIZE];        0 V% H7 u) O( C; X
  20.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
    # z: I5 G" `. S7 n
  21.         delay_init(168);    //初始化延时函数
    " ]& _$ D* U9 \* ]! J
  22.         uart_init(115200);        //初始化串口波特率为115200  I% \, @3 R) M; {  h2 C
  23.         LED_Init();                                        //初始化LED . l/ D) G& R* [: r1 s  d
  24.         KEY_Init();                                 //按键初始化  
    7 {) y3 c7 X% Y
  25.         W25QXX_Init();                        //W25QXX初始化
    $ k- e6 ?# M7 G+ j" F- a
  26.                          9 u8 P3 O$ \" Q8 w) N
  27.          while(W25QXX_ReadID()!=W25Q128)        //检测不到W25Q1288 j% O, v% g* h2 x+ ^
  28.         {1 b& x+ h8 ~" E8 ?- B; g9 R
  29.                 printf("W25Q128 Check Failed!");8 G2 d1 P3 s" C) F0 k# w# U
  30.                 delay_ms(500);3 m* F, ^' b- o: T; |, A
  31.                 printf("Please Check!      ");
    - i$ c% n- g& B2 R9 r
  32.                 delay_ms(500);
    3 C( S* ?9 j5 f: b: _" Z
  33.                 LED0=!LED0;                //DS0闪烁
    6 {9 n$ u4 k+ n) d; O$ @( u
  34.         }6 b- G9 Z( C9 T
  35.         + c1 ]7 _* }3 a9 s+ I/ V
  36.         while(1)( h8 P5 F' x) |% l- H# @
  37.         {% u3 C* F! W6 D7 f1 ~! O
  38.                 key=KEY_Scan(0);
    3 q( {# h$ r; m0 c4 E
  39.                 if(key==KEY1_PRES)//KEY1按下,写入24C02
    & ^0 N+ q# x  i1 s+ M6 K- W
  40.                 {
    6 a& o( p! B7 J5 E  H, r
  41.                         printf("Start Write W25Q128....");
    / j1 i% M3 M6 u1 }# _# ^$ K
  42.                         //从倒数第100个地址处开始,写入SIZE长度的数据  
    & L2 Y3 L0 k, v; e* \
  43.                         W25QXX_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);
    ! r: ]3 h$ F. }% ~% e
  44.                         printf("W25Q128 Write Finished!");  //提示传送完成
    ! K) |# b# p9 V* L  V2 _9 ~
  45.                 }
    - ]4 `& q  {2 [5 b
  46.                 if(key==KEY0_PRES)//KEY0按下,读取字符串并显示$ }% e- m  s2 U2 H  z; ]. l3 G
  47.                 {- _! @& I3 o4 a/ V6 ?
  48.                         printf("Start Read W25Q128.... ");2 X2 S6 e) }/ H
  49.                         //从倒数第100个地址处开始,读出SIZE个字节
    1 f( J  o, P  R+ ?4 _$ K" |+ J
  50.                         W25QXX_Read(datatemp,FLASH_SIZE-100,SIZE);                                       
    & a; _! r9 X9 j
  51.                         printf("The Data Readed Is: \r\n ");//提示传送完成$ y/ ^; f1 |# x5 }# v+ {6 S
  52.                         printf("%s\r\n ",datatemp);
    2 n5 ?8 v0 E8 M( y3 H3 B8 @
  53.                 }           5 `2 n( \- V2 Y* U: g
  54.         }            
    4 C6 j) O! ^4 v  a8 ]2 Z" n
  55. }
    2 }$ h: _* z# f: W, {- Q
  56.   P9 z0 ^9 I, W6 g2 ~4 c
复制代码
- E% f7 F$ ~/ _) r3 V3 R$ R

) L* l5 s2 {& L* w0 J
+ D/ B; Q* M( C4 w$ v2 V1 c$ A
A71_MM@Q(@D3Z`FQYIISTMD.png
收藏 评论0 发布时间:2022-5-16 11:53

举报

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