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

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

[复制链接]
STMCU小助手 发布时间:2022-5-16 11:53
一、SPI介绍6 B& ^; c5 H: s5 ~; I) y% K
SPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。/ r) g! `- \. C+ {! C, g
6 N2 u% C& d( u1 h6 b
正是简单易用的特性,如NRF24L01、VS1053、SD卡等皆集成了这种通信协议/ h2 x: j2 y  R& x% V+ H7 O+ |  |

) ]' e. y" p8 c  E9 a- k _A_6DRLZV3W0~DW}CEZQ7{O.png ; Y* F3 Q8 g( Q: a/ E  J3 j

2 x5 F1 x- F  f二、SPI接口框图
& Z4 N- `- E6 K8 ^7 K7 g' O5 \; e* H
@OFY~~NQ}EZ}HLQL{EB%0ND.png # C* \  v! ^* W( z$ i

: K, l2 L) |1 A/ m4 V三、SPI优缺点

" `. j+ C/ D3 F: pSPI接口是在CPU和外围低速器件之间进行同步串行数据传输,在主器件的移位脉冲下,数据按位传输,低位在前,高位在后,为全双工通信,数据传输速度总体来说比I2C总线要快,速度可达到几Mbps。
) X* f5 M) ?5 x. J" E7 A. [. D+ ~# H( H
信号线少,协议简单,相对数据速率高。$ V% h6 N- g" p! A

  U9 J& R  d- Z0 I3 N4 |缺点:没有指定的流控制,没有应答机制确认是否接收到数据
$ J8 U# Z3 P0 f# l, f- t
$ k6 v# J, n' I四、SPI工作原理总结
5 m' P  B( \3 @4 @+ e硬件上为4根线。3 s5 M4 ]6 Q# g0 _; e9 N( r
主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。
! D/ M% m$ E) j* B2 x( w1 a. `串行移位寄存器通过MOSI信号线将字节传送给从机,从机也将自己的串行移位寄存器中的内容通过MISO信号线返回给主机。这样,两个移位寄存器中的内容就被交换。
& |& x, E, c$ H8 D0 \6 `外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。
" l: V! U3 g* g! t0 Y) a! N多个设备使用SPI的应用举例6 _; v! r+ a$ k+ E
  w) i/ F# X% ?0 Q& C1 o  s( l
(_8GH(KLZ4`~[RSWOUL_B{Y.png
) ?8 y: O7 h4 e
& E( u- M5 Q5 M3 {* R& S五、时序图4 r0 d3 x3 T4 t# x+ Q5 f
时序为 SPI_CR1 寄存器中的 LSBFIRST 位复位时的时序
2 Q! Y6 ]6 m3 F) z1 E9 u# B6 u+ o0 @% Q/ y
SPI_CPHA的值将会影响SPI_CPOL的值, j2 Q" M9 g9 O2 D; a
. |1 K4 _7 w: \0 {
4R1]OS0}$R`DITZTCDX~T~D.png * b4 g' F2 N% S$ S' N( I

+ Q* ]/ Q4 I4 _" ]  o9 a8 d六、SPI程序编写过程
6 ]. v& K% r- r* t) m4 @
  1. //①使能SPIx和IO口时钟
    + k; [2 M* O! a
  2. RCC_AHBxPeriphClockCmd() / RCC_APBxPeriphClockCmd();7 a% r: ?% c# w" B+ r& Y
  3. , @# _( H1 z# b( ]' d3 k0 P
  4. //②初始化IO口为复用功能$ D7 p& Q/ x) v8 h  S* a# z2 X
  5. void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);1 D$ L$ m2 |0 n$ T( R5 v4 x- r* y

  6. 4 p+ g/ x, o- s) F; \6 R% ]" a
  7. //③设置引脚复用映射:
    7 {! H$ {( @- m
  8. GPIO_PinAFConfig();
    ) V  n! k1 p' W3 G
  9. - P* K: G5 n, U. b. q1 g5 D
  10. //②初始化SPIx,设置SPIx工作模式
      `- ^4 G8 }  b; D$ Y8 Q
  11. void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);! ]/ `# N4 o; t. n3 H; I. S% t
  12. ' l# W6 q! t1 a( ^
  13. //③使能SPIx! x9 S6 W5 }8 Q6 M0 x' u
  14. void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);4 z7 t( Y* Z6 K$ D! O) }
  15. - N2 i# R5 j, q. m
  16. //④SPI传输数据
    / j* H; n' c5 `5 K8 f# c) b
  17. void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
    , y* ?; P2 w/ x$ B8 l1 f; T: i
  18. uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;
    1 M# a7 h: ?: y  [/ K
  19. 0 ], l+ ]: Y" f9 u* _
  20. //⑦查看SPI传输状态
    ) R3 Q% l  r4 P! w2 ^8 o) T
  21. SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);
复制代码
  1. SPI.c:
    ) B1 t, `& `) U9 y4 p
  2. #include "spi.h"
    2 z8 R' w4 o& B: [

  3. 9 r6 h" M* g# m
  4. //以下是SPI模块的初始化代码,配置成主机模式                                                   
    8 ?7 }, Y) G7 r, b2 h- a5 \8 m/ K4 t( k
  5. //SPI口初始化
    ' P' l4 q/ ?; K/ I2 B
  6. //这里针是对SPI1的初始化" n4 C$ B, `( i- d; S
  7. void SPI1_Init(void)- R" R* y- _9 v& F3 R0 a
  8. {         ( c: K0 j! f' u. g/ E
  9.   GPIO_InitTypeDef  GPIO_InitStructure;
    & G+ u* _& P0 t
  10.   SPI_InitTypeDef  SPI_InitStructure;
    9 f6 Y5 }: G2 A# x+ t- f# z/ P  r
  11.         0 N% F4 \3 y) _' ?! v0 F" ?
  12.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟- N6 i: r$ M" \) [. [  E+ u$ o
  13.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟
    ' \6 E2 l% J5 m0 U
  14. * v' [; s& y5 Z( V3 C' c3 F
  15.   //GPIOFB3,4,5初始化设置
    4 J- K9 O5 K* w+ t* p, A
  16.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出        
    / H0 J+ r2 j: A4 ^
  17.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
    ! M7 v! T8 M' j/ Q6 i( e
  18.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
    5 J6 {: X* s1 ^  ]6 |+ u+ |
  19.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz0 Y; |' y2 V& ]& @, x8 c- o
  20.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
    ! \: ]8 K) S+ a# k) W
  21.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
    9 E1 p. r. ^4 ~
  22.         
    ) N9 F- x9 Y9 N
  23.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为 SPI1
    . A: T& E! U/ N! Y" l+ E
  24.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为 SPI1
    : R# }/ ?& m9 j/ d- Y) y9 P, G
  25.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为 SPI1
    % o% t6 s& F" T& Z+ \: f

  26. 8 ~4 L9 w; r3 O& j; ?4 `
  27.         //这里只针对SPI口初始化" e; `+ I; \% k5 J
  28.         RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI1
    ; E7 R$ B" I" u  C8 K- E/ a
  29.         RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1
    ) q5 F% C  f: ]" V- @9 W; n

  30. 9 X" J* E& U( w, N
  31.         SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
    6 U$ r' H& ~' ]. _; u$ t
  32.         SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                //设置SPI工作模式:设置为主SPI4 w7 D2 k5 e9 i0 s/ u! [. o
  33.         SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                //设置SPI的数据大小:SPI发送接收8位帧结构; C% Y" x: h7 M7 ]5 X' D
  34.         SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                //串行同步时钟的空闲状态为高电平
    + N7 C5 U  D4 ^
  35.         SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;        //串行同步时钟的第二个跳变沿(上升或下降)数据被采样* j. q5 T8 I" u
  36.         SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制8 }' l' q9 q" |
  37.         SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;                //定义波特率预分频的值:波特率预分频值为256+ j$ q% ^6 L" t% V3 w, o# T
  38.         SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;        //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
    6 r- a+ [, @2 a) }4 Y
  39.         SPI_InitStructure.SPI_CRCPolynomial = 7;        //CRC值计算的多项式4 |1 H# ^3 S  V% B0 w6 I  H
  40.         SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
    . \1 E3 {4 H. e/ H4 |( ^. E

  41. 5 \. Q5 q$ O( A% D
  42.         SPI_Cmd(SPI1, ENABLE); //使能SPI外设
    2 N, j. R& d8 _
  43. ( _4 A6 E$ K0 t: k# `- _/ A
  44.         SPI1_ReadWriteByte(0xff);//启动传输                 
    4 ?5 C; Y" j6 Y6 ?0 x! j% r
  45. }   
    ' J. Y# W9 w2 V1 @3 ^: A
  46. //SPI1速度设置函数" N* P0 f0 A* W- r& z+ @# o* ]
  47. //SPI速度=fAPB2/分频系数( V- l) y& |2 G/ h; F3 |& c
  48. //@ref SPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256  
    3 r' u# h' l5 A
  49. //fAPB2时钟一般为84Mhz:% t3 `- |5 T3 a' C$ u1 J! D) S( z
  50. void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)+ L! k/ G$ p$ ]/ y4 F
  51. {
    2 t( R: J; F. @' X
  52.   assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性0 l- P3 @+ X% N
  53.         SPI1->CR1&=0XFFC7;//位3-5清零,用来设置波特率+ M- m+ t: T, m* a9 b2 H
  54.         SPI1->CR1|=SPI_BaudRatePrescaler;        //设置SPI1速度
    ; ]- H% N7 N  F6 p& a  R
  55.         SPI_Cmd(SPI1,ENABLE); //使能SPI1
    , N: r& [/ G, p0 q! e
  56. }
    , A6 _# G3 H2 z: F: h
  57. //SPI1 读写一个字节
    . b/ R" m% j3 |: ]
  58. //TxData:要写入的字节
    % A* W1 M$ n/ p3 Z7 T) N
  59. //返回值:读取到的字节" M, B( x% t, }9 g+ a/ E- W
  60. u8 SPI1_ReadWriteByte(u8 TxData)$ s$ M4 q# P' O) g9 `
  61. {                                          
    2 p* j2 R5 a5 J0 O0 f

  62. & h  `3 Q4 k. f% P
  63.   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空  . }2 Q! R) P$ `) g) C5 w. O# ?
  64.         
    ! d% L9 `7 e  p) }$ L; c. L% A' S
  65.         SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte  数据; }0 [# g7 ?( [7 g
  66.                 4 s% h' ^5 l- c7 J: U: T1 i
  67.   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一个byte  ' v. q0 v0 s( [& x

  68. ) c% ]: j$ \6 ?4 f+ H% {3 Z. A
  69.         return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据        2 }8 R5 i% A* }; X4 j9 w  e% d" J
  70.                      & Q& D3 W% G9 F# R) C1 F# m8 C# N
  71. }
    " _  n8 S- J2 r, b2 v. E1 }) n
  72. + {( v1 q) L' B: l4 u: q
复制代码
. o9 y- N  B! A0 ?
七、W25Q12xx的原理及应用
* a5 B" a( W! ?" RW25Q128将16M的容量分为256个块(Block),每个块大小为64K字节,每个块又分为16个扇区(Sector),每个扇区4K个字节。W25Q128的最小擦除单位为一个扇区,也就是每次必须擦除4K个字节。这样我们需要给W25Q128开辟一个至少4K的缓存区,这样对SRAM要求比较高,要求芯片必须有4K以上SRAM才能很好的操作。
  |+ A1 V( Y+ }; z- }! A. D1 r7 G! [/ G0 X
W25Q128的擦写周期多达10W次,具有20年的数据保存期限,支持电压为2.7~3.6V,W25Q128支持标准的SPI,还支持双输出/四输出的SPI,最大SPI时钟可以到80Mhz(双输出时相当于160Mhz,四输出时相当于320M),更多的W25Q128的介绍,请参考W25Q128的DATASHEET。
) v  G# O# }6 p* D5 D  s! L, J% Y( Z# A% W' i& @- @
D%)W27U]]MDR`PRB[FT4~Z1.png ; B8 w: O+ N/ r1 m$ P+ {
: U7 o9 m+ b1 `# r& D
W25Q12xx可根据原理图查看,使用的是SPI总线通信协议5 v3 v0 h0 Y2 s& l/ ~- g, i) ?; f3 d, {

) v* a( g- r0 }, J- R比如原子的原理图, c$ e; k4 H) N* `- r
/ M$ v/ F6 L3 i5 k! T: x5 e  h9 x4 s
20190611221654679.png & T" z$ B! X) {6 [5 ^" F6 f* @

$ V) [. |) ^$ A7.1 分析W25Q128指令
9 H( G- L4 q7 ~: G3 p# F( B" t可参考W25Qxx的数据手册,这里列出W25Q128部分指令:
6 B) j$ e9 f( T; {. w3 ]$ l4 P$ b
$ Z% a7 ^1 z; U/ t: F% \9 v9 u3 t' a1 Y 2019091611364032.png
) w; E8 E: V' [/ R% Y$ k" C+ V
比如读取设备的ID的指令:0x90000000
  v0 i& Y2 N" G3 G4 G6 r( w+ K2 ]8 s; X& @0 A- D) S& H  K
20190916113704620.png 2 x/ A+ ]7 F6 X+ o

6 f8 F# r3 v" n7 P# X2 g* t3 J K$HWO9ZGFGE$CVLD47Q]_4G.png , O6 X! j0 Y8 E( T( E" j

% g0 `  f: o  E WLLL(({{_EVAN[005V791UU.png
0 i' R4 |% Q7 Z# A7 V; |1 R, G$ V, g  a
7.2 擦除扇区:
2 m0 N8 f" W# j. J1 B
- @: \1 Y! H7 d  }
NZ$~TU098T[KO5)GTQW){UQ.png ! b2 H: \6 J  |2 i/ ~0 L9 N: |- W
" _0 p- F: P# z; S6 C: M
7.3 部分常用设备读取指令:
! R: ~- L' ^$ l9 m

  \; a* \/ \- j1 D6 c Q`V(R2ZUHAZ8SAS2[A}1H%2.png
9 A! r, [) t- T5 B: k; t. ^1 e2 \3 X. p& {  K( I
每次操作前要使能写操作,且给一个低电平。结束时要给其为高电平,再失能写操作。也就是通过操作片选引脚来确定是否使能或失能。
% p- n  W" s2 H8 K5 B# g2 M  e$ Q: A0 Y7 O
LR6P1TX%{R][_M{CX4]K.png
1 x' A8 c; j  ~* W- B$ c
) o  z. z% x0 D' c* v8 H5 Q
  1. W25Q12xx.c:; R% D6 k/ I: `; D" n
  2. #include "w25qxx.h" - E( z- C( P( {' }* u# a
  3. #include "spi.h"
      z. t% N: k! v! C" g
  4. #include "delay.h"           
      X* \; Q% z; f  ]. C
  5. #include "usart.h"        : W; b9 v1 x# N+ V, M' {, {

  6. 0 _$ @8 Y4 z/ J: C% d) w' R
  7. u16 W25QXX_TYPE=W25Q128;        //默认是W25Q128
    / [' s0 a: \. K- M
  8. " r5 B) K$ c  g0 ^* o
  9. //4Kbytes为一个Sector& B& C; C& N/ x. Y! |' x
  10. //16个扇区为1个Block6 O0 x, B; ~" j3 v" b  f
  11. //W25Q128
    $ n( t$ V7 k& i7 b
  12. //容量为16M字节,共有128个Block,4096个Sector , u/ V& L1 f7 v0 |6 y8 n
  13.                                                                                                          $ L( n1 g) k, I  P# t& p
  14. //初始化SPI FLASH的IO口
    3 i3 ?  z5 d7 N: r  y
  15. void W25QXX_Init(void)7 Q2 \8 W: w5 H1 W6 z
  16. {
    2 a! E/ A+ u3 Y
  17.   GPIO_InitTypeDef  GPIO_InitStructure;' D* W0 B) a! o

  18. ( S- k8 u; R9 F( j& w# g
  19.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
    " s8 I' a; ~: `
  20.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);//使能GPIOG时钟
    7 {; ~! Z1 |6 S$ Y& r* |+ P; s! v

  21. 5 F# e( o# D6 b% J9 ?0 b  K
  22.           //GPIOB14
    ) X8 w1 h9 M. I: K# A# z
  23.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;//PB149 u+ C' R* I* Y9 A
  24.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出
      @* Z* |# Z( Z1 |* b! K6 k' P
  25.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出0 R8 @  B/ ?6 p3 `1 r- B
  26.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    3 D- d1 b$ s, A9 [
  27.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
    ; y  q/ T" B7 @
  28.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
    2 w# f2 S4 Q: }) I( ?
  29. 2 V, {9 O7 |. \0 N9 `
  30.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//PG7
    6 S  `! N' h6 D" J4 Y2 N
  31.   GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化( W! H: p/ }% r- p* V% a

  32. " ~, N0 C/ U) O- B6 W) |
  33.         GPIO_SetBits(GPIOG,GPIO_Pin_7);//PG7输出1,防止NRF干扰SPI FLASH的通信
    . W4 _+ t- v5 L5 @- Q0 f8 a6 X3 c
  34.         W25QXX_CS=1;                        //SPI FLASH不选中
    ' W; U- t" s/ ~
  35.         SPI1_Init();                                           //初始化SPI  c( M$ l8 w2 z; }5 Q
  36.         SPI1_SetSpeed(SPI_BaudRatePrescaler_2);                //设置为42M时钟,高速模式 - S! O! Y: `1 t: h# j
  37.         W25QXX_TYPE=W25QXX_ReadID();        //读取FLASH ID.3 M  O( o- B! R
  38. }  . w% q5 z5 x. e) y

  39.   ]9 I- \2 Y, l& @. s
  40. //读取W25QXX的状态寄存器) F  Y: R( z5 f1 u. D8 I
  41. //BIT7  6   5   4   3   2   1   09 ~& h9 Z1 T' B$ d4 v
  42. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY! P7 ~1 G; p2 ?6 y8 m" n
  43. //SPR:默认0,状态寄存器保护位,配合WP使用
    , ?* i3 T& U8 J( }% G
  44. //TB,BP2,BP1,BP0:FLASH区域写保护设置
    + r* I- G% r& S6 Z, ]( A7 z4 v0 b
  45. //WEL:写使能锁定3 R  L/ c; g1 ^* G2 L4 p
  46. //BUSY:忙标记位(1,忙;0,空闲)5 |" q8 A4 A( ?% j, ]5 r7 Y
  47. //默认:0x00
    ! I% ~2 K/ q5 }
  48. u8 W25QXX_ReadSR(void)   
    1 |0 |" s, [! W! M6 J
  49. {  3 j( u! \5 s" r( R% {
  50.         u8 byte=0;   
    ' c/ x- G; t3 b# \( r& N7 d
  51.         W25QXX_CS=0;                            //使能器件   
    1 B, N* X- q& v+ A- d" D. Y
  52.         SPI1_ReadWriteByte(W25X_ReadStatusReg);    //发送读取状态寄存器命令    ( N0 Y6 A( t2 Y" n: j
  53.         byte=SPI1_ReadWriteByte(0Xff);             //读取一个字节  
    + F& i7 x) a! s) B: s
  54.         W25QXX_CS=1;                            //取消片选     
    / u6 U# z, n5 H! ]; s2 _
  55.         return byte;   ; u, e. N6 R# ], b6 _# a% a* A" E
  56. } . W& C  g3 g& X3 {8 T
  57. //写W25QXX状态寄存器
    5 p9 H  C' f8 n1 A3 i* X, @
  58. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
    , N( Q5 K# w; T, g; P
  59. void W25QXX_Write_SR(u8 sr)   # C  q/ O% F+ G& u
  60. {   
    , y$ G# q! j- V- `
  61.         W25QXX_CS=0;                            //使能器件   
    7 ?1 {5 V  u  N- D
  62.         SPI1_ReadWriteByte(W25X_WriteStatusReg);   //发送写取状态寄存器命令   
    7 `6 N$ o' ?* C9 _
  63.         SPI1_ReadWriteByte(sr);               //写入一个字节  
    2 @  O# J) o$ F  L' R% K
  64.         W25QXX_CS=1;                            //取消片选                   ' P' s* E' C0 Y' E1 c
  65. }   1 U6 ?8 d% P2 J
  66. //W25QXX写使能        ! |9 G6 L* {: D) X) N4 O, h+ P
  67. //将WEL置位   
    ! x# v2 k/ t0 o; f5 z
  68. void W25QXX_Write_Enable(void)   8 c4 q: E. Z& r6 f) a
  69. {3 \' I0 b0 B$ d" f; j# ]* i; Z' x
  70.         W25QXX_CS=0;                            //使能器件   
    / U* I" q9 G' {% d- ?
  71.     SPI1_ReadWriteByte(W25X_WriteEnable);      //发送写使能  - c: ?4 j: @1 Q6 F
  72.         W25QXX_CS=1;                            //取消片选                   " g0 J5 N- l4 U9 ^6 }
  73. }
    ) C6 `& x& p8 y1 H
  74. //W25QXX写禁止        ! ?9 g0 n! N9 H- @2 Y! S# |
  75. //将WEL清零  ' a/ Y9 P7 R0 {  U3 B# O
  76. void W25QXX_Write_Disable(void)   
    , {) Z, T5 p+ g& v& j
  77. {  
    $ \! j; Q! u2 ~5 b5 g5 \
  78.         W25QXX_CS=0;                            //使能器件   1 a" _3 \& J( W9 L2 |
  79.     SPI1_ReadWriteByte(W25X_WriteDisable);     //发送写禁止指令    7 x' P3 z0 R$ l1 [
  80.         W25QXX_CS=1;                            //取消片选                   9 j$ ]1 O3 T, V  Q: ]4 ~
  81. }                 
    ) ]5 q8 a; r7 V' h; X
  82. //读取芯片ID. ]) s8 x1 g; |& ], j
  83. //返回值如下:                                   0 X7 Z4 o$ O, Q5 t6 W# o
  84. //0XEF13,表示芯片型号为W25Q80  
    $ w1 B) |8 Y# ?& R5 w
  85. //0XEF14,表示芯片型号为W25Q16   
    ; x9 p) L# x) J
  86. //0XEF15,表示芯片型号为W25Q32  
    % s, X  o/ ]% [3 T
  87. //0XEF16,表示芯片型号为W25Q64 , P# r3 f' U; i" \
  88. //0XEF17,表示芯片型号为W25Q128           
    8 D# p  k. g9 n- |4 k1 S( p# o
  89. u16 W25QXX_ReadID(void)" L; F9 p! T" E1 S
  90. {
    ; W: O8 b1 m4 H. ~- W
  91.         u16 Temp = 0;         
    0 k2 Y9 j5 T! A' u/ J
  92.         W25QXX_CS=0;                                    1 J) s7 i: A7 `
  93.         SPI1_ReadWriteByte(0x90);//发送读取ID命令            # B5 y9 @, s6 d+ S
  94.         SPI1_ReadWriteByte(0x00);            
    & f% i9 y1 B: I7 c
  95.         SPI1_ReadWriteByte(0x00);            
    * I. g, P- G; g* P  M; k6 E
  96.         SPI1_ReadWriteByte(0x00);                                     . b4 ?9 \1 h9 u, f0 L7 A+ \
  97.         Temp|=SPI1_ReadWriteByte(0xFF)<<8;  ; o  q; X. G! j8 c- F
  98.         Temp|=SPI1_ReadWriteByte(0xFF);         5 X+ J% T6 j% E7 @( v
  99.         W25QXX_CS=1;                                    7 ~3 R) U- e) h* R. K6 ]4 F
  100.         return Temp;
    ! {' N+ S0 \: z% Q/ C
  101. }                       ) p' k/ A& C6 _2 E" W
  102. //读取SPI FLASH  
    & E; O* V6 ?$ ^! r
  103. //在指定地址开始读取指定长度的数据1 k* v" |$ K, t; \! H) @
  104. //pBuffer:数据存储区
    9 n# E% b% d+ p3 L5 K5 E, N
  105. //ReadAddr:开始读取的地址(24bit)
    ! @0 |, i: J% e" p# B9 y8 L$ Y& B
  106. //NumByteToRead:要读取的字节数(最大65535)# P: Y8 ?" b1 z& c
  107. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   " ?* M' V' L4 T5 J8 G2 n3 g7 P8 C
  108. { 5 u! j" }# [$ i) ]5 p
  109.          u16 i;                                                                                       + y$ l5 K- B% V0 |4 c( l; {
  110.         W25QXX_CS=0;                            //使能器件   
    # f5 M1 j3 z" D
  111.     SPI1_ReadWriteByte(W25X_ReadData);         //发送读取命令   
    & o5 d; L+ i4 I
  112.     SPI1_ReadWriteByte((u8)((ReadAddr)>>16));  //发送24bit地址   
    ' ?% l9 i; m" d- ]0 K+ c5 I" p
  113.     SPI1_ReadWriteByte((u8)((ReadAddr)>>8));   
    6 a8 r- S; |" O5 f
  114.     SPI1_ReadWriteByte((u8)ReadAddr);   9 j2 A2 q5 P% P+ ^) `
  115.     for(i=0;i<NumByteToRead;i++)2 i0 B. _! `1 [" z+ u/ g6 H
  116.         {
    + K: Q: K0 r: |9 D% Q+ T% o! K5 E
  117.         pBuffer<i>=SPI1_ReadWriteByte(0XFF);   //循环读数  1 E/ D5 R2 c1 p
  118.     }/ N  K4 B1 y. E( K6 S" h  \) ^# x
  119.         W25QXX_CS=1;                                                   
    $ t! p! c4 n8 W: ?% N
  120. }  9 m, p5 ]  i. ]6 X4 S5 X
  121. //SPI在一页(0~65535)内写入少于256个字节的数据7 @' a! ], x- P+ o
  122. //在指定地址开始写入最大256字节的数据1 K& @. `# ~& q/ L' z6 P
  123. //pBuffer:数据存储区
    / O& |* r0 s+ U# U( R0 u& c9 A3 F
  124. //WriteAddr:开始写入的地址(24bit)
    5 P( @3 @5 }$ {2 p  l* p" J! ]
  125. //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!         " b$ A# n+ ]# M8 I5 S
  126. void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)& F4 _) [( M$ x# P& o4 v% w5 y
  127. {5 A# k. J/ A( ]. f9 D; T4 L8 K  c
  128.          u16 i;  . z5 V: d# L9 n: |, O
  129.     W25QXX_Write_Enable();                  //SET WEL
    % p! _* N) G8 s; D% H5 U
  130.         W25QXX_CS=0;                            //使能器件   , I6 E8 b  z( `, V
  131.     SPI1_ReadWriteByte(W25X_PageProgram);      //发送写页命令   
    7 Z" r: O& R9 m3 p: Z" T  Y' Q
  132.     SPI1_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址   
    ! X9 ^, u: U+ T5 F! }
  133.     SPI1_ReadWriteByte((u8)((WriteAddr)>>8));   / C& X( J0 \, C& ^; z! J
  134.     SPI1_ReadWriteByte((u8)WriteAddr);   6 ~! o5 m6 V8 ]$ K& k8 h- U$ @
  135.     for(i=0;i<NumByteToWrite;i++)SPI1_ReadWriteByte(pBuffer<i>);//循环写数  
    # M/ L/ [7 K; H
  136.         W25QXX_CS=1;                            //取消片选 + Z5 F3 F- q4 }! ]% [# o+ b
  137.         W25QXX_Wait_Busy();                                           //等待写入结束  |, |) D: N7 s& X# S& p
  138. } 3 m$ a4 S# F; `3 |$ m2 {* E+ U
  139. //无检验写SPI FLASH   e- m; ^8 d4 q6 p' c" D; W) q5 q) o
  140. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!0 ^4 s& K% I  J# g
  141. //具有自动换页功能
    7 E9 k7 B& Q" `. \' U
  142. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!+ E* o3 c" q; m! @( N3 S. z, J
  143. //pBuffer:数据存储区0 m9 E; ]/ K* F' u
  144. //WriteAddr:开始写入的地址(24bit)
      a: g4 l' ~( H8 |3 `7 r
  145. //NumByteToWrite:要写入的字节数(最大65535): h( S' \' i$ W1 M# x( E
  146. //CHECK OK
    7 L* o6 s& p/ I* w' P3 D; Y; h4 j
  147. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
    $ c; ?( b: Y. N7 u9 e! v) Z
  148. {                                           4 E  f/ Z  |3 O% U$ r7 _; U2 a5 S/ l
  149.         u16 pageremain;           / U  [  l2 H  m! p. u6 L+ ]
  150.         pageremain=256-WriteAddr%256; //单页剩余的字节数                             
    - R$ E6 ]4 J2 ~7 j1 H# H
  151.         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
    6 d. x, H4 S, h# ]9 P' Q- B# o+ X
  152.         while(1)
    4 p, z5 N  u9 x) s
  153.         {           3 X7 v: }; F+ G0 I  z/ U1 O
  154.                 W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);( x9 c  O, [$ I  N+ l
  155.                 if(NumByteToWrite==pageremain)break;//写入结束了+ {! Z4 b6 Y- s# v7 {! R2 l$ B
  156.                  else //NumByteToWrite>pageremain
    ' v( y/ L1 \- _" B, X3 n$ v2 q& y
  157.                 {7 e9 x6 j. V- e. B  ]7 Y' i# a
  158.                         pBuffer+=pageremain;
    - r7 k" M  W2 S$ L, _# P
  159.                         WriteAddr+=pageremain;        1 T& T& h% q, f8 ?% L# X

  160. # u5 S7 M# t( D$ N
  161.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数
    5 d& a5 p1 k% q% Z& C) Q+ f6 E% E. O
  162.                         if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节) I0 E& m3 k* x
  163.                         else pageremain=NumByteToWrite;           //不够256个字节了0 J# W8 U: }% [0 B* R8 C3 L7 @: X/ G
  164.                 }6 A$ t, G1 G: `3 K
  165.         };            
    % h0 a6 L. x" g: O
  166. } 9 t% s8 ]. L3 O7 P8 ~' c
  167. //写SPI FLASH  $ @  ^: @- k! W' `6 o9 Q
  168. //在指定地址开始写入指定长度的数据" Z# {4 V9 s7 I* z
  169. //该函数带擦除操作!* T! S, d  r: J! o: M2 D  [! [( o( h
  170. //pBuffer:数据存储区. w# e* t# }( {
  171. //WriteAddr:开始写入的地址(24bit)                                                
    - s# c% m+ ?' H
  172. //NumByteToWrite:要写入的字节数(最大65535)   1 T6 u( b$ L4 Y5 J
  173. u8 W25QXX_BUFFER[4096];                 
    2 z& \, k( B. _- N3 g
  174. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
    4 V. S6 z# x. y; I2 I
  175. { ' f2 [4 V% E5 h/ v
  176.         u32 secpos;: A0 [" Q& x) _7 l1 l/ P
  177.         u16 secoff;
    6 ?$ v1 z: }* e
  178.         u16 secremain;           
    5 p( P" p7 p$ }
  179.          u16 i;    5 {  D' B" D: h% |+ d( v' D
  180.         u8 * W25QXX_BUF;         
    & t+ c/ v* A) J: |+ v/ m
  181.            W25QXX_BUF=W25QXX_BUFFER;             " e5 b# {/ ]% V4 m# h, q
  182.          secpos=WriteAddr/4096;//扇区地址  1 N( v/ Y$ ~  w  Y
  183.         secoff=WriteAddr%4096;//在扇区内的偏移
    ) a; L* Z  e2 ]5 m" z3 O0 q
  184.         secremain=4096-secoff;//扇区剩余空间大小   6 Y$ |* r3 Z" m. j& d" l( a
  185.          //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
    ( ^( B+ e: u+ S, ~- ~5 ^% T
  186.          if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节
      g" i; E7 X7 ^# A) e' H( h. i  J1 y
  187.         while(1)
    0 o0 M5 ?2 x, l# w4 q# b' k3 x# {- {3 n4 U
  188.         {        
    1 C5 v4 m3 M3 L% Y. E7 o7 `2 ?
  189.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容% \0 o9 x( ^, p, P4 B6 Y
  190.                 for(i=0;i<secremain;i++)//校验数据
    9 P5 N0 p/ I$ Z" P" g) z8 s
  191.                 {2 @  P4 [0 ?' S3 \
  192.                         if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除            8 u/ d* l" c8 `$ u3 @# H
  193.                 }/ }7 x& H) i6 L
  194.                 if(i<secremain)//需要擦除
    0 Z3 J6 l) I# z9 E; T  d8 @
  195.                 {
    8 p2 z  y5 e4 s/ g
  196.                         W25QXX_Erase_Sector(secpos);//擦除这个扇区
    9 h* j/ f7 T) `0 j9 l% j, R
  197.                         for(i=0;i<secremain;i++)           //复制6 s8 v- I) }( D) p- [
  198.                         {
    ; C8 e8 `0 K/ a$ R/ b
  199.                                 W25QXX_BUF[i+secoff]=pBuffer<i>;          5 i' K% s/ c2 H: f. x2 d
  200.                         }
    ! r5 k5 K! T5 r
  201.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  ' |  d/ y# p5 j8 @

  202. ' |. s  K9 l$ H& V1 Y9 w' T' r
  203.                 }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    
    ) J  w& k7 u4 R- m( k7 z2 ^9 q7 K
  204.                 if(NumByteToWrite==secremain)break;//写入结束了3 _- n4 i2 r2 S5 V- S
  205.                 else//写入未结束
    9 D- h* k* Y4 J) X5 M# {' ~
  206.                 {
    : b/ \/ U- f+ O3 ]
  207.                         secpos++;//扇区地址增19 n5 O5 y! m$ e/ P$ F% Y
  208.                         secoff=0;//偏移位置为0         
      `7 c  C( H4 A) {8 k9 W
  209. , h! @3 z+ u0 s: [2 m! v) r5 u
  210.                            pBuffer+=secremain;  //指针偏移' w0 m" b! l" b7 P6 E# X0 k
  211.                         WriteAddr+=secremain;//写地址偏移           
    ) i1 a9 k& L) T/ x# i- L9 q
  212.                            NumByteToWrite-=secremain;                                //字节数递减
    + \9 U9 G2 {0 h: n2 }) p
  213.                         if(NumByteToWrite>4096)secremain=4096;        //下一个扇区还是写不完
    " g  h8 x4 c; k8 T- E& J
  214.                         else secremain=NumByteToWrite;                        //下一个扇区可以写完了
      [4 H9 }7 ?0 e( K% |6 _3 |5 u5 K
  215.                 }         $ p3 u8 Y/ N( H. v. W2 s' g. T
  216.         };         2 m( d! L" M: @% _
  217. }! V" h* V4 {! M
  218. //擦除整个芯片                  * L, k" y1 b! S' `- k# {3 a* H" N3 H
  219. //等待时间超长...
    $ }$ s, U& n4 O8 f0 S& u% H' j
  220. void W25QXX_Erase_Chip(void)   
    ; i7 X5 P; `& t# j6 C1 D/ g  b
  221. {                                   : z1 s2 T3 |- K0 `$ Y
  222.     W25QXX_Write_Enable();                  //SET WEL 5 n$ V7 R. r9 s0 Q
  223.     W25QXX_Wait_Busy();   
    1 e+ N% v. H9 \6 F: `# h8 C" B% Z
  224.           W25QXX_CS=0;                            //使能器件   . M* Y# c; J: f0 o. v$ b0 H' |
  225.     SPI1_ReadWriteByte(W25X_ChipErase);        //发送片擦除命令  8 h+ T5 P& a0 J4 ~8 v% Z
  226.         W25QXX_CS=1;                            //取消片选                   ! y7 ]  D# W  \5 K7 Y% d, J+ m
  227.         W25QXX_Wait_Busy();                                      //等待芯片擦除结束3 n3 B+ G7 Y  \/ C) t8 b
  228. }   7 J3 N% Z5 Y% R( K% P
  229. //擦除一个扇区# {7 B: z9 j/ o' q+ [: J8 b
  230. //Dst_Addr:扇区地址 根据实际容量设置
    7 X7 i  g  s0 ^- U' g9 {
  231. //擦除一个山区的最少时间:150ms
    0 r: @" t4 X% k5 t
  232. void W25QXX_Erase_Sector(u32 Dst_Addr)   
    5 s' ~! D1 y9 o' Q; ?1 j. ]
  233. {  . G" B  H' V/ I3 u) d- a( g$ N* I
  234.         //监视falsh擦除情况,测试用   
      \$ _5 H* @5 H& l
  235.          printf("fe:%x\r\n",Dst_Addr);         
    ! q7 T; q$ A! H2 j+ D
  236.          Dst_Addr*=4096;
      B9 w# ~* c# `7 P
  237.     W25QXX_Write_Enable();                  //SET WEL          , ?( n. `+ X4 [8 p6 |6 J, K% [6 [# a
  238.     W25QXX_Wait_Busy();   9 n3 J5 g( I/ F5 ^
  239.           W25QXX_CS=0;                            //使能器件   6 ]/ k1 y3 {  M/ O
  240.     SPI1_ReadWriteByte(W25X_SectorErase);      //发送扇区擦除指令 ' t. u( Z2 |- _) Q/ ?) R
  241.     SPI1_ReadWriteByte((u8)((Dst_Addr)>>16));  //发送24bit地址    / G$ V. c( M$ d7 {; W$ f; \3 n$ r
  242.     SPI1_ReadWriteByte((u8)((Dst_Addr)>>8));   2 P0 X' |( A; z; d  O
  243.     SPI1_ReadWriteByte((u8)Dst_Addr);  
    7 w: R  Z1 Z8 ]" H; p
  244.         W25QXX_CS=1;                            //取消片选                   3 l! Y/ y3 g$ c% x0 L; S8 }
  245.     W25QXX_Wait_Busy();                                      //等待擦除完成2 H3 e% i$ M" k* D' T" E
  246. }  0 T/ y/ w" m' f3 _
  247. //等待空闲
    : a% z0 Q, ]/ G' \6 Z5 m
  248. void W25QXX_Wait_Busy(void)   
    . \; T$ a7 A2 q7 g
  249. {   
    9 P1 F) }- X7 n( F$ n9 q( @: G
  250.         while((W25QXX_ReadSR()&0x01)==0x01);   // 等待BUSY位清空
    ' H; ]1 V. k% Q# s0 Q$ ~
  251. }  # H2 ~. G. a5 S7 s+ }' x6 e- T
  252. //进入掉电模式4 v1 g& C7 w* g2 g# \1 e
  253. void W25QXX_PowerDown(void)   
    , V/ `" E: {/ ?. U! B. v
  254. {
    * b# ^  u- a" t& \8 _/ S1 J# p  y
  255.           W25QXX_CS=0;                            //使能器件   & }: }; y  e7 u0 `+ U2 S  N
  256.     SPI1_ReadWriteByte(W25X_PowerDown);        //发送掉电命令  
      X8 \) J& \9 g
  257.         W25QXX_CS=1;                            //取消片选                  
    1 c: I  P/ J. C" `- d! b
  258.     delay_us(3);                               //等待TPD  9 {$ Z. ?5 L; v) ^" I  m3 G
  259. }   
    - E1 L/ M9 U3 _& j' U2 A& `1 ]; t
  260. //唤醒
    : C' a1 U( I, _$ y
  261. void W25QXX_WAKEUP(void)   ) w( r. u* E3 c- B1 V
  262. {  7 r7 o. {- }/ n, f1 r$ w- c5 j3 K
  263.           W25QXX_CS=0;                            //使能器件   
    * {+ O) C; N1 h2 ]
  264.     SPI1_ReadWriteByte(W25X_ReleasePowerDown);   //  send W25X_PowerDown command 0xAB   
    / P1 w7 k9 i8 U
  265.         W25QXX_CS=1;                            //取消片选                   8 A! ^7 @9 \- N
  266.     delay_us(3);                               //等待TRES1
    # n% |% v" w2 M. v
  267. }   </i></i></i>
复制代码
  1. 7 q6 V( r+ ?% u+ o7 u. |* J3 l
  2. W25Q12xx.h:
    ' p$ c/ M5 N* I8 x  q
  3. " Y5 V& N( u; o
  4. #ifndef __W25QXX_H
    * F  z- s7 v* B9 L/ v- O3 X/ K
  5. #define __W25QXX_H                            - ?& G1 l8 [0 V
  6. #include "sys.h"  ) y1 R3 Z7 ]/ ~) q' I
  7. 6 [. b1 l2 Y7 p5 q. {! e
  8. //W25X系列/Q系列芯片列表           * d$ ^; X0 R0 h0 }. W/ w2 j6 R
  9. //W25Q80  ID  0XEF13
    ( h1 W1 a/ l$ s9 P: K; a
  10. //W25Q16  ID  0XEF14
    / J2 \. c3 H+ ]
  11. //W25Q32  ID  0XEF15
    0 [9 f8 L# b  M- d* L" K+ Q) T
  12. //W25Q64  ID  0XEF16        
    : ^6 [  \! h! c; s* ^8 V
  13. //W25Q128 ID  0XEF17        
    2 ~# {8 \! e9 Z* u5 ~$ `9 z. M; t
  14. #define W25Q80         0XEF13         / l, r2 k7 z3 @3 H* [! x0 K
  15. #define W25Q16         0XEF14
    & q/ u" Y& E9 h" E7 c$ Z3 Q( R
  16. #define W25Q32         0XEF15
    4 Z8 M( o1 n, _& M% C
  17. #define W25Q64         0XEF16
    / N1 H6 j8 p) B
  18. #define W25Q128        0XEF175 ~4 L! U5 ^( i5 Y0 x$ e

  19. ) E1 p$ z2 E) Y1 B+ W
  20. extern u16 W25QXX_TYPE;                                        //定义W25QXX芯片型号                  
    % U, O$ g2 ~4 {, _2 U
  21. ! j) n& m% H  t* I* m
  22. #define        W25QXX_CS                 PBout(14)                  //W25QXX的片选信号0 |. K3 `' X4 {2 a) V
  23. // 2 D# P* B% ^& n& h
  24. //指令表
    , S+ c$ |& S3 u, \) M! u  N
  25. #define W25X_WriteEnable                0x06 - n* h/ ~% W1 ]7 m* c
  26. #define W25X_WriteDisable                0x04 % M7 J, c8 q1 l5 {
  27. #define W25X_ReadStatusReg                0x05
    , F* q4 P7 L7 x; P( @
  28. #define W25X_WriteStatusReg                0x01 6 t# _! t" x7 p7 W
  29. #define W25X_ReadData                        0x03
    # }, F! u7 a. U# E! V+ I' k6 v
  30. #define W25X_FastReadData                0x0B 0 h: B( K; L4 m0 \) t
  31. #define W25X_FastReadDual                0x3B 3 a* \% G# w& M% Y# B2 c5 P- m7 H' ]
  32. #define W25X_PageProgram                0x02 # @' d: ~: [8 f* ]# _1 @
  33. #define W25X_BlockErase                        0xD8
    5 Z1 v# n# B' ~3 B
  34. #define W25X_SectorErase                0x20
    , M& X- P+ k4 g
  35. #define W25X_ChipErase                        0xC7 # u1 _! W" x4 u" @  d. F
  36. #define W25X_PowerDown                        0xB9
    , B9 a8 I) p4 s. J0 c/ y
  37. #define W25X_ReleasePowerDown        0xAB
    1 W& O" m: s- l( E
  38. #define W25X_DeviceID                        0xAB
    7 J3 W2 s3 G/ S
  39. #define W25X_ManufactDeviceID        0x90
    # O: }7 c. {$ j: H) L
  40. #define W25X_JedecDeviceID                0x9F
    8 o* ~/ Q& c7 x. L' q+ l$ x+ t
  41. 9 j$ [) l! A8 h
  42. void W25QXX_Init(void);8 ~; L$ ]" p" t/ d
  43. u16  W25QXX_ReadID(void);                              //读取FLASH ID5 h. [' ^9 w' P7 I4 w
  44. u8         W25QXX_ReadSR(void);                        //读取状态寄存器
    " O+ d1 @* m& N8 ~* z
  45. void W25QXX_Write_SR(u8 sr);                          //写状态寄存器
    ; n$ S0 r3 ^$ [! G0 V7 X
  46. void W25QXX_Write_Enable(void);                  //写使能
    9 n. H! i, u+ Q& R- F2 p- T
  47. void W25QXX_Write_Disable(void);                //写保护! P. B: m) b" a- k" |
  48. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);6 M7 g; D3 g7 p% p
  49. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash5 l" t3 g- t+ y" e3 Z4 _( h
  50. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash
    5 n, a: b1 t/ _- z
  51. void W25QXX_Erase_Chip(void);                      //整片擦除& R5 H# B/ k1 `7 B: I5 k4 @' Q9 [
  52. void W25QXX_Erase_Sector(u32 Dst_Addr);        //扇区擦除9 Y8 q6 d# d; Y+ o& |
  53. void W25QXX_Wait_Busy(void);                   //等待空闲3 B2 p+ g+ L+ z7 J8 B# O
  54. void W25QXX_PowerDown(void);                //进入掉电模式
    " r, q% H5 \5 \
  55. void W25QXX_WAKEUP(void);                                //唤醒2 a9 `& A3 V$ |) }3 U9 u5 v
  56.   q3 Q  Z& ~$ \2 m  d( n
  57. #endif
    4 ^/ |: l, x& Y: q7 S2 x+ m
复制代码
( {8 U+ k  a0 O$ f# n$ W; ?$ R0 L
main.c:
' ]2 m" ~' u& C/ T& k
; J2 o4 H7 i: Y" Y: }& ~) v
  1. main.c:
    3 b; {7 h2 q- P5 J6 z; D! p
  2. #include "sys.h"
    % H1 O/ W5 N/ [) y4 U+ g+ h1 ~
  3. #include "delay.h"
    4 H9 G: U9 e# _) f( R
  4. #include "usart.h"0 E0 W, s. J* S8 {5 y+ R
  5. #include "led.h"
    7 i$ Q5 H+ {, Q- T! f4 Q
  6. #include "lcd.h"* N1 J$ F5 p3 O1 l, X& R) H0 P
  7. #include "spi.h"
    " j. n" H/ D3 l5 O0 d8 X% Q" h' v
  8. #include "w25qxx.h"
    , m  U+ l: W* c. y; I! N
  9. #include "key.h"    & U; K* f" t: C. J
  10. 8 b% U  M6 |3 y
  11. //要写入到W25Q16的字符串数组
    ; q, Y# j9 s9 V4 P9 l
  12. const u8 TEXT_Buffer[]={"Explorer STM32F4 SPI TEST"};- k1 |4 k% ?8 b5 ~  s
  13. #define SIZE sizeof(TEXT_Buffer)         
    , _5 {* D0 n+ ~8 b0 }5 F2 `6 _3 k5 x
  14.         / Y/ J% _8 b$ x. K( y  U9 g
  15. int main(void)
    , T+ Q. v! F  x9 L* o: N" t
  16. {
    # p' T; v% t. q& h6 y# ?" T6 a
  17.         u8 key;$ V$ k5 f2 u& x$ t
  18.         u16 i=0;
    * r. K1 o* C4 T+ z4 t3 x, t/ [
  19.         u8 datatemp[SIZE];        * ^& U! h) G; U! A) B3 u5 v6 c, A
  20.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
    ) g$ J  ~' p" ]* C- x1 E% b
  21.         delay_init(168);    //初始化延时函数+ y& X: l) c- x- u- r) o* p# m
  22.         uart_init(115200);        //初始化串口波特率为115200
    / q2 O$ A( j# y5 _  L3 g
  23.         LED_Init();                                        //初始化LED + e5 B+ M4 Q* c! O! l
  24.         KEY_Init();                                 //按键初始化  
    5 @- o" _( u+ N% W, T- M
  25.         W25QXX_Init();                        //W25QXX初始化 ; u/ K3 J7 F) Q% F7 u  C
  26.                         
    7 x9 }) t+ e: ]# U
  27.          while(W25QXX_ReadID()!=W25Q128)        //检测不到W25Q128
    1 B5 i& u( }+ {2 f1 P; ~
  28.         {: v6 \8 }4 ~7 v
  29.                 printf("W25Q128 Check Failed!");+ W( X& I! V% Z% R$ F, v
  30.                 delay_ms(500);( t$ g/ }. }, J" \
  31.                 printf("Please Check!      ");
    , e  u6 Y; g' ~' l
  32.                 delay_ms(500);
    $ V% t: C7 I% M# l7 O* e1 K+ z
  33.                 LED0=!LED0;                //DS0闪烁! L% p2 N8 Q- Y0 j$ |
  34.         }
    % O. |7 J" u3 y
  35.         
    1 ]2 x$ r# X- i3 l+ }
  36.         while(1)
    & i4 K9 \6 v# n# w
  37.         {
    2 D8 {- d5 m4 [3 y
  38.                 key=KEY_Scan(0);0 j1 O3 U- y0 A- M* M
  39.                 if(key==KEY1_PRES)//KEY1按下,写入24C022 o8 ~# n  @5 e- v* j9 L1 Y
  40.                 {6 E$ H0 C; L, G+ ]# d+ c+ w
  41.                         printf("Start Write W25Q128....");
    / G7 q/ D8 r2 A" u* ~& L
  42.                         //从倒数第100个地址处开始,写入SIZE长度的数据  . f* V( v5 R" a8 ~; n
  43.                         W25QXX_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);
    8 y- Y9 _! m, s  k
  44.                         printf("W25Q128 Write Finished!");  //提示传送完成
    * T5 G6 C( Q) Q7 F( z5 z6 C4 P: _$ G
  45.                 }
    0 E1 \; A' j- N  q: P/ G, [$ c
  46.                 if(key==KEY0_PRES)//KEY0按下,读取字符串并显示4 @3 |# Q7 R+ z- C
  47.                 {
    ( `3 i3 p5 `1 P0 S. E
  48.                         printf("Start Read W25Q128.... ");3 r# e5 Y; N0 m# l! n
  49.                         //从倒数第100个地址处开始,读出SIZE个字节; m3 ~0 b$ `1 ]
  50.                         W25QXX_Read(datatemp,FLASH_SIZE-100,SIZE);                                        ! X, ?, [) h* Y: h! d6 ]
  51.                         printf("The Data Readed Is: \r\n ");//提示传送完成
    - b3 Q! |" o) I4 s5 e( ^
  52.                         printf("%s\r\n ",datatemp);. K+ H  _( W. b4 q# C0 b0 w
  53.                 }           
    + O/ n) v3 F" u3 R% I) M0 {
  54.         }             3 @: S& l1 K- F
  55. }
    5 D' w- ]+ s5 m# e6 _
  56. 3 s% b* y- a+ s% j4 k, @7 }2 S9 @
复制代码

* l3 j8 G0 ]. B9 J6 ^% c) Q2 ?# F" F) `: r" B/ P

% a9 {# G) w4 q0 R! c2 z8 @  S2 ^
A71_MM@Q(@D3Z`FQYIISTMD.png
赞 1 收藏 1 评论0 发布时间:2022-5-16 11:53

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版