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

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

[复制链接]
STMCU小助手 发布时间:2022-5-16 11:53
一、SPI介绍# b. V' }8 ^( j  v- ]. B
SPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。. j0 ~/ N0 v9 ~8 _' A! [% X
, R4 Q" Y. `* b) }
正是简单易用的特性,如NRF24L01、VS1053、SD卡等皆集成了这种通信协议1 X' j% s7 D; b1 s$ Z) g

% b$ [0 a1 R! g _A_6DRLZV3W0~DW}CEZQ7{O.png 7 @. y3 {, Q- f- h

+ u( Z2 X& _' c! C0 R二、SPI接口框图, E2 J8 E+ A# Q- i

8 z. y. f/ g$ |* ^7 c
@OFY~~NQ}EZ}HLQL{EB%0ND.png
" o9 w# C* l0 x- m/ j
7 i  C0 ^: J2 P  E2 @! B三、SPI优缺点

6 t8 e+ z7 T; z: Q" U1 l- W# HSPI接口是在CPU和外围低速器件之间进行同步串行数据传输,在主器件的移位脉冲下,数据按位传输,低位在前,高位在后,为全双工通信,数据传输速度总体来说比I2C总线要快,速度可达到几Mbps。2 H) e8 P3 c, @3 J- ^* [, \

/ l4 j1 Q' @+ H% ^' K信号线少,协议简单,相对数据速率高。& c# I, K( S' X' W+ b" K2 P7 Z
  {( G9 s( @- U1 a# Z' m; C( I
缺点:没有指定的流控制,没有应答机制确认是否接收到数据: \; |" ]+ W; |

1 |: [8 Z9 \6 O: J/ S, c: u四、SPI工作原理总结
6 K+ s1 K8 n3 U' p* D2 Z- z0 [; K0 H% c硬件上为4根线。1 |6 P) B) x. J% u
主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。
) M& W1 w" W0 w8 J串行移位寄存器通过MOSI信号线将字节传送给从机,从机也将自己的串行移位寄存器中的内容通过MISO信号线返回给主机。这样,两个移位寄存器中的内容就被交换。  Z9 f# b9 A) v
外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。
8 N# B1 b- X, s! G" `5 T3 H4 T+ Q多个设备使用SPI的应用举例* X# I* X6 N: v2 {# E+ \

* g' E$ N% t0 p; Y9 P1 Q (_8GH(KLZ4`~[RSWOUL_B{Y.png . R. _8 l3 j  O7 g

, T1 ^, K3 w! \. e: v* K$ j  G8 {五、时序图4 b0 A$ o0 j# o6 N) e
时序为 SPI_CR1 寄存器中的 LSBFIRST 位复位时的时序
" B+ ~" |) Y' l$ I
8 b( H: h  j0 I! p) U% Q5 |$ ?, e7 MSPI_CPHA的值将会影响SPI_CPOL的值. d/ W& R# C% c! }( Y' Q1 n8 L
& T" a- q$ W+ C+ L% U
4R1]OS0}$R`DITZTCDX~T~D.png
4 ]7 G2 m: x; H- S' f# ~, Y8 Q- T3 N  l4 _( k  A  F% V
六、SPI程序编写过程, P6 I5 Q+ t# T  U/ D
  1. //①使能SPIx和IO口时钟3 U* p! v8 j0 ?' u: _
  2. RCC_AHBxPeriphClockCmd() / RCC_APBxPeriphClockCmd();. G; Y) y1 D+ @! b

  3. : }6 @6 E- c- w9 l! o* m# v3 @
  4. //②初始化IO口为复用功能# F. ?) G2 Y$ U3 K% `# d; Y. s" x
  5. void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
    9 @. [2 \' \" X

  6. $ F$ A) }5 n- @$ j
  7. //③设置引脚复用映射:
    # q0 Q, d# f, }. q6 y
  8. GPIO_PinAFConfig();3 v+ v& A8 Y1 Z% e
  9. ! T- h1 O# r: l
  10. //②初始化SPIx,设置SPIx工作模式0 T! E0 ~; S0 H3 o: u# g5 X+ X2 L) K
  11. void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
    ! u0 x- K- g" _* g
  12. / O# j, ~9 |8 T
  13. //③使能SPIx
    ! \, d( ]! N& }2 ?% R
  14. void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);0 J4 c7 B' O  r

  15. % A- v' G- z' }5 v4 A4 ^
  16. //④SPI传输数据
    % {0 j( q8 _9 [3 _) U  Q' I
  17. void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);- R4 P' ^: w; e% h6 i
  18. uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;
    " a/ v7 M' c; Y4 }& o
  19. % K% K, N( V' I2 n
  20. //⑦查看SPI传输状态
      [" ?" O$ N" f% \9 X
  21. SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);
复制代码
  1. SPI.c:. |, q! c, w4 h" o6 X0 `
  2. #include "spi.h"
    ( o. p6 k# w% G7 Y/ L8 R6 z1 j7 Y3 @
  3. . c' W' [  q2 a! o8 x3 \0 E- F
  4. //以下是SPI模块的初始化代码,配置成主机模式                                                   
    ( u, U6 _* f+ X- {2 B" |' x
  5. //SPI口初始化. E, M+ u3 E& v. ~- S6 O2 O
  6. //这里针是对SPI1的初始化- `3 @& G" l7 n0 T" R9 P& n1 ]6 r
  7. void SPI1_Init(void)
    , c2 s* \: o$ B* M6 l# Z* _0 z9 Z
  8. {         
    & o4 T3 B$ Q. N8 c1 v. v; }. F- ?/ H
  9.   GPIO_InitTypeDef  GPIO_InitStructure;
    9 D; K8 I; N3 ?9 W  h2 I, s! _+ l
  10.   SPI_InitTypeDef  SPI_InitStructure;$ i; Q5 K7 c0 x: q( z* A
  11.         
    , O( B6 N2 I+ D) Z
  12.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟& f" a! ~4 g3 A, o: d: X) [/ e/ W5 i
  13.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟
    1 z5 L) L. x) i

  14. ) Z& o) P1 f( M( w) o+ T
  15.   //GPIOFB3,4,5初始化设置4 f6 X, u  H9 D! }6 [: Y
  16.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出        
      t0 C& O. Y* n9 k8 d9 a0 ?" I
  17.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能: u( x4 g- }4 A/ ?) L2 g) `# h4 ^
  18.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
    4 ]( Q& {0 o. N
  19.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz7 e1 g# G. J' r- C; G
  20.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉6 D( X$ k4 W$ e- J! k! Y- M: }1 F
  21.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化+ \- g6 |; Q! O4 R
  22.         0 [* J3 B+ H/ v6 G! E- n
  23.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为 SPI1
    & I& r! G! f1 E1 z7 q. m# I1 j
  24.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为 SPI1
    % K9 d, H9 g' F( L& `# C, Z
  25.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为 SPI1. \$ Y/ L/ `' k1 f* k' x2 J
  26. 2 X8 \* Q8 x- x0 w
  27.         //这里只针对SPI口初始化( Z! B1 d7 a5 k- R/ r- W" G
  28.         RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI1# A1 F# e8 @" O3 R* T
  29.         RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI15 v5 s) s% u8 J2 Z: i( y& O+ P
  30. " X% V0 }- K' `! x6 A. v7 S
  31.         SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工! }8 \( @. i/ F3 ~1 f
  32.         SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                //设置SPI工作模式:设置为主SPI
    . G. q5 A- R" U
  33.         SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                //设置SPI的数据大小:SPI发送接收8位帧结构
    9 S4 }6 C4 E! S" z( ?  ~
  34.         SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                //串行同步时钟的空闲状态为高电平
    2 S2 J' a4 Y/ [8 D
  35.         SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;        //串行同步时钟的第二个跳变沿(上升或下降)数据被采样6 z0 U1 w" S% D' X$ X
  36.         SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
    2 U) ?) h- [& \% ]
  37.         SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;                //定义波特率预分频的值:波特率预分频值为256
    " x8 e: J2 Z6 t$ z6 V2 ]9 g
  38.         SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;        //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
    4 ]9 G0 ~. s9 U" J( i5 G
  39.         SPI_InitStructure.SPI_CRCPolynomial = 7;        //CRC值计算的多项式
    * m7 o2 o( V9 t% q. H% t- m
  40.         SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器% o- s8 W% F. T0 ?

  41. + Q9 M4 Y( P) o' y) K8 E2 U
  42.         SPI_Cmd(SPI1, ENABLE); //使能SPI外设* _$ F4 W$ r" ]' f( L) U/ O" ?9 z

  43. 8 z( _: a( `8 q- z3 I2 G
  44.         SPI1_ReadWriteByte(0xff);//启动传输                 + ~7 d1 D5 v# P: _
  45. }   
    2 z; J5 N1 M$ M. o! @6 w0 }
  46. //SPI1速度设置函数+ L) W+ C4 X+ L2 i
  47. //SPI速度=fAPB2/分频系数
    : a/ w. S7 y: R- m3 x) `, w+ A
  48. //@ref SPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256  / |% ?6 }5 m2 ^
  49. //fAPB2时钟一般为84Mhz:/ c  e3 X. P9 }+ I* J, s* x' ]$ m
  50. void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)8 O# h, w/ D7 \, h7 H& Q
  51. {: o6 Q3 e; n  j2 M! k
  52.   assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性: f" V( X; a& V' a5 ^
  53.         SPI1->CR1&=0XFFC7;//位3-5清零,用来设置波特率! Q) |8 F& o) S- M# N  P2 d
  54.         SPI1->CR1|=SPI_BaudRatePrescaler;        //设置SPI1速度 ' u( L* b. T4 n* e
  55.         SPI_Cmd(SPI1,ENABLE); //使能SPI1
    4 @% m# Z4 T" w; ]' E  U
  56. } 2 h9 @6 m" S, B) E7 ?8 D
  57. //SPI1 读写一个字节) E' k; T0 u1 v9 E
  58. //TxData:要写入的字节6 z" j, e+ C$ D. _/ j
  59. //返回值:读取到的字节" H' A, \: l) a8 Z4 Y8 B/ x
  60. u8 SPI1_ReadWriteByte(u8 TxData)
    ! B/ N; L+ U: j9 G+ K4 y8 `& o
  61. {                                          ) F4 m9 ~- S4 C8 H
  62. ( z! J0 F# F- S) W( `5 g( E" V
  63.   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空  " e/ v  j% n0 I( V" @$ U2 B
  64.         ; w5 u5 [; q6 B1 f3 M. U' B( d
  65.         SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte  数据
    1 @* l. O/ [# P9 ?& J8 `
  66.                 2 ~; {5 I$ H4 T% V3 V; _3 T
  67.   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一个byte  : @6 g% _% f' [( y% {5 X

  68. 1 u3 a, C1 m8 L) i/ H2 }
  69.         return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据        
    : U3 K2 R7 `: j3 S$ a
  70.                      
    8 @0 t1 ?( g" }% Z0 g% \: U
  71. }
    / Y4 M" P4 {4 O4 J# I
  72. ( ]' B+ a: f& ?+ X- C6 P- ]
复制代码
5 P) t/ l' l" |3 n
七、W25Q12xx的原理及应用
3 G% o$ }5 \( Z# Z% pW25Q128将16M的容量分为256个块(Block),每个块大小为64K字节,每个块又分为16个扇区(Sector),每个扇区4K个字节。W25Q128的最小擦除单位为一个扇区,也就是每次必须擦除4K个字节。这样我们需要给W25Q128开辟一个至少4K的缓存区,这样对SRAM要求比较高,要求芯片必须有4K以上SRAM才能很好的操作。% V9 _$ S1 o( Q- Q* n+ h

4 P3 @' ^/ O4 B0 k+ m; gW25Q128的擦写周期多达10W次,具有20年的数据保存期限,支持电压为2.7~3.6V,W25Q128支持标准的SPI,还支持双输出/四输出的SPI,最大SPI时钟可以到80Mhz(双输出时相当于160Mhz,四输出时相当于320M),更多的W25Q128的介绍,请参考W25Q128的DATASHEET。
' @+ Y, _' H1 J2 |& a/ g
. j, L7 M6 S. @: B! H. o D%)W27U]]MDR`PRB[FT4~Z1.png
7 @4 Q' C+ n" u1 C+ E; Y5 a6 z
6 h4 O' U; b8 B8 C! O/ j. \W25Q12xx可根据原理图查看,使用的是SPI总线通信协议
* w* k" i* x4 b! o
# U0 o, F" N/ I' k' J3 g比如原子的原理图
0 Y2 S6 |. |& w$ L( ~" I% c' j8 G- h& D7 g, y
20190611221654679.png - r4 P; F1 \9 q' [( @! d4 M) k, |. J2 S( W
6 V' ~' @+ `- D* A! h
7.1 分析W25Q128指令
5 A" X3 G0 R8 X# e可参考W25Qxx的数据手册,这里列出W25Q128部分指令:
9 m$ p+ j7 x/ E/ d2 G. @+ o2 S) ?+ }, R
2019091611364032.png . A" V& E; A8 X& P  m/ S

. D0 l: H, F" A8 s) c2 h9 W6 \比如读取设备的ID的指令:0x90000000+ e0 ?" _! R. P; ~# i. s) r7 }
, H- W" V+ ^' v, v+ g
20190916113704620.png
9 y( W; Z& P) c  {$ t% P/ E6 l
( ^' @. A5 T  q4 H, x% e K$HWO9ZGFGE$CVLD47Q]_4G.png * z, r9 {2 ?& N4 \
6 @! T2 e2 |" A
WLLL(({{_EVAN[005V791UU.png & b7 I& q) N! s, U

: O' x: L' W' e- d7.2 擦除扇区:. B1 B) a+ T1 t0 U

# G* S! Z; i- h: {1 f$ S# P
NZ$~TU098T[KO5)GTQW){UQ.png
* f( ~0 I! u/ ~+ m
( T' Q9 M: N7 ^& w' Z! z7 B7.3 部分常用设备读取指令:

" B: Z% p0 l$ T3 q$ v' |5 Z0 e  O4 \- ?' _5 ]& }9 X& d) y
Q`V(R2ZUHAZ8SAS2[A}1H%2.png / j. C% J2 r. V; ]) g1 z
7 o5 ^/ N: L. u# E% X2 ~: m* e
每次操作前要使能写操作,且给一个低电平。结束时要给其为高电平,再失能写操作。也就是通过操作片选引脚来确定是否使能或失能。
) @! n1 k7 [: o- {; H3 k* ~
1 S3 c) g2 T4 x, ] LR6P1TX%{R][_M{CX4]K.png
8 k5 ]" Y5 Z; A6 G/ `* Z+ M0 F$ j% `" W; b6 S% `2 G8 E+ G
  1. W25Q12xx.c:
    4 ]( h! b, I1 ~+ x6 @
  2. #include "w25qxx.h"
    1 V; s& s7 E  m( f6 b) Q
  3. #include "spi.h"' [3 c; N# g! u7 G
  4. #include "delay.h"             T' e, r3 s0 [, |& g
  5. #include "usart.h"        
    ! ?' T3 I: l! |- o, l
  6. 8 X* [- O  _, d2 ^7 m  U) ]
  7. u16 W25QXX_TYPE=W25Q128;        //默认是W25Q128$ Y, T/ v. Z6 I! e4 z  a& T

  8. 0 x% Y4 K/ N4 f! I
  9. //4Kbytes为一个Sector
    6 `! K* u# C! O
  10. //16个扇区为1个Block7 x" S" V* a4 k" ~: Z3 y; }
  11. //W25Q128
    2 C$ E) R4 R& g0 Z, e4 ^
  12. //容量为16M字节,共有128个Block,4096个Sector
    - _% T5 P1 d' M) r' I" B! p( V
  13.                                                                                                          $ N& b( Q$ u% D" n
  14. //初始化SPI FLASH的IO口9 ]' w/ q1 S. U* k) ]5 i/ i
  15. void W25QXX_Init(void)) B& R% p( b# L
  16. {
    8 z& q" B7 [2 N( J, y. m7 r
  17.   GPIO_InitTypeDef  GPIO_InitStructure;; w4 t! U1 y4 Y- ^9 ]

  18. 0 Y: P$ H6 W' c
  19.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
    ' R3 N7 }$ E- R& M* I
  20.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);//使能GPIOG时钟: S& M* T' ]& ]

  21. ' v5 X' @) {0 ]
  22.           //GPIOB14/ f7 W6 R& C9 V/ {
  23.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;//PB14
    ; e- T( y1 m) J) F! \2 g7 c) i$ F
  24.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出
    4 u( |2 P4 J( P
  25.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出2 n' u) t; X/ B
  26.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    - c' u9 g2 n8 P' K: W. w
  27.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉' N% F: g& m) A5 {* |2 N
  28.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
    3 ]6 U5 @) ^7 ?1 Z' L

  29. 8 e3 K3 Q$ X1 T* U7 W* ]
  30.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//PG7
    8 j  C: T; Z8 o0 Z( B* V+ n. R# x$ m2 h
  31.   GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化0 k+ o8 W1 @. g% l$ h1 V! I

  32. % |. A" ~2 U% H7 s
  33.         GPIO_SetBits(GPIOG,GPIO_Pin_7);//PG7输出1,防止NRF干扰SPI FLASH的通信
    - T: T+ ~# Y) n5 L* \$ y
  34.         W25QXX_CS=1;                        //SPI FLASH不选中
    % i- c6 F  Q* P% \7 }9 B/ w
  35.         SPI1_Init();                                           //初始化SPI' `- u0 b1 S7 y: h3 e7 l' C
  36.         SPI1_SetSpeed(SPI_BaudRatePrescaler_2);                //设置为42M时钟,高速模式
    $ W- I  S3 {5 H0 t2 m" l
  37.         W25QXX_TYPE=W25QXX_ReadID();        //读取FLASH ID.
    3 p5 G; @; j: R: k2 y
  38. }  " R* `9 [' ?! |1 L  L# h, m
  39. ! e& [4 f3 m! o8 s
  40. //读取W25QXX的状态寄存器
    0 ]7 I/ e5 P8 r! n! L& G
  41. //BIT7  6   5   4   3   2   1   0
    & _6 {8 q- b8 u6 N- E9 N% e" P' F
  42. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY
    1 ~! }, l$ _3 }  ?! A7 E# H! @
  43. //SPR:默认0,状态寄存器保护位,配合WP使用8 d3 T, A; V2 ~( a  C5 J3 [
  44. //TB,BP2,BP1,BP0:FLASH区域写保护设置4 G8 s, g, A% g5 `- y+ H
  45. //WEL:写使能锁定
    0 b& k# i, {$ M% a
  46. //BUSY:忙标记位(1,忙;0,空闲)  p; K; d% R8 T: S! D+ p
  47. //默认:0x003 E% P5 M& ]; I0 e1 u4 z5 O/ M
  48. u8 W25QXX_ReadSR(void)   
    ( D& a8 F$ x* U( p/ I6 T) E- g
  49. {    ^' a* T- O2 U4 `
  50.         u8 byte=0;   , i( G% C% F* U) |) ?$ q
  51.         W25QXX_CS=0;                            //使能器件   7 [7 ~, \0 t! C$ m5 z
  52.         SPI1_ReadWriteByte(W25X_ReadStatusReg);    //发送读取状态寄存器命令    + S7 `( S2 w" `& H( b8 r  \
  53.         byte=SPI1_ReadWriteByte(0Xff);             //读取一个字节  
    ! K8 R! k1 i. g/ o8 _
  54.         W25QXX_CS=1;                            //取消片选     . l( S, m* _( A/ x3 w0 I2 N
  55.         return byte;   
      x- l9 D5 n. V4 Q
  56. } 6 N! w/ }/ O0 O5 n8 e# z
  57. //写W25QXX状态寄存器
    % c6 E3 P# X3 T, L$ Y; ]9 H
  58. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!) O& C* M: L1 i; V4 w8 j
  59. void W25QXX_Write_SR(u8 sr)   , d$ d$ I8 P! O2 d% Q! B
  60. {   
    ' O, O9 `3 v4 u) r
  61.         W25QXX_CS=0;                            //使能器件   
    ' d0 l2 T! ^) V% \- F
  62.         SPI1_ReadWriteByte(W25X_WriteStatusReg);   //发送写取状态寄存器命令   
    8 Z/ s5 M+ z! L$ U( }
  63.         SPI1_ReadWriteByte(sr);               //写入一个字节  9 V4 a6 y  x/ T, J: \) E1 |
  64.         W25QXX_CS=1;                            //取消片选                   - Y1 `* k3 I$ M( Z! W
  65. }   
    ! |1 j# h. C$ q9 p3 Q
  66. //W25QXX写使能        3 n& q3 A3 k  k4 B
  67. //将WEL置位   
    5 D( M  e9 }4 p/ i
  68. void W25QXX_Write_Enable(void)     @! O  L/ T5 ?* B5 f; ~! t- T
  69. {8 ~! J5 N4 M# c+ \/ C1 U; w  U
  70.         W25QXX_CS=0;                            //使能器件   6 R1 _, F9 o1 ^$ I* [: L1 x7 I
  71.     SPI1_ReadWriteByte(W25X_WriteEnable);      //发送写使能  
    : @% c/ |; z( x9 U! {
  72.         W25QXX_CS=1;                            //取消片选                   8 {1 a2 [9 X: ~- H4 h
  73. } ) E* p* @& n- j" i
  74. //W25QXX写禁止        8 a/ v5 \7 d8 N# M
  75. //将WEL清零  
    ' o* X( d! ^$ b/ f6 U8 d. Q( `
  76. void W25QXX_Write_Disable(void)   ( i. F/ ^& k( }
  77. {  
    * ?, \6 i; k) f) X
  78.         W25QXX_CS=0;                            //使能器件   ' s& g: ~  a3 H/ D' x4 A
  79.     SPI1_ReadWriteByte(W25X_WriteDisable);     //发送写禁止指令    . g8 [5 ?2 H1 x: O' k) T$ M, `! M1 m
  80.         W25QXX_CS=1;                            //取消片选                  
    - G+ G. \5 y. o6 `' d
  81. }                 
    ' Y# p) w! [! _6 J+ N
  82. //读取芯片ID: f. w: f! i. i; u" k- ~! m
  83. //返回值如下:                                   
    7 |6 }2 z5 A6 E8 P  F! b
  84. //0XEF13,表示芯片型号为W25Q80  ) M. X9 F( c6 a- q& d! g7 e
  85. //0XEF14,表示芯片型号为W25Q16   
    7 D% b7 r4 P7 i7 ?0 t/ y; `1 e, z
  86. //0XEF15,表示芯片型号为W25Q32  
    - [! _  `9 M; Y# k6 w' P% W4 @
  87. //0XEF16,表示芯片型号为W25Q64 4 {# K" d! o) O3 I- ]3 e
  88. //0XEF17,表示芯片型号为W25Q128           ! u) @% n2 L2 y3 W
  89. u16 W25QXX_ReadID(void)0 l2 L0 \' f/ P  [+ _
  90. {+ }6 [) ^9 @  @( E9 R: A* K. N0 E
  91.         u16 Temp = 0;         
    ) m% O' s0 @2 y% B) S# |
  92.         W25QXX_CS=0;                                    
    , N1 [/ `! x- m3 O$ M1 H
  93.         SPI1_ReadWriteByte(0x90);//发送读取ID命令            5 y7 E; P  N; l5 E( K$ z
  94.         SPI1_ReadWriteByte(0x00);             8 Z1 O+ i( W6 X- l" O& W
  95.         SPI1_ReadWriteByte(0x00);            
    / e. L2 I; V, m% Z0 ~
  96.         SPI1_ReadWriteByte(0x00);                                     7 A% K  k: v1 ?7 F
  97.         Temp|=SPI1_ReadWriteByte(0xFF)<<8;  
    5 a3 B4 {4 g& S
  98.         Temp|=SPI1_ReadWriteByte(0xFF);         
    ' U& l& T) L/ S6 E6 [( u
  99.         W25QXX_CS=1;                                    ) Q/ C3 C- x& V" b
  100.         return Temp;' C2 X9 a: E4 c% [+ ^# a: N6 I
  101. }                       * k* @$ k8 V% c% r) O
  102. //读取SPI FLASH  
    ( ~% s( X: D$ Q  u& f4 a9 _
  103. //在指定地址开始读取指定长度的数据4 ]/ M" ~9 r' j6 v2 i5 }- K
  104. //pBuffer:数据存储区& @$ F8 }* `0 j0 b6 ]* @2 C5 s0 x
  105. //ReadAddr:开始读取的地址(24bit)- z; \- R0 D3 I, A* [
  106. //NumByteToRead:要读取的字节数(最大65535)" o! I; m( P1 t! Q; X
  107. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   
    : t0 f" b: ]2 l1 W, I# r  p& g
  108. { , W& n" w4 S5 g# Y
  109.          u16 i;                                                                                       0 O% u9 e. b3 [& |4 J
  110.         W25QXX_CS=0;                            //使能器件   6 m7 K  ]/ T; I8 f7 I: F
  111.     SPI1_ReadWriteByte(W25X_ReadData);         //发送读取命令   ' b& `$ j, E6 w" l# g( F
  112.     SPI1_ReadWriteByte((u8)((ReadAddr)>>16));  //发送24bit地址   
    " {" J' m+ o) Q% q$ |
  113.     SPI1_ReadWriteByte((u8)((ReadAddr)>>8));   
    & c% Z7 K+ M5 e* I+ I; n/ _
  114.     SPI1_ReadWriteByte((u8)ReadAddr);   3 ~: p/ S5 k" p1 Q
  115.     for(i=0;i<NumByteToRead;i++)
    2 w3 H2 {3 U0 h
  116.         {
    ! D, K9 q# p7 N- q! @7 d  I2 v/ e
  117.         pBuffer<i>=SPI1_ReadWriteByte(0XFF);   //循环读数  ' m5 ?7 l$ Z9 ^- I( u' Y& O
  118.     }) U! W4 K7 R8 T& X) }7 g( ~  R* a
  119.         W25QXX_CS=1;                                                   
    $ f) l& b, ^6 C+ [; H( i
  120. }  
    ; q) G/ ^# R+ d9 l" Z, t# Y( [, i
  121. //SPI在一页(0~65535)内写入少于256个字节的数据; F/ {3 g8 s/ U0 k- g
  122. //在指定地址开始写入最大256字节的数据2 v: A4 E" c1 o! q) r
  123. //pBuffer:数据存储区1 [1 |# Z; q7 K9 \
  124. //WriteAddr:开始写入的地址(24bit)$ E  i8 ?, x$ _% ^8 L1 W" G' o
  125. //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!         5 l0 }/ e4 |* u6 v+ P3 _! T
  126. void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)- ]% T  c3 k) |' T/ j4 c
  127. {8 `( ]% d% s9 b
  128.          u16 i;  
    - i0 W+ \7 Z. x1 o2 B% g
  129.     W25QXX_Write_Enable();                  //SET WEL " J+ r' f; r9 [% G1 [; y  f/ j! s
  130.         W25QXX_CS=0;                            //使能器件   4 _2 h5 i7 l9 y/ l; B9 r* b
  131.     SPI1_ReadWriteByte(W25X_PageProgram);      //发送写页命令   ( W& K# Z) r( N# P4 U
  132.     SPI1_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址    % r) N8 N$ o" J0 s' J; P2 W3 N2 v
  133.     SPI1_ReadWriteByte((u8)((WriteAddr)>>8));   ( }) w5 ~. b- Z2 T
  134.     SPI1_ReadWriteByte((u8)WriteAddr);   $ W8 ~  I4 F( y! c9 ?- g) ^  N! E
  135.     for(i=0;i<NumByteToWrite;i++)SPI1_ReadWriteByte(pBuffer<i>);//循环写数  
    & }+ S5 h+ B9 P7 O5 |- ~/ X
  136.         W25QXX_CS=1;                            //取消片选 3 M: ~+ K6 q1 d4 j  w
  137.         W25QXX_Wait_Busy();                                           //等待写入结束
    " u& K0 ~0 ~, W+ B
  138. }
    ; w' O. X$ i4 C  X1 S- ^; ?+ e) z$ X
  139. //无检验写SPI FLASH 2 t4 t7 j9 O. f  X! X5 j9 i+ B4 O
  140. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
    4 Y: q& T* ?$ Z8 X+ a2 L! ~
  141. //具有自动换页功能
    6 R7 m% x* A. q. b* u
  142. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!6 S" n: a: R# i7 U4 ]3 P4 [  z
  143. //pBuffer:数据存储区
    4 T0 ?) j3 e# M! ^7 S. S
  144. //WriteAddr:开始写入的地址(24bit)& @1 `8 f/ ?7 x: i  A
  145. //NumByteToWrite:要写入的字节数(最大65535)
    : `/ I5 c! b5 {3 j: \+ K4 e
  146. //CHECK OK
    1 n0 F6 g. S  K# J. q: O, E% p2 E4 ?
  147. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
    ! {2 h! \" P- U7 J
  148. {                                           2 z+ ?- S, |# i# p; `+ F; R
  149.         u16 pageremain;           , B# d9 f+ o9 @, ?, M8 d% E0 l
  150.         pageremain=256-WriteAddr%256; //单页剩余的字节数                             ' J" ~3 H% t$ r  P: C
  151.         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节. G& i; @6 A7 }( R
  152.         while(1)3 e3 X  T& [7 [, M0 z3 V4 B% N
  153.         {           
    * ^7 E7 q/ k6 K% t9 v9 I
  154.                 W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);1 J  E& `2 m! v1 r& V
  155.                 if(NumByteToWrite==pageremain)break;//写入结束了
    % p. F# u% a0 X6 }3 Q
  156.                  else //NumByteToWrite>pageremain
      ^4 D6 C" [: k2 r, b; J
  157.                 {
    0 H+ p1 x0 F# k# V! N
  158.                         pBuffer+=pageremain;
    ' ~9 R, v0 U, D# |( A; I9 z( @" S
  159.                         WriteAddr+=pageremain;        7 T, C- C2 Q: }) {. P3 r
  160. 0 P1 \4 e8 ?4 ]/ e; D( ?' c
  161.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数
      o/ \% C0 l% g) d2 y/ q
  162.                         if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节- h' y  U- C% Z
  163.                         else pageremain=NumByteToWrite;           //不够256个字节了
    & A$ F% M- N' q# D2 J
  164.                 }5 u8 O/ E9 S# g, }" S3 h5 z' g
  165.         };            1 T1 C! t/ ?- |
  166. } # `9 e9 A! }; v. ~$ a$ X
  167. //写SPI FLASH  
    9 R) Z* g# [1 v1 X
  168. //在指定地址开始写入指定长度的数据1 j( {6 U% G* Z. f5 @' t
  169. //该函数带擦除操作!' J4 e! p2 o* }2 O% \  O4 `8 v+ X
  170. //pBuffer:数据存储区0 Z4 f& R$ V3 t* C
  171. //WriteAddr:开始写入的地址(24bit)                                                : u- D2 V' \1 |* q* v* z
  172. //NumByteToWrite:要写入的字节数(最大65535)   , Z  `" @. L- R+ \& i
  173. u8 W25QXX_BUFFER[4096];                 
    5 E( B) C% D- K9 Q
  174. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
    " P/ x4 I* y% c5 L$ Q7 J
  175. {
    $ r) E8 J3 H- u- I3 a) O8 v* ], ^3 q
  176.         u32 secpos;
    # H& ~& n0 W/ v: C
  177.         u16 secoff;
    & D+ |8 F! `) `2 Y  I
  178.         u16 secremain;           
    & ~) D: r- Z- c7 L1 t5 x3 B) F
  179.          u16 i;    ! }! [; J3 a9 g- N$ r
  180.         u8 * W25QXX_BUF;          0 @- ]$ j5 A8 ~5 K% Z9 f
  181.            W25QXX_BUF=W25QXX_BUFFER;            
    % S  z8 r0 c8 f3 n& C
  182.          secpos=WriteAddr/4096;//扇区地址  + k6 B+ i3 j# D& E: }
  183.         secoff=WriteAddr%4096;//在扇区内的偏移
    2 S$ x* H( n: `* \
  184.         secremain=4096-secoff;//扇区剩余空间大小   & Q8 l" M5 O$ x8 k. l3 [6 L- Y
  185.          //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
    & ~/ W; o$ G7 q/ v
  186.          if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节4 _; Q; I* G8 S* B/ [% L
  187.         while(1) + ]  |3 M: }$ S6 a  E" I
  188.         {        # V$ L5 E( u5 R# {8 o3 Y( Q
  189.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容, t! @4 q7 m4 i) _. \3 s7 w
  190.                 for(i=0;i<secremain;i++)//校验数据5 Y$ h  e" I1 q# p9 ~8 C
  191.                 {2 x) ~4 W/ S: h8 z8 n5 p
  192.                         if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除            
    ( D# `, V: f) O6 B+ Y
  193.                 }7 I: e3 f: t  {( x
  194.                 if(i<secremain)//需要擦除' w# `6 P! G5 W- A3 F5 ]
  195.                 {% R; H  g: E' P" e; L0 @$ I
  196.                         W25QXX_Erase_Sector(secpos);//擦除这个扇区' \/ X( j6 I6 \- }: V
  197.                         for(i=0;i<secremain;i++)           //复制
    9 N' U4 p/ a* @5 m; L: M
  198.                         {
    ! ^. Q* M. U9 j( [. O" C
  199.                                 W25QXX_BUF[i+secoff]=pBuffer<i>;         
    " K9 v6 U. c+ [: H  S
  200.                         }2 W& Y, E: V1 v9 B% o0 A& t' p* Y
  201.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  
    . O" S7 x6 E# x
  202. ) [! I) D4 \* i. L$ h
  203.                 }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    
    8 ]5 C2 {9 H( Y
  204.                 if(NumByteToWrite==secremain)break;//写入结束了
    * ?) K) x- H' }, Q
  205.                 else//写入未结束
    9 F& N# [1 C# A. m$ N
  206.                 {
    1 D( V7 U& c; M% I: x( j3 {$ v
  207.                         secpos++;//扇区地址增16 O3 ]8 c4 s4 v5 g, y
  208.                         secoff=0;//偏移位置为0          + k% o  o- x# D6 W1 c9 C
  209. $ S3 O' O, l2 l2 s! o
  210.                            pBuffer+=secremain;  //指针偏移
    6 ~4 n2 p6 G  ~! a& D5 ?2 x( W
  211.                         WriteAddr+=secremain;//写地址偏移           7 o# n8 A* w2 C: m  p1 `2 c8 O! T
  212.                            NumByteToWrite-=secremain;                                //字节数递减6 ]/ J2 b* S" q* ^" U# q9 D
  213.                         if(NumByteToWrite>4096)secremain=4096;        //下一个扇区还是写不完
    3 M1 O* C" [3 F) U; v1 t3 h& H( h' X7 j
  214.                         else secremain=NumByteToWrite;                        //下一个扇区可以写完了
    8 y" U  P8 n% w3 i) o
  215.                 }         
    : ?, E- ]5 X* ~1 N3 l
  216.         };         
    2 Q  m4 u* V3 Q
  217. }" ~8 b" W( f+ R- L) r/ Q  _
  218. //擦除整个芯片                  ! u+ S! P. f& O, v: e; s
  219. //等待时间超长...: ?  w. T9 ?4 y9 g/ d- n
  220. void W25QXX_Erase_Chip(void)   + B: N6 m- K' v8 ]: \
  221. {                                   
    / F$ v( B9 @8 G6 E) L
  222.     W25QXX_Write_Enable();                  //SET WEL
    * [( u: m+ ~0 s( Q% O
  223.     W25QXX_Wait_Busy();   1 y- k6 i: ]! [- v
  224.           W25QXX_CS=0;                            //使能器件   ; ~, ?, N" e5 a5 |/ W8 @5 ], o9 g
  225.     SPI1_ReadWriteByte(W25X_ChipErase);        //发送片擦除命令  
    / }% ^! r1 ~3 i) q9 N5 [3 E" q
  226.         W25QXX_CS=1;                            //取消片选                  
    7 R+ |, p" `# p! F
  227.         W25QXX_Wait_Busy();                                      //等待芯片擦除结束2 U% O  C, ], K) j; H5 `7 h- v8 z# k
  228. }   
    : i$ I4 G" l% X$ {6 x% {2 W' l
  229. //擦除一个扇区# p3 a" \2 [( w
  230. //Dst_Addr:扇区地址 根据实际容量设置, d- [9 w# c) u' B) r
  231. //擦除一个山区的最少时间:150ms. i7 }, ^4 d8 ]9 o5 Z! A) i* V/ R
  232. void W25QXX_Erase_Sector(u32 Dst_Addr)   
    7 z* a$ y9 q+ [7 y2 G; e% g) U
  233. {  " _, M3 e0 U  C) F1 l
  234.         //监视falsh擦除情况,测试用   
    6 _: T6 [; T/ P& \8 ?
  235.          printf("fe:%x\r\n",Dst_Addr);          * j& ~: ~4 v9 R+ t
  236.          Dst_Addr*=4096;
    + ^9 c& Z2 f( H: S, E3 E2 [
  237.     W25QXX_Write_Enable();                  //SET WEL         
    5 a( z* s# O% c. b: `+ y
  238.     W25QXX_Wait_Busy();   # j, e1 k8 B2 \# p+ ~: C* K
  239.           W25QXX_CS=0;                            //使能器件   - k. o) M9 p7 O0 o
  240.     SPI1_ReadWriteByte(W25X_SectorErase);      //发送扇区擦除指令
    $ `8 w8 {* ~3 N1 b4 M* e0 B, o& @* g
  241.     SPI1_ReadWriteByte((u8)((Dst_Addr)>>16));  //发送24bit地址   
    ' d6 b- `; E6 W4 R/ b( Y4 T
  242.     SPI1_ReadWriteByte((u8)((Dst_Addr)>>8));   ; o3 S) e. G  P8 s; y3 K
  243.     SPI1_ReadWriteByte((u8)Dst_Addr);  
    0 l% A! \2 R% X3 j2 `  H
  244.         W25QXX_CS=1;                            //取消片选                  
    ; l/ c! R2 m. a* G  h+ R
  245.     W25QXX_Wait_Busy();                                      //等待擦除完成
    8 C  T; H8 V5 z  \1 D; c
  246. }  & l- K" ?) t- s' B; E
  247. //等待空闲9 V) q2 g) Z7 Y# e
  248. void W25QXX_Wait_Busy(void)   
    * I! n' u8 _; q  v
  249. {   9 p. h( W9 O: u" d/ L7 V( d
  250.         while((W25QXX_ReadSR()&0x01)==0x01);   // 等待BUSY位清空
    * H/ f( j9 K& j3 i% ?. O
  251. }  
    - o" V5 t2 b' \: S% h7 C/ _" t7 j
  252. //进入掉电模式
    & U# I% V5 d! ^8 q7 P
  253. void W25QXX_PowerDown(void)   6 A! ]5 J) Q; A( O; X: }: b: d
  254. { 5 V9 r; a. y. [$ d; Q+ G8 `* [
  255.           W25QXX_CS=0;                            //使能器件   ) K1 J- z) R  W3 r' P- ]
  256.     SPI1_ReadWriteByte(W25X_PowerDown);        //发送掉电命令  
    ! b+ \5 n) |: x7 D( l& W
  257.         W25QXX_CS=1;                            //取消片选                  
    ) `* e: E0 I8 c( W1 c  B
  258.     delay_us(3);                               //等待TPD  
    2 i$ O1 c4 X4 D4 C
  259. }   
    % W4 B: F* \' [; S
  260. //唤醒
    1 y" z& w% j" Z
  261. void W25QXX_WAKEUP(void)   
    4 |: M( c: N$ ?  ]7 s/ ^" j7 E
  262. {  
    * C3 Q$ o9 f6 m7 f
  263.           W25QXX_CS=0;                            //使能器件   
    : S2 ~0 D/ `1 j1 J# u
  264.     SPI1_ReadWriteByte(W25X_ReleasePowerDown);   //  send W25X_PowerDown command 0xAB   
    $ ]  d# u5 S+ w0 J
  265.         W25QXX_CS=1;                            //取消片选                  
    $ h0 J$ O6 H2 Q% v# L
  266.     delay_us(3);                               //等待TRES1, ~) n$ D" Z! ~
  267. }   </i></i></i>
复制代码
  1. . e5 }" S3 Y7 {$ |4 G* f
  2. W25Q12xx.h:) ~0 v  w" Y: j7 R6 y2 Q  {# M
  3. ( [8 M3 Z8 O8 b8 P
  4. #ifndef __W25QXX_H' u; m" w% i+ C) W% ^$ N) Q. x
  5. #define __W25QXX_H                           
    4 s- H0 a# y& b4 P) ]1 W: E8 `
  6. #include "sys.h"  
    + ?: r) i: q8 J$ J
  7. * \9 n/ F7 T+ Z6 ^
  8. //W25X系列/Q系列芯片列表           % Y; u6 I! K8 K0 G# U
  9. //W25Q80  ID  0XEF13- q3 [9 M+ N1 o- s
  10. //W25Q16  ID  0XEF14) `# N$ S( x+ X5 x7 b+ x
  11. //W25Q32  ID  0XEF15. B; w; A( }- u3 a& g" q' `4 n
  12. //W25Q64  ID  0XEF16        6 ]3 b, j; s: H. b7 q- b' E
  13. //W25Q128 ID  0XEF17        
    0 \' C; M# P, x4 [9 q& b
  14. #define W25Q80         0XEF13         ' d$ u( i$ g1 f/ Y4 [  J  h
  15. #define W25Q16         0XEF14
    % l0 F; Y1 k& o0 d( X
  16. #define W25Q32         0XEF15
    * N7 G+ Q# P! O; O- A/ Y) b8 k
  17. #define W25Q64         0XEF16
    ; d  Y4 U6 Z, k& y3 b8 A
  18. #define W25Q128        0XEF17
    9 W9 k! ~  T$ o+ d: X6 u2 k! X

  19. 0 O  p; P7 w9 H  K: H6 I( b1 q
  20. extern u16 W25QXX_TYPE;                                        //定义W25QXX芯片型号                  
    ) _7 @8 H/ W; |/ f+ M; v. o
  21. ) I- W: L1 i1 b: o1 e" V/ W# ?
  22. #define        W25QXX_CS                 PBout(14)                  //W25QXX的片选信号/ p2 d7 ~- }4 d
  23. // + g% u6 t& Q5 E" W
  24. //指令表
    " H6 {# X; v" t; V1 t8 m
  25. #define W25X_WriteEnable                0x06
    $ ?% b/ v) f* Z2 Q) j# f
  26. #define W25X_WriteDisable                0x04
      j" `+ h0 Q3 Y3 ~
  27. #define W25X_ReadStatusReg                0x05
    ; S, @, d) |6 K; o3 p
  28. #define W25X_WriteStatusReg                0x01
    % q) L$ G' I" H$ x
  29. #define W25X_ReadData                        0x03
    , y7 }9 v$ ?" q) ]9 i' ?
  30. #define W25X_FastReadData                0x0B
    + F  R" ^* [+ P& M5 d* L, I
  31. #define W25X_FastReadDual                0x3B * u: I6 F, A$ ~' ?3 A
  32. #define W25X_PageProgram                0x02 8 Y' K/ w6 k& b4 y( S: f+ W
  33. #define W25X_BlockErase                        0xD8 $ m8 B* [% {+ e. r
  34. #define W25X_SectorErase                0x20
    * }* |4 u; f+ P% i5 V. |0 S9 ^
  35. #define W25X_ChipErase                        0xC7
    * f$ p$ X; U3 K. o, e4 L
  36. #define W25X_PowerDown                        0xB9
    ; v% x3 z2 d% H! s2 `( r
  37. #define W25X_ReleasePowerDown        0xAB ( Y* J( O& t2 E; V
  38. #define W25X_DeviceID                        0xAB 5 x/ R/ W. o' @2 |( x
  39. #define W25X_ManufactDeviceID        0x90 # i# G, E& A: b9 {% ]& j7 k
  40. #define W25X_JedecDeviceID                0x9F . `0 k/ s+ s. j4 b4 b) Q9 r
  41. 4 J9 ]* q3 ^4 d- ^
  42. void W25QXX_Init(void);5 o( W7 D( a+ ^- V: a$ n4 v
  43. u16  W25QXX_ReadID(void);                              //读取FLASH ID
    ) a$ Q$ [& a$ s# Z% Y
  44. u8         W25QXX_ReadSR(void);                        //读取状态寄存器
    & p  w& o% |& ?& W, H5 j! J
  45. void W25QXX_Write_SR(u8 sr);                          //写状态寄存器6 |% y. |' u2 E1 q# P
  46. void W25QXX_Write_Enable(void);                  //写使能
    3 }( f& H  B( _8 b# [! |6 E: n
  47. void W25QXX_Write_Disable(void);                //写保护3 K6 G( {% W5 i  W3 y# ]& F
  48. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);7 G0 V$ f- f* A* o& P
  49. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash
      v+ T0 [: V) w* x4 \6 g% f, H5 U8 h
  50. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash) X' z3 d, e4 s% n" k
  51. void W25QXX_Erase_Chip(void);                      //整片擦除$ }! Z# j- s& I
  52. void W25QXX_Erase_Sector(u32 Dst_Addr);        //扇区擦除
    , y2 h' p) \) U  Z$ X# G
  53. void W25QXX_Wait_Busy(void);                   //等待空闲
    / l) Z0 F5 D, b6 Z. X
  54. void W25QXX_PowerDown(void);                //进入掉电模式
    2 M5 s, z0 X" y+ h$ I: K
  55. void W25QXX_WAKEUP(void);                                //唤醒  v6 F2 ]% X8 Y

  56. 8 C& R5 W% H2 W+ G
  57. #endif
    / L) @% ?) L" {# z! ]& A' y
复制代码
+ W, \  q( n" \8 M
main.c:* l" u. T* J4 T: Q( A  c# y* I4 f

2 ~% B# k( W( p& P4 M6 A
  1. main.c:
    ! X3 s7 J1 x+ c, m. [1 S
  2. #include "sys.h"0 t' H3 v+ A2 p0 b( j
  3. #include "delay.h"
    ; e! e4 f8 C& W5 z0 j
  4. #include "usart.h"$ R- P9 I3 `4 _9 A0 r
  5. #include "led.h"7 T3 I, Q1 j& ?  G; X0 m
  6. #include "lcd.h"
    7 g8 f) [9 Q, x7 [; P" X* e  ]
  7. #include "spi.h"* M! G- \6 g) |; N7 h  J* R
  8. #include "w25qxx.h"
    + p- p5 ~8 t; h5 Q
  9. #include "key.h"    7 x9 `, r0 t5 p! Y
  10. - ^# h+ o, J/ b  i( y/ ]
  11. //要写入到W25Q16的字符串数组* W& @. s# ?+ A, I
  12. const u8 TEXT_Buffer[]={"Explorer STM32F4 SPI TEST"};" n: |, T' t6 d
  13. #define SIZE sizeof(TEXT_Buffer)         / {& D' }( y) [. g
  14.         
      B1 m; l, m0 B- N/ T9 A8 B! \2 d
  15. int main(void)+ i  r% u2 Q( j' e# H& z
  16. { ) \5 N" a& s+ m' v- b. c+ r% e2 _5 D
  17.         u8 key;
    8 b2 ]  T; G( \  O/ v$ Q
  18.         u16 i=0;$ a' r; U) z0 Q3 g
  19.         u8 datatemp[SIZE];        % {# C% I3 [+ t% U: r) p$ d, j' z
  20.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
    2 i* J- F! [" }8 C
  21.         delay_init(168);    //初始化延时函数
    # |. f6 C) z/ ~$ [4 a4 k
  22.         uart_init(115200);        //初始化串口波特率为115200& i3 B& z% \! n( ~
  23.         LED_Init();                                        //初始化LED + {  U/ K& J: B0 Y( Z! L
  24.         KEY_Init();                                 //按键初始化  ! H3 I8 ~$ l8 W- {" b+ E! |, O
  25.         W25QXX_Init();                        //W25QXX初始化
    ) [' \% i- E6 X/ N: N9 Q4 ^; e) G
  26.                          # n1 n3 S+ n. h  `  c5 c7 Q$ B+ f
  27.          while(W25QXX_ReadID()!=W25Q128)        //检测不到W25Q128
    7 e/ H$ B% E: s1 ~2 H7 }9 [
  28.         {
    ( v+ V3 f0 r+ p  Z* D: u
  29.                 printf("W25Q128 Check Failed!");
    . }" w- S4 F& z" l* L7 P6 t4 ^
  30.                 delay_ms(500);" N4 k! ]  {$ j9 c" X/ r
  31.                 printf("Please Check!      ");
    ( w: y  ?* e" g
  32.                 delay_ms(500);
    " S: C% k6 H5 v3 y% m
  33.                 LED0=!LED0;                //DS0闪烁
    ) A$ c8 B1 W8 v$ X2 Q' o
  34.         }
    ( s. @2 K; r: p5 C7 K* R
  35.         
      \& M: \& A: b5 Q" l. \2 W
  36.         while(1)
    1 N& ~) G. s/ Z7 h. s
  37.         {% a8 o" l. A3 g
  38.                 key=KEY_Scan(0);
    & \& a" |; }' F9 u8 ]
  39.                 if(key==KEY1_PRES)//KEY1按下,写入24C02
    ; _% w) h3 U6 Z7 j* X5 t
  40.                 {
      s6 b+ p2 g. ~. o
  41.                         printf("Start Write W25Q128....");
    - F2 T0 A7 |5 I5 f- f1 e5 e  X" s1 s0 d
  42.                         //从倒数第100个地址处开始,写入SIZE长度的数据  
    * [, F# c7 e( \( Y0 p
  43.                         W25QXX_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);% j& O5 T6 \4 |% t5 z
  44.                         printf("W25Q128 Write Finished!");  //提示传送完成# ?' {9 L& A" F+ H  [, L, o. V
  45.                 }
    0 i  h, I% w5 Y7 w: E
  46.                 if(key==KEY0_PRES)//KEY0按下,读取字符串并显示4 n& y! o: [6 L; d
  47.                 {6 S/ S3 D9 A5 R- L: a$ g
  48.                         printf("Start Read W25Q128.... ");
    9 }1 S) M$ }4 ]5 M5 _1 ]2 Q
  49.                         //从倒数第100个地址处开始,读出SIZE个字节4 Y# e5 l/ ~  |) N  r+ |- v
  50.                         W25QXX_Read(datatemp,FLASH_SIZE-100,SIZE);                                       
    * @7 W4 b. d5 K2 M
  51.                         printf("The Data Readed Is: \r\n ");//提示传送完成
    . z) e4 f* U& V+ O: }1 Y8 L
  52.                         printf("%s\r\n ",datatemp);
    ) b4 D& U% |5 B7 m/ V
  53.                 }           
    3 d/ Q! f4 b7 P0 y  \1 a! k
  54.         }             ) \6 m0 t3 V3 C
  55. }
    % v; i. }( d7 f. ^; H

  56. + m: }( R# Y+ B& E, _* y
复制代码

7 ?! t! x& f0 w* s- \3 v2 q
& g: ~; C: h! V2 ^7 b; K
6 h/ D* e% }$ i  C' l8 J# u5 d
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 手机版