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

STMCubeMx入门教程(6) SPI 读写FLAH的应用

[复制链接]
STMCU小助手 发布时间:2021-1-6 09:47
STMCubeMx入门教程(6) SPI 读写FLAH的应用
导语“本教程将使用CubeMX初始化SPI,使用SPI对W25Q64 FLASH进行读写操作,通过HAL库的读写应用来数据FLASH的操作细节。”
01
------------ 系统要求-----------------
•硬件
野火指南者开发板
•软件
CubeMx & MDK & 串口调试助手
•原理图
1.1.png
根据原理图,我们看到FLASH连接在SPI1上,我们不适用SPI1自带片选,使用PC0开进行软件片选。
02
------第二节 CubeMx配置-----------------
(1) 我们还是使用前面的USART串口(串口的配置没有变化)项目,在此基础上进行SPI1 的配置:
1.2.png
我们从配置的信息上看,使用了SPI1,主从全双工模式,8位数据传输,高字节在前,模式3,CRC不校验,软件控制。SPI的模式配置看下图,采样时刻是偶数边沿所以CLOCK Phase =2:
1.3.png
(2) 完成片选信号的GPIO配置
1.4.png
完成上述配置后点击代码生成。
03
------------第三节MDK 配置--------------
将CubeMx生成的代码使用MDK打开进行应用代码编写:
在spi.h 中进行FLASH操作的指令宏定义:
  1. //指令表
    - T- F6 Z+ \/ R
  2. #define W25X_WriteEnable                     0x06
    8 s' J/ |) e5 C  J" L
  3. #define W25X_WriteDisable                     0x04 * D/ _$ N0 r+ C5 I) P9 c! k! o
  4. #define W25X_ReadStatusReg                     0x05
    ( I: N# T, z2 N- E
  5. #define W25X_WriteStatusReg              0x01
    1 o! O: a* p8 `' e' b
  6. #define W25X_ReadData                            0x03 7 I! x( U6 ~3 x) J) H1 e
  7. #define W25X_FastReadData                     0x0B
    * s8 j: Z3 w5 @1 s9 B
  8. #define W25X_FastReadDual                     0x3B 3 Z  t: Z, z/ R
  9. #define W25X_PageProgram                     0x02 8 Q6 v4 B1 \& J  s5 X9 m
  10. #define W25X_BlockErase                     0xD8
    / P+ c# g# t3 J- C
  11. #define W25X_SectorErase                     0x20 : p5 Y0 W0 B# O9 t' G
  12. #define W25X_ChipErase                            0xC7
    % O2 g1 l0 R$ n. Q
  13. #define W25X_PowerDown                            0xB9 " i- m% n5 L! ]
  14. #define W25X_ReleasePowerDown       0xAB 7 E8 e' o% P. h- t/ C3 A
  15. #define W25X_DeviceID                     xAB
    4 T% \' m8 @; l( e# N6 u4 w, t
  16. #define W25X_ManufactDeviceID       0x90 : a. w9 J. k$ }
  17. #define W25X_JedecDeviceID              0x9F
    7 E7 ~$ Q8 }3 E7 W1 B" C
  18. // others defined
    ; _$ P6 A) ]! d& X" K3 W) O. \. z
  19. #define sFLASH_ID    0XEF40178 o* l9 _* S3 W8 m
  20. #define Dummy_Byte   0XFF8 Z$ G  o3 L3 z$ [& d/ y% `% ?
  21. #define SPI_FLASH_PageSize                    256
    / h  D% ?* S8 W  Y. ~! v7 e
  22. #define SPI_FLASH_PerWritePageSize        256
    9 ]/ E: i7 G0 ?7 ?0 q9 F
  23. #define SPI1_TIME_OUT   
复制代码
2 N6 j: O( T; ?) ~! Q
并且申明应用的操作函数:
  1. void SPI_FLASH_SectorErase(uint32_t SectorAddr);
    % E! U7 T# ?1 N" x# D
  2. uint32_t  SPI_Flash_ReadID(void);3 w3 H+ f' r! Q  O) c6 m6 m$ Q+ R/ i
  3. void SPI_Flash_Erase_Chip(void);9 b, Y* h, ?$ b* V/ I" f
  4. void SPI_Flash_Read(uint32_t ReadAddr,uint16_t NumByteToRead,uint8_t* pBuffer);. g% n' A' s: j2 W3 X  H6 E, d- _
  5. void SPI_Flash_Write(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer);
    , M$ e5 F1 i  P1 ~
  6. void SPI_Flash_Write_Page(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer);
    : k& e3 v9 S; i5 V7 V
复制代码

' j8 M# s. I$ O
下面我们在spi.c 中实现读写FLASH的相关函数:
(1) 对片选的信号引脚进行宏定义操作:
  1. #define SPI_FLASH_CS_H()   HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET) 8 j8 o' V# U' J9 g
  2. #define SPI_FLASH_CS_L()   HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET)
复制代码
! w% P, j$ M; \) p( |  N
根据原理图使用的PC0引脚
(2)SPI 读写个字节函数
  1. /**function: SPI 读一个数据**/
    : q. c* f  O+ j. f. T. Y
  2. uint8_t SPI1_ReadByte(void)5 l5 b# a6 c4 U9 {& a: D) V
  3. {                     ( C  N) W9 p6 m3 \# _
  4.        uint8_t RxData;      
      r# _9 N+ `. `/ }
  5.                          % x" h) V1 t- Z; H! T
  6.        HAL_SPI_Receive(&hspi1, &RxData, 1, SPI1_TIME_OUT);" N- @5 U; a6 J7 O
  7.                                              8 f: f( W5 t. K5 D# P# E' G. b
  8.        return RxData; //返回通过SPIx接收的数据                                       ) i& I  i* u% i0 v
  9. }
复制代码

! Z4 Q- x7 [8 k2 F0 o; w
; ^' C- j$ a/ c( }5 P( D
我们使用了HAL封装的HALSPIReceive(&hspi1,&RxData, 1, SPI1TIMEOUT)函数来实现读一个字节。
(3)写一个字节
  1. /**function: SPI 写一个数据**/
    6 g) v. Z" o3 t; a  Q
  2. void SPI1_WriteByte(uint8_t TxData)! ~7 ^: u) w% D9 f& G
  3. {                                            * m1 f* S1 X+ c9 j
  4.        HAL_SPI_Transmit(&hspi1, &TxData, 1, SPI1_TIME_OUT);  //通过外设SPIx发送一个数据                                                                      + P" o) n! W8 U# K/ r
  5. }
复制代码
(4)FLASH的写使能和非使能
  1. /**function: SPI_FLASH写使能,将WEL置位**/  n; T9 Z4 w6 z# B  n6 i1 N
  2. void SPI_FLASH_Write_Enable(void)   & ?% S# A* D  {5 U7 f
  3. {
    # i: Z% U1 e6 _: M" c/ ^+ ]
  4.        SPI_FLASH_CS_L();                            //使能器件   2 ^' I/ n4 `- R5 B
  5.        SPI1_WriteByte(W25X_WriteEnable);      //发送写使能  7 R& `% Y, G# ]9 Y( p- w/ J5 n& V
  6.        SPI_FLASH_CS_H();                            //取消片选                  / |) T. {7 c  d% ^1 u
  7. }
    & A1 F6 R3 `6 A! Z1 ]$ X

  8. ' l. p4 [" V$ K. w; l" X7 _( P
  9. /**function: SPI_FLASH写禁止,将WEL清零**/
    ) `  u* B/ x7 m5 u
  10. void SPI_FLASH_Write_Disable(void)   
    & w! y# M4 f) Z" x6 d' e* y
  11. {  
    * b7 T: e- R# ^6 N; p
  12.        SPI_FLASH_CS_L();                            //使能器件   
    ; f6 i- h4 m" z( t! O4 T
  13.        SPI1_WriteByte(W25X_WriteDisable);     //发送写禁止指令   
    % x+ N. {3 m% H! {1 y4 P/ u
  14.        SPI_FLASH_CS_H();                           //取消片选                  
    / ^0 a( B8 o, b- @' m( g( T
  15. }
复制代码
& e) a  {. p, \( y! V" x2 P/ ]
(5) 读取SPI_FLASH的状态寄存器,通过这个函数我们可以判断FLASH的状态,一般用到的忙和空闲两种状态,这儿也实现了等待空闲函数。
  1. /**
    ) |9 B; G. `% M' t
  2. function: 读取SPI_FLASH的状态寄存器
      _/ z; L+ n0 v3 u3 O8 P& s7 u% B
  3. **/
    ) j& h! X% M. l- e5 J" C! O
  4. //' [+ d% N$ f7 ?- }3 P* k
  5. //BIT7  6   5   4   3   2   1   0* ]* A- _$ P5 c1 D5 \
  6. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY
    : @) i1 [& L* w$ {6 v
  7. //SPR:默认0,状态寄存器保护位,配合WP使用
    + a0 ^$ q0 Y5 L  K
  8. //TB,BP2,BP1,BP0:FLASH区域写保护设置" F) ~7 q; s$ h( \& M
  9. //WEL:写使能锁定
    0 I3 v# e3 L6 K7 q' e
  10. //BUSY:忙标记位(1,忙;0,空闲)0 j. P9 a# z& ~! R
  11. //默认:0x001 H0 n& T& y. i
  12. uint8_t SPI_Flash_ReadSR(void)   ; l* g# o) I( E
  13. {  
    6 a5 W# `( j; W7 D
  14.        uint8_t byte=0;   " S, Y3 p: ^; _; a' |: `9 w) l
  15.        SPI_FLASH_CS_L();                       //使能器件   5 w! }0 i6 V. t0 [( ~6 S7 d
  16.        SPI1_WriteByte(W25X_ReadStatusReg);           //发送读取状态寄存器命令   
      H3 B' J  T9 d0 O" L
  17.        byte = SPI1_ReadByte();                           //读取一个字节  
    6 y- c: E! n+ D9 q! [* o) I
  18.        SPI_FLASH_CS_H();                       //取消片选     
      h; y' g$ L4 [/ Z4 v
  19.        return byte;   
    & r' F& x) u/ n% S6 G
  20. } 1 G! ]% Y4 t$ z: z- Y
  21. + }; O# C; r, y" t, G8 t3 S( D
  22. /**
    0 }! x1 C3 u9 {. f, s; d( N+ Q
  23. function: 等待空闲  I; Q5 T2 x9 ?+ B0 L; J6 }
  24. **/
    5 D6 X: M2 \8 a5 }! j8 t
  25. void SPI_Flash_Wait_Busy(void)   ' n/ Y' R( }% r; M7 ^' J0 g
  26. {   
    ' _' {& J' W4 S( q8 |9 }8 e
  27.        while ((SPI_Flash_ReadSR()&0x01)==0x01);   // 等待BUSY位清空; g$ z+ A* \$ ?9 x8 i* W
  28. }
复制代码
(6)读取芯片ID W25Q64的ID函数,这个函数也是一般判断是否FLASH正常的方式。查看手册可以知道ID是0XEF4017
  1. /**
    . N% F2 j- g1 ?1 w5 m
  2. function: 读取芯片ID W25Q64的ID
    4 ~  l" l; g" S) C+ e3 @3 ~, u" a+ H
  3. **/" T3 J& C: m" ~7 O) o" k
  4. uint32_t  SPI_Flash_ReadID(void)! r5 w  ^7 V: A- Y  M$ d
  5. {
    5 Q* }4 X/ P) q% [, c9 `$ i
  6.        uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0; - h! ]3 l5 u; B8 n7 T" n
  7.        SPI_FLASH_Write_Enable();
    % t6 X. t  l& `) b6 W
  8.        SPI_FLASH_CS_L();               
    , _0 G, k  M! I$ F
  9.        SPI1_WriteByte(W25X_JedecDeviceID);//发送读取ID命令           6 G$ z6 r+ o/ A0 k
  10.        Temp0 = SPI1_ReadByte();       : Z% S, z4 Y) M/ y' [& M) v. m! I
  11.        Temp1 = SPI1_ReadByte();              : P5 X4 M" y3 s! |: p+ H
  12.        Temp2 = SPI1_ReadByte();      
    " [: s! ]3 _% z
  13.   /*把数据组合起来,作为函数的返回值*/3 ^( n$ S# r) u5 @
  14.        Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;6 d3 ^. N8 i8 q' e& J7 ^* R* [
  15.        SPI_FLASH_CS_H();              
    * z/ G  D6 H) `+ P  m- C
  16.        return Temp;
    7 v( M  K7 z4 G/ q  y9 c
  17. }
复制代码
8 P" [: R' J  k$ x) E2 [: _" I
(7) flash 扇区擦除函数,FLASH和EEPROM不同,在写之前一定要进行擦除,否则不能正常写入。这儿实现了扇区擦除和正片擦除两个函数:
  1. /**- @4 V' s% V. v( K4 s, i7 D' |+ f
  2. function: 擦除整个芯片
    1 s( {& f4 @9 M$ d
  3. **/+ G5 K- c9 {8 q6 ~
  4. //整片擦除时间:3 q5 P1 j( c8 n" _" J) g% ?; y
  5. //W25X16:25s
      a  j( }7 N( v2 J4 Q# s' \4 p
  6. //W25X32:40s
    ' ?( [' Y, l; N% v9 }
  7. //W25X64:40s
    + E. q1 Z* e) c: D  A! s
  8. //等待时间超长.../ O) e9 }6 v7 @  ?( e& ^  I
  9. void SPI_Flash_Erase_Chip(void)   % @( l+ Y7 ^7 ?3 h3 Y. W
  10. {                                             
    1 ]" ~4 Z% r( r' j2 U
  11.        SPI_FLASH_Write_Enable();               //SET WEL # ]& M; V) v, y& d: r# K5 B9 V
  12.        SPI_Flash_Wait_Busy();   7 c$ `' b0 A# l* a
  13.        SPI_FLASH_CS_L();                      //使能器件   # T( q0 \) C$ u, V& W6 j1 O) @
  14.        SPI1_WriteByte(W25X_ChipErase);        //发送片擦除命令  ( _" z/ ], N3 ]5 }. O
  15.        SPI_FLASH_CS_H();                      //取消片选                  
    : ]/ F7 D; s# X+ n
  16.        SPI_Flash_Wait_Busy();                                                //等待芯片擦除结束4 m; B, P9 ~9 r9 q
  17. }  % q: z: F, t/ K1 {; L8 q$ I$ Q5 H
  18. /*** [/ ~: h$ j! a- @) z
  19. function: SPI_FLASH_SectorErase4 ?$ `. r, ]# Y8 K( r3 j& D  i
  20. annotation:Flash的一个扇区是4K,所以输入的地址要和4K对其。
    , ~" a+ V; v$ ^( f) R, H
  21. **/
    - _: |5 N4 p8 ]& L5 \1 a% \* Z/ f0 @2 Y. u
  22. void SPI_FLASH_SectorErase(uint32_t SectorAddr)8 W0 k* b1 ~! r8 m& Z
  23. {& H; ]* _6 D% \! v( V
  24.        SPI_FLASH_Write_Enable();
    + E& P6 ^# A4 s6 {; E0 @. G! A
  25.        SPI_Flash_Wait_Busy();      
    7 R2 N$ [' @4 u/ y
  26.        SPI_FLASH_CS_L();
    2 U; Y" [7 I3 ]
  27.        SPI1_WriteByte(W25X_SectorErase);
    ( L: u+ \- Y- D  p
  28.        SPI1_WriteByte((SectorAddr & 0xFF0000) >> 16);
    ) i; T% p" ^" g6 w# P5 X9 H. @
  29.        SPI1_WriteByte((SectorAddr & 0xFF00) >> 8);
    , e; S& s7 e; L0 ^; t) G; m" D
  30.        SPI1_WriteByte(SectorAddr & 0xFF);* {; {$ R1 i3 q$ M
  31.        SPI_FLASH_CS_H();      
    : y8 [6 S4 ^" v7 `! M2 C
  32.        SPI_Flash_Wait_Busy();
    . T$ W2 C9 t. v" @3 C/ q; y$ f5 F4 h' U3 Z
  33. }
复制代码
(8) 读取FLASH函数,FLASH的读操作步骤很简单
  1. /**
    ' a5 A- ~% R- c9 r: ]
  2. function: 读取SPI FLASH  # _: v! l1 ?# Q% ~1 Y
  3. **/6 H/ y2 f( x. M; g# a1 z0 ?
  4. //在指定地址开始读取指定长度的数据
    # Z4 B$ B3 Z9 }  r# V
  5. //pBuffer:数据存储区
    & Q8 E- z7 w- f6 n3 A! ^
  6. //ReadAddr:开始读取的地址(24bit)
    : j1 h; Q$ S: @9 `: k
  7. //NumByteToRead:要读取的字节数(最大65535)
      P5 d2 E& j! ]
  8. void SPI_Flash_Read(uint32_t ReadAddr,uint16_t NumByteToRead,uint8_t* pBuffer)   
    # Y3 `7 c. W" u5 Z# F8 M: D
  9. {
    % z; t+ ~1 H% M$ K5 j7 U' Q) T
  10.        uint16_t i;                                                                                            
    - Q2 ^- Q6 K: @% s
  11.        SPI_FLASH_CS_L();                          //使能器件   
    / F; N& h4 r  B1 p5 ^
  12.        SPI1_WriteByte(W25X_ReadData);                       //发送读取命令   9 q& \2 v) {0 `' _% C! j. N0 W
  13.        SPI1_WriteByte((uint8_t)((ReadAddr)>>16));  //发送24bit地址    , Q' C, @1 ^2 V! n) J; X3 k$ I
  14.        SPI1_WriteByte((uint8_t)((ReadAddr)>>8));" O2 z( S0 L. ?/ B+ J
  15.        SPI1_WriteByte((uint8_t)ReadAddr);; n: A; N8 o/ b  H
  16.        for(i=0;i
    . x1 q3 c. h; [* q5 o6 g
  17.        { , y" w; q  ]7 L. N1 k$ w
  18.               pBuffer[i] = SPI1_ReadByte();   //循环读数  2 F) L& e( i" T7 }/ k8 A1 S  `% O
  19.        }
    * J, f" J" r# X8 \5 k1 m/ `
  20.        SPI_FLASH_CS_H();       //取消片选
    0 z4 O2 R0 Y- M$ \$ I6 x4 v* b
  21. }
复制代码
+ \# J! L- L" k
(9) flash 的按页写入数据,FLASH的数据的写入不是随机可以任意写入,按页写入,一页最大的数据是256个字节。
  1. /**2 ^/ v3 {) K0 {
  2. function: SPI在一页(0~65535)内写入少于256个字节的数据 9 d8 i, \& Y  y
  3. annotation:一页最大256个字节
    2 P$ Q  N2 \, w7 r, p: C. k& z$ @
  4. **/0 q1 m. X" P. q
  5. //在指定地址开始写入最大256字节的数据
    9 ^3 K0 W; c1 b! h' C: ~
  6. //pBuffer:数据存储区! B/ g! w: `9 s# F0 Q
  7. //WriteAddr:开始写入的地址(24bit)6 P, g( @6 m( H( \2 f( x3 R
  8. //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!$ ^$ [6 e" r5 I, I+ w/ s
  9. void SPI_Flash_Write_Page(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer)
    3 s; M. K4 [# ^7 h* e" P+ G! o! t
  10. {
    $ A$ S. V% C: U. u' D9 K
  11.          uint16_t i;  @& [2 S0 W$ \; `5 @, X1 c
  12.        SPI_FLASH_Write_Enable();                                       //SET WEL7 k% S- Z" C8 D" n: W& U& A
  13.        SPI_FLASH_CS_L();                                         //使能器件1 D5 D) B1 o. F* G
  14.        SPI1_WriteByte(W25X_PageProgram);                                  //发送写页命令+ T1 ]. Z' m+ |+ h: M
  15.        SPI1_WriteByte((uint8_t)((WriteAddr)>>16));               //发送24bit地址
    ) ]: M/ R/ Y; d$ W# W1 m6 x" g
  16.        SPI1_WriteByte((uint8_t)((WriteAddr)>>8));1 Y# k9 w1 L4 N2 z& y/ W
  17.        SPI1_WriteByte((uint8_t)WriteAddr);
    3 b; H0 ]. ^3 a" V7 f/ Y* ^7 k9 W8 b
  18.        for(i=0;i<NumByteToWrite;i++) SPI1_WriteByte(pBuffer[i]);       //循环写数. ?$ n+ d8 e* ]1 i; M& ^
  19.        SPI_FLASH_CS_H();                                                        //取消片选
    ; P2 P% P" l. D( K. i# |( g
  20.        SPI_Flash_Wait_Busy();                                                                        //等待写入结束                                                                                    
    # ?; _" |& O0 q0 v' |* w
  21. }
复制代码

5 @' g) \: T) W9 L6 y4 r
(10)flash 的随机的多字节写入,通过按页写入实现的函数来实现
  1. /**
    ; }5 E4 z9 V8 P. v  N
  2. @function 不定量的写入数据,先确保写入前擦出扇区! o2 u$ W+ w2 {& K
  3. @annotation
    " C2 _9 {. [8 H: [6 Y: |/ l0 n
  4. @param* d5 {9 r- t' Q# {7 T  c
  5. @retval
    , a* i1 \4 ]; L4 ?/ q
  6. **/
    . P- v8 S8 a) e6 \( C+ A( x
  7. void SPI_Flash_Write(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer)
    1 Q* }, C3 Y! g# D% a. t; \
  8. {0 d( N( ~  D' X" x
  9.        uint8_t NumOfPage =0, NumOfSingle=0, Addr =0, count =0, temp =0;
    " g" ^0 t+ t  ^( H& p2 ~. m
  10.        /*计算Addr,写入的地址是否和PageSize对齐*/5 o. a9 X! U& e! H& W" L% B
  11.        Addr = WriteAddr % SPI_FLASH_PageSize;9 d+ ^" K" G) Q+ D: m: c
  12.        /*count 为剩余的地址*/. M7 U) V- Q7 G8 l& ]2 d2 o& C, u
  13.        count = SPI_FLASH_PageSize - Addr;
      ?$ R. p( I% n" l4 W3 V
  14.        /*计算能写入多少整数页*/
    0 ]9 D7 P8 f# L3 t% ^) Q
  15.        NumOfPage = NumByteToWrite % SPI_FLASH_PageSize;9 N8 U5 W, L7 R5 L; g/ U+ Q7 O
  16.        /*计算不满一页的数据*/
    0 \& E8 Z, j4 I* \! z" y- j! h0 l
  17.        NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;/ \+ t8 x: i  d  H! o7 ]
  18.        /* Addr=0,则 WriteAddr 刚好按页对齐 aligned */! H4 ]8 l# ]* t. B7 f
  19.        if(Addr == 0), ?  C4 s' z2 x5 t# {' S
  20.        {: B1 P! }0 f) z0 p8 R
  21.               /*NumByteToWrite < SPI_FLASH_PageSize,一页能写完*/7 R) @" P' S+ h/ t: s  r
  22.               if(NumOfPage == 0)
    : y- D6 k" [6 X# O! ]
  23.               {
    , g% G: w1 c  t' d4 _
  24.                      SPI_Flash_Write_Page(WriteAddr,NumByteToWrite,pBuffer);
    7 j& u. p8 t+ o( n: Q6 b/ }$ }$ }3 x
  25.               }
    5 v: k9 X: R, E) ^0 B! m" g
  26.               else/* NumByteToWrite > SPI_FLASH_PageSize,一页写不完,先写整数页,在写剩下的 */
    2 N4 }) p! a3 ?; i  B  d
  27.               {: W' }9 N4 M/ O/ z" h
  28.                      /*先把整数页都写了*/
    ' A% ]4 p) m: Z( `. t* E
  29.                      while (NumOfPage--)$ v: F& Z6 x! W$ j" V
  30.                      {
    # K: R# E3 T% a. `: L
  31.                             SPI_Flash_Write_Page(WriteAddr,SPI_FLASH_PageSize,pBuffer);
    ) ?+ [3 C) L( S/ {
  32.                             WriteAddr+=SPI_FLASH_PageSize; // flash 的地址加一页的大小9 X. b9 A0 X3 f, J1 z% t3 z% w" c. k! A* `
  33.                             pBuffer+=SPI_FLASH_PageSize;   // 写缓存数据的地址加一页的大小
      t" o5 M- l6 q5 g. V. g
  34.                      }' R( m* k& X# j$ n
  35.                      /*若有多余的不满一页的数据,把它写完*/: {# f- b* u9 _; g( u' k* t0 o
  36.                      SPI_Flash_Write_Page(WriteAddr,NumOfSingle,pBuffer);
      f2 `; R: r+ u5 U; i) S
  37.               }9 S/ L# l! x3 g5 |' J
  38.        }
    8 m9 O5 W# P' e3 S% X6 p  x
  39.        else/* 若地址与 SPI_FLASH_PageSize 不对齐 */
    ( M$ ]) F# D# m- ^3 a6 n$ ~6 V( ^
  40.        {
      R$ P, h6 S9 o
  41.                      /* NumByteToWrite < SPI_FLASH_PageSize */
    ' a2 l) s9 ~! z9 [, N! i
  42.               if (NumOfPage == 0)  `3 H  Y! f; p7 r0 ?. e1 |
  43.               {
    / ^; f* |+ o- g  l/ c# m- b
  44.                      /*当前页剩余的 count 个位置比 NumOfSingle 小,一页写不完*/
    ' }2 X% O' x& w' @$ m: g
  45.                      if(NumOfSingle >count)
    & B' z, c& ?- n3 ^1 Z0 k6 u7 x3 `
  46.                      {
    3 e2 H1 r' H  \8 j
  47.                             temp = NumOfSingle -count;  ~  R$ `/ y! `) `4 S0 U
  48.                             /*先写满当前页*/
    $ w% a1 R) n+ N1 K: b; c7 r) [8 y. b
  49.                             SPI_Flash_Write_Page(WriteAddr,count,pBuffer);
    ; ~9 N, M- i4 h6 c8 ?) X" D  O
  50.                             WriteAddr += count;7 J& }* M6 f, c1 }) e
  51.                             pBuffer += count;$ c  l) R/ y( H' F9 d
  52.                             /*再写剩余的数据*/0 O; W) T( Q- h! x8 s2 p; v$ c# M# z
  53.                             SPI_Flash_Write_Page(WriteAddr,temp,pBuffer);
    # t8 J. w- k* z: A0 Z( |1 G
  54.                      }3 D" Y4 |* y# ^3 @3 N# n) b
  55.                      else/*当前页剩余的 count 个位置能写完 NumOfSingle 个数据*/
    ) X1 Z- m9 \) Q6 e
  56.                      {
      n1 [. @6 ]0 U. v- U0 S8 H! P( q" u
  57.                             SPI_Flash_Write_Page(WriteAddr,NumByteToWrite,pBuffer);
    , Q3 p9 v& i1 a
  58.                      }3 ~6 O+ r& F1 _5 b3 t5 X
  59.               }: _$ U: |6 e2 E- W/ j; n
  60.               else/* NumByteToWrite > SPI_FLASH_PageSize */
    " T$ d! m8 w! n. a4 R
  61.               {
    $ m$ f& A7 @/ g# f6 {
  62.                      /*地址不对齐多出的 count 分开处理,不加入这个运算*/
    ; v; U- P" _5 ~! Z
  63.                      NumByteToWrite -= count;0 s$ D/ N0 x6 M% i& w
  64.                      NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;' X; F9 X+ I- O# n0 l9 s
  65.                      NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
    8 [  L" O5 w/ u8 r- {* L! l
  66.                      /* 先写完 count 个数据,为的是让下一次要写的地址对齐 */8 O% Y$ ~+ X$ q; G
  67.                      SPI_Flash_Write_Page(WriteAddr,count,pBuffer);
    # R# k+ c, d; ^& }- f8 ^
  68.                      /* 接下来就重复地址对齐的情况 */( t. I# L2 B: C, U) J7 ]
  69.                      WriteAddr += count;
    2 |9 R* \$ G3 I2 {. Q; X2 L5 r
  70.                      pBuffer += count;
    , s# x6 C. R' _$ P3 U
  71.                      /*把整数页都写了*/8 u- g5 f/ m/ w5 z
  72.                      while (NumOfPage--)2 n) B! }. y0 S* ^& Z! `: N& }
  73.                      {
    2 M  l/ x- u$ M6 q' {* `
  74.                             SPI_Flash_Write_Page(WriteAddr,SPI_FLASH_PageSize,pBuffer);
    ( F2 `5 j: ^$ r
  75.                             WriteAddr += SPI_FLASH_PageSize;
    ( b  o; t/ U% E, D5 x( Z
  76.                             pBuffer += SPI_FLASH_PageSize;) f8 e" q5 Q" L( G! f* \
  77.                      }
    , r; G7 j& e5 ]1 T# b2 D
  78.                      /*若有多余的不满一页的数据,把它写完*/: Z8 g/ h: _& k8 g
  79.                      if (NumOfSingle != 0)
    . _0 A) r9 }7 H& K4 V- o
  80.                      {
    6 `/ l7 Q. e1 t# e6 r+ A" O# e# e
  81.                             SPI_Flash_Write_Page(WriteAddr,NumOfSingle,pBuffer);0 a" }. A- @7 I7 m1 g  T
  82.                      }; {# \/ e& k& ?
  83.               }
    4 N+ d7 P" r. N3 i
  84.        }              
    4 V/ Y) [- @# j' F/ Z
  85. }
复制代码
: S1 T! g( R2 @' p
通过上面的函数实现,我们就可以基本的来完成FLASH的读写操作了。有些函数参考了野火标准库的操作步骤。
在main.c 的主函数中对FLASH的读写地址进行宏定义:
  1. #define  FLASH_WriteAddress     0x00000
    , z; y; y( z  ^. U  ?  \2 Z
  2. #define  FLASH_ReadAddress      FLASH_WriteAddress& r% F" ?$ [2 |9 d  ^! p
  3. #define  FLASH_SectorToErase    FLASH_WriteAddress
    ' ?+ Y8 p# p7 z9 Z
  4. #define FLASH_SPI hspi1
复制代码
9 D' f2 w4 I; K) k0 d( n
在mian函数中定义变量:
  1. uint32_t flash_ID = 0;1 [+ Y; G" x  G* }9 q: r
  2. /* 获取缓冲区的长度 */$ L( |! u7 ^1 r3 L* q
  3. #define countof(a)      (sizeof(a) / sizeof(*(a)))
    ! M) F# z" p) s7 H
  4. uint8_t Tx_Buffer[] = "现在进行FLASH的读写测试\r\n";5 S! v9 G+ L0 J, b, C9 T! {: g
  5. #define  BufferSize (countof(Tx_Buffer)-1)
    6 s- ~9 I. r9 g
  6. uint8_t Rx_Buffer[BufferSize];
复制代码
/ Z  S9 ]: l0 y. B- `
在main函数中进行测试代码:
  1. printf("*************this is test for coding...**********\t\n");
    * h/ K3 ]! n0 P5 _5 L
  2.        printf("this is test code for spi1 read and write flash w25Q64 \r\n");  p; E5 |4 F; N# d1 h5 q
  3.        flash_ID = SPI_Flash_ReadID();+ o7 i% {+ N0 T8 Q8 o" I
  4.        printf("\r\n flash ID is 0X%x\r\n",flash_ID);( Q; X5 F4 h9 q6 q) B1 c
  5.        SPI_FLASH_SectorErase(FLASH_SectorToErase);      
      a7 `, p( v7 y
  6.        SPI_Flash_Write(FLASH_WriteAddress,BufferSize,Tx_Buffer);
    0 i2 |" f5 I& Z( \: z7 x; C1 o; ?
  7.        printf("\r\n 写入的数据为:%s \r\t", Tx_Buffer);8 X& M& O, j' H# ]2 G
  8.        SPI_Flash_Read(FLASH_ReadAddress,BufferSize,Rx_Buffer);1 g+ X8 R7 d0 d! i
  9.        printf("\r\n 读的数据为:%s \r\t", Rx_Buffer);3 z) ]' x+ k9 i* e+ F
  10.        /* 检查写入的数据与读出的数据是否相等 */
    3 ]% |4 J+ R2 }# s; E6 k  I
  11.        if(memcmp(Tx_Buffer, Rx_Buffer, BufferSize)==0)
      r9 v4 W1 Y8 a3 t& N/ T6 z
  12.        {) Y4 X5 U0 e) U- U. a
  13.               printf("写入的和读出的数据是正常的!\r\n");0 }5 K9 {$ F/ i1 i- _; a( ?% ~' i. P
  14.        }
    7 W0 ~1 F+ s5 g+ i
  15.        printf("SPI 试验结束......!\r\n");
    ) L! l1 r8 R' T. D8 c
  16.        printf("*************this is test end....**********\t\n");; ]6 j1 c8 e" m  Q
复制代码
0 h/ g6 F1 u$ X! x
04
------------第四节 效果演示--------------
我是使用串口将写入FLASH的数据进行读出来,比较数据是否一致,同时独处FLASH的ID看是否一致:
1.5.png
我们可以可以看到FLASH的ID是0xef4017和产品手册中W25Q64的ID是一致的,读写测试也是正常的,我们使用了C库函数,需要包含头文件<stdio.h>。
文章出处: 小鸟的早晨
收藏 1 评论0 发布时间:2021-1-6 09:47

举报

0个回答

所属标签

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