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

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. //指令表5 a6 |8 R8 }& [
  2. #define W25X_WriteEnable                     0x06
    . H' C: K) Y. @+ W
  3. #define W25X_WriteDisable                     0x04 - M9 t7 |1 g/ w3 \8 p& P6 O. x
  4. #define W25X_ReadStatusReg                     0x05
    $ N8 P0 |2 y) F3 Y- q' N& ~9 A. `
  5. #define W25X_WriteStatusReg              0x01
    9 W6 q( d8 G4 T6 q& Q3 A
  6. #define W25X_ReadData                            0x03 & T5 z( I! U& ^7 A) L% E0 L, b
  7. #define W25X_FastReadData                     0x0B
    2 Y) u3 V+ z3 |) J6 q0 ~- i
  8. #define W25X_FastReadDual                     0x3B
    . o, @( w8 A) ]- r) L" Q6 M0 o& C
  9. #define W25X_PageProgram                     0x02
    - t5 Y! @: v7 D5 T; I& u
  10. #define W25X_BlockErase                     0xD8   H, N# q0 u3 B3 B; }; G; d
  11. #define W25X_SectorErase                     0x20 ' D* V$ r& i$ A: }7 E+ w
  12. #define W25X_ChipErase                            0xC7 * p# w4 @6 \) E( U: h) Q# v4 Q* x: [
  13. #define W25X_PowerDown                            0xB9
    + X* T" l4 i! y$ v6 E: \
  14. #define W25X_ReleasePowerDown       0xAB - B+ m+ u: U' F) O8 g8 ~9 ?" U
  15. #define W25X_DeviceID                     xAB " |' p2 Q3 w  M7 K7 ]. `* d
  16. #define W25X_ManufactDeviceID       0x90 ) Z* {3 m+ N" W, }
  17. #define W25X_JedecDeviceID              0x9F5 S: |% S$ F) }4 {' Y
  18. // others defined) p6 j! C; i( D
  19. #define sFLASH_ID    0XEF4017
    7 [" Z5 T2 Y& M8 _8 R/ q2 b$ T( k
  20. #define Dummy_Byte   0XFF6 O+ A  t& v1 A* e8 b
  21. #define SPI_FLASH_PageSize                    256
    & u* J5 w  E6 i+ O  A5 @4 ^
  22. #define SPI_FLASH_PerWritePageSize        256/ |9 Y. N* R) u, F+ S1 P+ g5 o% J
  23. #define SPI1_TIME_OUT   
复制代码
, }; I( E$ Q$ i# j; r$ x) S1 W, q; Q
并且申明应用的操作函数:
  1. void SPI_FLASH_SectorErase(uint32_t SectorAddr);
    ) X* q& j) s' T* ?0 V: @4 V
  2. uint32_t  SPI_Flash_ReadID(void);
    : H6 q- T3 F" x: O+ N
  3. void SPI_Flash_Erase_Chip(void);
    # c8 m* f9 b; ^
  4. void SPI_Flash_Read(uint32_t ReadAddr,uint16_t NumByteToRead,uint8_t* pBuffer);' _, K! A& D$ L4 |& }( }
  5. void SPI_Flash_Write(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer);6 Y  `, r) D5 R$ D
  6. void SPI_Flash_Write_Page(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer);
    - Y0 x& @$ l$ I5 a4 |1 T/ f! c
复制代码

7 U, g. m: O4 Z2 ~1 f. d3 u
下面我们在spi.c 中实现读写FLASH的相关函数:
(1) 对片选的信号引脚进行宏定义操作:
  1. #define SPI_FLASH_CS_H()   HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET)
      j9 C, ~6 N7 ^) J3 A/ E
  2. #define SPI_FLASH_CS_L()   HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET)
复制代码
+ z0 ~4 g: O' u: F; V" F
根据原理图使用的PC0引脚
(2)SPI 读写个字节函数
  1. /**function: SPI 读一个数据**/
      A" c: }% Q+ e7 `' w
  2. uint8_t SPI1_ReadByte(void)
    , R# ?0 e# X4 P+ G2 a2 z
  3. {                     ( n% v$ l& ]9 g% ]! j
  4.        uint8_t RxData;      
    1 T+ q6 B& O6 J! i
  5.                          1 V8 |; ~# b7 T/ s' W2 n
  6.        HAL_SPI_Receive(&hspi1, &RxData, 1, SPI1_TIME_OUT);: g+ v/ N/ y3 ?" H3 C
  7.                                              4 F0 M( k( V. l2 @
  8.        return RxData; //返回通过SPIx接收的数据                                       
      W# m& Y/ {. l( x, E
  9. }
复制代码
; F* K( M6 C9 `3 @& q; S; ^9 N

0 ^' S5 P/ X+ [6 |9 W1 S3 N
我们使用了HAL封装的HALSPIReceive(&hspi1,&RxData, 1, SPI1TIMEOUT)函数来实现读一个字节。
(3)写一个字节
  1. /**function: SPI 写一个数据**/
    4 D# s  B+ k3 r4 E' P8 h$ i0 b: S
  2. void SPI1_WriteByte(uint8_t TxData)
    7 h4 l  K" @: c1 Z0 z2 {+ }7 H
  3. {                                            2 }9 M& S+ e( ?- n! z
  4.        HAL_SPI_Transmit(&hspi1, &TxData, 1, SPI1_TIME_OUT);  //通过外设SPIx发送一个数据                                                                      1 C6 L" R- m9 O, f1 H: W4 G0 V
  5. }
复制代码
(4)FLASH的写使能和非使能
  1. /**function: SPI_FLASH写使能,将WEL置位**/7 t/ z# \4 C5 h0 a# t( D
  2. void SPI_FLASH_Write_Enable(void)   
    2 c7 }* @& f; s% o! a" i, H; ]2 b# ]  Y- S
  3. {
    ( {- M3 V7 p8 H$ j+ i. @/ G
  4.        SPI_FLASH_CS_L();                            //使能器件   % {& K* Y8 A8 H) S1 t) v3 i
  5.        SPI1_WriteByte(W25X_WriteEnable);      //发送写使能  
    7 B& q$ ~6 f, ^( p) E
  6.        SPI_FLASH_CS_H();                            //取消片选                  
    3 _( G& g% J$ {$ K
  7. } 2 @$ Z: I- h  u# a: ]- D  q

  8. % M& g& X5 E9 f# m9 a# G
  9. /**function: SPI_FLASH写禁止,将WEL清零**/9 d* J2 L  B0 b% K( O
  10. void SPI_FLASH_Write_Disable(void)   / e6 F: v0 U9 @
  11. {  
      n; z- N9 w* k: f( w
  12.        SPI_FLASH_CS_L();                            //使能器件     c6 F  d% \8 Z" X: s1 d
  13.        SPI1_WriteByte(W25X_WriteDisable);     //发送写禁止指令   
    7 Z* X) \9 F$ Y# s; P7 m# ?* R  @2 _
  14.        SPI_FLASH_CS_H();                           //取消片选                  
    & p' S; d8 `$ I" b( z* S
  15. }
复制代码
$ q: n6 Y  e; S. S4 r5 r0 e
(5) 读取SPI_FLASH的状态寄存器,通过这个函数我们可以判断FLASH的状态,一般用到的忙和空闲两种状态,这儿也实现了等待空闲函数。
  1. /**) l& U: H5 \$ t4 Y
  2. function: 读取SPI_FLASH的状态寄存器
    ) G3 T1 K& C4 f0 w( O6 a8 |% i: j* V
  3. **/: y2 {9 J- I# e3 V) G2 M- @
  4. //- S3 w7 S3 B0 \0 d, u
  5. //BIT7  6   5   4   3   2   1   0) y8 S+ }3 \: ?# g, f# e6 a# K
  6. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY
    / z' n9 e2 e( G5 w) Y( s% N6 i
  7. //SPR:默认0,状态寄存器保护位,配合WP使用" ^  c9 J& M- \5 A6 A: }- H
  8. //TB,BP2,BP1,BP0:FLASH区域写保护设置
    0 \- E" R9 @4 J/ [/ t& A6 [6 i+ N
  9. //WEL:写使能锁定
    ' z4 l; v% u9 D4 h
  10. //BUSY:忙标记位(1,忙;0,空闲)$ t. S$ [$ R5 ^6 `4 ]
  11. //默认:0x00
    " ~% ^+ d$ e7 Z% u4 g
  12. uint8_t SPI_Flash_ReadSR(void)   9 C0 T' _! [2 s7 A
  13. {  
      R, D  e/ ^5 R  _
  14.        uint8_t byte=0;   
    5 C5 M9 R* L8 G( L
  15.        SPI_FLASH_CS_L();                       //使能器件   ) A+ H3 c& A) s/ ^8 y# J6 h
  16.        SPI1_WriteByte(W25X_ReadStatusReg);           //发送读取状态寄存器命令    ; N7 ~) {; A/ v/ {; M
  17.        byte = SPI1_ReadByte();                           //读取一个字节  
    + N- e  B6 X; X) H4 J
  18.        SPI_FLASH_CS_H();                       //取消片选     , s- E: g. A" w5 ?6 b; f
  19.        return byte;   
    % K' [& H! n1 d5 B1 I# u+ O
  20. }
    # K) B) M& ^1 ]8 b! e/ r; {9 [

  21. ) T: y- Z# I2 W+ n: ?; B
  22. /**
    1 n; k8 _0 f" a0 L# q
  23. function: 等待空闲
    # p( J8 U: \0 O" @$ w
  24. **/$ q9 X" L/ }/ o5 H
  25. void SPI_Flash_Wait_Busy(void)   7 D! c) I8 y# x) \' T' ?* Z& E* q6 A
  26. {   
    7 g9 e0 q3 Q9 V% _; N: b" n
  27.        while ((SPI_Flash_ReadSR()&0x01)==0x01);   // 等待BUSY位清空8 `/ R1 ^# Y, W2 n% Q
  28. }
复制代码
(6)读取芯片ID W25Q64的ID函数,这个函数也是一般判断是否FLASH正常的方式。查看手册可以知道ID是0XEF4017
  1. /**
    ) l) p& Q* ], [, `9 m+ Z# V* b
  2. function: 读取芯片ID W25Q64的ID
    + z  v1 w9 f  S2 c; z
  3. **/
    9 B& W$ c; X( t% y! k
  4. uint32_t  SPI_Flash_ReadID(void)+ J; c& Y6 T! l6 Y, f9 W
  5. {
      A$ v' x+ P, M0 T# A! n
  6.        uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
    8 C9 M; e" A/ N8 y. w! t& u
  7.        SPI_FLASH_Write_Enable();
      ?; l+ x+ D3 E9 Y' c) {9 _
  8.        SPI_FLASH_CS_L();                7 L1 j9 A' k/ L9 z
  9.        SPI1_WriteByte(W25X_JedecDeviceID);//发送读取ID命令           
    ' l* u' e) n3 H) q4 R& I. W
  10.        Temp0 = SPI1_ReadByte();       + v) I! k! G8 S' J' U2 C) @
  11.        Temp1 = SPI1_ReadByte();              
    % p0 S8 }" Q1 U, d6 c  G: L
  12.        Temp2 = SPI1_ReadByte();       . ?2 V6 G& Y/ B7 x- U6 L4 B
  13.   /*把数据组合起来,作为函数的返回值*/+ w% p5 p. f' N% k+ A0 K
  14.        Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;2 D4 M5 l) g; w3 s
  15.        SPI_FLASH_CS_H();              . }1 N- h6 i* X$ o) D; x" K
  16.        return Temp;
    0 Y) d+ p1 J" R. h( Q: f4 @
  17. }
复制代码

0 p6 _& N) R, j. s$ t6 f" S
(7) flash 扇区擦除函数,FLASH和EEPROM不同,在写之前一定要进行擦除,否则不能正常写入。这儿实现了扇区擦除和正片擦除两个函数:
  1. /**
    + E4 p! L% w8 m+ C) w
  2. function: 擦除整个芯片+ C. N# _6 H: I& T( v0 M
  3. **/
    * r% V% h4 ~* O9 }) |
  4. //整片擦除时间:4 L/ a0 h7 m9 t+ G3 c' O5 W3 ^, f
  5. //W25X16:25s
    * N! C: }' |8 T9 N9 `; G! t+ H, K
  6. //W25X32:40s 8 @0 v# B) Q) @" K$ C7 L# |% Y) I
  7. //W25X64:40s ! d# I2 z: I2 }4 Q/ R) E, U
  8. //等待时间超长...
    8 ?2 s- ?8 c7 Q, ]3 Y' w6 \
  9. void SPI_Flash_Erase_Chip(void)   ; G, `" M. F  B4 x1 c/ q
  10. {                                             
    ( B! C, O5 _7 g
  11.        SPI_FLASH_Write_Enable();               //SET WEL ; `, N' Y8 M9 \* e  _
  12.        SPI_Flash_Wait_Busy();   8 l9 F! F* @/ q2 N% G
  13.        SPI_FLASH_CS_L();                      //使能器件   
    ' G7 A, i: `* n) _& ~8 j! x
  14.        SPI1_WriteByte(W25X_ChipErase);        //发送片擦除命令  
    / @# S  O4 m/ L" Q* q4 l
  15.        SPI_FLASH_CS_H();                      //取消片选                  
    5 H8 _  C0 T( Z+ `7 B
  16.        SPI_Flash_Wait_Busy();                                                //等待芯片擦除结束
    ! b! @4 [, V7 [# Q. g
  17. }  : C  t& k" O1 b. T# O
  18. /**
    4 b4 F; l$ b- {1 h. t- {
  19. function: SPI_FLASH_SectorErase
    * Q5 V! S0 [& {% v3 P) I) W& Y8 l
  20. annotation:Flash的一个扇区是4K,所以输入的地址要和4K对其。
    ) R- \1 g; l: Q0 q0 `% _6 o
  21. **/6 E* C' k$ E/ c
  22. void SPI_FLASH_SectorErase(uint32_t SectorAddr)& }: a$ P7 P) V0 ^7 O/ t3 G- o% f
  23. {' u2 e+ B# N/ f
  24.        SPI_FLASH_Write_Enable();
    6 ]# X, _! t- L# q' W
  25.        SPI_Flash_Wait_Busy();       9 k' B( V% p5 ^9 Y* c
  26.        SPI_FLASH_CS_L(); * a' ?0 M- a7 M2 ~
  27.        SPI1_WriteByte(W25X_SectorErase);
    5 t- Q( G( e7 H
  28.        SPI1_WriteByte((SectorAddr & 0xFF0000) >> 16); # l9 D" I+ X9 b$ S9 A4 ?+ W
  29.        SPI1_WriteByte((SectorAddr & 0xFF00) >> 8);
    " b+ s  V8 K  c5 |0 f9 R$ d4 O' `
  30.        SPI1_WriteByte(SectorAddr & 0xFF);# A0 l9 ^# j4 i" q
  31.        SPI_FLASH_CS_H();      8 [. W, Y! U: l/ T  ^2 x/ H' _- c
  32.        SPI_Flash_Wait_Busy();
    : T) u0 b. y2 z$ d
  33. }
复制代码
(8) 读取FLASH函数,FLASH的读操作步骤很简单
  1. /**
    8 B: o  \/ u* }' \
  2. function: 读取SPI FLASH  % }- `% V# j- x6 H0 ^
  3. **/) V4 z$ l# K+ j) }2 e
  4. //在指定地址开始读取指定长度的数据
    " }1 U& U- G/ u3 k) L  N
  5. //pBuffer:数据存储区: ~: W3 Y7 ]( y* M9 I" `
  6. //ReadAddr:开始读取的地址(24bit)
    7 P* r* e7 J1 ?; X0 T( V
  7. //NumByteToRead:要读取的字节数(最大65535)# M! h: k, z6 K/ b  i8 V
  8. void SPI_Flash_Read(uint32_t ReadAddr,uint16_t NumByteToRead,uint8_t* pBuffer)   
    , E. b% L5 a- t, }, x
  9. {
    6 U. `" k$ V8 g: F, F/ o( }
  10.        uint16_t i;                                                                                            $ g8 U8 O* o. R7 x' r# d' a9 F& D/ g5 O6 S
  11.        SPI_FLASH_CS_L();                          //使能器件   
    8 L  P) V! Q. Y2 W' I4 Z
  12.        SPI1_WriteByte(W25X_ReadData);                       //发送读取命令   
    7 v4 c2 F4 [8 T+ C
  13.        SPI1_WriteByte((uint8_t)((ReadAddr)>>16));  //发送24bit地址   
    ; C$ R2 k$ j: _
  14.        SPI1_WriteByte((uint8_t)((ReadAddr)>>8));
    ; H: }% u% S4 d; K7 G& F
  15.        SPI1_WriteByte((uint8_t)ReadAddr);: E" F7 V3 d1 {' m) s/ v1 i3 R0 c. w
  16.        for(i=0;i
    ; L3 j9 g' X$ n; b( p
  17.        {
    , [( A% t! \) {0 N# a
  18.               pBuffer[i] = SPI1_ReadByte();   //循环读数  , x) w0 z* x( N0 s
  19.        }  L0 o" o& {: i- w& {! f# P
  20.        SPI_FLASH_CS_H();       //取消片选
    0 F8 E4 P) f! N3 N/ ^8 i/ @
  21. }
复制代码

$ q1 S3 e  e; J# Z3 g
(9) flash 的按页写入数据,FLASH的数据的写入不是随机可以任意写入,按页写入,一页最大的数据是256个字节。
  1. /**
    / h  P7 M  t# M8 k% t- t( p
  2. function: SPI在一页(0~65535)内写入少于256个字节的数据
    % e, r: H# ?: b
  3. annotation:一页最大256个字节$ V. }/ b! g; I6 v  p3 M/ n4 V
  4. **/
    ' `  r+ D; s, m) `
  5. //在指定地址开始写入最大256字节的数据5 a" h) k/ t$ Y1 Z/ c3 R6 k4 q, k
  6. //pBuffer:数据存储区
    1 |6 Y& P  Q  M$ _, l4 A' S
  7. //WriteAddr:开始写入的地址(24bit)
    8 d9 Z! H5 W% z& \
  8. //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
    , r& l8 K# K, U
  9. void SPI_Flash_Write_Page(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer)  Z, l1 V4 G+ i
  10. {
    4 x3 u0 Q  }7 R$ S7 K4 Z, p
  11.          uint16_t i;- p9 w; a3 f/ _$ X$ j4 k
  12.        SPI_FLASH_Write_Enable();                                       //SET WEL
    + F4 j+ l. j( j3 W
  13.        SPI_FLASH_CS_L();                                         //使能器件) q: ~2 z: `2 [9 b4 A  L
  14.        SPI1_WriteByte(W25X_PageProgram);                                  //发送写页命令
    ; r( H# z6 W/ z4 m( S& y  F
  15.        SPI1_WriteByte((uint8_t)((WriteAddr)>>16));               //发送24bit地址
    , C; g8 G/ T5 @5 ^
  16.        SPI1_WriteByte((uint8_t)((WriteAddr)>>8));/ e+ T3 G- g+ }9 n
  17.        SPI1_WriteByte((uint8_t)WriteAddr);
      K$ |: b1 W9 o2 G/ _1 m. ?
  18.        for(i=0;i<NumByteToWrite;i++) SPI1_WriteByte(pBuffer[i]);       //循环写数
    - \1 ~- X4 V% }- x, s- R+ K
  19.        SPI_FLASH_CS_H();                                                        //取消片选 . g0 {( s8 b0 W% ?$ P  v/ a
  20.        SPI_Flash_Wait_Busy();                                                                        //等待写入结束                                                                                     4 a% O/ ~9 Z- i1 u! R" b, a* C
  21. }
复制代码

/ z+ ^; J# M$ `7 ?1 Z( k0 j
(10)flash 的随机的多字节写入,通过按页写入实现的函数来实现
  1. /**2 b- q% w+ t* Z2 d0 {
  2. @function 不定量的写入数据,先确保写入前擦出扇区
    ! q; h: [- }6 [0 q- T3 H
  3. @annotation
    9 r6 {4 ~* Z2 k* r* m
  4. @param, V' I0 O( f4 X8 o. q9 i
  5. @retval
    7 G0 D3 J7 t5 i+ B$ E* Z
  6. **/
    + N7 y) b4 M) Y# _+ K7 f- a2 i( `/ w
  7. void SPI_Flash_Write(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer)
    ' F2 ^1 L* M. m0 P4 r
  8. {' |3 E; E: K+ x1 r( g; ~: \
  9.        uint8_t NumOfPage =0, NumOfSingle=0, Addr =0, count =0, temp =0;" E/ J4 V5 H3 l# Z& K
  10.        /*计算Addr,写入的地址是否和PageSize对齐*/
    , U4 V4 S% ^" i1 T" A' E
  11.        Addr = WriteAddr % SPI_FLASH_PageSize;$ O) Q! v# S5 G$ F) U5 ^/ N0 B
  12.        /*count 为剩余的地址*/! i2 q2 k" O! i/ I1 M$ g6 A3 {
  13.        count = SPI_FLASH_PageSize - Addr;
    3 X' W  x, ~' s+ R5 X( ]( m9 D3 v
  14.        /*计算能写入多少整数页*/7 Y. }! M  n( z. f1 w
  15.        NumOfPage = NumByteToWrite % SPI_FLASH_PageSize;
    3 W% {5 i5 H( x% h
  16.        /*计算不满一页的数据*/
    ) S6 t; Q! y. K: `7 V& k0 i
  17.        NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;6 f: u! a3 c7 M  Y: R$ N9 Y
  18.        /* Addr=0,则 WriteAddr 刚好按页对齐 aligned */% H' }. C/ g6 y% q
  19.        if(Addr == 0)" c; j/ f/ G' x
  20.        {* V- B4 l% Z% G+ M3 @# [3 s
  21.               /*NumByteToWrite < SPI_FLASH_PageSize,一页能写完*/
    0 E- r  T0 A9 t& }/ G1 ]6 p* \
  22.               if(NumOfPage == 0)
    - b/ V" A+ c" P8 L( B
  23.               {
    ( e" O& d* {- y1 z  n" |
  24.                      SPI_Flash_Write_Page(WriteAddr,NumByteToWrite,pBuffer);1 R7 f, M7 |0 l  ]7 M! X
  25.               }
    ) D$ C0 ?# e/ _
  26.               else/* NumByteToWrite > SPI_FLASH_PageSize,一页写不完,先写整数页,在写剩下的 */% U% L2 d: [. y
  27.               {. @) G* A; X. p/ }0 L( Z, B4 Z
  28.                      /*先把整数页都写了*/! e* }7 v" Z, g: p4 }- ?, d( ~1 y
  29.                      while (NumOfPage--)8 J( u! r1 P: R' u/ y/ F& Q0 z4 c1 X
  30.                      {; ^0 [  J  w- D8 a2 z
  31.                             SPI_Flash_Write_Page(WriteAddr,SPI_FLASH_PageSize,pBuffer);1 o/ M4 P. P% R% r1 u$ k! M0 g9 m
  32.                             WriteAddr+=SPI_FLASH_PageSize; // flash 的地址加一页的大小" @% n8 |1 w+ N5 g( M0 G
  33.                             pBuffer+=SPI_FLASH_PageSize;   // 写缓存数据的地址加一页的大小
    - g4 t1 L9 E+ `: B$ r5 W$ T
  34.                      }0 l" ?  U$ p% j/ W$ n
  35.                      /*若有多余的不满一页的数据,把它写完*/& R3 z# g7 `4 ^, \5 h" w8 y
  36.                      SPI_Flash_Write_Page(WriteAddr,NumOfSingle,pBuffer);+ ~) o/ [& U1 R! H
  37.               }
    8 k1 d5 V, w5 V/ W
  38.        }
    3 n& G, c# {; F- P) v
  39.        else/* 若地址与 SPI_FLASH_PageSize 不对齐 */* t, ?, N& r4 L: A
  40.        {/ @/ p$ [. U" {. I' T/ F$ \
  41.                      /* NumByteToWrite < SPI_FLASH_PageSize */
    + c4 j; Z) D4 g: N7 ?
  42.               if (NumOfPage == 0)8 Z& c8 \% s( q$ G5 J  |
  43.               {7 Z1 Z4 j+ v3 A* @1 O, u
  44.                      /*当前页剩余的 count 个位置比 NumOfSingle 小,一页写不完*/
    ! [% s! [6 S+ p& o
  45.                      if(NumOfSingle >count)
    - P) V+ W8 i) K; ]" W6 Y
  46.                      {
    9 o+ F* ]- e( g1 D
  47.                             temp = NumOfSingle -count;
    ' @( u2 y+ l; m2 w! _# ^
  48.                             /*先写满当前页*/; L7 K8 c7 C6 m9 p
  49.                             SPI_Flash_Write_Page(WriteAddr,count,pBuffer);8 ~$ |5 @# @7 L4 f
  50.                             WriteAddr += count;: [  \2 n' R) c9 T( q0 q8 w& q: A
  51.                             pBuffer += count;4 p2 B4 \" K3 Y3 t
  52.                             /*再写剩余的数据*/8 G$ @- o( h" R- Q0 q
  53.                             SPI_Flash_Write_Page(WriteAddr,temp,pBuffer);
    ! x( l! n6 z6 k5 v0 E( y. K5 k
  54.                      }# s6 k4 A& y9 ?5 j/ F% x* V# N1 j
  55.                      else/*当前页剩余的 count 个位置能写完 NumOfSingle 个数据*/
    ! Q3 }; O1 E# N* X- u
  56.                      {
    $ t3 n4 S3 Z/ J4 X
  57.                             SPI_Flash_Write_Page(WriteAddr,NumByteToWrite,pBuffer);2 x5 C6 C  P* k$ {+ a& x
  58.                      }
    " a/ G) k0 K$ s/ }' }
  59.               }
    . h  Y/ b( Q7 @  M2 \; [
  60.               else/* NumByteToWrite > SPI_FLASH_PageSize */
    5 Q, u4 _* S0 Z: g* X  g
  61.               {
    ) m, W! O1 \4 N' S1 H* Y1 Y
  62.                      /*地址不对齐多出的 count 分开处理,不加入这个运算*/: m% w; q1 c8 _/ Q+ i% I
  63.                      NumByteToWrite -= count;
    # _! u$ J  {8 @% F% z/ ]
  64.                      NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;4 f$ S4 p  t5 ?  |# |& [, M3 R
  65.                      NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;6 J6 S6 o0 h# h6 d* {6 i/ l, b% F
  66.                      /* 先写完 count 个数据,为的是让下一次要写的地址对齐 */  Z5 Z( ~4 c: r9 t" o- v/ W! D
  67.                      SPI_Flash_Write_Page(WriteAddr,count,pBuffer);
    2 r7 ^4 M$ n: D) i( n5 ~
  68.                      /* 接下来就重复地址对齐的情况 */* P3 `# P4 ]; b1 g
  69.                      WriteAddr += count;
    . }4 l' E8 t& Q2 q3 Q
  70.                      pBuffer += count;
    1 H7 \4 @9 _1 K' ^6 g
  71.                      /*把整数页都写了*/
    0 Q' Y% x# t7 h# T& l
  72.                      while (NumOfPage--)
    4 M/ I! Q. X/ {
  73.                      {
    % r7 }! V  E# `: \5 T9 n0 M4 Q9 C. ]
  74.                             SPI_Flash_Write_Page(WriteAddr,SPI_FLASH_PageSize,pBuffer);
    - T1 N3 X2 ~: h. O9 i
  75.                             WriteAddr += SPI_FLASH_PageSize;
    5 q' S9 c8 ]5 Z! v7 E5 w2 a
  76.                             pBuffer += SPI_FLASH_PageSize;
    ; y- {# c1 ?. Y+ Y, R6 R/ h% _0 ^
  77.                      }
    0 u1 ~* Q' s# i6 E
  78.                      /*若有多余的不满一页的数据,把它写完*/* {$ H# H2 q0 W' E3 U
  79.                      if (NumOfSingle != 0)
    & ]2 y- l0 E8 Z4 r6 v7 l
  80.                      {
    : k3 N# ?9 c8 o3 d( g& c& j
  81.                             SPI_Flash_Write_Page(WriteAddr,NumOfSingle,pBuffer);
    7 m$ R0 n/ @' G8 q  n, y9 k
  82.                      }
    0 |) I) Z, H- o6 c3 j9 d. ]
  83.               }
    . ^# n0 @$ a  ]; H
  84.        }              2 n5 b' N/ B4 b1 f# S7 r5 H
  85. }
复制代码

. J  A9 Q: A! a4 W$ x
通过上面的函数实现,我们就可以基本的来完成FLASH的读写操作了。有些函数参考了野火标准库的操作步骤。
在main.c 的主函数中对FLASH的读写地址进行宏定义:
  1. #define  FLASH_WriteAddress     0x00000
    * P5 H" C* ?9 N0 |/ A4 |# I
  2. #define  FLASH_ReadAddress      FLASH_WriteAddress! s4 E+ n) H5 d8 h# T0 I1 F
  3. #define  FLASH_SectorToErase    FLASH_WriteAddress: S$ J" ~5 I$ x4 H  @- _# w4 C0 d
  4. #define FLASH_SPI hspi1
复制代码

5 p% P: r  I+ ?% c1 p$ z% c
在mian函数中定义变量:
  1. uint32_t flash_ID = 0;% L* b0 B0 Y1 W  y; ?) p. x- m7 l
  2. /* 获取缓冲区的长度 */. \8 I0 m/ z. h3 T: v' ?
  3. #define countof(a)      (sizeof(a) / sizeof(*(a)))% v4 n+ |6 s2 h
  4. uint8_t Tx_Buffer[] = "现在进行FLASH的读写测试\r\n";; |$ f& }$ p; U8 ?  u; g( U
  5. #define  BufferSize (countof(Tx_Buffer)-1)
    , W' ]& _6 R. I3 N; i2 v; G; u3 s
  6. uint8_t Rx_Buffer[BufferSize];
复制代码
1 O7 j& R4 Y" U( D! E" P
在main函数中进行测试代码:
  1. printf("*************this is test for coding...**********\t\n");3 d; |6 Q) s1 [! s
  2.        printf("this is test code for spi1 read and write flash w25Q64 \r\n");" Y, J- o4 T# t5 a$ I" }
  3.        flash_ID = SPI_Flash_ReadID();; A' M) W& [; G
  4.        printf("\r\n flash ID is 0X%x\r\n",flash_ID);
    / ]0 g' F8 h9 f
  5.        SPI_FLASH_SectorErase(FLASH_SectorToErase);      
    & s" z6 a' T4 e4 E8 W
  6.        SPI_Flash_Write(FLASH_WriteAddress,BufferSize,Tx_Buffer);
    ! B. x# N3 v0 b( Z
  7.        printf("\r\n 写入的数据为:%s \r\t", Tx_Buffer);9 d: J  [- n4 u  H( E+ C" ~
  8.        SPI_Flash_Read(FLASH_ReadAddress,BufferSize,Rx_Buffer);
    5 D. }/ Y7 g& w% n( ^' s
  9.        printf("\r\n 读的数据为:%s \r\t", Rx_Buffer);
    : u( R8 V7 y: D& V0 B6 I
  10.        /* 检查写入的数据与读出的数据是否相等 */
    3 I; T( W7 z/ Z# Y$ F( [  S; X  U) x
  11.        if(memcmp(Tx_Buffer, Rx_Buffer, BufferSize)==0)
    ; q( P/ {# ]$ z# W5 [* l, u8 z5 C
  12.        {
    & |. s* U# H. P& b& u
  13.               printf("写入的和读出的数据是正常的!\r\n");6 @' E  Q% [- D8 _; L# y" J! B* ?
  14.        }9 h  o2 s3 ^* K; Q* N
  15.        printf("SPI 试验结束......!\r\n");
    8 a: r( C2 g) k# |6 s
  16.        printf("*************this is test end....**********\t\n");& M# h' _+ _( ^8 r0 G$ N% {
复制代码

# f$ @) c9 l( O' c( H0 \$ E0 C
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 手机版