请选择 进入手机版 | 继续访问电脑版

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

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

[复制链接]
STMCU小助手 发布时间:2022-5-16 11:53
一、SPI介绍
6 `; t& r5 K6 Y  s& {) C8 l( GSPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。9 H: K  N  [1 K7 F& Z
% P9 n4 K9 W) l/ H
正是简单易用的特性,如NRF24L01、VS1053、SD卡等皆集成了这种通信协议
7 U1 a; F/ ^) Y7 `6 J
$ ~& k9 B8 J; C* @! \ _A_6DRLZV3W0~DW}CEZQ7{O.png
8 @) f2 C, w( @8 `! x3 \! b9 Q- _7 {! O8 |1 C9 a# x: p
二、SPI接口框图  T3 n% J/ L7 C

  P# C1 z" e* ]4 D0 \
@OFY~~NQ}EZ}HLQL{EB%0ND.png
, J+ U# q5 d  ^1 O
$ Z2 L& i! j; I! m" `. U三、SPI优缺点

: y$ ?! d5 S$ h. Z# ]/ l- ~$ n, aSPI接口是在CPU和外围低速器件之间进行同步串行数据传输,在主器件的移位脉冲下,数据按位传输,低位在前,高位在后,为全双工通信,数据传输速度总体来说比I2C总线要快,速度可达到几Mbps。+ J, v- S% S7 V! f/ q' R
/ j5 U6 r; J/ `8 Z6 {- U) {% ^
信号线少,协议简单,相对数据速率高。2 W4 B; S2 o% a% K/ a0 o" J3 g1 V9 J

" W/ W3 K( B6 E4 E6 G) i缺点:没有指定的流控制,没有应答机制确认是否接收到数据
3 U# j9 @. \; J; g+ k( j6 {+ k; L# y4 R4 y, O. ~1 o
四、SPI工作原理总结
, b, P' F+ W& G( V6 k硬件上为4根线。: \; _, w& S( q' [% Y% |& ]: {
主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。
' L5 ~) X1 N6 r6 R0 J串行移位寄存器通过MOSI信号线将字节传送给从机,从机也将自己的串行移位寄存器中的内容通过MISO信号线返回给主机。这样,两个移位寄存器中的内容就被交换。
% i3 b, O9 f+ E/ c! f外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。
5 z! P& t& o- e3 q; K4 C* N. j多个设备使用SPI的应用举例
7 }1 T0 T. A4 ?  M7 v. c7 o1 `, ~6 K
(_8GH(KLZ4`~[RSWOUL_B{Y.png - |8 w* r' |  \) O

% O# Z: c8 _% ?) ^0 B' \# r- G五、时序图
: Z) M) \' n& G6 P* I' s时序为 SPI_CR1 寄存器中的 LSBFIRST 位复位时的时序
/ B" Q, G4 }( q1 _, K  N; |6 `; W/ @
SPI_CPHA的值将会影响SPI_CPOL的值
% O8 w' V3 I$ H9 N3 c& r# c1 W$ ]
6 y2 g9 c) m! A& N# l4 n 4R1]OS0}$R`DITZTCDX~T~D.png / X  R9 S0 R+ Z1 p, C, W

$ N) D' C/ q: Y! s六、SPI程序编写过程
, L/ f6 h; O4 ?/ L5 Q. ?; T
  1. //①使能SPIx和IO口时钟
    : T# X- ?1 |/ ~
  2. RCC_AHBxPeriphClockCmd() / RCC_APBxPeriphClockCmd();
    ; Y9 w$ s8 x( P6 b$ X+ k* E

  3. 7 E' n1 \; r. i+ V8 @
  4. //②初始化IO口为复用功能
    8 y1 N( L! D+ Q
  5. void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);) s6 ~5 L5 F3 a) W2 f
  6. 8 K- e  m9 D: C* A$ e
  7. //③设置引脚复用映射:2 q( S  Q: h$ H+ e) d8 b8 M
  8. GPIO_PinAFConfig();3 i4 F+ K: K) V5 r  Y+ C- s

  9. % A; X# e5 ^( {7 \2 f- J. z
  10. //②初始化SPIx,设置SPIx工作模式
    7 c. d% P( q- |4 K. {; p4 [
  11. void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);+ b6 }3 N1 ?8 }# b! k

  12. 8 b! t: k8 I2 q4 C
  13. //③使能SPIx9 G) f9 m7 b: R5 X8 d  F! h/ q
  14. void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
    % ^# G; f4 ]' }: O

  15.   f' f% ?# R6 ?
  16. //④SPI传输数据8 o# O# O- x& l+ b; y, y, h& d
  17. void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
    ( `, S; t  Y* c" E: S+ q1 w
  18. uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;
    / Y) h" W, F: V! H* T( {

  19. . ~  u9 a9 {" k0 m
  20. //⑦查看SPI传输状态
    8 P# n/ t/ B! k4 {& s5 x& F/ {- y
  21. SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);
复制代码
  1. SPI.c:4 W/ ~+ A% a4 s  ]( T- t# z+ W
  2. #include "spi.h"
    * ^  A( O2 K1 t# e- e6 `
  3. 3 O" N" g" `* w8 K# R
  4. //以下是SPI模块的初始化代码,配置成主机模式                                                   2 i) Z/ m9 o' q2 q2 E) n1 z% A
  5. //SPI口初始化3 f9 Q4 N* o- T( }! D2 f
  6. //这里针是对SPI1的初始化
    * I6 o* p. w. d2 z" o' o4 f8 I, [
  7. void SPI1_Init(void)9 O* }8 ^+ V: Q' m' c/ f
  8. {         + ^$ n3 }* Q3 s& S  K  x9 y
  9.   GPIO_InitTypeDef  GPIO_InitStructure;0 V" V+ W, Q- E, O
  10.   SPI_InitTypeDef  SPI_InitStructure;
    * P6 ~, P9 y: X* A2 Z# X
  11.         
    " o. s3 z6 V6 f: J1 s
  12.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟4 k- u/ T0 E" r1 c
  13.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟- f3 I& G; S+ D9 y% ]$ ]$ d
  14. 9 P8 X/ Z& \% j6 p9 @. H
  15.   //GPIOFB3,4,5初始化设置* |; D6 Y* K. c8 U: E  g
  16.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出        % `1 \3 |# Q) m2 T$ ?/ P; W/ Q# J
  17.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能; V9 j; }% m; E' h) M3 y: b' {6 ^' a# ^2 g
  18.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
    ; m2 i. k/ m+ i' g  T; m: @, v( X
  19.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz/ ^! ~  y) K- B9 S' F
  20.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
    0 S6 q  O/ r/ n2 j' U0 i
  21.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
    " U* n6 N! k3 _, s+ B$ [# I* O
  22.         
    ! {( a4 U9 f* E
  23.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为 SPI10 S5 a! N1 e2 `* Q
  24.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为 SPI1. b& B6 a9 N5 `9 ]0 \9 S
  25.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为 SPI1
    5 _! E5 ~" M6 V: y& [
  26. " C7 D0 S0 j$ A7 w6 l! a" c2 o
  27.         //这里只针对SPI口初始化
    3 v0 ^# g9 j8 k" }, \
  28.         RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI13 ~/ x; B, R% `5 L5 \$ E
  29.         RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1
    , `1 u' s, ^% g% t( d

  30. & P  S  O- K; W  Y/ D) E3 _8 G: i
  31.         SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工" I9 }/ r! o, z( |# Q
  32.         SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                //设置SPI工作模式:设置为主SPI
    $ v3 h: R- l2 E: G# Z& p* }
  33.         SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                //设置SPI的数据大小:SPI发送接收8位帧结构
    6 g0 p+ D* b* a, l  o/ ~
  34.         SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                //串行同步时钟的空闲状态为高电平7 W+ U; b) H' x$ X
  35.         SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;        //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
    % W* e( {  X# k2 F
  36.         SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
    . _# [9 m/ B: ^# w
  37.         SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;                //定义波特率预分频的值:波特率预分频值为2561 E; d" O1 K( Z/ T
  38.         SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;        //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始' n" z' |  J$ o1 L" B
  39.         SPI_InitStructure.SPI_CRCPolynomial = 7;        //CRC值计算的多项式
    + ^6 f5 `8 {# m- i1 r+ [
  40.         SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
    - _7 z0 U- C- o- J3 z

  41. / T, ]; ~; B. s. ^1 ^
  42.         SPI_Cmd(SPI1, ENABLE); //使能SPI外设
    : q$ `' t: t6 O) G) }: x

  43. , p2 h# ^% g: c+ ^, D/ Y
  44.         SPI1_ReadWriteByte(0xff);//启动传输                 
      L0 ]# w% B+ Z. H/ P6 }
  45. }   ) u( `- w$ U( s2 c: j
  46. //SPI1速度设置函数- M* @9 B8 W7 k) o( y; e
  47. //SPI速度=fAPB2/分频系数
    % {0 Q: Y3 Y  i
  48. //@ref SPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256  
    0 O" ?" R6 _8 H/ s
  49. //fAPB2时钟一般为84Mhz:% F# S1 T/ |# @9 N9 [" C
  50. void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)
    % F/ K# F9 L4 e5 H
  51. {
    ' N8 y8 J" @* N" s1 A- Y
  52.   assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性
    8 C+ K4 n, F* `# A7 {
  53.         SPI1->CR1&=0XFFC7;//位3-5清零,用来设置波特率& Q4 N: k3 ]5 r) u
  54.         SPI1->CR1|=SPI_BaudRatePrescaler;        //设置SPI1速度   r3 a+ j/ k; S
  55.         SPI_Cmd(SPI1,ENABLE); //使能SPI1
    ! l% E5 N; k# e) X% C; ?* W
  56. } . J* F5 f) s7 E( `/ Z
  57. //SPI1 读写一个字节
    7 {$ M* r+ t4 |. d- O$ s0 [$ }6 K
  58. //TxData:要写入的字节
    & ^) j; \9 M5 X! O8 i) A
  59. //返回值:读取到的字节
    7 z+ C8 C' m5 y& v* M
  60. u8 SPI1_ReadWriteByte(u8 TxData); z" D; |* A2 [% ~3 ~# s
  61. {                                          / n$ B  A- M% ~* \6 f. G
  62. 2 F* ^. L* |, j  {
  63.   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空  & G9 B1 f/ F8 A
  64.         : [$ n* y) e4 r0 z5 _5 E
  65.         SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte  数据+ q* k. Q' m6 W1 ^& ?9 K, E  H7 y# l0 q
  66.                
    2 ~) t" _9 [4 ^% F0 A- w6 I! Q
  67.   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一个byte  
    & F3 y  x/ H" R) d) l8 W

  68. 7 H8 d3 j4 z8 R/ v
  69.         return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据        
    1 E* T& z; n: S+ B$ N5 U2 [
  70.                      
    2 t+ t* f8 f; M3 {6 e
  71. }
    0 \  e+ b3 o& a2 {! S

  72. 4 D2 }/ ^; [2 o! H
复制代码

) |% N7 W9 f: Q+ y; c! @七、W25Q12xx的原理及应用; _# I+ v) l4 e
W25Q128将16M的容量分为256个块(Block),每个块大小为64K字节,每个块又分为16个扇区(Sector),每个扇区4K个字节。W25Q128的最小擦除单位为一个扇区,也就是每次必须擦除4K个字节。这样我们需要给W25Q128开辟一个至少4K的缓存区,这样对SRAM要求比较高,要求芯片必须有4K以上SRAM才能很好的操作。
/ s9 d1 D. {" w1 s1 N8 K7 x) O. h7 v/ u3 U4 l* S+ B
W25Q128的擦写周期多达10W次,具有20年的数据保存期限,支持电压为2.7~3.6V,W25Q128支持标准的SPI,还支持双输出/四输出的SPI,最大SPI时钟可以到80Mhz(双输出时相当于160Mhz,四输出时相当于320M),更多的W25Q128的介绍,请参考W25Q128的DATASHEET。
4 t5 z% f4 ~( A# \2 [
+ z% ], |# E9 x, ^7 d0 [ D%)W27U]]MDR`PRB[FT4~Z1.png , O. H& ~, i2 r/ {
) Z4 a! A& j5 M
W25Q12xx可根据原理图查看,使用的是SPI总线通信协议+ Z, P8 W) V' h$ k

- a. I3 x5 G7 R8 m# l$ C比如原子的原理图
* l8 N* o' e" G9 p7 s" B3 M8 d
/ d! s) J" a* R6 n% V' k4 {4 [ 20190611221654679.png
4 z  u3 X. G5 w2 |% _- r
2 O: w; {+ r2 U7 G9 D7.1 分析W25Q128指令
! z# Z0 T4 h9 v- W! ^/ L" r可参考W25Qxx的数据手册,这里列出W25Q128部分指令:
! r1 n# L) A1 Q- g$ X
* ^0 c4 ~  s8 ]9 d1 a) o, B  X2 c 2019091611364032.png 5 F: |; B: S5 [+ J+ j. j! M( H

9 `6 i0 P4 j; A# _比如读取设备的ID的指令:0x90000000
! S8 g, L- u( f, R7 O$ H9 r+ H9 N6 U: ]# u) K. N# y* x* [
20190916113704620.png   `1 ~# _- }7 a
( ?  E0 z1 q; T/ r* f
K$HWO9ZGFGE$CVLD47Q]_4G.png
4 D: ?' C4 ~; u/ x3 R8 |1 Z/ ~. c% S7 o% y
WLLL(({{_EVAN[005V791UU.png
( m, m# ?$ f5 D! Z! G" B: g! d$ Z6 d0 I3 n. B7 q9 q4 h+ t
7.2 擦除扇区:5 P+ [' |, S, @' z0 D0 r6 I
4 F  I9 s" V* ^
NZ$~TU098T[KO5)GTQW){UQ.png # R! Y; w: U$ j% J* `1 Y$ S
$ W. G& }4 I. o2 K) N  N2 z- b
7.3 部分常用设备读取指令:

% t7 u, F0 s6 g: r2 m+ n! X' a1 r7 [: V: S: i. e
Q`V(R2ZUHAZ8SAS2[A}1H%2.png
8 V% o" w, _) Y: |# [$ D+ X" P1 [" \6 ]: P6 ^8 N* K
每次操作前要使能写操作,且给一个低电平。结束时要给其为高电平,再失能写操作。也就是通过操作片选引脚来确定是否使能或失能。
  r! g$ [7 K6 w9 y& i/ d# m+ j2 v( b; m
LR6P1TX%{R][_M{CX4]K.png : h+ j) f: `- t1 x; P1 z
$ T) G7 k) M4 ~* m- ^3 k
  1. W25Q12xx.c:
    6 U' q2 U$ a9 R, }# m$ |' I
  2. #include "w25qxx.h" 5 L0 ?# P1 |0 E) a- Z6 n6 E
  3. #include "spi.h"! [& o! `( ]) ?' h: O  `
  4. #include "delay.h"           
    ) D2 K2 M$ l0 Z! D# S
  5. #include "usart.h"        ; n. B0 x" U: @: G# ~
  6. 2 p7 {1 D' J8 Z# O
  7. u16 W25QXX_TYPE=W25Q128;        //默认是W25Q128
      Y: [, W! @% K
  8. ) _  n' M, Z8 F9 m- u
  9. //4Kbytes为一个Sector, m, ]* `2 V. Y; b4 |" N* M
  10. //16个扇区为1个Block: D9 W# j5 R$ M
  11. //W25Q128
    - }' _0 A+ n. b" M$ x
  12. //容量为16M字节,共有128个Block,4096个Sector + Z2 J+ A  ?7 K9 P
  13.                                                                                                          
    ( v, ~- h7 a. N! A' S
  14. //初始化SPI FLASH的IO口" N. e( [9 X( h5 v
  15. void W25QXX_Init(void)
    , e- G8 z' h( R/ m
  16. {
    $ F$ N# h" \9 Y& O- ^  r: w% Y
  17.   GPIO_InitTypeDef  GPIO_InitStructure;: C5 ?- }/ c9 D0 Z

  18. 6 K$ f6 d! y3 f6 I% \6 L
  19.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
    ' X6 F2 u& D0 D
  20.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);//使能GPIOG时钟& f# w: \% I8 u1 a* O
  21. 2 X# C* J. M% z' q4 ?
  22.           //GPIOB14
    7 J$ }9 Z6 H; m7 ~6 B& _; c
  23.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;//PB14
    1 n$ p, P2 s/ |3 P
  24.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出# }2 ]$ y5 x2 A: N0 [2 g$ I; D
  25.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出$ e% \7 \0 L' X/ R2 _: _  {( M4 q7 `
  26.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz0 i2 E) ^" s. @  Y; n
  27.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉1 v0 D& ]5 x) H. I( k# Y
  28.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
    4 v+ [" s( Y7 {% s$ o+ B- G7 U

  29. * o. n! {* H8 A
  30.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//PG7$ b. Z& I2 U/ |" P
  31.   GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化
    - w- ^" _: f  y) S1 K; n$ A

  32. 3 w9 z! x4 W. y. N3 M
  33.         GPIO_SetBits(GPIOG,GPIO_Pin_7);//PG7输出1,防止NRF干扰SPI FLASH的通信 : d5 j( K6 h0 Q; E
  34.         W25QXX_CS=1;                        //SPI FLASH不选中
    " ^0 C# o8 `4 Y5 C- A
  35.         SPI1_Init();                                           //初始化SPI/ D" Q8 _, O; @  ^: l
  36.         SPI1_SetSpeed(SPI_BaudRatePrescaler_2);                //设置为42M时钟,高速模式 % `" }8 \% X7 U0 T) A3 ~/ c
  37.         W25QXX_TYPE=W25QXX_ReadID();        //读取FLASH ID.
    9 n6 ]' t3 S$ ~; O) \  b$ d  r
  38. }  8 W" K; ]6 \$ l* i( w! n  D

  39. 2 i8 c9 m5 U* O8 ^
  40. //读取W25QXX的状态寄存器
    ( u% p/ O# x( Z* ~1 g
  41. //BIT7  6   5   4   3   2   1   01 q% q7 Y1 h( J( k7 g. P- q
  42. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY
    ) F9 i; b; r) C/ p, W) j( H
  43. //SPR:默认0,状态寄存器保护位,配合WP使用
    / h" a4 E6 w: q; g( F
  44. //TB,BP2,BP1,BP0:FLASH区域写保护设置- t+ D. j' Q6 o" j$ F% w
  45. //WEL:写使能锁定
    ! A: W* b/ c; I8 l
  46. //BUSY:忙标记位(1,忙;0,空闲)0 A1 o, i. M- ]) S6 K
  47. //默认:0x00
    9 r3 H% e% j7 X5 Y3 |6 c: k
  48. u8 W25QXX_ReadSR(void)   * c  y0 ?! ]1 N6 K. B8 J+ A
  49. {  6 Y. N- r# T/ o! w0 p
  50.         u8 byte=0;   1 b! I/ J) ~" A# O% Q0 \0 o7 ?
  51.         W25QXX_CS=0;                            //使能器件   
    2 J" H, J2 M% D5 |  t7 n
  52.         SPI1_ReadWriteByte(W25X_ReadStatusReg);    //发送读取状态寄存器命令   
    7 p1 K& g* d! L0 a) J+ k2 O0 x0 G
  53.         byte=SPI1_ReadWriteByte(0Xff);             //读取一个字节  ( z4 j" b8 {3 {) j& O+ h
  54.         W25QXX_CS=1;                            //取消片选     ! e9 f+ Q, s- K  W
  55.         return byte;   
    % N/ b! i2 }  a" V
  56. }
    . s/ m- O5 n) {& a3 ^" \
  57. //写W25QXX状态寄存器
    0 X' V4 S# H/ P! g' x" Z9 Z% o
  58. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
      N& s+ r, _7 ~  `8 U8 t' Z! j) k
  59. void W25QXX_Write_SR(u8 sr)   
    ; L% Y; q, _4 c
  60. {   " S, g' ]4 A9 T; O/ T9 M0 O( d
  61.         W25QXX_CS=0;                            //使能器件   
    ; u, C* C4 o& j3 z- j
  62.         SPI1_ReadWriteByte(W25X_WriteStatusReg);   //发送写取状态寄存器命令    & K; T. p* l5 G2 ]0 F$ M
  63.         SPI1_ReadWriteByte(sr);               //写入一个字节  2 t# k% R1 ^: B9 L2 p7 i
  64.         W25QXX_CS=1;                            //取消片选                  
    ) ]1 L1 \4 t9 }6 M" O
  65. }   
    * w, L# V5 u" _
  66. //W25QXX写使能        
      x+ w2 H' c7 e
  67. //将WEL置位   
    : O8 g/ {( d7 ?+ q' a  z
  68. void W25QXX_Write_Enable(void)   $ _+ S2 N  K7 {% c, o: `
  69. {
    ( @, ]# b+ M# S8 R! X! w
  70.         W25QXX_CS=0;                            //使能器件   
    ; ]! K4 s" Z( Y4 ~: s
  71.     SPI1_ReadWriteByte(W25X_WriteEnable);      //发送写使能  7 B/ {0 B$ ?3 y! O: G* z2 W
  72.         W25QXX_CS=1;                            //取消片选                   8 y7 K% j. S+ a0 X) l% S
  73. }
    # j4 z$ z$ ~8 W$ _% n; N/ g
  74. //W25QXX写禁止        
    6 d1 ~  H6 M/ F0 q5 o
  75. //将WEL清零  % }% s, _( G4 ]0 S
  76. void W25QXX_Write_Disable(void)   
    3 X+ p, ]' s# u' ?# U0 q8 t
  77. {  
    5 q9 Y! W- B% ?+ v  A! c
  78.         W25QXX_CS=0;                            //使能器件   
    7 U3 Q& Z& R+ b: w2 E/ ?" \
  79.     SPI1_ReadWriteByte(W25X_WriteDisable);     //发送写禁止指令   
    + M+ {/ P  k8 X& x% Z& d8 X
  80.         W25QXX_CS=1;                            //取消片选                   / _% d+ d6 v, R) e1 u) N
  81. }                 ) w* s7 X3 ^. T% \4 l! d/ C6 o" o
  82. //读取芯片ID
    / P" u2 w6 v* W( [" H2 K
  83. //返回值如下:                                   
    7 ~; b: t% c, @% M6 M
  84. //0XEF13,表示芯片型号为W25Q80  , D  m, ?% c3 @. S! m5 R
  85. //0XEF14,表示芯片型号为W25Q16    $ {! v7 n2 F$ V( ?+ g: q
  86. //0XEF15,表示芯片型号为W25Q32  ) f. r8 e* {4 }% d7 \
  87. //0XEF16,表示芯片型号为W25Q64 * D- h7 t& o8 \% j0 O
  88. //0XEF17,表示芯片型号为W25Q128           
    6 X/ e  r6 K* B3 _$ a
  89. u16 W25QXX_ReadID(void)
    3 T6 D: p7 }: X
  90. {) N% P! M! ?/ ^: Q' ]( w, T
  91.         u16 Temp = 0;         
    7 d4 i% H& q0 J5 E0 }5 f" R; I
  92.         W25QXX_CS=0;                                    
    ' @6 p/ h5 U, u- }
  93.         SPI1_ReadWriteByte(0x90);//发送读取ID命令            0 h. @5 M1 r7 L* o, i0 ~* N; F
  94.         SPI1_ReadWriteByte(0x00);             ; Y( ~* r8 A4 H; Z3 ]& W
  95.         SPI1_ReadWriteByte(0x00);            
    0 u3 }# c! {! c9 A% F
  96.         SPI1_ReadWriteByte(0x00);                                     " _& U: U$ G# k* N
  97.         Temp|=SPI1_ReadWriteByte(0xFF)<<8;  
    * q% \8 R  ~, j7 h! N% q* D
  98.         Temp|=SPI1_ReadWriteByte(0xFF);         
      r/ R. C' P! s$ y  A, X7 S
  99.         W25QXX_CS=1;                                    6 b; W- c- V# \# @% q& O' T  W
  100.         return Temp;  F  Q' T% |: t& B# q+ N: U
  101. }                       5 F' B0 e' [) i3 e! M
  102. //读取SPI FLASH  
    $ _9 h5 y; j$ T6 Y
  103. //在指定地址开始读取指定长度的数据
    3 o: v1 W& g, j6 d
  104. //pBuffer:数据存储区" a2 L* |2 f# S) ~! r1 k) a
  105. //ReadAddr:开始读取的地址(24bit). Y9 m/ P2 z# B* E! n
  106. //NumByteToRead:要读取的字节数(最大65535)
    * o9 ?- r. g( Y
  107. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   " o4 v+ p1 ^4 x. `. c# K9 D- c  r* K
  108. { ( w: M: l# @; C2 b, P( v; G' j6 x& F
  109.          u16 i;                                                                                       " H- x5 E$ Y6 x; d2 a; u
  110.         W25QXX_CS=0;                            //使能器件   
    $ s- W6 V3 U1 w, M# R  B5 v0 `+ D
  111.     SPI1_ReadWriteByte(W25X_ReadData);         //发送读取命令   
    * A/ H' B; j4 ?& ~  M7 K, [
  112.     SPI1_ReadWriteByte((u8)((ReadAddr)>>16));  //发送24bit地址   
    6 \7 X, |! k# j8 G7 p* L
  113.     SPI1_ReadWriteByte((u8)((ReadAddr)>>8));   
    + [9 H' n8 a, K9 e% Y
  114.     SPI1_ReadWriteByte((u8)ReadAddr);   
    / w2 J6 u  u+ V1 |7 @$ F
  115.     for(i=0;i<NumByteToRead;i++)
    7 M0 b( z8 Y/ t# e; i8 \  m
  116.         { 2 y6 @/ ?  o5 i0 R  i- ]. G* @7 h# L
  117.         pBuffer<i>=SPI1_ReadWriteByte(0XFF);   //循环读数  
    / i/ {- |+ C7 I$ i' i& A) c% y- }( {3 f
  118.     }
    1 Q+ T# _$ }9 b: |: @
  119.         W25QXX_CS=1;                                                    ; W* b0 V4 ^; {% [  F0 W, Y6 ]
  120. }  - B& d. q4 I5 x: X
  121. //SPI在一页(0~65535)内写入少于256个字节的数据8 |5 O7 j- Q2 I4 t+ p1 r( q' a8 c
  122. //在指定地址开始写入最大256字节的数据" f& [* c% }+ {1 d9 M: C
  123. //pBuffer:数据存储区6 w& C" E& g& J6 {$ k! D
  124. //WriteAddr:开始写入的地址(24bit)
    ' Z5 o: V, ?- I4 r* Z4 C
  125. //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!         
    ' @0 Y; B( q9 k4 P2 f: m
  126. void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
    " b% |; x+ ]  \$ x1 J, t
  127. {
    : D. U# ]) E9 G
  128.          u16 i;  8 |& @! y* u+ l3 P/ z1 S$ }5 }8 G: B
  129.     W25QXX_Write_Enable();                  //SET WEL * D  V" z: ?  |: ]% I# M
  130.         W25QXX_CS=0;                            //使能器件   ) t' G. f, [0 `2 J: N0 ^! A) F! D
  131.     SPI1_ReadWriteByte(W25X_PageProgram);      //发送写页命令   7 i; j, h- m% O+ y
  132.     SPI1_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址    6 _" \! Z7 f, P- y( p
  133.     SPI1_ReadWriteByte((u8)((WriteAddr)>>8));   
    . `1 K/ [" K5 T* [, S3 U
  134.     SPI1_ReadWriteByte((u8)WriteAddr);   3 V& H# L/ {5 K. i, c/ F0 M) g
  135.     for(i=0;i<NumByteToWrite;i++)SPI1_ReadWriteByte(pBuffer<i>);//循环写数  # ^5 I2 i; `9 H* o2 x3 o$ t% u) k
  136.         W25QXX_CS=1;                            //取消片选
    * ^; I9 f# B: Z( R3 ~
  137.         W25QXX_Wait_Busy();                                           //等待写入结束
    1 i5 |& C, \' ]0 V- K
  138. }
    + x" u! p) g: e
  139. //无检验写SPI FLASH
    1 N! I% }" u  }9 k5 e
  140. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
    # m% y/ V' L5 _& f/ s
  141. //具有自动换页功能 ) I" l: J' U6 X
  142. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!% L& g4 A/ H1 v! x$ G4 |: e+ ~
  143. //pBuffer:数据存储区9 L6 U$ N. U# z0 ]; j/ y/ J
  144. //WriteAddr:开始写入的地址(24bit)- @1 B& K8 v6 r! V. u( t& {
  145. //NumByteToWrite:要写入的字节数(最大65535)
    / B# R- d" e( I6 ?) k
  146. //CHECK OK
    # b- [" h3 U; t. Z- Y* ~, e0 c
  147. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   2 T8 J' z9 Q' w2 }+ O* p: }
  148. {                                          
    # F4 C  U6 Y5 Y: C& N" R2 k
  149.         u16 pageremain;           
    % _7 G$ j8 w$ y# W+ E
  150.         pageremain=256-WriteAddr%256; //单页剩余的字节数                             
    ) e" U1 o( o- j  l2 ?
  151.         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节; q* m" y  r6 S: x% ~. X* {
  152.         while(1)
    ; H" `5 ~& k4 G4 t* T7 f  e
  153.         {           + ?( d" M3 y6 M6 i; {# }
  154.                 W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
    4 J- T$ E' _+ A5 ^
  155.                 if(NumByteToWrite==pageremain)break;//写入结束了: T- Z8 o8 U1 E/ Z) K
  156.                  else //NumByteToWrite>pageremain8 b2 ~& y1 e) K, x! t
  157.                 {
    2 M) M* a* \" S! F% F
  158.                         pBuffer+=pageremain;
    8 S! f; H- s+ [# y3 T
  159.                         WriteAddr+=pageremain;        
    7 |5 q$ s" j4 a. k% O
  160. + \: B' E9 p1 w9 Q9 d) ]$ B
  161.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数7 p$ D1 s# L# H5 G- a
  162.                         if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
      I$ Z" k  c, f8 M2 o
  163.                         else pageremain=NumByteToWrite;           //不够256个字节了. n: @/ J1 B$ I9 `
  164.                 }
    3 F) E5 E& d2 y1 d
  165.         };            
    8 D5 h- C& L; F; c
  166. } 4 P+ h5 y) s9 g& m1 X) L4 l
  167. //写SPI FLASH  ; }/ v  u) K6 W4 p
  168. //在指定地址开始写入指定长度的数据5 ~& U# z& u* \4 C- O3 v+ C
  169. //该函数带擦除操作!
    % K+ n, b, s: v. ^  M; F# ~) |
  170. //pBuffer:数据存储区- Y  k7 O' u* n; c% g. z4 }4 H# U5 S! }
  171. //WriteAddr:开始写入的地址(24bit)                                                0 Q! }2 H3 z( Y" k/ ^
  172. //NumByteToWrite:要写入的字节数(最大65535)   
    5 X/ h2 O1 ?0 ?) r& i
  173. u8 W25QXX_BUFFER[4096];                 
    # e; x7 T  e- U0 A" Y8 x
  174. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
    " [- Y; K$ K" ^, b; @
  175. {   S5 Y4 Q8 l( U5 t: ^+ t
  176.         u32 secpos;
    4 S: t, p8 T- e8 F
  177.         u16 secoff;
    5 w( Z( }5 Y- A' P7 L  B) F
  178.         u16 secremain;           ) P& N( o( b( q  R9 _
  179.          u16 i;    5 r9 @  f' t: P7 x
  180.         u8 * W25QXX_BUF;          7 ^, m1 m2 k5 W2 Y- f1 t3 }( q( K
  181.            W25QXX_BUF=W25QXX_BUFFER;             $ f, J6 x- _& x3 V2 U9 Y) {
  182.          secpos=WriteAddr/4096;//扇区地址  / ^% X. [0 q% x
  183.         secoff=WriteAddr%4096;//在扇区内的偏移
    5 C2 N, H# [' j+ j5 V2 B
  184.         secremain=4096-secoff;//扇区剩余空间大小   " t% _/ M7 H) D' ?# N$ m, G
  185.          //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
    2 @0 @  x( h; X) L" \% g- J& ]
  186.          if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节+ l, \3 h# Y" s
  187.         while(1) 1 |, D/ j( V9 E" O, A# \4 q
  188.         {        
    & }7 f& |3 h" p6 f7 k+ C
  189.                 W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容. P1 J$ b5 S- ~1 N& |
  190.                 for(i=0;i<secremain;i++)//校验数据/ \4 @6 o" _& _3 G; s; ]- N8 {
  191.                 {
    + {3 i: s' g( {
  192.                         if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除            
    ; @# J5 M  Q9 T: K3 }- g0 Z
  193.                 }) \" P& Q' w% J4 Q7 W
  194.                 if(i<secremain)//需要擦除9 ]: n1 T6 P4 M" P' b
  195.                 {
    8 c6 |8 [2 ?6 Z
  196.                         W25QXX_Erase_Sector(secpos);//擦除这个扇区) k% d  _2 |& c# c( @) b5 ?9 F9 g
  197.                         for(i=0;i<secremain;i++)           //复制* x4 A! D! V3 y/ r1 B
  198.                         {
    . p1 y( |0 o- {9 Q8 ?
  199.                                 W25QXX_BUF[i+secoff]=pBuffer<i>;            I1 Z; A5 X( t9 K  ]% |
  200.                         }& x* \: E: J: L( n1 l! s: v* V
  201.                         W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  2 N9 I& M- g' y, a' G

  202. ( Z- v! o7 D3 ]' z+ J, \
  203.                 }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    % U4 ^* y6 N: y; ~5 y" q# N5 v/ B, b3 U
  204.                 if(NumByteToWrite==secremain)break;//写入结束了
    / Z/ b4 W% C& V8 q1 E
  205.                 else//写入未结束
    . E' e- I; j& P8 O8 J% w# s
  206.                 {
    6 W) L/ s$ N) b/ t; ]4 C  m. z
  207.                         secpos++;//扇区地址增1
    3 f* @0 r' \8 }3 q
  208.                         secoff=0;//偏移位置为0          # l8 q0 {/ ^3 o; m8 [+ I

  209. $ U$ R6 D: x* N7 U- D: b5 X6 X& u( [
  210.                            pBuffer+=secremain;  //指针偏移2 ?4 I( x" L7 J% h( Y2 [8 {
  211.                         WriteAddr+=secremain;//写地址偏移           * A2 X4 M1 @& T
  212.                            NumByteToWrite-=secremain;                                //字节数递减
    7 b- D! W6 n# k  T, y# c& c
  213.                         if(NumByteToWrite>4096)secremain=4096;        //下一个扇区还是写不完0 m! v; F1 s7 J  M
  214.                         else secremain=NumByteToWrite;                        //下一个扇区可以写完了- _& N% m8 h5 {1 J
  215.                 }         
    ( N, i0 {% P, u; ^3 Z) D
  216.         };           o1 f- _6 M. C5 t% f- E
  217. }
    " D& T% {+ Z! z: d* C
  218. //擦除整个芯片                  , S8 r( y# e4 ^1 `* h5 N$ I+ `, X1 R
  219. //等待时间超长...4 z, D/ B9 f8 j# ]$ {* o; n
  220. void W25QXX_Erase_Chip(void)   
    . r- ~+ R; Y" w/ i
  221. {                                   
    8 }2 }7 r/ G6 {+ d3 B! l* s0 r
  222.     W25QXX_Write_Enable();                  //SET WEL + n; d4 R  W% X
  223.     W25QXX_Wait_Busy();   4 W2 H' j$ {  r2 A- ^
  224.           W25QXX_CS=0;                            //使能器件   
    % b) u% b" p, {3 z9 o
  225.     SPI1_ReadWriteByte(W25X_ChipErase);        //发送片擦除命令  
    : E. U( l3 U! D  S6 ~" K
  226.         W25QXX_CS=1;                            //取消片选                  
    9 ^/ F, ^  R: R( ^
  227.         W25QXX_Wait_Busy();                                      //等待芯片擦除结束
    ) o, B2 _, X) x' O; B) [7 v2 X
  228. }   , z" g+ m0 S4 M
  229. //擦除一个扇区
    & P' u8 e$ N, e8 w4 e, F- {
  230. //Dst_Addr:扇区地址 根据实际容量设置4 j( o. I! F/ m+ H9 c; }
  231. //擦除一个山区的最少时间:150ms* ~" B+ P* a: X- L  \1 u* }! a
  232. void W25QXX_Erase_Sector(u32 Dst_Addr)   - I8 h' Y6 j+ V+ {4 S* |8 N
  233. {  
    + w: i' ?4 n% U' C, `
  234.         //监视falsh擦除情况,测试用   
    " s. r( T7 h, p+ u
  235.          printf("fe:%x\r\n",Dst_Addr);          , {" b% `  w- w, J  I5 r- o
  236.          Dst_Addr*=4096;1 Y. h, a7 S9 D- R4 \& e# Y% U% Y
  237.     W25QXX_Write_Enable();                  //SET WEL          , d+ A' K9 @& o3 E8 B6 B7 y
  238.     W25QXX_Wait_Busy();   
    & g' L" C- |& e  T5 N
  239.           W25QXX_CS=0;                            //使能器件   1 f6 K( L  i+ \( t' m
  240.     SPI1_ReadWriteByte(W25X_SectorErase);      //发送扇区擦除指令
    . L% M8 |, E- B9 n! z/ ~
  241.     SPI1_ReadWriteByte((u8)((Dst_Addr)>>16));  //发送24bit地址   
    ! x: w# F# h2 t2 {7 @! z
  242.     SPI1_ReadWriteByte((u8)((Dst_Addr)>>8));   
    $ y# |2 _) W( x2 f; k
  243.     SPI1_ReadWriteByte((u8)Dst_Addr);  " f" u2 D; A7 ~! S" Z$ U
  244.         W25QXX_CS=1;                            //取消片选                  
    " S/ d# a: R) Z8 V9 Z
  245.     W25QXX_Wait_Busy();                                      //等待擦除完成
    : k# K  Z1 d+ S  U
  246. }  
    $ R; l7 ^0 e# x4 P: n7 [2 Y
  247. //等待空闲
    3 k7 U$ [: A7 t. G% q2 Q3 r
  248. void W25QXX_Wait_Busy(void)   
    * o! h/ f( m0 j8 Y" M0 @
  249. {   4 W* _+ k& r2 K- w* x9 k0 ~/ c
  250.         while((W25QXX_ReadSR()&0x01)==0x01);   // 等待BUSY位清空' i3 Y" H3 o( F+ s
  251. }  ( V$ D; s+ p3 G& u# j2 A
  252. //进入掉电模式
    . x- h2 p& `0 D* ^
  253. void W25QXX_PowerDown(void)   8 i# v  C2 M- ^
  254. { & J; n" _0 G! _% x# `+ u
  255.           W25QXX_CS=0;                            //使能器件   
    9 g$ L8 y+ [3 Q# R6 g# \
  256.     SPI1_ReadWriteByte(W25X_PowerDown);        //发送掉电命令  3 y+ m- Z. j6 C! S* G
  257.         W25QXX_CS=1;                            //取消片选                  
    + t! ^; L0 s9 B! G' {
  258.     delay_us(3);                               //等待TPD  2 N4 L: y' o7 [7 h9 C% u
  259. }   % B. e6 o! H9 v; _* O7 K
  260. //唤醒
    # J2 E" ]; j! L- N8 R& H
  261. void W25QXX_WAKEUP(void)   
    ; `) e- y& v7 e5 m' K( G
  262. {  
    6 p8 r8 M5 {& b
  263.           W25QXX_CS=0;                            //使能器件   2 }7 l" U& \2 h5 {2 w" S2 O9 v- q
  264.     SPI1_ReadWriteByte(W25X_ReleasePowerDown);   //  send W25X_PowerDown command 0xAB    % m. l/ F1 ^  _4 y5 [
  265.         W25QXX_CS=1;                            //取消片选                   , V9 H6 C% B7 `5 ]5 |6 c+ j
  266.     delay_us(3);                               //等待TRES1
    6 W7 H+ k7 ~6 ~& x6 s
  267. }   </i></i></i>
复制代码

  1. - O# A- I, e; N: v$ C4 t# \5 V, B
  2. W25Q12xx.h:
    ( u4 r8 E: m7 T. Z( V

  3. 7 G2 h( ~( `" W. K. t
  4. #ifndef __W25QXX_H
    1 m! C0 L' i" t+ s
  5. #define __W25QXX_H                            ! L* [8 F! K3 `) v
  6. #include "sys.h"  
    * k1 F* l6 z) O7 Y3 ]

  7. 5 ~% G0 Q4 r6 n# x! X0 [
  8. //W25X系列/Q系列芯片列表           
    3 P6 U" k$ ^- K, z8 X7 Z
  9. //W25Q80  ID  0XEF13
    ; r5 f; q1 q* \( Q
  10. //W25Q16  ID  0XEF14$ v: e0 m. V7 A7 B" ~
  11. //W25Q32  ID  0XEF15; g  e9 r. {5 m, C6 o& d
  12. //W25Q64  ID  0XEF16        6 f' n. G! }+ u
  13. //W25Q128 ID  0XEF17        ; u( h4 g9 z# Q' A
  14. #define W25Q80         0XEF13         
    # l, n0 M1 D5 k. ]5 J/ N4 T
  15. #define W25Q16         0XEF14: L4 f7 d' S& t" T  b! D9 t$ g5 s
  16. #define W25Q32         0XEF156 r' ^, [' p0 D1 X
  17. #define W25Q64         0XEF16- Y; J$ R4 T1 Z2 q0 ~; k6 `1 M4 ^0 j
  18. #define W25Q128        0XEF17$ [  L1 J; h9 w
  19. ' ~+ u# T+ A4 c. C: X9 k0 g  W
  20. extern u16 W25QXX_TYPE;                                        //定义W25QXX芯片型号                   4 b  D9 t$ r2 g. ~0 z4 J) S% ?; E! L
  21. 6 _) X$ b: `) }8 E2 z8 @. b# j
  22. #define        W25QXX_CS                 PBout(14)                  //W25QXX的片选信号
    0 Y8 \% M2 g% o; x6 {7 j
  23. //
    4 C1 i$ N- j! O' }9 Y
  24. //指令表/ p/ n) d8 m7 Z) g; d
  25. #define W25X_WriteEnable                0x06
    4 e% B3 y/ s3 N) {# Z8 i# G6 X1 @: E
  26. #define W25X_WriteDisable                0x04
    5 {! M2 W2 `" D! ^& C$ |
  27. #define W25X_ReadStatusReg                0x05
    ; i8 V# \9 b% S$ b2 T3 ]; L& _
  28. #define W25X_WriteStatusReg                0x01
    + \# ]; ?0 N5 J! G. z
  29. #define W25X_ReadData                        0x03
    ! R1 v) Q4 ?6 D! S) x  X
  30. #define W25X_FastReadData                0x0B 9 d! `  Q3 Z6 X: l- [
  31. #define W25X_FastReadDual                0x3B " H. r# K0 w' R2 I2 h
  32. #define W25X_PageProgram                0x02
    0 p" G& {8 }: t( c& t4 u# l* ]+ d
  33. #define W25X_BlockErase                        0xD8
    " F3 C7 t. f5 c. L# R2 O
  34. #define W25X_SectorErase                0x20 ( J1 J/ w& g& Z- d, ?$ {
  35. #define W25X_ChipErase                        0xC7 2 @1 L0 h; k7 U9 Q: L# D$ a+ \
  36. #define W25X_PowerDown                        0xB9 ; h9 I# H" X8 u. h/ {( o
  37. #define W25X_ReleasePowerDown        0xAB
    ; G* B2 p7 h: i* W  s
  38. #define W25X_DeviceID                        0xAB
    : O& C) d4 v* R
  39. #define W25X_ManufactDeviceID        0x90
      P8 {6 s% o. B1 R3 ]  n- m% a
  40. #define W25X_JedecDeviceID                0x9F
    0 h( G" k1 W2 [0 d& `

  41. 5 `2 q0 p* W. i. z
  42. void W25QXX_Init(void);
    ) u0 O2 }1 q: A& L7 E
  43. u16  W25QXX_ReadID(void);                              //读取FLASH ID0 Z* P- c* O' M% p0 u5 j( t; a
  44. u8         W25QXX_ReadSR(void);                        //读取状态寄存器 : z1 j, Q* @- L4 K
  45. void W25QXX_Write_SR(u8 sr);                          //写状态寄存器3 {4 j% R# h) j
  46. void W25QXX_Write_Enable(void);                  //写使能 8 S5 d0 D, t5 T0 \
  47. void W25QXX_Write_Disable(void);                //写保护$ y. b+ b$ _* n
  48. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);: X6 L: R1 q. H( G' Q
  49. void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash
    : W( g1 A, M. j& J; E
  50. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash5 z& r6 F0 I: |
  51. void W25QXX_Erase_Chip(void);                      //整片擦除) D9 R  ]+ k  _) A
  52. void W25QXX_Erase_Sector(u32 Dst_Addr);        //扇区擦除
    6 V* C4 |1 D0 v2 E" Y
  53. void W25QXX_Wait_Busy(void);                   //等待空闲
    8 j5 i9 N" W- A6 |1 d; w1 [0 M
  54. void W25QXX_PowerDown(void);                //进入掉电模式, {3 T0 D" U& ]4 {3 o. j( f4 E. V
  55. void W25QXX_WAKEUP(void);                                //唤醒
    2 g9 I1 C9 V) m4 M. m- K& r
  56. 1 x  K6 S  y5 w8 X4 Q) Z# N7 h
  57. #endif+ ~7 p9 ~* W0 H& T3 Q% Z  g3 n
复制代码
" g* [1 Q& a- y; Z: a2 v
main.c:
' a" [5 Y' K# a: u. b5 a0 }2 d; \5 i+ B9 m' O2 G
  1. main.c:5 S4 }3 b5 F4 Q  E$ T
  2. #include "sys.h"9 U% c/ B5 M2 e' T- B( l
  3. #include "delay.h"2 j$ y2 ^5 R: _$ E* ]  W! K- n
  4. #include "usart.h"
    7 d& [0 k; R% Q8 V. n& J  z
  5. #include "led.h"# }0 C  V* s0 v* m9 n$ @
  6. #include "lcd.h"% l% M3 @9 ^" V; J
  7. #include "spi.h"7 I3 k7 J4 [5 \0 b9 H% n. x
  8. #include "w25qxx.h"
    5 Q( d1 L' T8 |; s
  9. #include "key.h"    ( m% ]. V. }1 Z0 Y* b* `
  10. ( i/ j% m5 z* R: a. ~+ k
  11. //要写入到W25Q16的字符串数组
    / z9 }  b" {2 J' [
  12. const u8 TEXT_Buffer[]={"Explorer STM32F4 SPI TEST"};* j" b0 t4 z  D8 `* P& y5 Y
  13. #define SIZE sizeof(TEXT_Buffer)         
    8 Q: C9 N8 i& G' D% C$ n$ X) y& g
  14.         
    2 G. A( o8 o$ V# ?: A1 l6 F' C6 G
  15. int main(void)# Z1 [$ Y& O; u0 V$ M. n4 {- y
  16. {
      S% a( d, {0 [% V. g8 W% p% v
  17.         u8 key;" g- }" y( C! |, |4 ?$ c
  18.         u16 i=0;% E  l: [1 a3 s7 k9 I$ a: R
  19.         u8 datatemp[SIZE];        
    6 a5 c; P5 ?$ z2 ^
  20.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2$ s* n7 A! ^: t3 E" u( H; x' G, W0 Y
  21.         delay_init(168);    //初始化延时函数
    1 i5 A4 |6 c, `' u/ `2 S" U) j
  22.         uart_init(115200);        //初始化串口波特率为115200, K% V  u, S2 Z3 z9 i
  23.         LED_Init();                                        //初始化LED * Y. v% E( s% Y4 ~
  24.         KEY_Init();                                 //按键初始化  
    4 w4 }2 b- e7 n. r
  25.         W25QXX_Init();                        //W25QXX初始化
    9 b2 }  \3 I5 y2 }
  26.                         
    7 _* d' Q& u  F" v
  27.          while(W25QXX_ReadID()!=W25Q128)        //检测不到W25Q128" q0 [7 K- P4 }% W4 ~9 b
  28.         {
    " C, F4 O2 H' O4 P! C, J
  29.                 printf("W25Q128 Check Failed!");
    ! Y8 c, h; X& p  I8 Q% k( b0 i
  30.                 delay_ms(500);
    & H0 ?3 [4 H: n, b
  31.                 printf("Please Check!      ");
    ( W( W& W" E( X/ I! M3 M6 F% }
  32.                 delay_ms(500);2 \7 d  u! S/ \1 [4 q/ R! l) g& B
  33.                 LED0=!LED0;                //DS0闪烁
    9 W) w% P/ W- y: K
  34.         }
    " {  V2 |+ b$ K  x6 ?7 h6 t
  35.         
    5 U5 k( N: c( }
  36.         while(1)8 v, p2 e  S( U4 }, }! D- P
  37.         {* |" _% N  H" ~! |/ ?0 ?; ]
  38.                 key=KEY_Scan(0);
    * Z1 w+ K) d4 z7 V1 a* E
  39.                 if(key==KEY1_PRES)//KEY1按下,写入24C02% w" [3 L6 c+ x/ C
  40.                 {4 p" B+ R1 n7 c! q
  41.                         printf("Start Write W25Q128....");
    - U7 ^% Q. @! h/ L. Q" o6 d
  42.                         //从倒数第100个地址处开始,写入SIZE长度的数据  - C% l( z( f5 b9 i8 c3 ^$ |
  43.                         W25QXX_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);
    * E1 q- b2 }* t" I. F
  44.                         printf("W25Q128 Write Finished!");  //提示传送完成
    ! U0 `+ g* r, c- m! N
  45.                 }! Q. x$ ^# t3 h% w/ j5 N/ k8 \+ H
  46.                 if(key==KEY0_PRES)//KEY0按下,读取字符串并显示
    - J' w7 `! m: s1 j+ z5 H
  47.                 {
    " y! j2 D/ X, v7 c1 o8 U5 \, I5 M+ y, W
  48.                         printf("Start Read W25Q128.... ");4 M# t8 Z" c8 W) ?8 c3 A! T
  49.                         //从倒数第100个地址处开始,读出SIZE个字节
    1 a) q- H* L2 w  K% F8 l: w
  50.                         W25QXX_Read(datatemp,FLASH_SIZE-100,SIZE);                                       
    ! `- J) l; k* H" J! m) s6 V' ?& j" D7 `
  51.                         printf("The Data Readed Is: \r\n ");//提示传送完成
    # ^0 Q, p7 N2 \# H% s2 d
  52.                         printf("%s\r\n ",datatemp);$ t1 Q; B& y; L6 C2 V+ B$ l
  53.                 }           
    , v. m/ Q+ V; `4 E6 w% p
  54.         }             $ _5 m4 k, U' }" p8 ~7 V8 ~
  55. }" t/ H9 Z0 g+ f

  56. : c/ }5 a# z$ U
复制代码
6 c) I) b5 ~: t2 |

" o0 x3 s# H# K. B+ R
' k) }  D5 M7 C- B+ S: k. e. ^
A71_MM@Q(@D3Z`FQYIISTMD.png
收藏 评论0 发布时间:2022-5-16 11:53

举报

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