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

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

[复制链接]
STMCU小助手 发布时间:2022-5-16 11:53
一、SPI介绍4 a) y0 H: N6 J) u# ], _
SPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。* a) Q) i) C0 u. e' ^# y/ P3 B/ B
8 [* h/ L0 c) C. D
正是简单易用的特性,如NRF24L01、VS1053、SD卡等皆集成了这种通信协议5 P3 b; `  p# V3 K: J" o' K
/ O& l! U9 K  q9 Y+ I- y
_A_6DRLZV3W0~DW}CEZQ7{O.png
5 r; x) T" j5 z
7 R' k7 i0 `2 \  X8 D1 y二、SPI接口框图( W/ G" W5 ^2 z: o) G

0 ?7 p- F) N! H7 y4 }. b
@OFY~~NQ}EZ}HLQL{EB%0ND.png
. \$ r8 c! U1 ^8 h( D# y
' U1 ]. a1 r) L: L三、SPI优缺点
( _2 c, x, I% N/ \* f
SPI接口是在CPU和外围低速器件之间进行同步串行数据传输,在主器件的移位脉冲下,数据按位传输,低位在前,高位在后,为全双工通信,数据传输速度总体来说比I2C总线要快,速度可达到几Mbps。
, d, ?( f0 z$ y7 K+ q5 b) h0 S; n) e, H+ |4 R- r3 [8 R
信号线少,协议简单,相对数据速率高。
$ L6 i9 l3 [6 |2 b% q
. P8 ~, ?6 E5 m4 n) v缺点:没有指定的流控制,没有应答机制确认是否接收到数据+ @$ @9 N3 \3 j8 P  V+ r/ R
* e3 M$ L( k; p0 K  D; k! l
四、SPI工作原理总结
( ~* q! e$ |; l3 D3 v( `硬件上为4根线。
8 G* m! Z+ q8 _- \1 ^( m$ x主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。" P7 e8 k5 A2 `: X# `
串行移位寄存器通过MOSI信号线将字节传送给从机,从机也将自己的串行移位寄存器中的内容通过MISO信号线返回给主机。这样,两个移位寄存器中的内容就被交换。
, c; e: d0 c  v6 Q, D/ X# B外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。
# Z1 `8 g6 K8 ?1 k6 D多个设备使用SPI的应用举例% j* M' y  L* a( [

- S$ y/ l3 f. S (_8GH(KLZ4`~[RSWOUL_B{Y.png 8 H& E7 x' b/ L* p6 ?; K% S* V* \5 m

$ a( y7 p' v# A% H8 ~五、时序图5 c/ q* T7 Z. w- i! O, d3 g7 @- G
时序为 SPI_CR1 寄存器中的 LSBFIRST 位复位时的时序
& U" J+ i/ `* |$ G$ g
4 p  ?! \0 r7 g% kSPI_CPHA的值将会影响SPI_CPOL的值
! d9 q9 m; Q! P- w7 E+ j0 x9 C  H6 s# b  }
4R1]OS0}$R`DITZTCDX~T~D.png
/ ~3 P& V: I; k7 i% |% z/ K
0 _6 ]% ?8 u& n) h2 y) u六、SPI程序编写过程. o4 b* h/ V# e/ }: _1 o, K% \
  1. //①使能SPIx和IO口时钟
    * @! v8 N- s7 S7 o% [( j
  2. RCC_AHBxPeriphClockCmd() / RCC_APBxPeriphClockCmd();, q$ ]3 @2 n! n, A0 R
  3. % p! c0 b, K* ^& V& Q, a
  4. //②初始化IO口为复用功能
    / Y9 Z' M) Y4 `4 Q% F
  5. void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);+ D6 v( T" p3 k. ~

  6. 1 C# [+ L/ E* t# L" T' o- M7 C
  7. //③设置引脚复用映射:
    9 s0 `- ~% P! P8 _- ^& w! e' X
  8. GPIO_PinAFConfig();
    : C7 A& h5 T. k4 M

  9. " o0 n; g4 n  V
  10. //②初始化SPIx,设置SPIx工作模式4 U! W1 x+ _+ j0 A& n
  11. void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
    " @0 {, e% q8 L2 ^

  12. & N- _/ D: X0 U& X3 d" y9 j1 q) m
  13. //③使能SPIx
    5 }) R) \* f4 ]  m3 l5 B% u
  14. void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
    ( X+ ?/ U% Q" I0 l3 \
  15. ( {8 {0 {  T! C
  16. //④SPI传输数据& b- k) _, j+ [8 q9 }- R
  17. void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);4 a3 R6 @( J1 ~" ]6 E
  18. uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;* l; s4 v. k. V
  19. ! ^4 r: I6 W7 o1 }
  20. //⑦查看SPI传输状态
    * z6 [, l4 w& L- I5 x
  21. SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);
复制代码
  1. SPI.c:$ \7 b  q: O- M1 H
  2. #include "spi.h"
    / G  k; {% w0 x7 c
  3. : i+ w  u1 K( e! y1 E, X9 N* M
  4. //以下是SPI模块的初始化代码,配置成主机模式                                                   
    * `$ R$ ?( w0 ^* p8 P5 ]
  5. //SPI口初始化
    6 |; ~9 h( V! P( Y$ j8 f
  6. //这里针是对SPI1的初始化
    9 E. a- e3 N  h& O" W2 D
  7. void SPI1_Init(void)
    / y( b9 x7 O1 f; N8 J  `' C& ]
  8. {         0 w, h4 K' Z; m0 [
  9.   GPIO_InitTypeDef  GPIO_InitStructure;8 r1 X, S# F' t' ^3 K4 m
  10.   SPI_InitTypeDef  SPI_InitStructure;. N! [, X( w2 B/ ?
  11.         
    , U8 C" @. h) `2 |8 n" _4 s" z! H
  12.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
    ! Z# X+ F1 ]: D3 f& \
  13.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟/ t7 `, p, Y5 l5 `& }) j

  14. $ X# Y' P- c2 n  ?% Z  n% v, M9 a
  15.   //GPIOFB3,4,5初始化设置
    4 c2 Z, P0 a& F6 l8 E! e
  16.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出        2 _8 F  l9 D* s6 U# J- p
  17.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能% K, n0 ~+ t, |0 i, q4 v# o7 I
  18.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出4 Y, d) l0 f# H# z) ]& |0 e' ^
  19.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz& X7 F( `0 \1 f5 o
  20.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
    + Z5 ^$ c/ ~. J8 x5 X
  21.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
    6 e1 T- Z* J  i+ i# i6 [
  22.         
    : C: N1 K* q, Z  f8 Q) m' P
  23.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为 SPI1
    5 r8 B, M9 ?; O- E* F
  24.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为 SPI11 [9 R: h3 I9 R
  25.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为 SPI1
    # I- f5 q" O* ?& L! R$ q' ?
  26.   t: K9 j+ k1 U* [; r$ B' w* p
  27.         //这里只针对SPI口初始化8 s8 a2 T6 u0 p
  28.         RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI12 d9 V$ T4 |: v( B0 j% P
  29.         RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1
    9 k  s1 Q0 m  F5 G2 Y* e* S
  30. , d7 q9 N. o/ W1 a9 j
  31.         SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工0 y0 a2 d, u' \2 i6 f* M/ _
  32.         SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                //设置SPI工作模式:设置为主SPI
      O7 Y$ V2 q+ V" G) N
  33.         SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                //设置SPI的数据大小:SPI发送接收8位帧结构
    - l' ^! K3 h" X/ |; P4 p* |
  34.         SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                //串行同步时钟的空闲状态为高电平; O. ^( a/ ~9 O  |: |
  35.         SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;        //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
    2 I4 U- `  V  t! |) v
  36.         SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制5 l1 a6 ^: u- v8 F) o9 D% ?
  37.         SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;                //定义波特率预分频的值:波特率预分频值为256: ~$ [/ Y# l; b
  38.         SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;        //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始- j' J' q( q- ~& _3 i2 H! J; ]4 B1 m
  39.         SPI_InitStructure.SPI_CRCPolynomial = 7;        //CRC值计算的多项式
    # D* T' m6 n. U3 h
  40.         SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器3 v) o, X3 L" t! q8 C' i2 p; B

  41. " i" i' ~% n5 C1 u
  42.         SPI_Cmd(SPI1, ENABLE); //使能SPI外设
    / d! h4 l9 R1 B% M$ X9 p
  43. : @/ |3 O; w7 k8 {
  44.         SPI1_ReadWriteByte(0xff);//启动传输                 ) J5 q2 }8 S& Q& s( s+ t
  45. }   
    ! v# i2 i2 D8 c
  46. //SPI1速度设置函数( }, ^+ w5 s$ H% @/ K  e
  47. //SPI速度=fAPB2/分频系数
    9 w# C- H9 U+ h+ E
  48. //@ref SPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256  & p( G4 Z/ T1 B$ H5 _
  49. //fAPB2时钟一般为84Mhz:
    : q" R, N; t! ?
  50. void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)2 }/ g5 e% ~* Q' g8 L
  51. {9 \% T+ W8 N5 z! a: I( N. S6 L; I
  52.   assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性
    0 X* L" D2 o# i( \1 V! p1 D
  53.         SPI1->CR1&=0XFFC7;//位3-5清零,用来设置波特率5 B! o+ Y7 s; n, m
  54.         SPI1->CR1|=SPI_BaudRatePrescaler;        //设置SPI1速度
    5 }7 g0 f1 F' a( r' Y; f+ |
  55.         SPI_Cmd(SPI1,ENABLE); //使能SPI1
    " E( h+ }1 E0 G  d4 g
  56. } 2 z7 s: e3 u& r) u" `
  57. //SPI1 读写一个字节
    : o# @1 K" I: P. r
  58. //TxData:要写入的字节  S1 n: B- q6 x1 O. R
  59. //返回值:读取到的字节) |) v7 ?  x/ ~/ y
  60. u8 SPI1_ReadWriteByte(u8 TxData)
    2 v4 o: R# G: J. D& X
  61. {                                          ; H5 a+ ~  N  x! X8 z
  62. 6 _- ]& R2 h# ^1 e. J' a* f! `- J
  63.   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空  
    ' Z: [0 B- e; P
  64.         
    9 d' @" L6 }! j
  65.         SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte  数据7 G6 X& z! W/ K2 X2 I% t
  66.                 & x! L' j7 }8 p, w0 }7 Q  J
  67.   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一个byte  
    8 y3 \; V* y2 y5 Y

  68. # {, A7 |( g  w. }* O! C
  69.         return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据        
    & {# V5 \2 v: D5 G# F
  70.                      
    ! T3 X& }7 y) X" S4 {5 j# t3 q' F
  71. }
    5 u, I9 i3 S; y: I

  72. ; Y. H9 {' D- X5 t2 f, b
复制代码

: r. r) U: h! X, M, Q; Y- Q七、W25Q12xx的原理及应用* u' s* J, a0 k' L) ^
W25Q128将16M的容量分为256个块(Block),每个块大小为64K字节,每个块又分为16个扇区(Sector),每个扇区4K个字节。W25Q128的最小擦除单位为一个扇区,也就是每次必须擦除4K个字节。这样我们需要给W25Q128开辟一个至少4K的缓存区,这样对SRAM要求比较高,要求芯片必须有4K以上SRAM才能很好的操作。! p, h3 G% u1 Z- D
# {4 M' c8 I/ @: J, J) @, m2 F% k
W25Q128的擦写周期多达10W次,具有20年的数据保存期限,支持电压为2.7~3.6V,W25Q128支持标准的SPI,还支持双输出/四输出的SPI,最大SPI时钟可以到80Mhz(双输出时相当于160Mhz,四输出时相当于320M),更多的W25Q128的介绍,请参考W25Q128的DATASHEET。2 Z6 Q/ F; c/ t% X6 D

. P: e/ x% K, }" T" W% y' m D%)W27U]]MDR`PRB[FT4~Z1.png
2 `1 S! h/ I2 H, W. z7 a
, P8 P5 k; l, P7 k: q0 QW25Q12xx可根据原理图查看,使用的是SPI总线通信协议+ w/ D; u' ^) C' t4 r. }+ I' _
& k4 Q3 D- {" N9 F1 F5 Q, A
比如原子的原理图
" C$ E: p" _8 l4 E6 G. I, G, t* z0 C5 U
20190611221654679.png 5 m- a( ?" J2 a( n+ u3 B7 S
: B, \9 b3 G4 i* P
7.1 分析W25Q128指令
6 N0 ^3 T0 M- d可参考W25Qxx的数据手册,这里列出W25Q128部分指令:
* u$ N) C3 ~8 d5 x  M6 K$ m' E7 U, c! A
2019091611364032.png
9 V4 v& @  I6 k+ ^9 y( X
) g) S! C- }/ T: }, C& M7 e' m7 M比如读取设备的ID的指令:0x90000000
2 G" _. ^5 {3 P7 z2 g% o) Y/ ]
( G. w3 ~4 g8 e& W6 h( m+ x 20190916113704620.png * Y5 i8 C+ S6 Q8 V
' z* E0 q8 n, M; r8 [0 A9 O, d
K$HWO9ZGFGE$CVLD47Q]_4G.png % n# Z8 r. }: Z" h
! G5 v* g$ V. p  Q
WLLL(({{_EVAN[005V791UU.png
3 j* c  {/ h1 q; m5 Q& A! p; [
! R7 v$ b2 F! b* U' K# b$ S7.2 擦除扇区:
! |. R6 W4 V. i9 l1 x- v; e
; g, _6 N$ O& `  R# g
NZ$~TU098T[KO5)GTQW){UQ.png 8 A# G+ d* ]2 [( ], g

: C, [' W+ m4 I% z7.3 部分常用设备读取指令:

  Y) h' Y! ?+ W% e/ z7 P0 n' {. w/ B$ O, p: a9 {1 M
Q`V(R2ZUHAZ8SAS2[A}1H%2.png ' f7 E6 A/ O# S& j: d# T0 V$ {/ ]
' j: i0 L' S. _; A1 {3 O1 _
每次操作前要使能写操作,且给一个低电平。结束时要给其为高电平,再失能写操作。也就是通过操作片选引脚来确定是否使能或失能。' B: N1 e! @" u! i! |9 U2 d

/ c+ Q0 }: ~' f  V3 M8 H  V LR6P1TX%{R][_M{CX4]K.png
  K2 k1 z- f) b6 w' h. L" H' B
3 e0 P3 R; z) S. Z* G' u- h
  1. W25Q12xx.c:' t1 N/ x! R9 ]) `/ N  ?! v1 s
  2. #include "w25qxx.h"
    3 c+ L- @8 _6 v/ y$ x" V* q
  3. #include "spi.h"
    * y( `* Z0 l* K2 o4 a- |) O( ~
  4. #include "delay.h"           5 X& A/ M  ?% t7 ^2 }" v" l
  5. #include "usart.h"        / h, G* j, o4 D' @

  6.   q0 [) W; P9 t  P' m% Q$ m
  7. u16 W25QXX_TYPE=W25Q128;        //默认是W25Q1283 W2 E2 U: `7 N, p8 N
  8. - N2 \: s$ V1 {/ L) z. [2 ?4 e
  9. //4Kbytes为一个Sector
      Y" V/ O* a4 G/ ~$ X( I  |; y% E
  10. //16个扇区为1个Block
    4 ]) d( n/ I7 H% S% h+ n5 I
  11. //W25Q128
    9 b, O3 Z5 Q5 ~8 X7 a
  12. //容量为16M字节,共有128个Block,4096个Sector $ H' V% E: \9 G$ _" m2 h2 N
  13.                                                                                                          3 F2 Y$ A3 G! n; ?8 H9 W
  14. //初始化SPI FLASH的IO口
    0 t9 M& u4 V; U. w$ q4 \3 a7 K4 D8 w
  15. void W25QXX_Init(void)
    7 a- o) m4 w: D$ F0 m, v& T% p
  16. { $ V; W) q, ?7 s$ ~
  17.   GPIO_InitTypeDef  GPIO_InitStructure;
    9 b( P5 O; i$ i3 t# |7 Z" w
  18. . U0 v1 U$ }1 a) u! Y# B4 W7 h
  19.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
    $ p5 j( {: ~+ n. ?1 S
  20.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);//使能GPIOG时钟6 k4 D6 f$ |. H% @/ a
  21.   A6 @/ @! X: b
  22.           //GPIOB14
      c8 Z% G5 ]3 E- r5 t& h
  23.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;//PB145 F% W3 j3 \8 @6 x- N# Z) F" {
  24.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出0 N8 @+ X9 E/ s* T" C' z/ y
  25.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出+ ?  {- w% r9 Z$ k0 T
  26.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    % m: e( _5 O$ B
  27.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉; ~9 s' O0 t. ^
  28.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
    . o. u% s! j# p2 S' S2 Z
  29. . O( B- a2 Y( b5 d' H
  30.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//PG7
    6 u, t9 M8 j$ h- L
  31.   GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化, o6 Z! B( q' f0 L. G% R; K
  32. 5 I: k  W' T3 I% r- @6 [
  33.         GPIO_SetBits(GPIOG,GPIO_Pin_7);//PG7输出1,防止NRF干扰SPI FLASH的通信 & y0 y1 q3 x6 U. l9 ?" C
  34.         W25QXX_CS=1;                        //SPI FLASH不选中
    2 [5 N1 H, K5 u
  35.         SPI1_Init();                                           //初始化SPI
    " f& w5 L4 @* z, b( ^" x
  36.         SPI1_SetSpeed(SPI_BaudRatePrescaler_2);                //设置为42M时钟,高速模式
    7 U* \6 J+ a0 t# q/ N
  37.         W25QXX_TYPE=W25QXX_ReadID();        //读取FLASH ID.9 B" e" @5 k# ]  Q- O* Y: v
  38. }  
    , Y! \, Z: Q5 J* }. ^1 v
  39. $ e, O, x! S: ^+ b. f- z
  40. //读取W25QXX的状态寄存器
    $ X  I+ _3 |1 P& V6 W2 Q
  41. //BIT7  6   5   4   3   2   1   0  R/ C4 R% \7 ]# ]0 s
  42. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY
    ' G8 {7 J. b6 T
  43. //SPR:默认0,状态寄存器保护位,配合WP使用& l. J2 Q7 t6 g- x3 @5 l( v
  44. //TB,BP2,BP1,BP0:FLASH区域写保护设置
    6 V& Q, j# \" Z* \, `6 }
  45. //WEL:写使能锁定
    4 F: o8 g6 K& o* P0 q6 y( A% D
  46. //BUSY:忙标记位(1,忙;0,空闲)9 e6 C% g3 {$ x& y: j" S
  47. //默认:0x00
    / ]/ K5 f" c$ i4 ^
  48. u8 W25QXX_ReadSR(void)   # g1 |+ ^% R; q- @# o
  49. {  8 F, Q# `: {( w& ?4 w2 M8 |8 C* _. h
  50.         u8 byte=0;   6 w& b/ s- X/ i) V
  51.         W25QXX_CS=0;                            //使能器件   : p  s2 n1 |3 n, \. G* ]6 k
  52.         SPI1_ReadWriteByte(W25X_ReadStatusReg);    //发送读取状态寄存器命令    6 q2 z- A7 V& J% I4 x  Y9 W4 z2 k
  53.         byte=SPI1_ReadWriteByte(0Xff);             //读取一个字节  3 J7 q: R! N+ _
  54.         W25QXX_CS=1;                            //取消片选     $ t% y' |: p" N* Y8 \# R4 R
  55.         return byte;   
    ) Q7 D5 q9 Q& R7 ]4 M3 a9 Z
  56. }
    0 G( L. k5 E2 m) J
  57. //写W25QXX状态寄存器; ?! l" n& ^6 z, t" i& e; O
  58. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
    : ^& I. W' o0 T
  59. void W25QXX_Write_SR(u8 sr)   4 f6 }5 U% a% W
  60. {   
    ! a! c- l& E5 s% \! p# J6 s. \7 |
  61.         W25QXX_CS=0;                            //使能器件   5 V+ d) n3 I0 S- O/ j+ W
  62.         SPI1_ReadWriteByte(W25X_WriteStatusReg);   //发送写取状态寄存器命令   
    9 t: J' C0 d8 j) }% X! N; M
  63.         SPI1_ReadWriteByte(sr);               //写入一个字节  3 a" x; H$ h  p- A/ O
  64.         W25QXX_CS=1;                            //取消片选                   * U6 I1 o8 L( \
  65. }   : H; w5 N9 A1 ^+ Z2 W7 d) d0 _5 U
  66. //W25QXX写使能        
    - P* G& d" c, Z* z5 ]
  67. //将WEL置位   2 \# }- E; F1 o2 b3 ^1 R& }
  68. void W25QXX_Write_Enable(void)   
    . B, O3 I- G! k9 \4 `5 O
  69. {
    % `/ m* k. r. b  C) C8 G
  70.         W25QXX_CS=0;                            //使能器件   
    ( o& X: i/ ]6 Q  _9 |; Z9 B
  71.     SPI1_ReadWriteByte(W25X_WriteEnable);      //发送写使能  1 K( m( \% t# J& X* G  u+ g; k' O
  72.         W25QXX_CS=1;                            //取消片选                  
    + _3 a  a# E: l+ |" ~( d3 e. R. n
  73. }
    + z) P% I7 k+ x' e; _
  74. //W25QXX写禁止        , G0 h+ E3 m6 |5 }; ?# v
  75. //将WEL清零  
    1 p4 t0 u- |9 ^9 L- I# g9 ^
  76. void W25QXX_Write_Disable(void)   
    ' V$ ^- D3 O+ m. @+ v& R' y0 G3 C3 m
  77. {  
    4 F# w7 Z' [" z7 k
  78.         W25QXX_CS=0;                            //使能器件   4 m4 z6 w! O! T$ Y2 u( A% G. S
  79.     SPI1_ReadWriteByte(W25X_WriteDisable);     //发送写禁止指令   
    3 z5 B7 A& z3 I- V4 z! W9 p
  80.         W25QXX_CS=1;                            //取消片选                  
    ( Q1 ]* Q/ t, a* N2 t
  81. }                 ; q* R# m) ]1 m  P$ t8 `4 r
  82. //读取芯片ID
    ' |; d' ~' Y  Z! b, F) m9 y' X# @
  83. //返回值如下:                                   * F+ r! Y& {; f; O1 r: N
  84. //0XEF13,表示芯片型号为W25Q80  
    ! }7 B# N/ b% _6 C# F
  85. //0XEF14,表示芯片型号为W25Q16   
    8 e- S; a! I' e- N( k; |
  86. //0XEF15,表示芯片型号为W25Q32  0 a" S" `& o8 c9 [
  87. //0XEF16,表示芯片型号为W25Q64 6 x0 ^6 i7 ~8 M# R$ v" H
  88. //0XEF17,表示芯片型号为W25Q128           5 i, N6 j! L3 x6 X% ~1 G* {
  89. u16 W25QXX_ReadID(void)  O5 q% Z) D7 h' r1 j! d
  90. {
    3 |6 U& V/ v0 H% N+ l
  91.         u16 Temp = 0;         
    : h% F: _6 x2 P! T' e
  92.         W25QXX_CS=0;                                    
    ( p/ W4 ]/ s3 P8 j) O4 g6 @* C/ o
  93.         SPI1_ReadWriteByte(0x90);//发送读取ID命令            ) p& X) f5 T+ P$ M! i  Y
  94.         SPI1_ReadWriteByte(0x00);            
    " r" ~% J2 D, s0 V; z
  95.         SPI1_ReadWriteByte(0x00);            
    6 _% M3 F7 `, U) P0 J
  96.         SPI1_ReadWriteByte(0x00);                                     , s: ]: v+ `7 T8 K, d% H+ a) D
  97.         Temp|=SPI1_ReadWriteByte(0xFF)<<8;  & O, m) `  ^, R
  98.         Temp|=SPI1_ReadWriteByte(0xFF);         6 P8 q, h2 r" g8 o
  99.         W25QXX_CS=1;                                    
    & Q. }0 f# [- z# s( ~
  100.         return Temp;
    ) f6 F3 m% l2 q% I( z5 u& d  m
  101. }                       / D% ^' c/ b: |/ m' V
  102. //读取SPI FLASH  8 G( k& ?0 Y% X  q& [
  103. //在指定地址开始读取指定长度的数据2 _& B+ O7 a# L" P) g1 A$ G  n
  104. //pBuffer:数据存储区
    , y2 e' x& b% l) T+ g; E( B' c
  105. //ReadAddr:开始读取的地址(24bit)
    # B* V+ I# U8 `
  106. //NumByteToRead:要读取的字节数(最大65535)
    5 }9 j0 U- W5 O1 r% g9 \+ Q# ~
  107. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   
    $ n' Y- n6 }1 n( I
  108. {
    3 T7 V# Y! ^2 Y) @
  109.          u16 i;                                                                                       
    " E) ?5 U" D  [
  110.         W25QXX_CS=0;                            //使能器件   
    5 ^! ?6 f, w# X
  111.     SPI1_ReadWriteByte(W25X_ReadData);         //发送读取命令   
    ) K2 n! m- s0 _
  112.     SPI1_ReadWriteByte((u8)((ReadAddr)>>16));  //发送24bit地址    & c6 B3 a% i7 a4 i+ B
  113.     SPI1_ReadWriteByte((u8)((ReadAddr)>>8));   
    - Q2 z. m# j. e- ?! k2 d4 U1 q
  114.     SPI1_ReadWriteByte((u8)ReadAddr);   6 Y; a# I" \! b. E2 |
  115.     for(i=0;i<NumByteToRead;i++)
    $ U, T, _4 ^- \7 U4 L7 ~. A! F0 x) s$ g
  116.         { # |3 p, r: O& O4 r1 Q( I* O
  117.         pBuffer<i>=SPI1_ReadWriteByte(0XFF);   //循环读数  5 \! h2 E1 x- f; s# X
  118.     }
    7 x* X! R4 u- b5 _& q
  119.         W25QXX_CS=1;                                                   
    1 e+ {0 q$ x7 x- l8 F( P
  120. }  2 R9 O% R2 F7 g4 t
  121. //SPI在一页(0~65535)内写入少于256个字节的数据1 t; A" v4 x2 J( a* P% r3 Y
  122. //在指定地址开始写入最大256字节的数据; N5 F9 c4 j. \. h
  123. //pBuffer:数据存储区
    % K$ N" P5 A2 J# Y; w
  124. //WriteAddr:开始写入的地址(24bit)# A/ G" d$ U! x
  125. //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!         " v- ^6 ^0 J; E+ f5 J/ F
  126. void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
    ' x) E" X* n2 W( F1 c$ q: j
  127. {6 p6 z( |% k! F
  128.          u16 i;  . d* p2 |1 n. Y# I
  129.     W25QXX_Write_Enable();                  //SET WEL   ?! f8 Z/ U" [+ R
  130.         W25QXX_CS=0;                            //使能器件   8 }8 }; G4 }7 o, p
  131.     SPI1_ReadWriteByte(W25X_PageProgram);      //发送写页命令   8 [1 Z. |  u' c8 m) K3 U
  132.     SPI1_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址   
    0 \' V3 e+ P( z
  133.     SPI1_ReadWriteByte((u8)((WriteAddr)>>8));   
    3 L  u" q7 [9 d+ ^) s! k
  134.     SPI1_ReadWriteByte((u8)WriteAddr);   ) k' [+ ~" n; M; D8 e
  135.     for(i=0;i<NumByteToWrite;i++)SPI1_ReadWriteByte(pBuffer<i>);//循环写数  7 G6 }2 K: R: P% q+ h
  136.         W25QXX_CS=1;                            //取消片选
    ' [" }' q8 F4 z8 W
  137.         W25QXX_Wait_Busy();                                           //等待写入结束8 c1 z4 W+ x! s. k% U. |" Q5 @
  138. }
    ' H, V( T% O! _1 W  |2 e5 b
  139. //无检验写SPI FLASH   N. a, b% `, A4 j* L& [" m) K
  140. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
    + P, K- O, [8 a6 }3 ]7 S
  141. //具有自动换页功能 ) W: b  N' ^' H2 d) R1 c' _
  142. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!5 F% O# T, Q2 |" K+ `
  143. //pBuffer:数据存储区2 p' E6 E, G0 v1 o
  144. //WriteAddr:开始写入的地址(24bit)
    $ Y0 b# g- ~2 [, i$ e) b
  145. //NumByteToWrite:要写入的字节数(最大65535)
    1 O  t; g) {' B. i% x6 T3 e
  146. //CHECK OK# J$ q% k9 X" m& X2 x/ y' p
  147. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
    / w; Q. f  g7 k( i2 Z
  148. {                                           ! k2 u( x3 m+ u+ y" S. d& X
  149.         u16 pageremain;           6 C& h! h4 |% p) m1 P: \. T% o9 V
  150.         pageremain=256-WriteAddr%256; //单页剩余的字节数                             - ^2 C1 S6 \0 R0 ?5 o
  151.         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节4 j% H1 l, }# @1 {/ v# u0 q# W/ {
  152.         while(1)  l" G1 W2 `- ^4 n5 C  p
  153.         {           
    2 v4 [1 |3 P" q
  154.                 W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
    / l8 I) ~" y' E" M2 i4 E( ~5 j
  155.                 if(NumByteToWrite==pageremain)break;//写入结束了$ V! p& T4 j& V0 R, B( X" N# P& B
  156.                  else //NumByteToWrite>pageremain
    % H" Y# P/ j, S5 ]* Z$ }
  157.                 {
    % {/ ^! t0 _' ?- z8 E' S
  158.                         pBuffer+=pageremain;/ I, R! `1 ^* T. W' N5 C" N" j
  159.                         WriteAddr+=pageremain;        
    " @; D$ \6 [: i  Y
  160. : H& V" ~  a; e/ Z% I7 H0 Y3 J
  161.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数
    ) o6 l: ]7 t% L# D
  162.                         if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
    , S$ f& |) c. m
  163.                         else pageremain=NumByteToWrite;           //不够256个字节了$ A, G4 V% ~1 B2 V$ H9 m! k( i
  164.                 }
    , v+ l8 F$ B! C  O
  165.         };            8 S  r- [6 Z3 N. }
  166. } 4 V' R* V5 }8 _. J3 i* P
  167. //写SPI FLASH  - _  A" W0 j* j
  168. //在指定地址开始写入指定长度的数据
    # ~6 V2 E! G  \) S2 \
  169. //该函数带擦除操作!" h8 }- ~. K4 p9 _7 J
  170. //pBuffer:数据存储区
    $ R0 O9 M0 Q- P0 X
  171. //WriteAddr:开始写入的地址(24bit)                                                
    / u( y  ]$ M  N, Z& f8 c
  172. //NumByteToWrite:要写入的字节数(最大65535)   ! p9 P0 _: K* b  a
  173. u8 W25QXX_BUFFER[4096];                 5 F% |) e8 N) U  u2 T
  174. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   ' O6 c  t3 K: O+ m! v" q4 n  H- \
  175. { 2 g1 P* t+ L- [, n, y2 x4 O
  176.         u32 secpos;" u* w( d' h- l
  177.         u16 secoff;" w+ u* S& p# }0 h' z
  178.         u16 secremain;           
    7 R$ R* c8 t6 w- x' q! t
  179.          u16 i;    4 s# Q7 u3 m' [8 }( e
  180.         u8 * W25QXX_BUF;         
    / K5 @" o& T* p7 ]: {; {
  181.            W25QXX_BUF=W25QXX_BUFFER;             ' f: C& b) {' }; d8 P% }, z8 p
  182.          secpos=WriteAddr/4096;//扇区地址  9 K$ N: @3 m8 J" W& h
  183.         secoff=WriteAddr%4096;//在扇区内的偏移
    1 n! \' l% D0 C9 |& x  f7 C; ?
  184.         secremain=4096-secoff;//扇区剩余空间大小   
    $ h9 q' q) r( X: A
  185.          //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
    ; P# ~6 \' L1 \0 Q% ^
  186.          if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节
    " d, e' ?, {' z' E3 q4 V
  187.         while(1)
    " Y' }' s% a* g+ C
  188.         {        4 q7 h! i4 X8 }1 b
  189.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容0 o: z& e/ ?$ c* z/ R( V
  190.                 for(i=0;i<secremain;i++)//校验数据
    3 F& S2 q/ I, X1 F
  191.                 {
      E( ?3 n& [# W* P
  192.                         if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除            ) h- @7 Q4 E" Q4 u
  193.                 }
    3 Y* ?' O9 g: r8 j. C
  194.                 if(i<secremain)//需要擦除
    + h* H' N, r* ]8 ~, f  _; [
  195.                 {
    : c9 z, b  z( |6 q) Q* x. }- k: N
  196.                         W25QXX_Erase_Sector(secpos);//擦除这个扇区
    ) p! V) J2 E1 d9 i& e
  197.                         for(i=0;i<secremain;i++)           //复制
    0 a* }$ \5 _$ a# C( \9 b
  198.                         {
    9 Q4 e; g' B- {8 F' b
  199.                                 W25QXX_BUF[i+secoff]=pBuffer<i>;         
    8 H1 M. X! @" o
  200.                         }6 X2 ~/ P" O- N& T  n
  201.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  3 |& m# R7 D) G3 C' o

  202. 3 F/ U" l5 r% _# D7 p
  203.                 }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    
    9 |/ w- E2 Y/ A( B* N: }/ K( |
  204.                 if(NumByteToWrite==secremain)break;//写入结束了
    / |2 O# O$ I+ D$ N& @# Z
  205.                 else//写入未结束) e- u- |) Q0 t
  206.                 {
    ! K% G5 w+ N) l. E7 u
  207.                         secpos++;//扇区地址增1
    - p: P) E# X8 I3 E% Z6 {/ l
  208.                         secoff=0;//偏移位置为0          0 x5 c2 l7 y+ \( \6 n! W: h

  209. . m( t1 R6 \+ \: m; Q+ o" W8 v
  210.                            pBuffer+=secremain;  //指针偏移# V% q0 Z& y" n9 r
  211.                         WriteAddr+=secremain;//写地址偏移           
    ; ^" _6 ~6 h$ R8 [6 h$ b6 N! p  y+ L% ~
  212.                            NumByteToWrite-=secremain;                                //字节数递减! j, g2 ]9 R( r& r9 G& Y
  213.                         if(NumByteToWrite>4096)secremain=4096;        //下一个扇区还是写不完
    6 O2 r4 }0 J6 u- ]
  214.                         else secremain=NumByteToWrite;                        //下一个扇区可以写完了( j" b2 T' q* c5 l& j4 Z
  215.                 }         
    2 ?% v+ T1 j) [/ c  r2 F
  216.         };         
    9 j/ L8 u* m* Y6 P
  217. }. _) _5 C! @. `# k, m
  218. //擦除整个芯片                  6 R0 S% M8 D  v; W* q5 F
  219. //等待时间超长...3 t$ X9 W7 G6 O, N, J) i
  220. void W25QXX_Erase_Chip(void)     M6 N+ k( ?2 J' }+ q  q/ J
  221. {                                   
    ; v3 x0 e7 \2 ~3 |
  222.     W25QXX_Write_Enable();                  //SET WEL 2 Y0 y) |/ E; ]; N. F2 q
  223.     W25QXX_Wait_Busy();   
    ( Z" ]3 c, j) K) l9 T
  224.           W25QXX_CS=0;                            //使能器件   
    0 d) w- _/ G- H$ N4 q4 {, R
  225.     SPI1_ReadWriteByte(W25X_ChipErase);        //发送片擦除命令  
    8 z7 b$ |& q5 W% T- L+ N- [4 ]' z
  226.         W25QXX_CS=1;                            //取消片选                   0 |) S, o2 ?1 ]2 Q3 [
  227.         W25QXX_Wait_Busy();                                      //等待芯片擦除结束- B3 k1 O; r6 m  E5 k9 U
  228. }   
    1 V* d2 Z7 j( B# t) Y
  229. //擦除一个扇区3 F( z6 ~- Y1 `+ L* b1 S& @
  230. //Dst_Addr:扇区地址 根据实际容量设置8 _7 q) ?3 y  e# H4 e
  231. //擦除一个山区的最少时间:150ms& @, V: f9 k! [- g8 _7 N
  232. void W25QXX_Erase_Sector(u32 Dst_Addr)   
    ! b) K% K  m  H8 j9 k% t
  233. {  ( U( y* M! {$ a% V/ T. h) G
  234.         //监视falsh擦除情况,测试用   7 E$ C* g7 M. u1 ^2 J: n0 k5 w. l
  235.          printf("fe:%x\r\n",Dst_Addr);         
    + A7 ]8 C; d7 y" h1 b' |0 w  H
  236.          Dst_Addr*=4096;+ T) j1 m: C5 l) _
  237.     W25QXX_Write_Enable();                  //SET WEL          ' F1 T, l$ i) i% f8 @5 K
  238.     W25QXX_Wait_Busy();   7 @$ U, W+ N, x* i4 ^3 d6 e
  239.           W25QXX_CS=0;                            //使能器件   ' m3 |+ u% x. @0 P' d' q% r
  240.     SPI1_ReadWriteByte(W25X_SectorErase);      //发送扇区擦除指令 2 I5 }/ _, s4 e0 n# k: K# ^1 o. X" R
  241.     SPI1_ReadWriteByte((u8)((Dst_Addr)>>16));  //发送24bit地址   
    3 g% g& ~' j& R5 T2 c
  242.     SPI1_ReadWriteByte((u8)((Dst_Addr)>>8));   
    + G0 y. e$ J- H/ i0 D
  243.     SPI1_ReadWriteByte((u8)Dst_Addr);  
    1 b1 C8 M. a) s* D$ w6 R
  244.         W25QXX_CS=1;                            //取消片选                   " o3 z% K2 ^0 h0 M' A& l
  245.     W25QXX_Wait_Busy();                                      //等待擦除完成
    + [& |% z5 E- ?
  246. }  4 F; J0 v  F0 n2 c( l' o8 h5 e
  247. //等待空闲
      n9 ?, C2 o3 j& h6 W
  248. void W25QXX_Wait_Busy(void)   * M: w2 d3 q: W+ I7 Q4 ?
  249. {   ) [" w9 o0 n9 ?  @8 c7 k0 k
  250.         while((W25QXX_ReadSR()&0x01)==0x01);   // 等待BUSY位清空
    1 H! H9 [: {( w4 O4 R4 b/ L8 L
  251. }  ; x2 v, l( A- i+ g6 [3 P: X
  252. //进入掉电模式
    6 X% X9 u. t" {" g- y
  253. void W25QXX_PowerDown(void)   9 ]4 ~! ?% Y3 k" S) k6 `
  254. { $ J  T0 p# Y2 C) b/ l
  255.           W25QXX_CS=0;                            //使能器件   3 k) g+ t1 U, \# f4 U5 d; B) S
  256.     SPI1_ReadWriteByte(W25X_PowerDown);        //发送掉电命令  8 C( W- V4 t5 ^5 r/ r
  257.         W25QXX_CS=1;                            //取消片选                   * }. Z4 @. R& F3 Z( n
  258.     delay_us(3);                               //等待TPD  - D: j* l# u  X9 n5 K$ U/ o
  259. }   ( K/ U( {1 G+ P6 V
  260. //唤醒
      Q6 R3 l& F3 H- o
  261. void W25QXX_WAKEUP(void)   
    $ ~, l. J$ m  _% m, M' y: j9 W
  262. {  
    * ?9 L" z' I% j$ K, j& @5 ^
  263.           W25QXX_CS=0;                            //使能器件   
    , L# j' p+ s( b" N+ u  G
  264.     SPI1_ReadWriteByte(W25X_ReleasePowerDown);   //  send W25X_PowerDown command 0xAB    $ i8 M3 I& V7 `. R/ f7 S
  265.         W25QXX_CS=1;                            //取消片选                  
    2 Z6 t3 ?9 H! b8 ]4 H8 t3 f- r
  266.     delay_us(3);                               //等待TRES1
    % @" d2 M8 ]2 A- d* t; z
  267. }   </i></i></i>
复制代码
  1. 3 n* S" R! G: f% r( b& m5 m
  2. W25Q12xx.h:
    - U$ y+ i- M, c3 Q8 F' W
  3. # r# ^; b) }+ U% I: f; z  o+ a( o' b
  4. #ifndef __W25QXX_H. G6 c+ R* o; N! b' r- Z
  5. #define __W25QXX_H                           
    + y5 z; G6 B' m" h5 x6 U
  6. #include "sys.h"  * r' L6 W" w" q; j1 f& L0 J
  7. / `3 z1 k  s% R' |5 b$ K7 F3 ?: }
  8. //W25X系列/Q系列芯片列表           8 @3 \# s9 s- ^& h! ^' ~" L: W/ n
  9. //W25Q80  ID  0XEF13
    0 I+ E) P/ N, M4 t7 x
  10. //W25Q16  ID  0XEF141 h2 s# s# G4 z9 `( h+ H) t2 F4 g
  11. //W25Q32  ID  0XEF15
    ( }/ E: ]1 Z& _
  12. //W25Q64  ID  0XEF16        
    , w: P* `! z* Q) X, a* v# i3 A9 D
  13. //W25Q128 ID  0XEF17        ! R4 Z! i  c9 Y* I/ j
  14. #define W25Q80         0XEF13         
    / Y' O$ }# x5 S; P
  15. #define W25Q16         0XEF14
      r; R1 m) z& S2 c
  16. #define W25Q32         0XEF15
    2 U! H9 m# r$ j+ K2 z" W: t
  17. #define W25Q64         0XEF16
    . J/ {- c( X0 S/ N/ @5 q% m1 g
  18. #define W25Q128        0XEF178 ?7 g( Y% b3 H/ ?* z
  19. 3 V  E% s* m6 I+ c/ \+ C5 `
  20. extern u16 W25QXX_TYPE;                                        //定义W25QXX芯片型号                  
    ; }: U2 A' ]% T% j# |2 Z' h
  21. 0 k! o$ w( k7 N% {* L8 [9 k
  22. #define        W25QXX_CS                 PBout(14)                  //W25QXX的片选信号1 }& N" A3 _- R+ B' p/ w5 J% u: p
  23. //
    7 ~; X$ @3 Q4 k1 K
  24. //指令表* {2 s1 F7 B  a9 m* N, m
  25. #define W25X_WriteEnable                0x06
    . v" k0 b) O! w- j! T: Z
  26. #define W25X_WriteDisable                0x04   o4 c5 g) @( _7 _+ O
  27. #define W25X_ReadStatusReg                0x05 " s$ N$ l, y- v% a
  28. #define W25X_WriteStatusReg                0x01
    # f+ }- l$ c, N& Y
  29. #define W25X_ReadData                        0x03
    ! w' a+ g+ l4 n
  30. #define W25X_FastReadData                0x0B
    0 A7 R" e7 K' o% U" d" K
  31. #define W25X_FastReadDual                0x3B
    % T" ^0 R0 n" p5 C
  32. #define W25X_PageProgram                0x02
    ! m7 z/ R& c, ]/ u( \1 n- \* D
  33. #define W25X_BlockErase                        0xD8 & e( {% T+ i8 U4 v% ]
  34. #define W25X_SectorErase                0x20
    / t. r  R% y. K% t
  35. #define W25X_ChipErase                        0xC7 . R% V& y! ^/ c7 i9 @: R$ y: y
  36. #define W25X_PowerDown                        0xB9 4 d' u$ _' u# N. H+ x: e; W& k2 Y% s
  37. #define W25X_ReleasePowerDown        0xAB
    ' J  o# e5 |$ H1 e
  38. #define W25X_DeviceID                        0xAB / s6 B3 B: S, J4 U8 |
  39. #define W25X_ManufactDeviceID        0x90 7 x* f# }5 `" d
  40. #define W25X_JedecDeviceID                0x9F
    , [+ ?6 J8 `8 d$ Y
  41. & j7 A3 ?# E. `" @  m
  42. void W25QXX_Init(void);6 @1 d* E' g% D& _5 K2 }+ k
  43. u16  W25QXX_ReadID(void);                              //读取FLASH ID7 J9 U* ]1 F  S* h. |8 R- L+ E# P
  44. u8         W25QXX_ReadSR(void);                        //读取状态寄存器 9 e8 Q( l' i' h2 ?" R
  45. void W25QXX_Write_SR(u8 sr);                          //写状态寄存器, Z& K+ P: \' L8 R2 H2 t! y. ^  p+ t
  46. void W25QXX_Write_Enable(void);                  //写使能
    2 F: y, k* l! `, \2 n& G& O
  47. void W25QXX_Write_Disable(void);                //写保护7 F' @% [/ W9 g; c0 U, d
  48. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
    ; F6 E: K6 z) z  M
  49. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash3 t5 }( w, i6 x5 J- T0 e
  50. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash0 q& p% ^+ H. F1 ^
  51. void W25QXX_Erase_Chip(void);                      //整片擦除, k$ V& d7 s* _- O! t, x
  52. void W25QXX_Erase_Sector(u32 Dst_Addr);        //扇区擦除- q3 B0 [% o. d, n- e* P
  53. void W25QXX_Wait_Busy(void);                   //等待空闲$ v9 B) o8 I7 [4 }0 M
  54. void W25QXX_PowerDown(void);                //进入掉电模式' d0 q$ B: b5 E( p# }" R
  55. void W25QXX_WAKEUP(void);                                //唤醒% w, ~; @. Q" j7 L! V' ]5 F

  56. . ?6 a! s9 B/ n; r7 n
  57. #endif6 p8 Z! d; e5 r5 K! m" l
复制代码
7 P9 z# O" {5 E1 [0 _. E
main.c:
4 f1 x0 f+ Z5 U9 H6 j  {4 C& w$ e; f# T" E3 r4 P" _# J
  1. main.c:
    ' `) x7 u& X! p1 Q  G9 f- F2 e
  2. #include "sys.h"9 Z! P; o9 p$ ]- g- i9 C
  3. #include "delay.h"4 ]. g4 Y1 h6 X6 G0 Y: ^; o( j3 B
  4. #include "usart.h"! x4 m" L* |: V$ b9 m
  5. #include "led.h"7 q- A+ g* q1 N8 T/ N, |2 n+ B
  6. #include "lcd.h"
    + x4 I* ~; b3 [! h+ _( l0 ^
  7. #include "spi.h"
    # u$ a1 c5 d. I8 Z( H
  8. #include "w25qxx.h"$ Q! {) `2 ^+ T0 L% |) O3 b
  9. #include "key.h"   
    7 c0 H+ r- t) e, ~0 r2 i
  10. 9 G& v8 V2 u' k7 r1 H3 P' h/ I
  11. //要写入到W25Q16的字符串数组
    " W  H' n8 i" f  R$ F# `. \
  12. const u8 TEXT_Buffer[]={"Explorer STM32F4 SPI TEST"};
    ! g+ J. _  c( ?) j/ ]
  13. #define SIZE sizeof(TEXT_Buffer)         
    6 n: b: S% a3 I, G  R* B* @6 Q- X
  14.         
    " I( @" j0 i4 R# I, H
  15. int main(void)
    3 ^! D) ^) S0 e, v
  16. {
    8 R# T: y3 W$ T7 w* Z7 D7 p
  17.         u8 key;
    8 O2 Y) @% |. e  z
  18.         u16 i=0;; U9 Q; b: Q/ N: O$ c
  19.         u8 datatemp[SIZE];        5 b3 q% v( D8 V2 Y, ^
  20.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
    % F/ a. _; D  h  W
  21.         delay_init(168);    //初始化延时函数
    ( n6 ^0 t: R- k: \' Q4 }7 g
  22.         uart_init(115200);        //初始化串口波特率为115200) ]  M1 b4 B) O0 p2 C9 P6 G/ U
  23.         LED_Init();                                        //初始化LED
    6 o1 {- A- [- P3 u
  24.         KEY_Init();                                 //按键初始化  3 z3 z# I' _8 d  ]
  25.         W25QXX_Init();                        //W25QXX初始化 6 p- d3 G+ e1 m( Y# V7 E3 ^
  26.                         
    7 `5 R3 |, K* A* i5 d3 |
  27.          while(W25QXX_ReadID()!=W25Q128)        //检测不到W25Q128
    1 w' P: Z6 o) g) S/ D( c" H8 f
  28.         {& d2 q: ~* X, I: {& X. H# o
  29.                 printf("W25Q128 Check Failed!");8 y0 F% ~. l6 R, M- w6 O& z* j& O
  30.                 delay_ms(500);: F1 W( L/ n$ g1 q5 i4 u
  31.                 printf("Please Check!      ");
    & B! F- \3 ^4 s
  32.                 delay_ms(500);+ N7 h  B" W* h) }
  33.                 LED0=!LED0;                //DS0闪烁0 Y# i# {* A8 ~/ L# [  z9 Q$ q
  34.         }; S4 I' Z2 U) _+ D3 h! b
  35.         " J6 v2 }  C1 T* A6 L* c. i1 |
  36.         while(1)
    ' U# e8 k% u) I
  37.         {7 K- z( ~  d8 s, c  [
  38.                 key=KEY_Scan(0);; ?. ]) n- d* X: U+ ?! e* J
  39.                 if(key==KEY1_PRES)//KEY1按下,写入24C02; X% E' Z4 u" Q* P  {1 g3 f% U1 a
  40.                 {9 _4 ~! a; X& h1 b7 V, C+ P  ]
  41.                         printf("Start Write W25Q128...."); & J" \5 p2 L8 R6 Y0 n
  42.                         //从倒数第100个地址处开始,写入SIZE长度的数据    @* m8 S/ |# u) g3 K8 A. }) `: r
  43.                         W25QXX_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);
    + _! r1 T4 n) l9 _
  44.                         printf("W25Q128 Write Finished!");  //提示传送完成
    0 j6 v  v3 M( x6 F6 M! m% H1 t
  45.                 }# u2 Y& g# a( J/ ~, p
  46.                 if(key==KEY0_PRES)//KEY0按下,读取字符串并显示4 T) _% Y% s7 \! v4 j/ H  g
  47.                 {) |: [; X1 j* s4 V
  48.                         printf("Start Read W25Q128.... ");% @. {- F1 l2 S, W( O  {
  49.                         //从倒数第100个地址处开始,读出SIZE个字节: N! j) l9 @# F. Q. b9 }
  50.                         W25QXX_Read(datatemp,FLASH_SIZE-100,SIZE);                                       
      A$ m7 V; q! }
  51.                         printf("The Data Readed Is: \r\n ");//提示传送完成
    , N. O$ @# g$ C6 p* s. ^; f; i
  52.                         printf("%s\r\n ",datatemp);* M/ G: D$ O3 Y) `- c1 ?
  53.                 }             r8 O! y, O; i" ]) p
  54.         }             . e- B! J/ L2 Z3 |0 ?9 I# ~
  55. }8 p  L0 H0 w, H; z1 D. n

  56. 5 i5 L( b( B9 j; y# D% ]
复制代码

/ M, a& H; P9 O' _/ b0 e, P
: n1 ?+ q5 {3 G7 `( D) T

, d& _% [& @5 |
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 手机版