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

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. //指令表7 w% U. w# J" G: h
  2. #define W25X_WriteEnable                     0x06 8 H9 `0 n0 I, l1 C% c' S8 Z0 N
  3. #define W25X_WriteDisable                     0x04
    ' A* l- _6 T. y* w0 O4 I& A
  4. #define W25X_ReadStatusReg                     0x05 0 i( _+ o% K7 l% R  a( x: \) A* f
  5. #define W25X_WriteStatusReg              0x01 $ x& q0 m: O* o8 k8 \4 d7 X
  6. #define W25X_ReadData                            0x03 + I4 y, O  h2 [  D, _; @& W" J
  7. #define W25X_FastReadData                     0x0B
    8 h4 n8 ~, r" H  v. ^( x
  8. #define W25X_FastReadDual                     0x3B
    3 K( T  u/ w" l) k, @! S' J
  9. #define W25X_PageProgram                     0x02 2 W( |! y5 R3 ]1 h2 n2 {. @3 z' J& R
  10. #define W25X_BlockErase                     0xD8 ) e0 Q+ ?+ f' g' [& w1 j5 Z
  11. #define W25X_SectorErase                     0x20 ( V8 I! C: Q. r9 U1 c
  12. #define W25X_ChipErase                            0xC7 7 R, U6 g2 ]1 [$ z( v2 ]9 v; |
  13. #define W25X_PowerDown                            0xB9 + A$ t0 F1 w' X1 d) i
  14. #define W25X_ReleasePowerDown       0xAB
    7 W7 g4 d( Y5 |5 Z! h0 t9 F
  15. #define W25X_DeviceID                     xAB
    / o8 f7 P3 b- `9 H; d
  16. #define W25X_ManufactDeviceID       0x90
    4 I2 X( Y( G0 w
  17. #define W25X_JedecDeviceID              0x9F
    8 K" t' f' w$ ~; ?7 D
  18. // others defined
    ( O! s2 T! u) d8 L6 y& \6 @
  19. #define sFLASH_ID    0XEF4017" e1 w& U# _4 t! X) M
  20. #define Dummy_Byte   0XFF
    ; g* E4 J- k# l, I' a: v
  21. #define SPI_FLASH_PageSize                    256$ W* i, F0 p) Z; h9 S
  22. #define SPI_FLASH_PerWritePageSize        256" W3 M5 ^% Z, D6 e; k
  23. #define SPI1_TIME_OUT   
复制代码

" Y" `% U. \  W) C% m* J
并且申明应用的操作函数:
  1. void SPI_FLASH_SectorErase(uint32_t SectorAddr);$ y2 ]: C) \8 Z2 z3 s, t$ I
  2. uint32_t  SPI_Flash_ReadID(void);
    ) {; X: f8 d4 Y( a% R: h2 t
  3. void SPI_Flash_Erase_Chip(void);
    $ [. C( N! E. x0 P1 W
  4. void SPI_Flash_Read(uint32_t ReadAddr,uint16_t NumByteToRead,uint8_t* pBuffer);
    1 j! W8 }& R7 u( t
  5. void SPI_Flash_Write(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer);0 S4 q& z4 K: s+ x) j
  6. void SPI_Flash_Write_Page(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer);
    7 b1 x! ~* x+ s& h0 V9 d) Y1 M
复制代码

% o  Z3 r, r$ W
下面我们在spi.c 中实现读写FLASH的相关函数:
(1) 对片选的信号引脚进行宏定义操作:
  1. #define SPI_FLASH_CS_H()   HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET) $ p( J1 G/ X' m. {
  2. #define SPI_FLASH_CS_L()   HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET)
复制代码
* Y/ A1 V8 {2 p
根据原理图使用的PC0引脚
(2)SPI 读写个字节函数
  1. /**function: SPI 读一个数据**/
    5 U+ w+ H- C3 i5 f. }9 N% W* L! [
  2. uint8_t SPI1_ReadByte(void)
    % f+ L9 K5 {/ N4 d6 H
  3. {                     , S, I! p* {& \0 @; a2 ^; [
  4.        uint8_t RxData;      
    9 E- j; A' l4 i: c0 ^
  5.                         
    4 S: g! _. ?) R* F8 u. |* m# O( x
  6.        HAL_SPI_Receive(&hspi1, &RxData, 1, SPI1_TIME_OUT);
    3 M( T. p9 o: O# i0 e2 y5 n
  7.                                              - G  S- u- g6 m# J: Q3 Y5 c
  8.        return RxData; //返回通过SPIx接收的数据                                       8 h3 ?! |0 K. ^+ x; w2 z, G" Q+ c) K
  9. }
复制代码
) t2 X5 l& _4 ?

8 Q* D# D6 u8 x* z+ n7 \' X
我们使用了HAL封装的HALSPIReceive(&hspi1,&RxData, 1, SPI1TIMEOUT)函数来实现读一个字节。
(3)写一个字节
  1. /**function: SPI 写一个数据**/
    . O3 Z) O+ X' V1 }, P
  2. void SPI1_WriteByte(uint8_t TxData)) w7 m, C% K3 S
  3. {                                            
    % H; P; p9 h3 L+ E
  4.        HAL_SPI_Transmit(&hspi1, &TxData, 1, SPI1_TIME_OUT);  //通过外设SPIx发送一个数据                                                                     
    # n$ B; _0 k% J7 N4 a4 I; Q+ g/ t
  5. }
复制代码
(4)FLASH的写使能和非使能
  1. /**function: SPI_FLASH写使能,将WEL置位**/
    + B9 v2 G1 J6 k
  2. void SPI_FLASH_Write_Enable(void)   * G+ j0 E6 s" b4 \+ r" Y8 |3 N$ L
  3. {
    9 t+ t; Q" N/ b+ b5 [
  4.        SPI_FLASH_CS_L();                            //使能器件   4 X8 z7 I* Y6 z- o/ G- b
  5.        SPI1_WriteByte(W25X_WriteEnable);      //发送写使能  
    & ~# p4 U2 n9 N. j0 B1 Y( r
  6.        SPI_FLASH_CS_H();                            //取消片选                  / j( `2 ]: J: l3 F& T1 q2 F7 N8 |
  7. } 2 w8 O2 f& _; V2 a; }' Q  @

  8. ( H+ r9 U% G; v* F1 l: J2 I7 s5 |5 ?7 T
  9. /**function: SPI_FLASH写禁止,将WEL清零**/3 o6 b. E, P( @  w3 x
  10. void SPI_FLASH_Write_Disable(void)   
    5 H; q/ x+ U$ h! \1 ^
  11. {  
    7 N) D* h' T/ ?# @/ z1 g  I# L
  12.        SPI_FLASH_CS_L();                            //使能器件   ( |. P5 B/ b" w, W- D5 o
  13.        SPI1_WriteByte(W25X_WriteDisable);     //发送写禁止指令   
    ; p2 `; }: L$ j* c
  14.        SPI_FLASH_CS_H();                           //取消片选                  
    * i6 S& ?- P, D' Y1 d  s. f% D
  15. }
复制代码
2 u7 T& q) w+ F" w+ k* b
(5) 读取SPI_FLASH的状态寄存器,通过这个函数我们可以判断FLASH的状态,一般用到的忙和空闲两种状态,这儿也实现了等待空闲函数。
  1. /**
    ( t( c; a! z3 ~) k
  2. function: 读取SPI_FLASH的状态寄存器
    5 `' ]2 ^- O- f$ [2 O7 R
  3. **/
    ! w6 M. ?6 \8 x4 j. x0 j3 I
  4. //
    4 K9 l1 |6 A, P8 n/ l
  5. //BIT7  6   5   4   3   2   1   02 p# ?4 _! P& }# m% C5 c9 G
  6. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY* N/ _& u6 @, p' t( H: v
  7. //SPR:默认0,状态寄存器保护位,配合WP使用& _$ {& o8 O; z* j+ ?
  8. //TB,BP2,BP1,BP0:FLASH区域写保护设置# O. W  }, ]* _2 K* D0 n% \6 A# s
  9. //WEL:写使能锁定
    ' k+ S4 c$ Q+ s+ ]( U
  10. //BUSY:忙标记位(1,忙;0,空闲)
    $ w& y$ p5 g9 B5 _* j" f
  11. //默认:0x00
    9 x/ K& i3 {" l! c
  12. uint8_t SPI_Flash_ReadSR(void)   ; Q$ @2 @) K- `% K
  13. {  5 H1 y( l: P) E5 b
  14.        uint8_t byte=0;   ! G: w3 p6 G. m1 F+ R
  15.        SPI_FLASH_CS_L();                       //使能器件     `5 Z9 b# `; L: Z$ L9 O% U8 m* |7 \
  16.        SPI1_WriteByte(W25X_ReadStatusReg);           //发送读取状态寄存器命令    $ u6 E- U8 G. F  n
  17.        byte = SPI1_ReadByte();                           //读取一个字节  
    3 U- n2 H- K6 d; r' L- ^
  18.        SPI_FLASH_CS_H();                       //取消片选     ( ?  P3 [8 v( b
  19.        return byte;   . N6 ?; q% S& s9 n; e
  20. }
    # j) t( j- U& m# ?, L7 J

  21. 6 z9 R" ^$ K3 M9 t! q5 [6 R
  22. /**
      a3 u6 n5 m2 ]; s: a0 [8 Q; v  L" p
  23. function: 等待空闲
    $ }  n2 m: j- Q0 ~5 m
  24. **/
    ! w6 Q/ N0 q% v+ ^
  25. void SPI_Flash_Wait_Busy(void)   
    , x& {/ C  w5 ?% N1 w, K% G+ ]
  26. {   
    $ {' X* E* ]2 W1 |; B5 n, N
  27.        while ((SPI_Flash_ReadSR()&0x01)==0x01);   // 等待BUSY位清空* _; @+ r% T- N1 d: E5 X4 O
  28. }
复制代码
(6)读取芯片ID W25Q64的ID函数,这个函数也是一般判断是否FLASH正常的方式。查看手册可以知道ID是0XEF4017
  1. /**! c) V% _+ Y/ ]9 o
  2. function: 读取芯片ID W25Q64的ID' {9 t& p. O9 ]
  3. **/
    & S6 z6 f2 k# d8 E' I, O& d# L
  4. uint32_t  SPI_Flash_ReadID(void)
    9 t: h" g; `# X1 a) S1 H0 f1 u' i" m
  5. {
    $ h1 M5 d5 m! ?% z$ y
  6.        uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0; - x  I7 z1 T' K6 N
  7.        SPI_FLASH_Write_Enable();
    ' O( D: i( Y* H1 n' d+ \
  8.        SPI_FLASH_CS_L();               
    # [" N  R: A9 c& ]; k; u+ g
  9.        SPI1_WriteByte(W25X_JedecDeviceID);//发送读取ID命令           
      y+ J% j5 o- I" s6 Q
  10.        Temp0 = SPI1_ReadByte();      
    / I9 y8 }$ f7 \
  11.        Temp1 = SPI1_ReadByte();              , x, `( i* Z) y( D, M
  12.        Temp2 = SPI1_ReadByte();       + Q9 v& b, j0 ]- J4 ?) l
  13.   /*把数据组合起来,作为函数的返回值*/
    & H" B3 H1 V  \* m/ D
  14.        Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
    ) r( j+ u8 v9 [4 K6 q+ j
  15.        SPI_FLASH_CS_H();              9 E! a7 F& v8 I6 ^+ g
  16.        return Temp;
    2 U  U$ D$ k2 g7 V5 M% W# D
  17. }
复制代码
3 p) P4 ^5 A3 T
(7) flash 扇区擦除函数,FLASH和EEPROM不同,在写之前一定要进行擦除,否则不能正常写入。这儿实现了扇区擦除和正片擦除两个函数:
  1. /**
      k, w5 _' |  j8 j" A; n6 \
  2. function: 擦除整个芯片# A$ }% W' x) n
  3. **/
    - a: Q- m1 {5 {) U! y% E
  4. //整片擦除时间:
    ' a/ b$ o, b8 R6 r8 E
  5. //W25X16:25s 9 t& f. L0 c$ ^+ Q) U  d, d% @, m
  6. //W25X32:40s ; m* u/ y8 m1 L" D, d% Z
  7. //W25X64:40s / g; l! X% L* C  a7 v3 C# H1 Y5 F
  8. //等待时间超长...
    / G, P6 v( K9 \% n
  9. void SPI_Flash_Erase_Chip(void)   
    ' }5 W4 q1 p, I
  10. {                                             2 N8 F% m' j1 O+ |% o
  11.        SPI_FLASH_Write_Enable();               //SET WEL
    ' ]8 \/ y; U0 a2 w
  12.        SPI_Flash_Wait_Busy();   
    3 P" y# V# A: f* h: k
  13.        SPI_FLASH_CS_L();                      //使能器件   7 o1 {. y* J  ]4 H  S: i
  14.        SPI1_WriteByte(W25X_ChipErase);        //发送片擦除命令  + j$ D# m' J* L+ ^" q; T3 E
  15.        SPI_FLASH_CS_H();                      //取消片选                  
    - W+ ^7 W' d9 ^
  16.        SPI_Flash_Wait_Busy();                                                //等待芯片擦除结束" v' `/ n* [0 q" t: G1 R
  17. }  
    7 k& o' _% b/ ~5 L) g9 W9 w
  18. /**
    / ?5 c, S# A6 t1 a# s
  19. function: SPI_FLASH_SectorErase
    % D, C- F; z( w) u% A' o1 V# S& N
  20. annotation:Flash的一个扇区是4K,所以输入的地址要和4K对其。5 u. x& f9 D$ }3 Z, r
  21. **/
    1 M4 {: N" K- y  z: W
  22. void SPI_FLASH_SectorErase(uint32_t SectorAddr)& L6 c4 ]; n; H! h
  23. {
    , N6 p/ g) R6 N) A- P/ l3 S3 U5 ]9 J
  24.        SPI_FLASH_Write_Enable();
    5 V+ g! P4 O$ h1 V; V& q, d
  25.        SPI_Flash_Wait_Busy();      
    ' v% W6 i2 D) Q7 f7 L% H$ O
  26.        SPI_FLASH_CS_L();
    " [2 r; C: M$ p/ D/ E: B! t, D
  27.        SPI1_WriteByte(W25X_SectorErase);
    3 h0 T& J& C+ O+ r6 T/ s) D
  28.        SPI1_WriteByte((SectorAddr & 0xFF0000) >> 16);
    5 Q* w5 i8 y$ }" C! D) V% W- Z
  29.        SPI1_WriteByte((SectorAddr & 0xFF00) >> 8);
    , ~3 K7 k6 R8 ~3 o7 D" `
  30.        SPI1_WriteByte(SectorAddr & 0xFF);
    / K, {# @# J8 r" h8 |! a6 j8 k
  31.        SPI_FLASH_CS_H();      
    * w4 }/ F! o0 m: i6 ?! p* }( ]# C( t5 \
  32.        SPI_Flash_Wait_Busy();
    % z9 x$ |4 x; _; O( f. n
  33. }
复制代码
(8) 读取FLASH函数,FLASH的读操作步骤很简单
  1. /**1 T+ k2 X( K8 _
  2. function: 读取SPI FLASH  
    : E* P1 B' o7 v
  3. **/
    1 H, z3 v% {/ U! ?
  4. //在指定地址开始读取指定长度的数据  }$ P: K% Z% C7 v4 C5 c$ K
  5. //pBuffer:数据存储区  [; b8 ^+ J  O' R
  6. //ReadAddr:开始读取的地址(24bit)
    5 X9 J! M: x! C, W; Q% C: o
  7. //NumByteToRead:要读取的字节数(最大65535)8 }1 Z  H( R( H
  8. void SPI_Flash_Read(uint32_t ReadAddr,uint16_t NumByteToRead,uint8_t* pBuffer)   ' I9 O! p; Q, d0 ^$ O. K7 f9 q
  9. { ; s1 T" b8 E; w& x8 i5 D, x
  10.        uint16_t i;                                                                                            ( V0 W3 b8 p" _% S0 L7 t7 ~1 \
  11.        SPI_FLASH_CS_L();                          //使能器件   
    : m9 W; t+ {3 S0 C; L9 \& `, ]% r
  12.        SPI1_WriteByte(W25X_ReadData);                       //发送读取命令   
    / t) S/ N3 h3 U" q6 m9 p
  13.        SPI1_WriteByte((uint8_t)((ReadAddr)>>16));  //发送24bit地址   
    ) h) }& ~; D3 p4 l  Y/ t
  14.        SPI1_WriteByte((uint8_t)((ReadAddr)>>8));; B( h2 ~: I, e0 r5 M: Z7 m
  15.        SPI1_WriteByte((uint8_t)ReadAddr);& f2 Q/ ?4 q/ G) A- G# E; B
  16.        for(i=0;i
    2 K) S$ I4 \0 L# C2 B: r/ c( w
  17.        { 6 `7 y$ U' u, q( E
  18.               pBuffer[i] = SPI1_ReadByte();   //循环读数  & U0 Z7 ~+ H5 l
  19.        }
    ! @! |/ j, a/ J$ D3 n
  20.        SPI_FLASH_CS_H();       //取消片选* G1 p' Y: }  y3 h' u; M
  21. }
复制代码

: u4 U/ u8 W8 b, u1 }
(9) flash 的按页写入数据,FLASH的数据的写入不是随机可以任意写入,按页写入,一页最大的数据是256个字节。
  1. /**  x6 w2 @& c, G( x
  2. function: SPI在一页(0~65535)内写入少于256个字节的数据
    2 J8 W9 T& G6 k3 Z
  3. annotation:一页最大256个字节' ~5 Y9 O" b! j6 {
  4. **/. y3 }8 n6 ?; k1 U6 u5 X
  5. //在指定地址开始写入最大256字节的数据
    , p, o& e" e. l  o% N8 X+ j
  6. //pBuffer:数据存储区
    $ X/ i, ^9 k! o6 c
  7. //WriteAddr:开始写入的地址(24bit)
    4 E; H, c( g  o
  8. //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!8 ]: t" ~( q7 ]6 U" [# j4 h
  9. void SPI_Flash_Write_Page(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer)# N* w( l5 E5 @: b+ K, }
  10. {
    3 \: S0 P) y4 x9 o# j& Z
  11.          uint16_t i;* J3 k  Z' ~: Q" q: Q9 r
  12.        SPI_FLASH_Write_Enable();                                       //SET WEL9 Y4 y* X( x4 C
  13.        SPI_FLASH_CS_L();                                         //使能器件
    7 Y7 G! D( s9 I0 _" B, j
  14.        SPI1_WriteByte(W25X_PageProgram);                                  //发送写页命令' ?4 ^$ V, |; Y' y  P% Z
  15.        SPI1_WriteByte((uint8_t)((WriteAddr)>>16));               //发送24bit地址
    3 D* A$ Y2 G' ~- @' c4 \
  16.        SPI1_WriteByte((uint8_t)((WriteAddr)>>8));
    9 O, b+ T6 ~( ?7 T$ G5 G
  17.        SPI1_WriteByte((uint8_t)WriteAddr);
    # d9 _+ X2 p! ]8 d( K
  18.        for(i=0;i<NumByteToWrite;i++) SPI1_WriteByte(pBuffer[i]);       //循环写数; t2 ~9 h% _+ ^* K9 k' @% j
  19.        SPI_FLASH_CS_H();                                                        //取消片选
    8 u. h" g% }6 P/ D) L# a/ \; [
  20.        SPI_Flash_Wait_Busy();                                                                        //等待写入结束                                                                                    
    0 U) ]3 I. l* }" G; y7 ?% [- d5 n
  21. }
复制代码
* }5 [1 U' r# ]9 e. P
(10)flash 的随机的多字节写入,通过按页写入实现的函数来实现
  1. /**
    & P! F+ C% ~( ^8 q2 T" a! H
  2. @function 不定量的写入数据,先确保写入前擦出扇区1 |4 i2 u" F7 K! B+ u6 P6 Z" N
  3. @annotation; M( T% z6 B% s, r. D
  4. @param% L! `4 L$ N- n
  5. @retval1 e* p/ ^" Z% Z1 v2 C* A
  6. **/
    , Q  B, K; K# b: ~
  7. void SPI_Flash_Write(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer)! R' O' x1 R: Q7 H" J
  8. {; p- G# X) B# Z
  9.        uint8_t NumOfPage =0, NumOfSingle=0, Addr =0, count =0, temp =0;
    * j" P- Q# x9 H4 n
  10.        /*计算Addr,写入的地址是否和PageSize对齐*/
    7 N8 F7 v# q' }8 `+ `! W; j, r
  11.        Addr = WriteAddr % SPI_FLASH_PageSize;6 U# H2 a' U8 m$ h' w' e  w' x& P
  12.        /*count 为剩余的地址*/9 l: m: N9 [- r4 O6 R. Z! c+ Q
  13.        count = SPI_FLASH_PageSize - Addr;8 u  Z/ w4 D$ P4 ^  {
  14.        /*计算能写入多少整数页*/
    / Y$ S5 t8 d7 D
  15.        NumOfPage = NumByteToWrite % SPI_FLASH_PageSize;
    8 x- n' y# [. @
  16.        /*计算不满一页的数据*/
    6 F& X4 Y* s. l. S- P6 v
  17.        NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;' M0 p5 N; a! s$ J
  18.        /* Addr=0,则 WriteAddr 刚好按页对齐 aligned */
    " J3 k& n6 `# M& Z0 E. C
  19.        if(Addr == 0)
      i7 G# t& `! |" p7 y0 S9 z; J
  20.        {. U$ k2 E, p0 B" y# j/ s
  21.               /*NumByteToWrite < SPI_FLASH_PageSize,一页能写完*/
    5 B2 i3 ?# C' }
  22.               if(NumOfPage == 0)
    3 M/ s$ ]- ], i' v2 ]$ p8 X2 T. a" R
  23.               {
    ) \1 ~4 C, i  l$ W2 h8 s' C. M6 h' I
  24.                      SPI_Flash_Write_Page(WriteAddr,NumByteToWrite,pBuffer);
    ! m) c: j0 a) G6 g
  25.               }, y  v. {4 Y  P" K6 R
  26.               else/* NumByteToWrite > SPI_FLASH_PageSize,一页写不完,先写整数页,在写剩下的 */
    6 |) b2 _  S/ R7 T
  27.               {+ ^- S; V6 o( D
  28.                      /*先把整数页都写了*/
    3 N. X5 m- ~" e  L
  29.                      while (NumOfPage--)1 v3 u' L/ U4 {3 l
  30.                      {2 d, R/ Z5 l' ~: ]5 j8 ?
  31.                             SPI_Flash_Write_Page(WriteAddr,SPI_FLASH_PageSize,pBuffer);
    3 ^7 n/ ?# u# r3 v" J6 n/ l
  32.                             WriteAddr+=SPI_FLASH_PageSize; // flash 的地址加一页的大小
    , s( I9 V, y1 o5 p7 u5 B) }- h+ r
  33.                             pBuffer+=SPI_FLASH_PageSize;   // 写缓存数据的地址加一页的大小- W3 {( E& _" \
  34.                      }3 ?2 B2 n2 o9 Z4 i7 {% M
  35.                      /*若有多余的不满一页的数据,把它写完*/4 p' h4 ]; x/ k5 Z
  36.                      SPI_Flash_Write_Page(WriteAddr,NumOfSingle,pBuffer);8 B6 U) ?" L6 A- c- {
  37.               }
    + K9 J- y) k. ?4 R' B  F# N- E
  38.        }
    ! m" a/ D, s$ _$ m( T
  39.        else/* 若地址与 SPI_FLASH_PageSize 不对齐 */. L5 s+ M  o! ?2 p: l) v
  40.        {
    # p5 J8 a. ?1 z5 b3 p- Z
  41.                      /* NumByteToWrite < SPI_FLASH_PageSize */6 a; e9 P3 V, D$ b
  42.               if (NumOfPage == 0)
    ' K( m2 j4 i" ^' z
  43.               {
    ! z3 O0 j) C, L, z7 O' c
  44.                      /*当前页剩余的 count 个位置比 NumOfSingle 小,一页写不完*/3 ]; J3 f, A0 S  R7 V
  45.                      if(NumOfSingle >count)  \; i5 y: b: e- E3 [) P6 O
  46.                      {. I8 j! B% i5 }" Z
  47.                             temp = NumOfSingle -count;
    ; B5 n" m( d5 E5 J# [% b+ Z7 t6 m
  48.                             /*先写满当前页*/: A( l& g" y) R7 ]
  49.                             SPI_Flash_Write_Page(WriteAddr,count,pBuffer);
    . h! _0 c5 d9 W$ ^- T& b5 r
  50.                             WriteAddr += count;
    7 C! j+ q( m0 y# e
  51.                             pBuffer += count;
    6 J, j8 ^- a; `9 _& @# q
  52.                             /*再写剩余的数据*/6 A8 t  T$ i3 p' E0 J
  53.                             SPI_Flash_Write_Page(WriteAddr,temp,pBuffer);
    3 [; o: {% U7 `: J- P+ Z
  54.                      }
    ! k+ D. Z9 N8 H$ h* A
  55.                      else/*当前页剩余的 count 个位置能写完 NumOfSingle 个数据*/( W0 P( w3 J% i) z0 X4 m
  56.                      {+ @3 i* }( @2 {- d- v8 F
  57.                             SPI_Flash_Write_Page(WriteAddr,NumByteToWrite,pBuffer);
    # b0 B; O1 z, e5 S- b' o( {: r
  58.                      }! k& }' w& i8 L, f: U
  59.               }& S) w. u9 q* O) F* g
  60.               else/* NumByteToWrite > SPI_FLASH_PageSize */5 `% j4 B- W# z: r+ H2 h/ g" r9 G
  61.               {; U  G' z( m( w, n  H9 Q! n
  62.                      /*地址不对齐多出的 count 分开处理,不加入这个运算*/
    - j- e0 s/ E1 c$ d8 Y5 M2 o
  63.                      NumByteToWrite -= count;
      B" X! T0 y# W1 R, o1 l( r
  64.                      NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;3 n; I( o) g. A7 e
  65.                      NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;5 C1 G( x$ q- S
  66.                      /* 先写完 count 个数据,为的是让下一次要写的地址对齐 */# u4 d9 F( a6 a, ^; H9 ^3 }/ \
  67.                      SPI_Flash_Write_Page(WriteAddr,count,pBuffer);
    + `  g" m8 I) g8 x- f7 c
  68.                      /* 接下来就重复地址对齐的情况 */
    $ G1 y+ B, G* A% h. J
  69.                      WriteAddr += count;
    . u, c, s' I( b" N) m
  70.                      pBuffer += count;
    # _- _  T" ?1 d8 A- p9 B
  71.                      /*把整数页都写了*/
    2 c' f$ \+ V2 b8 ]5 p, Z. u* k
  72.                      while (NumOfPage--)3 c: G4 q5 j# X) V8 Q
  73.                      {
    & y# |" B; i8 g, k- P. L. e& i" O
  74.                             SPI_Flash_Write_Page(WriteAddr,SPI_FLASH_PageSize,pBuffer);' D( j2 A3 }. z9 V; I* \. I: ^
  75.                             WriteAddr += SPI_FLASH_PageSize;
    ( F0 ~6 H9 J# K' d# n
  76.                             pBuffer += SPI_FLASH_PageSize;
    ; K/ l7 l4 H* {, K
  77.                      }
    - V% s& ^# i6 A) I
  78.                      /*若有多余的不满一页的数据,把它写完*/
    & b, w9 C) [5 ^7 G  Y
  79.                      if (NumOfSingle != 0)
    3 n& w( X2 N. X( c+ k: E
  80.                      {
    ( Z* z0 Z( E; H  u8 [' ?
  81.                             SPI_Flash_Write_Page(WriteAddr,NumOfSingle,pBuffer);
    ) T3 g, l6 I: x
  82.                      }3 P5 \; A% b0 B2 e5 |
  83.               }! Y! M( [' R7 v6 T1 x
  84.        }              0 e1 h1 d( u+ V
  85. }
复制代码
/ u4 g1 {2 l$ \5 a% K( g: t
通过上面的函数实现,我们就可以基本的来完成FLASH的读写操作了。有些函数参考了野火标准库的操作步骤。
在main.c 的主函数中对FLASH的读写地址进行宏定义:
  1. #define  FLASH_WriteAddress     0x00000- T" T, ?& R7 b+ V+ T3 c
  2. #define  FLASH_ReadAddress      FLASH_WriteAddress, k# B" R2 U. i1 a$ C6 j; n
  3. #define  FLASH_SectorToErase    FLASH_WriteAddress
    ( W6 u, H. q9 o+ P$ L
  4. #define FLASH_SPI hspi1
复制代码
. e: W% y' V5 d8 E; w6 K  S$ L
在mian函数中定义变量:
  1. uint32_t flash_ID = 0;
      a2 I  I. W' o# H! V$ y2 d1 ~9 v
  2. /* 获取缓冲区的长度 */
    " m+ D. B# ^6 u- H
  3. #define countof(a)      (sizeof(a) / sizeof(*(a)))& z; u# y8 Z( V9 }0 H& o0 M' D( m
  4. uint8_t Tx_Buffer[] = "现在进行FLASH的读写测试\r\n";
    9 R  E. q+ n5 H2 B
  5. #define  BufferSize (countof(Tx_Buffer)-1), N* e4 V' m, i0 C$ |+ U# t9 n. `" l
  6. uint8_t Rx_Buffer[BufferSize];
复制代码

! [! W4 V/ K  Q( A& j
在main函数中进行测试代码:
  1. printf("*************this is test for coding...**********\t\n");; m( {5 |7 d4 P) q: U$ `% n& K1 O
  2.        printf("this is test code for spi1 read and write flash w25Q64 \r\n");- \$ x4 ^3 a2 [
  3.        flash_ID = SPI_Flash_ReadID();: W7 v* C, Y5 R  g- |. X
  4.        printf("\r\n flash ID is 0X%x\r\n",flash_ID);
      l* O' Y# m8 C1 B7 G" e) X
  5.        SPI_FLASH_SectorErase(FLASH_SectorToErase);      
    8 o; q8 M: [3 i) F0 G
  6.        SPI_Flash_Write(FLASH_WriteAddress,BufferSize,Tx_Buffer);0 W& {: i/ A( b5 I0 e- D1 {. b2 [
  7.        printf("\r\n 写入的数据为:%s \r\t", Tx_Buffer);
    ; i# \& j3 P) n: q' f8 S
  8.        SPI_Flash_Read(FLASH_ReadAddress,BufferSize,Rx_Buffer);$ p/ T! n3 O0 Y& T
  9.        printf("\r\n 读的数据为:%s \r\t", Rx_Buffer);$ H- V' ^3 v4 ^
  10.        /* 检查写入的数据与读出的数据是否相等 */8 R: P6 h$ e$ e  J% U- S- j
  11.        if(memcmp(Tx_Buffer, Rx_Buffer, BufferSize)==0)0 ^8 F9 @* q: [2 H
  12.        {( z1 z3 x) W- A* W
  13.               printf("写入的和读出的数据是正常的!\r\n");0 @: ]5 D* ~8 i) @% I
  14.        }" B. Z: i1 p# y% h, k: r8 ?) O) ?
  15.        printf("SPI 试验结束......!\r\n");
    7 y7 F( C4 I: H+ S/ C
  16.        printf("*************this is test end....**********\t\n");' y2 m* A# Q! L8 i+ a% j! b
复制代码
8 B* ?5 I( v! s* _1 t& z3 w  J
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管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版