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

【经验分享】STM32 SPI程序示例

[复制链接]
STMCU小助手 发布时间:2022-3-27 17:00
01. SPI简介
8 T6 ~6 r& |, @- ^+ ?SPI 是英语 Serial Peripheral interface 的缩写,顾名思义就是串行外围设备接口。是 Motorola首先在其 MC68HCXX 系列处理器上定义的。SPI 接口主要应用在 EEPROM,FLASH,实时时钟,AD 转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为 PCB 的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,STM32F4 也有 SPI 接口。4 x: h* I5 t$ T3 {& p
+ u$ Q- \2 k$ Z6 G
02. 功能描述
8 n5 i8 |7 t+ b* K" |使用 STM32F4 自带的 SPI来实现对外部 FLASH(W25Q128)的读写,并将结果显示在 TFTLCD 模块上。% r6 w* a* H9 m

/ k" E! K1 ]' b8 }5 |03. 硬件模块
9 ?: u! D& e% f7 O开机的时候先检测 W25Q128 是否存在,然后在主循环里面检测两个按键,其中 1 个按键(KEY1)用来执行写入 W25Q128 的操作,另外一个按键(KEY0)用来执行读出操作,在 TFTLCD 模块上显示相关信息。同时用 DS0 提示程序正在运行。  x$ q! I+ s7 s. i
所要用到的硬件资源如下:
' {, M% K( ]) {* ^2 d1) 指示灯 DS0' x) A) K; V. {. t
2) KEY_UP 和 KEY1 按键
$ C: X$ h, P% L+ O3 _- V* w3) TFTLCD 模块) Z; p  `* i; ]" t
4) SPI
8 V5 W, t5 M% \& u) j' P; `; }5) W25Q1289 t4 X& Z3 d" W) U- F

2 G3 S/ r4 s1 c* O {7_[%D]0MBS3MDCC4{_A@{U.png
8 m% V' Z5 U: y" V1 m2 l
( y7 ~! P4 q/ h" ^5 N04. 软件设计
! R! j) p/ R& T0 {! ~2 y" Xspi.h' X/ w  y6 E1 O! m, s
  1. #ifndef __SPI_H
    " C) A) d8 h- [$ V; ]7 ?$ E
  2. #define __SPI_H
    ; z0 ^0 t( y) \) @. u. W
  3. #include "sys.h"4 ?5 w8 A' m9 J  |' `
  4.                                                                                                                      
    " T/ `8 ?$ d+ y$ H8 F
  5. void SPI1_Init(void);                         //初始化SPI1口
    ; ^: j( \/ u0 R' ^
  6. void SPI1_SetSpeed(u8 SpeedSet); //设置SPI1速度   3 g: P+ K$ p5 Y9 b7 \
  7. u8 SPI1_ReadWriteByte(u8 TxData);//SPI1总线读写一个字节
    . f6 v6 f1 G( r: g3 `
  8.                  
    5 C" p1 g0 |( T  z$ c: L2 T- ^7 a
  9. #endif
复制代码

( h" @5 M3 r7 Z9 q: M. z9 |% Qspi.c
; P+ ~- Q6 G$ p' Y+ E( z
& |! y3 Y2 d$ v8 A* _$ B
  1. #include "spi.h" / V/ |3 L3 Q; Y& ~9 N1 Z

  2. : f3 |1 q5 B3 u4 h% a* }
  3. ( ~1 o3 ?% N, z. Y% s! H+ ]; E
  4. //以下是SPI模块的初始化代码,配置成主机模式                                                   ' P; I% q$ ~' ~7 q
  5. //SPI口初始化% M: |* w% v, }+ [1 M
  6. //这里针是对SPI1的初始化. l; U' H- K! W
  7. void SPI1_Init(void)
    ( F( B/ ?$ T: E: H
  8. {         
    - M7 x, `1 F7 |; e
  9.   GPIO_InitTypeDef  GPIO_InitStructure;
    + p9 X- \: o1 K9 r2 J6 P; x: D
  10.   SPI_InitTypeDef  SPI_InitStructure;
    5 Z9 U% {; x9 y+ ?* T; h+ @
  11.         3 x  @3 c2 L7 T0 p& o6 p/ B, g
  12.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
    + \3 Z( U. Q8 j. q8 F; c% c0 r  q
  13.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟' T7 l: F3 V- \' r& j
  14. 2 B, q$ ^6 n* u
  15.   //GPIOFB3,4,5初始化设置! `* W, e+ {: _7 v/ Y
  16.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出        
    6 l% Y9 g( _7 V' m
  17.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
    2 x! x# F* X1 O& x& a
  18.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
    : G! V$ ?& z" L
  19.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    . B' t* P3 e! u0 \
  20.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉4 D) ]. M) S7 ]( ~- {& D* K  N' s4 u" r
  21.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化- M# ^/ H* T8 A8 j) m% Y9 J7 S
  22.         ( z$ X6 H* }* V2 j+ f/ ?
  23.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为 SPI1# n. g+ J$ M0 w" t
  24.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为 SPI1
    ' g+ O* D/ U5 t9 M
  25.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为 SPI1( z6 U* t4 a* }7 J8 Q+ [% ~
  26. & H% |; Y1 }, Z
  27.         //这里只针对SPI口初始化# ?  c& I3 ?% e: H
  28.         RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI1
    , V0 m! e" D( }+ Q& m
  29.         RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1# |, E# @$ U4 L, q  y

  30. " U. e  E/ q. E# L9 m9 n
  31.         SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
    6 @: D, [2 v4 |% O, \" Q  c
  32.         SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                //设置SPI工作模式:设置为主SPI
    3 q  k/ ~6 z, }; B
  33.         SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                //设置SPI的数据大小:SPI发送接收8位帧结构: i1 o6 r3 b( X5 ?% X( h
  34.         SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                //串行同步时钟的空闲状态为高电平/ ^; C& C  ?$ f+ @8 f$ o. e- B. i
  35.         SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;        //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
    : s5 ?7 X3 s; U: j( S, Z5 @
  36.         SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制2 |- j$ ]& g. A* |4 Z
  37.         SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;                //定义波特率预分频的值:波特率预分频值为256% z+ i6 L. Q4 ]9 `" t, e% U
  38.         SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;        //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始) G0 l5 h+ e8 e7 g  s
  39.         SPI_InitStructure.SPI_CRCPolynomial = 7;        //CRC值计算的多项式
    5 `- ]5 x3 x8 F
  40.         SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
    ! |# p! S+ s4 r) C; y9 m9 w( {
  41. 4 \: s, f: Q4 O- j3 |/ ~: V
  42.         SPI_Cmd(SPI1, ENABLE); //使能SPI外设, N- ~6 S9 U9 y& b+ a- U0 G! K

  43. 3 `- o" @! y6 |  u. N+ k" w. u8 p
  44.         SPI1_ReadWriteByte(0xff);//启动传输                 ' j  v! P, y5 H5 t6 a" v/ U  [' F
  45. }   * @5 q9 F5 G/ B8 h: r
  46. //SPI1速度设置函数4 o7 Z3 P# n$ x' U" C7 j9 Z
  47. //SPI速度=fAPB2/分频系数  z+ |* ?$ H/ T: f/ y( `3 M
  48. //@ref SPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256  ; f0 G, G9 Y' j8 m0 ]$ L
  49. //fAPB2时钟一般为84Mhz:% n0 r- I9 w% |' K0 j
  50. void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)/ ~6 G, [; y2 I- N2 f# ^& @+ ~
  51. {$ d1 [: \+ N5 \$ j
  52.   assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性7 Y$ }  \/ z$ {  S$ Y2 W
  53.         SPI1->CR1&=0XFFC7;//位3-5清零,用来设置波特率' H+ |; H5 {  k$ g  ]
  54.         SPI1->CR1|=SPI_BaudRatePrescaler;        //设置SPI1速度 2 Q) }! I5 E- l$ y( y" j
  55.         SPI_Cmd(SPI1,ENABLE); //使能SPI1$ v8 d4 f/ `- C; d2 \/ C6 a+ {) A0 Q
  56. } - x& D1 u2 D" C) W0 _* F
  57. //SPI1 读写一个字节
    - X6 R! f9 _8 U- b: V: b8 z
  58. //TxData:要写入的字节
    1 X3 k/ d0 {9 b0 ]! w: _0 b, e1 {. ?
  59. //返回值:读取到的字节
    1 L* A' z- e6 ^1 S7 l* Z
  60. u8 SPI1_ReadWriteByte(u8 TxData)/ V" m" P6 Z7 H
  61. {                                          6 }& |7 u. G0 W2 X" k
  62. 2 A* T! p+ I: ^4 ~
  63.   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空  
    1 X; Q$ U3 K6 @! G) l
  64.         8 A. k1 B  n. r3 n( C& l( t
  65.         SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte  数据
    ; q$ S* n3 a2 {
  66.                   v3 w1 Z9 K# U+ q$ _
  67.   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一个byte  ( J3 B7 O" g- l% Z9 v4 U5 o8 m8 U

  68. 1 U3 ~7 J7 k' h1 w+ C6 ]
  69.         return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据        ! [3 h% r% A, u1 c2 w
  70.                      , J* b/ g/ [" B) u# O3 j" E
  71. }
    6 a2 E3 b6 D" W1 Q1 X! H
复制代码
0 Q" B, T; C8 q$ y" l0 B
w25qxx.h
& L) u( o7 g+ q; R
6 K% Z+ ?# o5 \( I
  1. #ifndef __W25QXX_H
    " G% Q& `0 {& R" M
  2. #define __W25QXX_H                           
    + y$ [4 P- s* w/ |" `; Y, `) J
  3. #include "sys.h"  
    ( A& H; F! |1 F, X- F' r* X

  4. & z3 U, \2 l. E! L1 V+ Z/ ^
  5. //W25X系列/Q系列芯片列表           2 o  G$ k1 _; G" h  H
  6. //W25Q80  ID  0XEF13
    $ @2 k% V' K8 `: z
  7. //W25Q16  ID  0XEF147 m% ~; u) \/ a6 R2 k
  8. //W25Q32  ID  0XEF15' D9 M, E, j2 Z4 e, H
  9. //W25Q64  ID  0XEF16        4 T0 V* G% k& Q4 z6 r- Z* a) X* F' g
  10. //W25Q128 ID  0XEF17        
    & B/ f) j/ J: s" K: W
  11. #define W25Q80         0XEF13         $ B; u/ `# N0 N! `9 Y0 j- k
  12. #define W25Q16         0XEF145 D! Z( J8 d$ c. D3 u1 ~
  13. #define W25Q32         0XEF15
    0 \1 n+ w+ M& o
  14. #define W25Q64         0XEF16# x8 }# B% K3 j  o
  15. #define W25Q128        0XEF17: f3 @$ I) z0 ^8 ?  x
  16. 5 ^+ S0 z7 H, F) n: B  O2 _
  17. extern u16 W25QXX_TYPE;                                        //定义W25QXX芯片型号                  
    " n/ j% R( m1 W  g3 r2 {) b& k2 Z
  18.   G% d% i) s* U. k2 j3 s- e
  19. #define        W25QXX_CS                 PBout(14)                  //W25QXX的片选信号
    % v: v% |4 v+ _5 j$ v8 m

  20. ; Z: q7 r: v& _: z* B
  21. // 2 |% ^# k9 ?. s0 p3 R1 I) d5 ?% D# e
  22. //指令表9 `  I2 O& _2 u6 |( c$ E  F
  23. #define W25X_WriteEnable                0x06
    $ l# Q  A6 A- A) D" A* ^
  24. #define W25X_WriteDisable                0x04 % Y7 o! s& N6 W2 ^6 e" S% b
  25. #define W25X_ReadStatusReg                0x05
    4 N0 K7 |3 E- Q
  26. #define W25X_WriteStatusReg                0x01
    ' D0 Z4 M- r6 ~( o
  27. #define W25X_ReadData                        0x03
    6 T$ Y9 }, F4 h9 j$ H/ Q, l
  28. #define W25X_FastReadData                0x0B & B+ n3 Y! f* K& Z
  29. #define W25X_FastReadDual                0x3B
    : Z3 N2 G% h3 X6 _7 n0 q1 }# Y% @
  30. #define W25X_PageProgram                0x02 * a9 P! A1 c! y% H1 Q; s$ ?
  31. #define W25X_BlockErase                        0xD8 " f  h; J4 ^. l4 t0 S
  32. #define W25X_SectorErase                0x20
    4 Z$ Y+ x1 _7 x0 b
  33. #define W25X_ChipErase                        0xC7 / ]$ }/ a$ @( M, v
  34. #define W25X_PowerDown                        0xB9   [; w, j/ N1 C! ~5 I+ @
  35. #define W25X_ReleasePowerDown        0xAB , [& R$ y& s8 g0 `" P
  36. #define W25X_DeviceID                        0xAB ; Y9 P( ?9 c; V  J" G0 z
  37. #define W25X_ManufactDeviceID        0x90
    1 e) ]/ h! V( Y' v2 `& G
  38. #define W25X_JedecDeviceID                0x9F
    / r4 B1 ]: @& K9 }" f; |" K! N

  39. 4 x7 L+ \* o, G, i4 L! c- m/ }
  40. void W25QXX_Init(void);# s3 I& w2 B+ T
  41. u16  W25QXX_ReadID(void);                              //读取FLASH ID
    , |( X2 D. Z% `' L) b! y
  42. u8         W25QXX_ReadSR(void);                        //读取状态寄存器 $ L9 E; n& [! D7 M/ X
  43. void W25QXX_Write_SR(u8 sr);                          //写状态寄存器& L! O- ]2 j& G1 |, p
  44. void W25QXX_Write_Enable(void);                  //写使能 : g( g! H; Y. d7 H* c9 m1 h# F' L9 L
  45. void W25QXX_Write_Disable(void);                //写保护2 C/ ^& L6 _2 ]7 C
  46. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
    . X9 D- G% Y  W  ^. Q8 \  n
  47. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash& W3 L1 Z4 x) r1 y
  48. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash" ]& C. S0 y9 W4 [
  49. void W25QXX_Erase_Chip(void);                      //整片擦除
    ( N  D+ T" ^( I" `9 t
  50. void W25QXX_Erase_Sector(u32 Dst_Addr);        //扇区擦除
    0 t7 r4 A) Y, s) `% r9 W" r4 ]
  51. void W25QXX_Wait_Busy(void);                   //等待空闲
    8 S2 ?7 `6 C* o* i9 L. X/ w
  52. void W25QXX_PowerDown(void);                //进入掉电模式
    4 C# I, @' ?* x& g
  53. void W25QXX_WAKEUP(void);                                //唤醒5 E  Z2 O  r5 p# q/ w6 \4 x. z; d( ~
  54. #endif
复制代码

% k5 U* y5 P* q3 {% Vw25qxx.c! _' p% e- Q# i- x

: A& J- x1 M; [
  1. #include "w25qxx.h" 6 C  N4 _- V; T% m' }
  2. #include "spi.h"
    ) _/ Q' Q) O8 Y, m" e# o* |! i
  3. #include "delay.h"           1 m1 a/ {8 B; V
  4. #include "usart.h"        / S; F$ k" ^6 V9 E3 z0 P" g
  5. 7 ~7 ~+ S6 k- |3 W! [

  6. # [, l- A2 O7 F4 F8 V  H" A, B; W+ O
  7. u16 W25QXX_TYPE=W25Q128;        //默认是W25Q128
    ! k9 u) ^8 n* H7 J

  8. - U& ?8 c. C4 m: D! k( T! S5 l% C0 ~
  9. //4Kbytes为一个Sector
      E& g& r2 z+ v- S+ r
  10. //16个扇区为1个Block
    $ w' i) h! h  ?% q
  11. //W25Q1287 ]) o9 b, L7 F3 F7 h6 `3 @
  12. //容量为16M字节,共有128个Block,4096个Sector
    ) [0 \, I0 h6 K# d! K. S" F
  13.                                                                                                          " i& Q' X& Q- n: F, X6 d
  14. //初始化SPI FLASH的IO口
    ' _6 j( Q0 p$ T+ l7 `/ |6 S% e) f
  15. void W25QXX_Init(void)
    ( d' G7 V# Z/ ~+ g
  16. {
    1 u5 s/ j0 k* k& _, S  G
  17.   GPIO_InitTypeDef  GPIO_InitStructure;
    ; C+ o) G' X1 m/ |" H2 ?3 P
  18.   ~, g" t, O7 \1 F' j$ s
  19.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
    ) ~/ P. ^0 ]2 I1 Q
  20.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);//使能GPIOG时钟( X! T& W( j  _; a) K
  21. 4 d/ }1 ]3 o% H# x; j7 p. k  t. N. o; i
  22.           //GPIOB14
    % i5 M5 ^4 ~$ |7 Q, Z' }; D
  23.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;//PB14
    ( f( A8 u7 S& R) V
  24.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出7 `. h: Y0 O( z& D/ i
  25.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出4 Z& K1 ?) t% L; n& L
  26.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz0 N, ]* e/ D4 W3 \! B4 Q7 o
  27.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
    6 {+ Z5 [' L' O; M8 r! H  }+ Z
  28.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
    5 F+ }' v" t" T9 J0 S# V# @$ n
  29. * q9 Q5 @( _+ @2 {/ m+ Q8 g; s* Z
  30.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//PG71 O" g9 l, E7 e1 f' R
  31.   GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化
    3 R0 ^  ?& r/ P& ~

  32. % B7 a8 \( c# z! p  l) {: M
  33.         GPIO_SetBits(GPIOG,GPIO_Pin_7);//PG7输出1,防止NRF干扰SPI FLASH的通信 , l! W) x9 e0 y; k8 y; n
  34.         W25QXX_CS=1;                        //SPI FLASH不选中
    / `8 _8 j! T- B* p/ G: f2 \6 T- A
  35.         SPI1_Init();                                           //初始化SPI: A& g) p: p6 v& F
  36.         SPI1_SetSpeed(SPI_BaudRatePrescaler_4);                //设置为21M时钟,高速模式 # R2 A, i# P9 R8 ^5 R" n* B
  37.         W25QXX_TYPE=W25QXX_ReadID();        //读取FLASH ID.
    ( ^( @% G6 t, F4 |9 E  r
  38. }  
    + R5 S9 s. |+ l$ r4 j& ]  a' R9 _

  39. 8 q0 I' n1 i2 I( w1 b$ X1 R  ^, e
  40. //读取W25QXX的状态寄存器
    + v7 H: m, o& z
  41. //BIT7  6   5   4   3   2   1   09 M* K" N: r' N7 H" f
  42. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY% k0 ~, b/ F5 @& M8 y+ l! b  e
  43. //SPR:默认0,状态寄存器保护位,配合WP使用
    8 ?4 O+ x. i$ }; U
  44. //TB,BP2,BP1,BP0:FLASH区域写保护设置' w4 `$ n' {/ B
  45. //WEL:写使能锁定5 V- B* z% a4 d: X
  46. //BUSY:忙标记位(1,忙;0,空闲)5 p% ~- n0 `9 }4 m' V0 c+ ]) y6 H! _
  47. //默认:0x00. x! ]6 b* l; N) m. g$ F( r+ m
  48. u8 W25QXX_ReadSR(void)   
    ' x+ F  z5 V* n# D5 o  m
  49. {  
    ' z  x+ _' m& t! m$ p+ [5 D
  50.         u8 byte=0;   
    & b" V; k( h/ N( z; ^9 s
  51.         W25QXX_CS=0;                            //使能器件   
    3 a  L% C  _6 H3 K
  52.         SPI1_ReadWriteByte(W25X_ReadStatusReg);    //发送读取状态寄存器命令    2 v  q, Y4 E+ T7 {7 X: i
  53.         byte=SPI1_ReadWriteByte(0Xff);             //读取一个字节  % P6 @% {4 d; _+ l! ~  K: W4 ~
  54.         W25QXX_CS=1;                            //取消片选     
    3 a$ L  F( q9 Z# Y9 e( t
  55.         return byte;   6 b# w9 u% _9 h- z. i1 o* l( M
  56. } 9 n5 r, O+ F6 S8 t0 @1 ^
  57. //写W25QXX状态寄存器
    - T- C+ P2 V. [. X
  58. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!; X2 T+ u9 q3 N
  59. void W25QXX_Write_SR(u8 sr)   
    7 c; o; x# X0 |  `1 j
  60. {   ) |9 ?+ u5 C" \; {! }7 Y7 _, A
  61.         W25QXX_CS=0;                            //使能器件   
    ) f0 a4 U/ c) l0 }3 O& V4 K
  62.         SPI1_ReadWriteByte(W25X_WriteStatusReg);   //发送写取状态寄存器命令   
    # N4 B: M5 _$ w7 q
  63.         SPI1_ReadWriteByte(sr);               //写入一个字节  1 i% Q1 a3 x; s- k9 Y% o0 V" F  H
  64.         W25QXX_CS=1;                            //取消片选                  
    0 O! u$ x- _5 e2 J4 ]" v: c& O4 e
  65. }   ' M7 |4 l$ n! X% {4 V. |0 g: N
  66. //W25QXX写使能        
    ) Z* M4 ]( E& u# o  g- V
  67. //将WEL置位   3 R" Q6 y5 g" m0 G8 r$ S4 m
  68. void W25QXX_Write_Enable(void)   
    2 B4 z' q& m7 z; G6 R+ i# I/ P
  69. {
    1 n( n4 G/ ^6 G" o" _/ h  f
  70.         W25QXX_CS=0;                            //使能器件   
    / E/ d  n; x' y& `% P9 V
  71.     SPI1_ReadWriteByte(W25X_WriteEnable);      //发送写使能  
    ! C3 r: `" ]$ C
  72.         W25QXX_CS=1;                            //取消片选                   , q$ C: r9 f9 T5 q
  73. }
    " U: y9 D& a" o  h; P6 V: h
  74. //W25QXX写禁止        ; a) W3 C, j% _: M* U* y: _
  75. //将WEL清零  
    * L" }9 L# M& p  t0 G5 S7 Z
  76. void W25QXX_Write_Disable(void)   5 F7 G) e3 k; b; h! b" n8 g9 Y
  77. {  
    8 G2 D1 X" `' Q; C6 a
  78.         W25QXX_CS=0;                            //使能器件   0 X) k. {+ ]% M# y: b
  79.     SPI1_ReadWriteByte(W25X_WriteDisable);     //发送写禁止指令   
    9 l) _% Q4 L# b( J9 ?9 L1 @8 ~
  80.         W25QXX_CS=1;                            //取消片选                   : Z0 v8 X. e1 c) p$ c
  81. }                 + Q$ d5 [% o  i6 j! {
  82. //读取芯片ID
    $ K; \/ M5 ^+ D5 y6 w
  83. //返回值如下:                                   + m6 r: ]3 d6 u, N7 A
  84. //0XEF13,表示芯片型号为W25Q80  
    2 V7 S# }; ~; L1 f
  85. //0XEF14,表示芯片型号为W25Q16   
    3 H/ H5 K( g# o: u
  86. //0XEF15,表示芯片型号为W25Q32  " v* `' U' a  L
  87. //0XEF16,表示芯片型号为W25Q64
    6 [/ s: x+ Q. y& ?
  88. //0XEF17,表示芯片型号为W25Q128           
    & f+ [& j* a2 N: f' B8 x9 U5 {
  89. u16 W25QXX_ReadID(void)
    # G: f6 s2 C9 I& }+ V0 O' w
  90. {
    * c4 D2 B) O! I; b2 F
  91.         u16 Temp = 0;         
    " ~! A$ Y: |# T9 m% V6 p( s0 ^
  92.         W25QXX_CS=0;                                    % N! U1 x6 e# y8 {( a3 L
  93.         SPI1_ReadWriteByte(0x90);//发送读取ID命令            
    4 e) y6 S; o  F8 P
  94.         SPI1_ReadWriteByte(0x00);            
    . a, G$ @" ~  \
  95.         SPI1_ReadWriteByte(0x00);            
    & V# L2 Y3 O' k8 S$ g7 E7 H
  96.         SPI1_ReadWriteByte(0x00);                                    
    - c% b: |0 x4 r
  97.         Temp|=SPI1_ReadWriteByte(0xFF)<<8;  + W: w0 _5 t' ^1 |
  98.         Temp|=SPI1_ReadWriteByte(0xFF);         2 e- s: B2 s8 L9 R. h6 k
  99.         W25QXX_CS=1;                                    5 p" }7 U  i/ d. w3 M; u
  100.         return Temp;
    : k6 _/ x. l2 A( S# t
  101. }                       
    # Y; |9 c  |3 H2 C# H; e0 ?
  102. //读取SPI FLASH  
    # y  w% c4 M* k( L
  103. //在指定地址开始读取指定长度的数据
    . E1 `' D. e+ ]; L. T; v
  104. //pBuffer:数据存储区: ?1 B& \5 x5 ^) r
  105. //ReadAddr:开始读取的地址(24bit)
    7 p2 O) E$ C2 Z: ~. N) A
  106. //NumByteToRead:要读取的字节数(最大65535)9 [2 B2 m3 `2 ~% V
  107. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   
      }, T6 w7 Z( F0 G( T; }8 m
  108. {
    ) {) e/ Y9 E: ]$ I) o
  109.          u16 i;                                                                                       
    ) P  q5 L* I" B/ h# V0 g: S6 q
  110.         W25QXX_CS=0;                            //使能器件   # H! J$ R0 Y$ b5 u% K7 U* G
  111.     SPI1_ReadWriteByte(W25X_ReadData);         //发送读取命令   
    8 F3 a9 ?) ?1 A- J7 }+ a
  112.     SPI1_ReadWriteByte((u8)((ReadAddr)>>16));  //发送24bit地址   
    4 t0 X$ Z- U4 |% |; Y
  113.     SPI1_ReadWriteByte((u8)((ReadAddr)>>8));   % x2 y& u/ l# x3 |. K5 A' v# Y
  114.     SPI1_ReadWriteByte((u8)ReadAddr);   
    5 o: p2 W/ D9 t" a. d$ ]
  115.     for(i=0;i<NumByteToRead;i++)
      l1 K: a: D8 _. j
  116.         {
    0 X; @# @0 r" J: {' V
  117.         pBuffer=SPI1_ReadWriteByte(0XFF);   //循环读数  / T3 z" a: i: e) ^# s
  118.     }- q* K3 Y7 Y' ~# z) v' H- m
  119.         W25QXX_CS=1;                                                    0 A" x) C! U: g! o: W" M, k  n& z8 {, z
  120. }  / [, u( ?; P; P( b- r3 y
  121. //SPI在一页(0~65535)内写入少于256个字节的数据
    4 o  _8 d0 X& J: L# o, g& ?
  122. //在指定地址开始写入最大256字节的数据
    9 T& R0 X# \. |+ Q# d5 N
  123. //pBuffer:数据存储区
    5 }) M" f1 y0 F% ]& H4 }. C2 B7 ^
  124. //WriteAddr:开始写入的地址(24bit)6 e+ P3 y& S6 \8 O, H+ R
  125. //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!         ) h2 K7 z* x1 u" a: f
  126. void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)8 f3 ^1 A3 j5 U; C9 S; i2 O3 ]
  127. {
    # b( j' P; m; ?: j4 U/ q
  128.          u16 i;  
    9 N3 o7 t0 G. g' V5 _
  129.     W25QXX_Write_Enable();                  //SET WEL # O2 X* V1 ^' z$ h6 V" \1 F" \
  130.         W25QXX_CS=0;                            //使能器件   7 ?, F  L6 ~+ x, I( o
  131.     SPI1_ReadWriteByte(W25X_PageProgram);      //发送写页命令   
    ( J9 ]. E2 o- w# P
  132.     SPI1_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址    0 Z3 i7 Q5 F5 r, T- B0 E4 e9 u' d
  133.     SPI1_ReadWriteByte((u8)((WriteAddr)>>8));   9 Q2 G/ f8 g* t
  134.     SPI1_ReadWriteByte((u8)WriteAddr);   / {# A- c. J+ T+ N$ V: w, W9 d" B" W
  135. for(i=0;i<NumByteToWrite;i++)SPI1_ReadWriteByte(pBuffer);//循环写数  4 S" R  p  J) `8 \( A$ B
  136.         W25QXX_CS=1;                            //取消片选
    % C9 x& @" M0 z4 f! J$ h
  137.         W25QXX_Wait_Busy();                                           //等待写入结束
    5 @3 L& \! [$ `" V/ W4 Z- Z0 ^
  138. } / S* r% x& q' E3 s+ b. u7 t8 k# }
  139. //无检验写SPI FLASH
    # _2 o. m6 Z( T2 Y0 y3 ]
  140. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!( f. Q3 B6 f; H* [& t3 s8 ]1 }3 p
  141. //具有自动换页功能 3 @) }: E1 z3 Y: U8 M
  142. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!& ?  [$ @6 [( l) A* z, |" o
  143. //pBuffer:数据存储区
    " `# M2 A% k0 S- @4 }! m
  144. //WriteAddr:开始写入的地址(24bit)" w' K8 F+ X; l- w8 A
  145. //NumByteToWrite:要写入的字节数(最大65535)
    1 F8 F, P7 D6 n% `/ z6 g
  146. //CHECK OK. B3 Z# O& E" o' L; j
  147. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
    - I' T' S- o& q6 O0 G
  148. {                                           * W0 I. t( K* W
  149.         u16 pageremain;           - a) A" G9 \. x7 G
  150.         pageremain=256-WriteAddr%256; //单页剩余的字节数                             
    . _+ t9 v$ \8 E3 i0 |( W1 a( H: P
  151.         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
    3 L" c) i+ b( r" v
  152.         while(1)3 V- t! v& {+ Y9 X5 [# ]+ a; Q
  153.         {           
    - W2 v% k# d9 G# j' @
  154.                 W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
    7 Q# p3 n( s; a# r3 |
  155.                 if(NumByteToWrite==pageremain)break;//写入结束了3 B" N6 J. V% t9 O  y
  156.                  else //NumByteToWrite>pageremain1 z8 N) e0 U  i% v  _( V% Z8 Q
  157.                 {
      ~" s- _& k& b$ a* b& E+ O, o: J$ |
  158.                         pBuffer+=pageremain;
    7 u6 T" D1 |: R) v  R. ]
  159.                         WriteAddr+=pageremain;        1 E4 c& S* a+ M) l
  160. 5 b9 P- U) W' B. x. O1 c
  161.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数
    " |8 y0 a; `* s8 E7 l. c+ x
  162.                         if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节; t4 T4 X6 l( l, R% B8 l: ]
  163.                         else pageremain=NumByteToWrite;           //不够256个字节了+ k- b+ t: C5 W1 B3 H
  164.                 }1 o) s& h. _5 x8 X" b/ y
  165.         };            % {5 Q3 F# I+ k  j% ^
  166. } $ q3 Y' c# U; v6 H, X
  167. //写SPI FLASH  , d; B+ x6 G/ J  C
  168. //在指定地址开始写入指定长度的数据5 p1 z, z# O# c$ `2 v
  169. //该函数带擦除操作!
    ; K/ H  i" a4 X4 |4 b4 L# y2 r
  170. //pBuffer:数据存储区
    ( @" t# q, C6 U/ ]
  171. //WriteAddr:开始写入的地址(24bit)                                                3 T1 l; G1 g- s; n) V2 [- w
  172. //NumByteToWrite:要写入的字节数(最大65535)   
    - M& \: c3 M4 X: _/ y* g  y
  173. u8 W25QXX_BUFFER[4096];                 
    8 _' y/ A  `% w0 `6 N6 ^
  174. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
    0 b& g) z6 }7 D
  175. { 7 a8 t% n' r) }' I
  176.         u32 secpos;% |6 [; h" I, X: y
  177.         u16 secoff;0 E) l; E0 a' ^! D
  178.         u16 secremain;           7 Q: O" i4 s% w. ]; F4 O' Z* z5 i
  179.          u16 i;   
    & N9 b! o1 n+ a
  180.         u8 * W25QXX_BUF;         
    & \9 g- h/ r0 D- \! s8 u
  181.            W25QXX_BUF=W25QXX_BUFFER;             + o1 K( l' a$ K6 M! i/ W' p, c9 ]
  182.          secpos=WriteAddr/4096;//扇区地址  
    . [' W& Z7 c: N( X8 h' `) z
  183.         secoff=WriteAddr%4096;//在扇区内的偏移4 F, b! M& L1 m/ h* X
  184.         secremain=4096-secoff;//扇区剩余空间大小   4 u: t+ v1 j! f6 R) ]2 h. J
  185.          //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
    $ S1 y$ u0 ~, a0 f
  186.          if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节2 @1 ?9 ^" j9 V) d3 m
  187.         while(1)
    / c7 r* e- R8 R
  188.         {        
    7 p$ ?+ q& O% ?3 d1 [# W
  189.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容" r# m, \4 ?0 r* X2 G. O; z2 \: X0 |
  190.                 for(i=0;i<secremain;i++)//校验数据3 p7 g7 h. N) R4 r" L  r0 p' W
  191.                 {
    ) H/ [. M& G5 a4 n$ ?4 O2 t
  192.                         if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除            
    ; r* h2 A2 {8 S
  193.                 }
    % ]6 `, H! T6 `& f& t, w
  194.                 if(i<secremain)//需要擦除2 j- v  D. K" Y! v
  195.                 {+ E' T. _4 Z$ V" h2 i
  196.                         W25QXX_Erase_Sector(secpos);//擦除这个扇区
    2 G0 W' b: I' x) D. \1 ~# Z2 m6 Z% a
  197.                         for(i=0;i<secremain;i++)           //复制
    & H# ^! [9 O; f7 o, j
  198.                         {
    6 Q  g9 h9 U/ r, N" I; i' U1 t" ]
  199.                                 W25QXX_BUF[i+secoff]=pBuffer;         
    % `; G5 l( r9 @+ p  x: Y% u
  200.                         }
    ) _5 v+ Z2 N1 H
  201.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  7 A- T- K7 e/ N. }; n# _

  202. ( o6 p8 ^# }, X* ~+ G
  203.                 }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    
    6 A7 G  M5 K" ?/ b% ^
  204.                 if(NumByteToWrite==secremain)break;//写入结束了4 ~1 e9 _7 r# [& }# u3 g
  205.                 else//写入未结束5 E& v: t: S6 K- j
  206.                 {1 ?  e% V9 S/ M* ~; p
  207.                         secpos++;//扇区地址增1
    7 r) C, P/ j6 Y; |
  208.                         secoff=0;//偏移位置为0         
    . |7 P+ m7 B0 j7 O6 p% c7 g
  209. " `2 ~( \" e5 R% q9 ?
  210.                            pBuffer+=secremain;  //指针偏移
    0 Y1 @  v& X$ H: H: A
  211.                         WriteAddr+=secremain;//写地址偏移           ) A9 t: Z& y0 C. J5 g% v
  212.                            NumByteToWrite-=secremain;                                //字节数递减2 s5 G4 R0 b/ g2 p* T2 f
  213.                         if(NumByteToWrite>4096)secremain=4096;        //下一个扇区还是写不完
    . D: F9 p' c1 j
  214.                         else secremain=NumByteToWrite;                        //下一个扇区可以写完了
    8 ]: {/ a! ]6 D6 L. z; c+ W% M8 \7 Y
  215.                 }         
    0 H5 E/ v" U5 e% p$ L
  216.         };         
    " `! K7 O/ Z. U0 C  g# B7 U
  217. }
    / s! X- a: X4 C2 t" `% R
  218. //擦除整个芯片                  5 d& P; I  I8 a4 ^# [/ w
  219. //等待时间超长...
    ' E8 |: m  ?2 t% n
  220. void W25QXX_Erase_Chip(void)   
    ! I% r* a4 r, o0 O$ ?. y+ l
  221. {                                   
    , a. _/ R& Y6 q# y: q7 g. P* s9 U0 P
  222.     W25QXX_Write_Enable();                  //SET WEL
    . b' W7 X6 v/ l+ ~8 N1 E
  223.     W25QXX_Wait_Busy();   % h. \$ _" Y% J8 ~5 B& n+ Q& H
  224.           W25QXX_CS=0;                            //使能器件   / c7 R0 m' k% B. ?( `8 E8 t# {  A% g
  225.     SPI1_ReadWriteByte(W25X_ChipErase);        //发送片擦除命令  + C# G. {# s% G; C' L
  226.         W25QXX_CS=1;                            //取消片选                  
    1 {8 c- |) I5 ?* h( _  f
  227.         W25QXX_Wait_Busy();                                      //等待芯片擦除结束+ w5 K$ P3 F. ?$ e( u. `) R! ~
  228. }   
      `& T, h* Q$ b% ~
  229. //擦除一个扇区4 k' F- c+ p& N1 t9 U8 @: B
  230. //Dst_Addr:扇区地址 根据实际容量设置- B) ^- \2 n* C. @- i" U
  231. //擦除一个山区的最少时间:150ms
    8 b9 L# \$ E/ N, |4 C/ N* z- c
  232. void W25QXX_Erase_Sector(u32 Dst_Addr)   
    0 T/ a# l/ G" V* Q! l
  233. {  
    9 \- [1 w1 x7 f# D
  234.         //监视falsh擦除情况,测试用   
    ! i. B2 E. X, g+ c2 H
  235.          printf("fe:%x\r\n",Dst_Addr);          ! ~$ i: ?. o  M  M) [0 Q
  236.          Dst_Addr*=4096;: D& w6 g7 N& D# P1 i) d6 o9 e  u
  237.     W25QXX_Write_Enable();                  //SET WEL         
    + z. c* l# D- r, Y, e% L, Y
  238.     W25QXX_Wait_Busy();   
    9 {' }5 @8 L4 _$ v: @( j$ i
  239.           W25QXX_CS=0;                            //使能器件   ' ~5 Z  a# n( E
  240.     SPI1_ReadWriteByte(W25X_SectorErase);      //发送扇区擦除指令 & D9 ~* k4 o. X0 K
  241.     SPI1_ReadWriteByte((u8)((Dst_Addr)>>16));  //发送24bit地址    / b* l0 V/ U2 J  ~# k, k( l+ L
  242.     SPI1_ReadWriteByte((u8)((Dst_Addr)>>8));   
    $ R6 z; q$ [, E6 s+ G
  243.     SPI1_ReadWriteByte((u8)Dst_Addr);  
    1 K3 J# C5 x" n& C; v
  244.         W25QXX_CS=1;                            //取消片选                  
    $ m9 \8 S# j* g6 c0 K. e
  245.     W25QXX_Wait_Busy();                                      //等待擦除完成2 q+ N* `! \* Q; a
  246. }  
    + _0 r0 F; C9 e: p
  247. //等待空闲$ O7 _: U  d9 a! ^" L
  248. void W25QXX_Wait_Busy(void)   / f* X! {- \& I: B) n
  249. {   
    / ?2 m( g: P0 p3 I3 ]2 l% L
  250.         while((W25QXX_ReadSR()&0x01)==0x01);   // 等待BUSY位清空" M. x4 a! d6 p2 e. J* [: K
  251. }  
    ( w. m* c3 l* q7 f4 ^
  252. //进入掉电模式/ t8 ?: ~  J4 g1 ]! L
  253. void W25QXX_PowerDown(void)   
    6 G0 `! J; T. w" Y7 u4 q- V
  254. {
    # k8 @7 h4 f2 p* d
  255.           W25QXX_CS=0;                            //使能器件   
    2 O" P. H" F* L( _5 N+ ]% E2 m% m4 g
  256.     SPI1_ReadWriteByte(W25X_PowerDown);        //发送掉电命令  
    5 ~/ \$ O: U4 O* l
  257.         W25QXX_CS=1;                            //取消片选                   2 T! u  K" v% s$ P
  258.     delay_us(3);                               //等待TPD  1 v7 l1 V) {  {* g- Y
  259. }   ; O8 S; J8 E! i, ]3 [
  260. //唤醒1 i" k: `0 ]" {; h/ C9 f& q
  261. void W25QXX_WAKEUP(void)   . G, B) [5 l/ k
  262. {  
    ! f+ U9 M+ a3 q6 B6 ~2 u
  263.           W25QXX_CS=0;                            //使能器件   
    0 B7 z7 @( I1 z# C
  264.     SPI1_ReadWriteByte(W25X_ReleasePowerDown);   //  send W25X_PowerDown command 0xAB    3 w9 h4 k1 @4 B  h
  265.         W25QXX_CS=1;                            //取消片选                  
      h* C$ Z7 l; f+ Y
  266.     delay_us(3);                               //等待TRES1
    # l4 K% {' f/ |+ `  e* E9 o
  267. }   
复制代码
, M2 Y, L% f3 l
main.c8 W) V* e$ x* l1 z8 T- k
. U' K" N% X/ v) r3 h" d
  1. #include "sys.h"
      ^; d) F3 Y/ E& K
  2. #include "delay.h"
    ; ?% v( m7 o7 O& Y# ~
  3. #include "usart.h"1 w' Z7 T9 c: Y9 ^* u
  4. #include "led.h"
    9 U3 J# ~) Z; @4 P
  5. #include "lcd.h"& r& Q; o9 v, F+ G6 N1 N
  6. #include "spi.h"
    5 j1 m8 b) e) m. Y6 T/ H
  7. #include "w25qxx.h"
    * S/ A7 t& o! }$ W: \* ~
  8. #include "key.h"  
    + {& {; C$ }$ R! f& Y% t! ^

  9. " Z" t: Y3 d; h, v7 N8 s# Q5 e

  10. , Q& I; T; k% i4 b2 |: q* M0 k3 k
  11. //要写入到W25Q16的字符串数组) e( a; K# V' f0 n6 O  i& w0 r
  12. const u8 TEXT_Buffer[]={"Explorer STM32F4 SPI TEST"};
      N( D3 M+ F. U% _2 d
  13. #define SIZE sizeof(TEXT_Buffer)         $ I8 ?3 h! d2 I4 I* c. O2 z
  14.         $ {5 F7 J+ f" w
  15. int main(void)
    / V; ]; u( F8 b
  16. { ; K% u( i; x& M4 h3 L
  17.         u8 key;# |$ Q& v  A% A1 m* W# O6 S  V
  18.         u16 i=0;9 j2 v% V! u$ d  w
  19.         u8 datatemp[SIZE];$ |" i* s& m* A( y- t( R4 {9 S, V3 \
  20.         u32 FLASH_SIZE;
    + W0 X: }: J- U/ ~5 }7 W1 M
  21.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组28 q+ l) w+ j& o" d3 T- n. A4 P
  22.         delay_init(168);     //初始化延时函数8 M& o7 k) m' ^7 `. t
  23.         uart_init(115200);        //初始化串口波特率为115200
    . I: G4 p7 b% B+ x5 I) r- n& I
  24.         LED_Init();                                        //初始化LED
    " u3 K5 h2 Q, Q- L* [* r) S" C
  25.          LCD_Init();                                        //LCD初始化 ( I% |; h& X0 P2 t& j$ t+ d6 G' e
  26.         KEY_Init();                                 //按键初始化  
    - B5 V3 d" r4 c5 D) ?' y/ X5 R
  27.         W25QXX_Init();                        //W25QXX初始化8 E# X) V+ q$ c* W' W1 U$ H* s: c; l
  28.          POINT_COLOR=RED; " ~; D( E& y% z* x7 h8 x4 y9 C
  29.         LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");        0 F6 @; _% q/ F
  30.         LCD_ShowString(30,70,200,16,16,"SPI TEST");        
    9 X, k1 g2 F5 I1 H, m
  31.         LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
    * R$ y1 |. I7 k: }# |- d
  32.         LCD_ShowString(30,110,200,16,16,"2014/5/6");         
    % w: D) C) a" q4 W& Q: }
  33.         LCD_ShowString(30,130,200,16,16,"KEY1:Write  KEY0:Read");        //显示提示信息                * K, e- W  Q/ E0 f
  34.         while(W25QXX_ReadID()!=W25Q128)                                                                //检测不到W25Q128. I' y5 D! U% v. J
  35.         {7 t# v1 s3 j7 |1 N6 P
  36.                 LCD_ShowString(30,150,200,16,16,"W25Q128 Check Failed!");
    6 ~' ^) v7 ^! J' k5 T
  37.                 delay_ms(500);
    : u' R+ R' L3 Y9 T6 m
  38.                 LCD_ShowString(30,150,200,16,16,"Please Check!      ");
    + k# C: x# N, U' X: i5 y9 Q
  39.                 delay_ms(500);  P- H; V1 H5 `( `9 w- a/ l6 m
  40.                 LED1=!LED1;                //DS0闪烁
    8 T/ J& d8 H% g5 C- x
  41.         }
    3 Z/ d! a# h' K/ N, ^% _5 g
  42.         LCD_ShowString(30,150,200,16,16,"W25Q128 Ready!");
    # t6 a+ J5 \' \- f; ^4 q' l' i
  43.         FLASH_SIZE=16*1024*1024;        //FLASH 大小为16字节
    & i+ Y( d8 t7 p$ V+ H: a
  44.           POINT_COLOR=BLUE;                        //设置字体为蓝色          & F9 `; _" F) V' ?" _" E" q
  45.         while(1), C# N/ Y/ }  o( T% R. |" A9 K# j
  46.         {
    - p4 {; I2 i( z3 O
  47.                 key=Key_Scan();7 f- b! `3 w0 x
  48.                 if(key==KEY1_PRESS)//KEY1按下,写入W25Q128
    9 e2 b* I0 @7 Q0 w' i2 r0 w
  49.                 {9 s/ h) |1 B2 F% K" T& H9 U
  50.                         LCD_Fill(0,170,239,319,WHITE);//清除半屏   
    ; s" P: M6 Y+ w6 X) N0 _
  51.                          LCD_ShowString(30,170,200,16,16,"Start Write W25Q128....");
    8 E# ], z$ }( _1 M5 p% K2 s
  52.                         W25QXX_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);                //从倒数第100个地址处开始,写入SIZE长度的数据
    8 |  }' w' H% h1 _$ h
  53.                         LCD_ShowString(30,170,200,16,16,"W25Q128 Write Finished!");        //提示传送完成+ a- A! y: u; K  x
  54.                 }
    * B7 T+ B! V7 h  i3 [5 X9 ^" g: q' ~
  55.                 if(key==KEY0_PRESS)//KEY0按下,读取字符串并显示
    : q) o0 H% k0 `+ g' \9 {" \% f
  56.                 {( ]$ M7 u, S4 V# G, `2 i
  57.                          LCD_ShowString(30,170,200,16,16,"Start Read W25Q128.... ");  L9 N* ?, a3 a, ]& j
  58.                         W25QXX_Read(datatemp,FLASH_SIZE-100,SIZE);                                        //从倒数第100个地址处开始,读出SIZE个字节
    & r5 d0 E* Y! \. u7 g! t5 `& Q
  59.                         LCD_ShowString(30,170,200,16,16,"The Data Readed Is:   ");        //提示传送完成
    5 P& b9 W* p& L: E8 |; j6 P
  60.                         LCD_ShowString(30,190,200,16,16,datatemp);                                        //显示读到的字符串
    4 I$ r. B, s$ I- Z& T. r
  61.                 } . S5 w, P7 D7 r4 o$ L
  62.                 i++;$ y" x& y8 q3 S
  63.                 delay_ms(10);2 c' @/ S$ @- X5 A- D0 v
  64.                 if(i==20)
    4 t- ]" p7 B; b' p
  65.                 {
    4 [0 }8 Z  f( h2 N" U
  66.                         LED1=!LED1;//提示系统正在运行        
    8 f5 {: C8 Q) {* X1 v
  67.                         i=0;+ O4 W, q1 S; O( S/ Z
  68.                 }                   ( e/ ~3 r3 K( ^; {: C. W
  69.         }       & {3 O4 k: T6 a: e9 a' |
  70. }
复制代码
# A/ ~  w! \4 Y& ]
05. 结果验证
1 ^$ w; j" |* L按 KEY1 按键写入数据,然后按 KEY0 读取数据。DS0 的不停闪烁,提示程序在运行。
/ E- ?3 ^' o+ U. w) r* ]4 G' A' S
# C0 l+ ^8 P7 J* w4 u  a
6 K1 W; a6 P' m3 b9 Y8 O3 `
收藏 评论0 发布时间:2022-3-27 17:00

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版