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

【经验分享】STM32Cube HAL SPI

[复制链接]
STMCU小助手 发布时间:2022-4-7 14:43
一、原理图, u, e0 d- x, a2 g+ m( ~* w: p1 `2 t

3 I4 |1 p$ z( k) |7 B$ q ~`B8KDN6X~)RJC(VYCHIXTF.png & b. s4 D8 [0 g1 F9 k2 e, O

* B% `. O; m) j, e8 p4 j# {二、 CubeMX配置7 h9 ?0 E/ _- c$ }  f
2 j1 y/ w; _; P: B% q, K" U
Step1.打开 STM32CubeMX,点击“New Project”,选择芯片型号,STM32F103VETx。
7 I7 m4 V0 m4 N1 I* h/ m) q4 _  a9 U& X# a+ o
~VC35QGY$LT7]BKM36_EXDI.png ( S- Y% n6 V  J  \; l6 z# w( j

  g" D$ b# j- W Step2.选择时钟源,并配置时钟树。选择Crystal/Ceramic Resonator,并配置系统时钟为72M。
) h& o. ?7 g' j3 }: H3 m% {( B1 n( U! D7 B
WAHULLSI5C[``04N~VQW4.png
: l; r; R: V, [- x1 @( Z* t0 S6 _. n6 B! O5 N; J
0DCI2X2%V)[8A]8YSI1PJYT.png
* w& v2 o* `9 l$ L% z3 M0 Q5 o: H/ O6 j+ a% k: s! I
Step3.配置SYS,我们这里选择的是Serial Wire。(正常情况配置不配置不影响,debug可以使用。但是你不可以把这两个引脚用于其他复用功能,如果用于其他复用功能,debug就不起作用了。)( q$ O2 R# F1 x/ `

9 f# ~' y) ^" R* S* f. j {2FB@N$$U]][`3E$R83TUIV.png
2 n1 g! i; i  W3 }* k2 v7 P8 X. P7 ^
  Step4.串口配置(主要为了在串口调试助手显示读写数据),因为没有用到中断和DMA所以我们就不过多讲解。& t2 H, J% S+ o% r! @! V7 T6 o
# N+ {% _; j8 t0 O: Z/ G
4TZAP%EOLDQGG0X4]@RFJ)B.png ( y5 s( s7 J/ S8 g* V4 T( N
/ j9 w- x3 O' v' S% R7 O
step5.SPI外设配置,这里选用的SPI1。因为没有使用到中断和DMA所以只需要把硬件SPI基本参数配置好就行。
/ A) S! g# J- ^) {7 a8 `( f* g9 `' V: Z) u' V% J. k$ B
(U{3@]I@BK}G(VQ](VH[ARL.png 9 ~5 P) a- R: K  }' d" Z
9 i2 w' T2 F9 N4 @  M  y# m) g' ?
step6.因为是采用软件NSS,所以还需要配置相应的IO口。0 S( I: f; _5 t6 R5 ?
$ u: S: F+ b8 K# ^/ K0 U
VBRF{_UYCO3DPPZ[MW6M@2S.png
! A" y6 K  W$ `" I+ x$ U' ^
6 `) ]8 F; h" t5 k4 F9 ?8 n  {. k 到这里关于硬件IIC参数配置基本已经完成,只需要根据之前文章《STM32Cube HAL:GPIO输入/输出(一)》Step4-Step8,设置相关工程参数和生成代码。5 R6 N; \  l3 [* n
( E- Y1 r8 D( Z- F# \- Y( w
三、添加功能代码5 B7 `$ u' U) t' h
8 l* {  U! d% U1 x% t  T8 E+ S
1、我们等会会向串口调试助手发送数据,进行实验结果的验证。 发送数据我们采用printf函数,所有需要重定向c库函数printf到串口。注意使用时需要在keil设置中勾选微库(use mircolib),同时需要添加头文件#include <stdio.h>。2 [2 A# @3 e; f7 f- V( a' g2 s. p

& j; Z2 j' x+ W: o. Q
  1. //重定向c库函数printf到串口DEBUG_USART,重定向后可使用printf函数/ A! D' C. S7 H6 c$ y: w
  2. int fputc(int ch, FILE *f)
    $ A5 z1 T: B, w3 g# C
  3. {8 F5 U; F* ?& Q8 f) x0 a
  4.         /* 发送一个字节数据到串口DEBUG_USART */$ _2 q# ?2 k' X. h; `  @( C
  5.         HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);        
    : I( {, C* ]6 y
  6.         8 t1 N2 w6 q- G2 I
  7.         return (ch);
      [8 P5 X/ T6 T, @( R! ]  w
  8. }
    8 M1 K% t+ P) X2 h5 I& W& t
  9. . O( R& L4 d! ]& X* T2 o! e& e
  10. //重定向c库函数scanf到串口DEBUG_USART,重写向后可使用scanf、getchar等函数
    * r3 k+ u' O7 z) E6 f
  11. int fgetc(FILE *f)
    * {( S8 {  |$ ]
  12. {               
    $ a/ K7 ^( n! J+ X; g* }, M- k& F
  13.         int ch;/ K% F9 q( e! ^2 v3 [* h1 z
  14.         HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, 1000);        
    ( t9 X' X9 ^% S# U8 V
  15.         return (ch);5 D# V9 [1 d$ k5 g8 S; n; V
  16. }
复制代码
' t8 o; Q& ~+ w, Y0 B/ e0 K

8 w% N: j4 g5 L2、在gpio.h文件中添加相关的宏定义。因为是使用软件控制NSS,下面宏定义通过IO口模拟NSS信号。(NSS低电平控制,NSS高电平释放)
& I% H' B, |7 ~7 f" R3 ]. I- u2 V* `# N+ ?
  1. #define NSS_HIGH() HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET)6 O# |3 `& r5 E9 ?* }  N
  2. #define NSS_LOW()  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET)
复制代码

. S/ ~. K: Q- L1 W1 C9 z/ B3、在spi.h添加宏定义,和函数的声明。方便移植和提高可读性。
6 `7 t5 J5 N' L2 C3 y. w0 d' w! [" I6 O- j7 F( N
  1. #define FLASH_PAGESIZE         256                          //W25Q64的页面大小 ) w! A: E" a7 }: D5 b9 n9 `
  2. #define _Flash_ID        0xEF4017
复制代码
  1. extern SPI_HandleTypeDef hspi1;
    , U7 C8 }& X( ^$ c+ H' }' U
  2. void MX_SPI1_Init(void);
    " ~! N: G$ [8 M" p
  3. uint32_t SPI_FLASH_ReadID(void);
    2 _- C5 e8 ?' ?
  4. void SPI_FLASH_WriteEnable(void);
    ) f  t+ N/ a! X0 V8 U3 _! N
  5. void SPI_FLASH_WaitForWriteEnd(void);
    9 E  O; r, E4 O( y. h6 y- o
  6. void SPI_FLASH_SectorErase(uint32_t SectorAddr);
    . }7 n7 o' l- M
  7. void SPI_FLASH_PageWrite(uint8_t * pBuffer, uint32_t WriteAddr,uint16_t NumByteToWrite);
    " q! I8 i# N" L* V7 S# n
  8. void SPI_FLASH_BufferWrite(uint8_t * pBuffer, uint32_t WriteAddr,uint16_t NumByteToWrite);
    " Y1 W. P, Q# n4 Q3 C+ G
  9. void SPI_FLASH_BufferRead(uint8_t * pBuffer, uint32_t  ReadAddr,uint16_t NumByteToWrite);
复制代码
; h' ?; N' c. E2 D3 o
然后在spi.c中实现声明函数的具体功能。$ F9 Q; N! F$ N. Q* x# o0 q4 r

+ e4 B$ f5 a* ]( \+ `
  1. /*读取制造商和设备ID*/; }/ B% Y) ]4 i
  2. uint32_t SPI_FLASH_ReadID(void)$ s; e7 a& I* f4 l% X3 ^
  3. {6 y& u3 r. Q2 y- R% V
  4.         uint8_t W25X_JEDEC_ID=0X9F;) r; w1 E7 S. Z, V4 L
  5.         uint8_t temp0[3];
    8 [7 w7 j6 |4 T! S, j
  6.         uint32_t temp;1 L2 b( D8 U$ I! R% E  B
  7.         
    , D# w! F& \, h* h; ^
  8.         NSS_LOW();//选择FLASH:NSS低电平
    9 V  R8 N9 j6 ?: d* `* }: a
  9.         7 e! u9 W$ N# Z! `; L4 w% o
  10.         HAL_SPI_Transmit(&hspi1,&W25X_JEDEC_ID,1,10);//发送指令' a1 H3 M" [6 A4 v9 A
  11.         
    % {' ]/ j* F- s. |" u$ e$ J* ~& @5 I
  12.         HAL_SPI_Receive(&hspi1,temp0,3, 10);//读取制造商和设备ID: m- j% h( _8 ^. x( f; p
  13.         
    ! \- k; W/ t( {# B
  14.         NSS_HIGH();//停止信号 FLASH:NSS高电平$ P4 E: m+ k: u' Y/ Z3 W5 Z! u- S
  15.         & ~. Z* j# c" i" |" @0 F4 Q5 \
  16.         temp=(temp0[0])<<16|(temp0[1]<<8)|temp0[2];//把数据组合起来,作为函数的返回值; e5 F+ D" [4 t  |

  17. : _8 X  C" ^  u5 B; O$ v
  18.         return temp;7 |# O3 x4 Y& G8 X2 v1 {6 S4 s8 C
  19. }9 _+ E$ X0 f: q

  20. 4 T6 B7 t8 Q! l# Q) z
  21. /*写使能*/4 B' x9 j6 D5 ~8 O$ U
  22. void SPI_FLASH_WriteEnable(void)! q/ i; P! g- I0 `7 ?8 e- l- M
  23. {) q3 g  S9 O& }3 n& Y& F
  24.         uint8_t W25X_WriteEnable=0x06;
    ( d! @0 \6 ?6 q% Q" i6 s; t3 l4 {
  25.         
    + S& c: i5 k' J+ K
  26.         NSS_LOW();//选择FLASH:NSS低电平
    1 h  r: D/ b# s; Z5 c; P
  27.         
    & {$ o) K8 D" z" V
  28.         HAL_SPI_Transmit(&hspi1,&W25X_WriteEnable,1,10);//发送指令, e( o! f) }$ K1 `
  29.         
    7 c  m! J. W9 }
  30.         NSS_HIGH();//停止信号 FLASH:NSS高电平! G; X9 R6 ?( [! \/ m5 a( B
  31. }
    & l& e" q, q; s, I$ Z

  32. & f3 b; y. `( t# l1 `9 M
  33. /*扇区擦除*/% B* }1 u6 j8 o: W0 X2 N3 [  P( d
  34. void SPI_FLASH_SectorErase(uint32_t SectorAddr)
    $ ~5 E# F5 x: M4 }: [( s/ l
  35. {
      z$ k. }& |! P
  36.         uint8_t W25X_SectorErase=0x20;3 N4 w; T; Q# P" g/ R) [
  37.         uint8_t temp1,temp2,temp3;6 C) x& G# z6 m# Y- b( Y
  38.         * ]9 d( m/ T) X! {9 b, K6 _
  39.         SPI_FLASH_WriteEnable();//写使能
    8 L% O) c; K% I9 h9 G
  40. , t. X5 Q6 b, ]$ A3 L: }
  41.         NSS_LOW();//选择FLASH:NSS低电平
    1 D0 e8 e3 Z: c! V- {$ k
  42.         
    # l% v0 a6 ]0 }9 y2 G
  43.         HAL_SPI_Transmit(&hspi1,&W25X_SectorErase,1,10);//发送指令
    . a: l( d2 Q2 O, y. R7 E% F6 j& \( N
  44.         ( i3 G7 s; u6 P* H
  45.         temp1=(SectorAddr&(0xFF0000))>>16;//发送地址5 R) z) L1 m9 r2 K( \3 I) K' s
  46.         HAL_SPI_Transmit(&hspi1,&temp1,1,10);. C% z( Q  b6 a9 _- u
  47.         temp2=(SectorAddr&(0x00FF00))>>8;9 ]3 ^- \4 e5 e7 x) @; U
  48.         HAL_SPI_Transmit(&hspi1,&temp2,1,10);
    ! C/ M/ e: [2 [3 W* r4 ^8 N/ n
  49.         temp3=(SectorAddr&(0x0000FF));
      m' |+ ~+ a* i) ]/ {, T- F6 b
  50.         HAL_SPI_Transmit(&hspi1,&temp3,1,10);0 Y9 c) [8 k" w: |: i  o
  51.         NSS_HIGH();//停止信号 FLASH:NSS高电平5 D+ p9 W- J' I1 _
  52.         " m/ u# C* c7 T+ T
  53.         SPI_FLASH_WaitForWriteEnd();//等待擦除完毕
    0 ]3 x  ~, {0 ?; h* v! x4 v
  54. }
    5 n7 m8 }  a/ D; K
  55. ' Z7 A" t4 {( Y' H& l
  56. /*等待 WIP(BUSY) 标志被置 0,即等待到 FLASH 内部数据写入完毕*/4 l: a4 ]1 x3 d8 c, y
  57. void SPI_FLASH_WaitForWriteEnd(void)
    - W. u- V4 G" P
  58. {
    5 x$ k8 j; z1 {  s
  59.         uint8_t W25X_ReadStatusReg=0x05;
    ( K2 u" J6 Q2 M* M. n
  60.         uint8_t temp;) b) V  _/ G/ f+ w  K) I, g
  61.         
    3 R; J* B& h  u# r6 E- F* H
  62.         NSS_LOW();//选择FLASH:NSS低电平" q# T+ {. c- ]  n0 `
  63.         3 b) P3 i% F  }+ O1 o
  64.         HAL_SPI_Transmit(&hspi1,&W25X_ReadStatusReg,1,10);//发送指令
    $ N6 S. ~$ @7 \' q' ?: R
  65.         do
    1 s/ K/ o" r2 i. A# q4 l! u  `
  66.         {
    6 e% K2 B, f/ F0 I- K
  67.                 HAL_SPI_Receive(&hspi1,&temp,1,10);// 读取 FLASH 芯片的状态寄存器4 [6 D3 u5 {7 r9 o, _9 f, p7 Y
  68.         }
    2 R9 s) @; T  S: b. T* F
  69.         while((temp&0x01)==1);. Y+ ~1 Q. H; Q, p
  70.         
    " o. ^9 H  c- z( g  a6 M
  71.         NSS_HIGH();//停止信号 FLASH:NSS高电平
    4 a8 O! G3 F% x$ H/ f* ~5 s
  72. }
    - C' T8 c7 M: Q) t/ G5 \
  73. * w: K+ p# T1 z- d! l; D
  74. /*在FLASH的一个写循环中可以写多个字节,但一次写入$ S' n/ a9 I+ ]9 K
  75. 的字节数不能超过FLASH页的大小,W25Q64每页有256个字节*/
    8 ~, u# k7 U) R: P
  76. void SPI_FLASH_PageWrite(uint8_t * pBuffer, uint32_t WriteAddr,uint16_t NumByteToWrite)
    2 f! l! P1 w( |3 i5 G. V0 S
  77. {
    - ~  G7 R, @' U, [( Q
  78.         uint8_t W25X_PageProgram=0x02;
    8 |8 m9 Z1 H- N% t
  79.         uint8_t temp1,temp2,temp3;1 ]) I+ w, b+ d
  80.         
    3 ~- r7 F& [# G4 t, Y4 J
  81.         SPI_FLASH_WriteEnable();//写使能
    * C3 s& o2 p; k, A# C
  82.         ; p* o4 I3 q1 G, G$ |& c: Z
  83.         NSS_LOW();//选择FLASH:NSS低电平
    ( n- |. Z: b2 Q* _. V
  84.         
    ! O+ \. S8 F8 {* h7 w' n+ E& n) F
  85.         HAL_SPI_Transmit(&hspi1,&W25X_PageProgram,1,10);//发送指令
    8 C( Q; D' W3 g! d( B
  86.           d+ D$ S7 T: p2 w. t- o
  87.         temp1=(WriteAddr&(0xFF0000))>>16;//发送地址
    3 ?- R" _8 _7 @' r
  88.         HAL_SPI_Transmit(&hspi1,&temp1,1,10);8 i8 p8 S: f+ a& A
  89.         
    + Z" j& D: H. C' [; C
  90.         temp2=(WriteAddr&(0x00FF00))>>8;2 ?6 k* e6 L$ g. t! g. U
  91.         HAL_SPI_Transmit(&hspi1,&temp2,1,10);' A) e7 O0 m3 l3 T# R; q
  92.         
    # N* g/ `: r: i
  93.         temp3=(WriteAddr&(0x0000FF));6 P7 r9 _  B3 S: {5 ^
  94.         HAL_SPI_Transmit(&hspi1,&temp3,1,10);/ R& c* u' U: J6 h; ^; `
  95.         $ v! C3 U3 s0 c2 I( L  ^3 [' v
  96.         HAL_SPI_Transmit(&hspi1,pBuffer,NumByteToWrite,10);//发送数据
    / s: v7 O8 O7 `4 F
  97.         
    % g# ]4 g+ H  G+ i
  98.         NSS_HIGH();//停止信号 FLASH:NSS高电平
    2 u" }0 m8 _  f- F* O
  99.         9 F; z2 i3 ]  Z. V5 w6 C
  100.         SPI_FLASH_WaitForWriteEnd();//等待写入完毕+ p% e5 l! H2 Z4 b
  101. }
    # R& x7 N  t, g: b
  102. . h: p4 q9 T! |& X) F
  103. /*不定量数据写入*/. f4 y1 e( O- ]. g2 L4 Q
  104. void SPI_FLASH_BufferWrite(uint8_t * pBuffer, uint32_t WriteAddr,uint16_t NumByteToWrite)8 z0 O: {6 G! v3 x( w1 O
  105. {2 e7 l: `8 y! G
  106.   uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;' B1 Q  F1 n( X- T, g. L) D5 R+ W

  107. # M) s% h( S. i2 E" s  L
  108.   Addr = WriteAddr % FLASH_PAGESIZE;//判断写入的首地址是否与EEPROM页的首地址对齐,0为对齐
    - P( V+ t" r2 B/ _$ X, W
  109.   count = FLASH_PAGESIZE - Addr;//计算从写入的首地址需要写多少数据才能填满当前页
    $ s  i, {$ x1 F9 f
  110.   NumOfPage =  NumByteToWrite / FLASH_PAGESIZE;//计算写入数据需要写几个完整页(地址对齐的情况)
    ! A2 c8 R; T( F* l/ n& G0 w
  111.   NumOfSingle = NumByteToWrite % FLASH_PAGESIZE;//计算写完完整页剩下的数据个数(地址对齐的情况)
    ; m1 v$ ~! e7 h- X  S& ~* J
  112. + J* ?9 T5 g: a$ r. D+ U
  113.   if(Addr == 0) //判断写入的首地址是否与页地址对齐
    & _; W% ?6 g; W' \
  114.   {! z! F- |2 d5 P" ^
  115.     if(NumOfPage == 0) //如果页对齐,判断数据是否不满一页) Y9 W6 D, F9 y2 ]
  116.     {. R% N3 w0 b9 U9 O1 F) m; `) z" X
  117.       SPI_FLASH_PageWrite(pBuffer,WriteAddr,NumOfSingle);//如果不满一页,直接写入数据
    1 \: r# b3 o: a  k
  118.     }' [: }  b5 @4 f% r
  119.     else  //在数据满一页的情况下,通过地址自增方式,循环写入数据(页写入的形式)
    * [# g! M5 N- F
  120.     {4 u% G! B3 d! O2 Q+ x" w
  121.       while(NumOfPage--)//循环写入数据:先写入完整页
    * |; F/ B6 |5 p2 |) V6 K: ^% e
  122.       {" o: @, m& H) `6 N
  123.                                 SPI_FLASH_PageWrite(pBuffer, WriteAddr, FLASH_PAGESIZE);
    2 v4 m5 P+ @3 h9 n8 b4 U
  124.         WriteAddr +=  FLASH_PAGESIZE;6 T+ @# i" k4 g) C
  125.         pBuffer += FLASH_PAGESIZE;4 n- P' c$ P  j' y( x# g- s
  126.       }
    + y0 S4 W& P2 a! |0 P1 q- q

  127. 0 t1 {: `! y9 P) Z" V% [
  128.       if(NumOfSingle!=0)//循环写入数据:再写入不满一页的数据
    # @( p3 ?% U& `1 n1 r/ w# d( \
  129.       {$ r# E! p7 W8 k" n) M* x7 A& u+ p
  130.                                 SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
    9 H7 e6 J( W2 e" V& ~2 q
  131.       }7 T4 Q! S" |6 I3 |& ~1 L
  132.     }) g8 O& e. h4 l- K1 a) R
  133.   }5 Q/ c5 V4 N2 d" U
  134.   else
    ) Z; K5 Y5 S7 k' `. c8 J
  135.   {
    # z- c' |! i2 h* {
  136.     if(NumOfPage== 0) //如果页不对齐,判断数据是否不满一页: j4 I; H  l: z( L
  137.     {3 u4 P9 k1 K- o5 V
  138.                         if(NumOfSingle<=count)//如果不满一页,判断数据是否跨页
    - w9 S. Q, `+ }$ K  d8 ^
  139.                         {         
    / y5 B0 g! C# |% k- J, r, L
  140.                                 SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);//如果不跨页,直接写入数据
      Y: `: i+ }" Q
  141.                         }
    ; v0 }# e/ p, ?$ a' h+ K0 V, q
  142.                         else
    0 ?; {5 s; R% h; l
  143.                         {( e/ ~# ]  ?; N! C8 E
  144.                                 SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);//如果跨页,先写首页数据,再写次页数据
    ) z% O6 {0 f& d
  145.                                 SPI_FLASH_PageWrite(pBuffer+count, WriteAddr+count, NumOfSingle-count);& F. Q5 x: x& d) E$ U
  146.                         }6 n: d3 S% O: z+ O1 r8 m8 \& t
  147.     }
      F" S! X/ L+ R" Z
  148. 2 {$ M1 m: o1 V  v; C
  149.     else
    3 ~/ `$ d' e/ w: v
  150.     {! _9 t  n8 B- s
  151.                         /*如果数据满一页,对数据进行分离*/
    - }$ U4 l0 A& y7 @
  152.       NumByteToWrite -= count;//扣除第一页数据个数/ I# c0 |! A; l9 |3 |% w1 k
  153.       NumOfPage =  NumByteToWrite / FLASH_PAGESIZE;//计算写入数据需要写几个完整页# q' t, m( C: }$ P
  154.       NumOfSingle = NumByteToWrite % FLASH_PAGESIZE;        //计算写完完整页剩下的数据个数! \* t! |& T9 ^; p# G, F

  155. ) z2 P* H/ q: L  F& t- Y
  156.       if(count != 0)6 B  H( f" _: }& G! T6 N
  157.       {  3 j6 |5 Z) z! e, G" V
  158.         SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);//写入首页数据. t" z. b- h* R# ^- f4 D
  159.         WriteAddr += count;//写地址自增6 b1 p% X& T0 z5 f6 u: U% K0 _
  160.         pBuffer += count;//缓冲区指针自增/ l& L* g4 Q* t, [+ v7 x
  161.       }
    7 c. G7 R4 c0 ?
  162. 5 _( o, c: G/ `. }7 H; q* s6 o
  163.       while(NumOfPage--)//依次写入完整页的数据
    ) [. _$ w& W5 K; ^! P# W
  164.       {; r) N! k  ?. I, t* J
  165.                                 SPI_FLASH_PageWrite(pBuffer, WriteAddr, FLASH_PAGESIZE);
    2 G& z  f- D4 b( u& I# j& h
  166.         WriteAddr +=  FLASH_PAGESIZE;//写地址自增
    3 B) _5 J3 K' n8 ]6 B  v4 y- K
  167.         pBuffer += FLASH_PAGESIZE; //缓冲区指针自增 ' |6 }# a0 ~5 D  p: [3 z0 G2 T5 O
  168.       }
    4 [) _- V3 o/ H+ @5 k& x; f" q
  169.       if(NumOfSingle != 0)//判断最后一页的数据是否是填满完整一页的1 k& G& Z, h. Q  w0 H, f, J
  170.       {
    4 n3 z0 E( |0 T7 s% o) C' R6 |
  171.                                 SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);//写入最后一页的数据) @, v. |: [" [" U9 q
  172.       }
    / z9 P! G* m2 P, i1 j2 m% U
  173.     }
    $ s0 E0 s8 Z6 S6 P" |
  174.   }  
    ; H. k. v! ?% B0 {
  175. }
    1 f8 l9 ^. C9 G' B% A3 S

  176. ( p6 `( ?; L: z6 b4 u! h% g
  177. /*读取FLASH数据,读取的数据量没有限制*/
    $ z2 S5 |8 V2 ~2 _" \7 V
  178. void SPI_FLASH_BufferRead(uint8_t * pBuffer, uint32_t  ReadAddr,uint16_t NumByteToWrite)% f- t5 j2 W6 p0 u% c9 f# t
  179. {
    8 ^: q" U8 V# U3 a8 e/ }! ?
  180.         uint8_t W25X_ReadData=0x03;6 z9 \2 _- p0 v1 P3 p: l$ l
  181.         uint8_t temp1,temp2,temp3;
    ) {* ]" }" U/ W0 g6 e! D
  182.         ( M* O% d3 C! i! c# @/ e
  183.         SPI_FLASH_WriteEnable();//写使能6 b: ]4 U8 j( Z
  184.         - h6 e) j7 B) p. d. R! [1 ^
  185.         NSS_LOW();//选择FLASH:NSS低电平
      Y! h. J4 ?2 Y( {, J
  186.         & n$ l7 q+ Y) L
  187.         HAL_SPI_Transmit(&hspi1,&W25X_ReadData,1,10);//发送指令
    9 j$ E- ]  v' X, t+ ^! W% S0 f7 n
  188.         + }- c! b( a( @: o% i; L) X# n1 ~: S
  189.         temp1=(ReadAddr&(0xFF0000))>>16;//发送地址
    / o; G; O; l  C% g7 a! q' q9 y
  190.         HAL_SPI_Transmit(&hspi1,&temp1,1,10);
    4 t( L( q1 I/ x9 X9 r+ _5 l
  191.         4 B/ R% _3 B: g6 k0 r! a8 t* s1 ~! K
  192.         temp2=(ReadAddr&(0x00FF00))>>8;# j# `. {6 I: O* T# F
  193.         HAL_SPI_Transmit(&hspi1,&temp2,1,10);
    , c' z* m/ S8 ^) t$ A7 M
  194.         
    0 g, _: m* h- m
  195.         temp3=(ReadAddr&(0x0000FF));' ^1 a6 f" G# ?- P% Z- o
  196.         HAL_SPI_Transmit(&hspi1,&temp3,1,10);
    ' U2 S( q0 |! D9 ?2 W& m
  197.         3 r( w/ q  f3 Y" r! W9 R
  198.         HAL_SPI_Receive(&hspi1,pBuffer,NumByteToWrite, 10);//这里超时时间不能设置为1,否则会读取错误
    & f" a& t( ~) ?9 s8 a5 z
  199.         
    , w; y, p2 i: d* c$ P
  200.         NSS_HIGH();//停止信号 FLASH:NSS高电平1 V7 _3 f9 L* v% L4 r8 w# @+ @% c$ K
  201. }
    + |2 b% P/ p2 x- a7 y& ~
复制代码
2 g: }. f4 D0 T) U, i7 O' v; ]
3、最后在main.c文件中,编写测试代码进行验证。: x! h8 @* L& i

+ B* p  G3 e  J3 W6 D6 s
  1. /*测试代码涉及到变量,宏定义*/
    : y+ c8 f* U, I% Y
  2. /* 获取缓冲区的长度 */: I% s- y! p0 ^/ O
  3. 6 Y% A4 }- m1 w3 L4 A$ d4 I
  4. /*sizeof():数组占用字节除以数组类型所占字节,结果为数组元素个数*/
    , K! T" n6 W' @8 Q& U
  5. /*使用方法:sizeof(数组名)/ sizeof(数组类型名) */- Y  c' D& j/ y6 r: t5 R
  6. #define countof(a)      (sizeof(a) / sizeof(*(a)))0 v7 I1 |' k, C0 f) J! L4 w
  7. #define  BufferSize (countof(Tx_Buffer)-1)( _- d' b4 Q) l6 j: V# v3 @7 [4 N) a

  8. 0 m8 |" V7 [, D
  9. #define  FLASH_WriteAddress     0x000006
    / k; n; f& L% T- M1 r  n/ b2 [
  10. #define  FLASH_ReadAddress      FLASH_WriteAddress& g2 Y7 [9 t3 T& G9 a& L) n
  11. #define  FLASH_SectorToErase    FLASH_WriteAddress9 w- y; z. D4 p# O
  12. " M8 b6 j( q2 A, ^1 ?
  13. uint8_t Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength);
    9 ^: x5 U3 Y8 o0 r! r4 _
  14. / H  l% {& q7 Z' l' U) D+ V
  15. uint32_t Flash_ID;- T$ J5 V* U; z/ G
  16. uint8_t CMP_RES;
    # r+ e* ]+ I9 x! q: X& n
  17. uint8_t Tx_Buffer[] =
    8 G9 Q, y* f1 X4 n: @. P2 N
  18. "采薇采薇,薇亦作止。曰归曰归,岁亦莫止。靡室靡家,猃狁之故。不遑启居,猃狁之故。\
    " p+ T1 |& O3 l& R4 A! {" M3 H/ s
  19. 采薇采薇,薇亦柔止。曰归曰归,心亦忧止。忧心烈烈,载饥载渴。我戍未定,靡使归聘。\! O1 }4 K# O0 `% w, V$ h
  20. 采薇采薇,薇亦刚止。曰归曰归,岁亦阳止。王事靡盬,不遑启处。忧心孔疚,我行不来!\& p. @7 r4 l& e: x7 g) |
  21. 彼尔维何?维常之华。彼路斯何?君子之车。戎车既驾,四牡业业。岂敢定居?一月三捷。\; K# v; _# R) q
  22. 驾彼四牡,四牡骙骙。君子所依,小人所腓。四牡翼翼,象弭鱼服。岂不日戒?猃狁孔棘!\
    5 A1 U4 W* y0 r& |( K
  23. 昔我往矣,杨柳依依。今我来思,雨雪霏霏。行道迟迟,载渴载饥。我心伤悲,莫知我哀!";5 T  B& f4 m% e7 }  a' H
  24. uint8_t Rx_Buffer[BufferSize];
    * i: I- {" y' q' [, g% l9 E
复制代码
  1. /*比较函数,用于比较写入和读取的数据是否一致*/
    & A0 W, }( k7 k6 c0 U! X/ z& l
  2. uint8_t Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength). l) j' z0 ^: `# W
  3. {9 A( R& z& G5 ^, @! V' `
  4.   while(BufferLength--)
    8 I8 f/ I% _/ c" O, H
  5.   {8 V3 T8 R( e4 s1 s& C$ z
  6.     if(*pBuffer1 != *pBuffer2)$ `, S# ?0 K# C& k+ i
  7.     {
    6 U, |1 ]; _( Z& n9 |, t, e
  8.       return 0;5 s6 l5 z+ L: L/ W
  9.     }
    / s4 L$ Q. \% Y7 e  l

  10. 6 E8 f, e3 R9 C- i7 ?( `9 r3 k
  11.     pBuffer1++;
    9 I4 h9 T5 L; Q8 X
  12.     pBuffer2++;5 d( f* I! O4 U& k
  13.   }% k* D, h8 ]  G9 h7 q, B, y- S7 Q* U
  14.   return 1;6 h3 a- n8 a8 ^, _4 S
  15. }
    9 O7 L/ m: C- P' @1 |* }
  16. /*在主函数编写测试代码:ID验证,读写比较*/
    , x' p% e% C3 N' i3 E
  17. Flash_ID=SPI_FLASH_ReadID();//读取Flash ID
    ; I7 n1 `6 j9 y" i. e
  18.         if (Flash_ID == _Flash_ID) //判断读取的ID和数据手册的ID是否一致7 m! v9 s6 z2 Z8 b( w
  19.         {        ( i" q7 J& n1 u0 ^0 I* c
  20.                 printf("\r\n系统检测到SPI FLASH W25Q64 ! FlashID:0x%X\r\n",Flash_ID);6 S! r7 {4 |6 P6 ^* t4 h# Z
  21.                
      O% C  F& x) y. U& k8 H) d
  22.                 /* 擦除将要写入的 SPI FLASH 扇区,FLASH写入前要先擦除 */
    2 n( P: v, \3 L$ r3 P" Z
  23.                 SPI_FLASH_SectorErase(FLASH_SectorToErase);                  8 o$ `- U5 ?* j: Z
  24.                
    * {  U2 {3 P; a$ B
  25.                 /* 将发送缓冲区的数据写到flash中 *///
    * y& D7 c  u. [0 J) U' C
  26.                 SPI_FLASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize);; g* R8 M/ V" E3 S
  27.                 printf("\r\n写入的数据为:\r\n%s", Tx_Buffer);" x8 j) @/ V# k4 A, |6 l, A8 d
  28.                
    0 h8 v+ L8 ~, S8 s5 W
  29.                 /* 将刚刚写入的数据读出来放到接收缓冲区中 */5 ]# ~& ^+ b1 g" i
  30.                 SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);! A0 R$ A- R& i6 N
  31.                 printf("\r\n读出的数据为:\r\n%s", Rx_Buffer);
    7 \% h5 q. E5 \7 f, z
  32.                
    # E! e! t, R3 H: {! g& K. I
  33.                 /* 检查写入的数据与读出的数据是否相等 */
    ! d! `& \) Y3 g" @1 ?1 n, [
  34.                 CMP_RES= Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);7 c0 R) n: z6 H/ h; C; `! n1 X3 d, T9 f
  35.                 9 {) S- b5 X/ V6 g6 M9 S
  36.                 if(CMP_RES)/ m0 W7 Z1 l: T6 p2 o- `3 G0 P
  37.                 {    , q, n1 [$ {! Y# d; Z3 j
  38.                         printf("\r\n16M串行flash(W25Q64)测试成功!\n\r");
    * S8 ^3 q* H+ z6 y
  39.                 }
    ; e3 H  I( V! B- W, [2 g
  40.                 else) |# D" T- J7 l$ ]4 s: o
  41.                 {        
    4 i" u9 U# N& `; h; ~" S! C3 [
  42.                         printf("\r\n16M串行flash(W25Q64)测试失败!\n\r");* v0 O4 t( S" @
  43.                 }) M5 N( B' e! ^: ?! P% [5 `% N
  44.         }// if (FlashID == sFLASH_ID)' J4 n% i, O) @' u
  45.         else
    % ]  _" s, P: e. a1 Y
  46.         {    ' L, w' o: T: G" J) n
  47.                 printf("\r\n获取不到 W25Q64 ID!\n\r");
    : m* I) _- ~/ ^% Y7 k, y: `8 ~
  48.         }                        
复制代码

2 E) G- e6 P4 n( H" `2 J- p; P9 m) m, a9 c
调试过程碰到几个问题,也是比较无脑的。( q% G! B; L6 |& N
1、编写页写入的时候,常规流程:写使能->NSS拉低->发送页编程指令->发送地址->写入数据。
0 p7 ^9 f3 C& P. }6 D& O我忘记写发送页编程指令,导致了每次读取的时候,数据前面总是多出一些空白。(数据内容:; F" {5 _+ ?5 R- F& q7 W
空格(发送内容越多,空格越多)+数据)。刚开始在论坛看到了,一个网友也出现类似情况,评论区,都是说等待函数加个延时就可以了,试了下还是不行,后来才发现是指令忘记写了。* @; e$ D5 Y3 X* j1 A. s
0 C8 h, R; s/ h; L( c
  1. HAL_SPI_Transmit(&hspi1,&W25X_PageProgram,1,10);//发送指令
复制代码

! r( P) r) b; s! @# J0 g2、还出现一个问题,读取大量数据的时候(超过一页),后面的内容总是读不全。刚开始以为是自己本身写入就有问题,后来用了例程读取我写入的数据,可以正常读取。基本可以锁定是读取函数的问题,看了一下整个读取的流程没有问题,唯一需要更改就是库函数自带的接收函数的参数:超时时间的设置。果然把原先设置的1改成10就可以正常读写了。(因为这个原因,我索性把发送的超时时间也都设置为10,直接设置为1也会正常的就是了。)" s1 G4 p7 x0 _% \( Y
- r7 e* Y% _/ X
  1. HAL_SPI_Receive(&hspi1,pBuffer,NumByteToWrite, 10);
复制代码
1 u. d4 m( V0 x) z& j
3 `( p  ^/ p3 o: a1 @

: L) T) M/ K9 H7 @+ Z! N8 ?* B: G
[)2DJ%}K07NF8IKA]B7442L.png
JG7BVI6$2D$Y%P43UAGZY_V.png
P@_1HFM(@P86BV2P@7P{)RU.png
赞 收藏 评论0 发布时间:2022-4-7 14:43

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版