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

【经验分享】STM32Cube HAL SPI

[复制链接]
STMCU小助手 发布时间:2022-4-7 14:43
一、原理图
" K/ o5 v- L/ a' i# n* l" R% P; M- e; a& Z  _
~`B8KDN6X~)RJC(VYCHIXTF.png # Y8 ?, ^; e+ ]+ i- i0 W

! g# T3 N; l' a二、 CubeMX配置
' ^8 f4 M# \$ d0 |% ?. {! j" q4 Z' m6 m% N4 E+ O8 j' r
Step1.打开 STM32CubeMX,点击“New Project”,选择芯片型号,STM32F103VETx。
$ b3 o3 H, K: S8 j. y) s. P3 D; E- t. o+ g$ P
~VC35QGY$LT7]BKM36_EXDI.png - b9 U( I$ O. x  s" i, Z3 O) o6 F

' A% Y1 y" }& ]* u! J/ S Step2.选择时钟源,并配置时钟树。选择Crystal/Ceramic Resonator,并配置系统时钟为72M。
, t5 G  t& p! C! }
( y' w, V* i; c6 i: @' n WAHULLSI5C[``04N~VQW4.png
9 p7 _9 \9 |% C3 L
" I) P8 m, u, ^0 n 0DCI2X2%V)[8A]8YSI1PJYT.png & D" D5 X5 N& B9 N
1 g2 V2 s1 d5 E4 {$ `
Step3.配置SYS,我们这里选择的是Serial Wire。(正常情况配置不配置不影响,debug可以使用。但是你不可以把这两个引脚用于其他复用功能,如果用于其他复用功能,debug就不起作用了。)
. _+ i: [; @# Y7 e) y* O; L6 N& Y3 b; v" }) o6 O; Q, f. }5 t2 H
{2FB@N$$U]][`3E$R83TUIV.png + t& N4 h! Z' ?9 @+ J5 B
8 B& d, {6 c# d3 k0 @9 J  Q
  Step4.串口配置(主要为了在串口调试助手显示读写数据),因为没有用到中断和DMA所以我们就不过多讲解。
( L( w4 @/ d* l6 m
/ T  H3 s" O+ i2 B2 h9 s% R% p' i 4TZAP%EOLDQGG0X4]@RFJ)B.png ! D3 f, \" u( Q1 S* O

8 r2 I' ?9 B5 K& a step5.SPI外设配置,这里选用的SPI1。因为没有使用到中断和DMA所以只需要把硬件SPI基本参数配置好就行。. L; q0 E! v. _' }% P

& e; H& j2 ]3 M0 ^ (U{3@]I@BK}G(VQ](VH[ARL.png ( M6 c" r$ z, I  ]8 [) V2 x  S
9 C- Z' L2 D, M8 b/ M4 p
step6.因为是采用软件NSS,所以还需要配置相应的IO口。3 x3 b9 ^" k7 w- I& D

% p! [8 f* q3 ~' E( I. b" C VBRF{_UYCO3DPPZ[MW6M@2S.png
  L% z3 V; z+ Q* S& t7 Q! G
) G! H# D0 u/ A) b* l 到这里关于硬件IIC参数配置基本已经完成,只需要根据之前文章《STM32Cube HAL:GPIO输入/输出(一)》Step4-Step8,设置相关工程参数和生成代码。
0 B: Q( C9 A( q4 ~1 ^! A8 B- [( u! A, T# g, E; B/ v' C) w
三、添加功能代码
. _) W1 I9 C4 y8 e
* q! a, I5 U) d1、我们等会会向串口调试助手发送数据,进行实验结果的验证。 发送数据我们采用printf函数,所有需要重定向c库函数printf到串口。注意使用时需要在keil设置中勾选微库(use mircolib),同时需要添加头文件#include <stdio.h>。
0 \" m) z$ P9 [0 ^! m4 G2 v# N9 c2 N/ K' H
  1. //重定向c库函数printf到串口DEBUG_USART,重定向后可使用printf函数/ q. o1 ]7 m: h! D3 f
  2. int fputc(int ch, FILE *f)
    - D$ v) U; F# i" U- s4 t
  3. {
    ! @% l9 g! _/ Y3 d2 w& l; U( F
  4.         /* 发送一个字节数据到串口DEBUG_USART */
    - X3 U; i1 Z! o( F. a+ a6 D
  5.         HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);        
    - P3 v2 a% S' O, j( M
  6.         
    & K" s. a+ D. C7 l
  7.         return (ch);
    3 D. `' }  ?: H: g
  8. }
    ' V, s. t! O& a4 X) m9 |$ S
  9. * b9 M& ~( X# Q  r( `  f
  10. //重定向c库函数scanf到串口DEBUG_USART,重写向后可使用scanf、getchar等函数
    8 l; `, N" ^1 j- |
  11. int fgetc(FILE *f)9 Q9 w, B  _+ B  b1 h
  12. {               
    + E5 @# Q2 \, A% U8 |& N% [# ^
  13.         int ch;
    6 h4 o9 R4 C; x' M  \0 ]
  14.         HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, 1000);        3 p* e5 @+ {& [% s& A
  15.         return (ch);
    # ?! h: z5 a! y/ f! s1 K9 @9 }
  16. }
复制代码
6 A: x: C0 m. p4 ^6 }# p( L8 O

2 d5 A1 Y' ~& K& k2、在gpio.h文件中添加相关的宏定义。因为是使用软件控制NSS,下面宏定义通过IO口模拟NSS信号。(NSS低电平控制,NSS高电平释放)
# S* C7 N" b1 A* Z8 U3 T& R7 `; w2 I# p  y( _
  1. #define NSS_HIGH() HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET)
    - \4 x( H/ U' c, m0 g4 u+ b& j
  2. #define NSS_LOW()  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET)
复制代码
9 L# ~6 a0 ~. D
3、在spi.h添加宏定义,和函数的声明。方便移植和提高可读性。% Z) f+ N! O/ b1 G. y" Q6 ]- ]. w! y
9 i7 `# g1 I$ U( f* T
  1. #define FLASH_PAGESIZE         256                          //W25Q64的页面大小 . y9 m% w, _. Q% V% U8 J7 G3 h
  2. #define _Flash_ID        0xEF4017
复制代码
  1. extern SPI_HandleTypeDef hspi1;
    . o$ j% _4 l- G7 B: c
  2. void MX_SPI1_Init(void);
    $ c8 @% M( x2 [% t! L8 U* e
  3. uint32_t SPI_FLASH_ReadID(void);
    ) X' e. Y9 ?& V5 W8 R
  4. void SPI_FLASH_WriteEnable(void);& h2 L( C. T4 U. r7 a. J- ?$ c
  5. void SPI_FLASH_WaitForWriteEnd(void);
    2 p4 {& C: N7 k& P  y; Y
  6. void SPI_FLASH_SectorErase(uint32_t SectorAddr);
    ; V' X7 h" _" p8 U6 A: z
  7. void SPI_FLASH_PageWrite(uint8_t * pBuffer, uint32_t WriteAddr,uint16_t NumByteToWrite);- I1 b& }' x/ u$ ^: Z7 u6 ^
  8. void SPI_FLASH_BufferWrite(uint8_t * pBuffer, uint32_t WriteAddr,uint16_t NumByteToWrite);
      b! s6 ~5 `' n- s, F
  9. void SPI_FLASH_BufferRead(uint8_t * pBuffer, uint32_t  ReadAddr,uint16_t NumByteToWrite);
复制代码

$ {& b% t! @! Y, }9 I6 z 然后在spi.c中实现声明函数的具体功能。
; f, f9 W0 f" W9 h, t3 T
5 y( I. U* t- V) M0 h
  1. /*读取制造商和设备ID*/
    9 T7 ]# g2 M4 N  }) H
  2. uint32_t SPI_FLASH_ReadID(void); A2 k) }) V' s$ U8 D
  3. {
    6 a! n1 B* c) ]7 W$ v/ _" W
  4.         uint8_t W25X_JEDEC_ID=0X9F;
    , u# j; f4 E7 g: S7 ]1 e: A$ Q% o; h
  5.         uint8_t temp0[3];' m( N5 s0 f, E9 q
  6.         uint32_t temp;
    9 L, H+ w% A+ h. D6 G
  7.         ) x& v& U. ^- i
  8.         NSS_LOW();//选择FLASH:NSS低电平
    4 k8 c( a1 s/ L/ L
  9.         * W- h1 e9 S; w% b. s
  10.         HAL_SPI_Transmit(&hspi1,&W25X_JEDEC_ID,1,10);//发送指令" g) R( ~  F6 P3 O# w& S' Q3 G' P
  11.         ) D* T, R: m7 ]. S% H$ R- R3 P: ]
  12.         HAL_SPI_Receive(&hspi1,temp0,3, 10);//读取制造商和设备ID  x: H4 ]% r: r- Q, L
  13.         
    / W. A8 \2 z& [& q) o1 [
  14.         NSS_HIGH();//停止信号 FLASH:NSS高电平
    + h- f1 }4 d( x! f
  15.         
    3 G6 h* e$ c$ T8 z. a. T# X
  16.         temp=(temp0[0])<<16|(temp0[1]<<8)|temp0[2];//把数据组合起来,作为函数的返回值
    ' a# ?9 H6 d/ [6 E$ {; _' J6 ]$ }' ~& c

  17. 5 Q4 ~4 x, B& ]& l" k7 S# ]. t
  18.         return temp;3 ^2 L! c7 R& n
  19. }, ]% Q' D4 o1 p
  20. " S9 B  f% @+ c' ?7 S) I* e
  21. /*写使能*/
    # B/ g; f  V. k- }+ F1 C
  22. void SPI_FLASH_WriteEnable(void)
    & m2 k; T, J: K& R! p
  23. {
    7 v! }* l' s  I+ m& l
  24.         uint8_t W25X_WriteEnable=0x06;
    8 x1 U8 L# l9 T# r
  25.         
    , }/ G% L. L- G: C3 X& n8 p
  26.         NSS_LOW();//选择FLASH:NSS低电平5 B9 D& A5 v0 l5 T$ Y' Y. s/ \
  27.         4 k- N# i% }- y+ A2 Y4 ]3 C
  28.         HAL_SPI_Transmit(&hspi1,&W25X_WriteEnable,1,10);//发送指令( Q: m  l& [$ ~, a/ R- F6 ^
  29.         
    ; M4 E% C6 E. k" o+ n
  30.         NSS_HIGH();//停止信号 FLASH:NSS高电平) P7 h0 m2 c, b* s. ]
  31. }
      B/ h3 |* V0 F, n7 X
  32. 5 M4 u1 C" E* D9 j, C1 g5 N
  33. /*扇区擦除*/
    % q) A- Y( ]) Y) M3 X1 X: v& A
  34. void SPI_FLASH_SectorErase(uint32_t SectorAddr)
      V+ i) a) w1 m' l' m
  35. {
    ; G0 B- z0 _$ g' v8 S7 d# m2 P
  36.         uint8_t W25X_SectorErase=0x20;
    7 J% e- q$ b9 i2 g. _; h
  37.         uint8_t temp1,temp2,temp3;( p2 r! C$ F5 H6 m# T
  38.         
    6 `% c. g: I5 j! R9 F* E( ?
  39.         SPI_FLASH_WriteEnable();//写使能
    3 y" J; C4 X+ `& @5 W. Y
  40. 5 N0 p. M; ^3 v. ^! R
  41.         NSS_LOW();//选择FLASH:NSS低电平) G( _: M+ Y, C2 ~0 f  ~
  42.         
    ( ?. Y* w3 F" u3 O- N) z5 [
  43.         HAL_SPI_Transmit(&hspi1,&W25X_SectorErase,1,10);//发送指令
    # d% @6 x  r+ `( _
  44.         
    * f5 v1 J; R6 l6 r, @
  45.         temp1=(SectorAddr&(0xFF0000))>>16;//发送地址$ U" Z& y+ }; |7 I& \' ~
  46.         HAL_SPI_Transmit(&hspi1,&temp1,1,10);
    * L& b* Q  M- [# _
  47.         temp2=(SectorAddr&(0x00FF00))>>8;
    4 l' L" u5 ], L! N( S" x  G8 W
  48.         HAL_SPI_Transmit(&hspi1,&temp2,1,10);9 B. n* A" _* S' d. S
  49.         temp3=(SectorAddr&(0x0000FF));
    : z! E6 M7 L. M7 l
  50.         HAL_SPI_Transmit(&hspi1,&temp3,1,10);
    : E& {, u: s2 x0 J; F( q4 i
  51.         NSS_HIGH();//停止信号 FLASH:NSS高电平
    * n& O! u7 s$ T( W
  52.         % n) j0 n: a0 a0 @( V5 I
  53.         SPI_FLASH_WaitForWriteEnd();//等待擦除完毕7 I9 Y9 H6 m6 ?
  54. }1 u* A8 E- m0 \0 y

  55. & r# ~* \: _3 B2 v8 {9 A8 J
  56. /*等待 WIP(BUSY) 标志被置 0,即等待到 FLASH 内部数据写入完毕*/
    + j+ s! {  L$ \4 T8 c- x/ ]# @
  57. void SPI_FLASH_WaitForWriteEnd(void)
    ; l/ X' e- l; n4 G4 I+ |7 O5 w
  58. {
    : n& V' P/ t7 t# g1 |$ E. B9 _
  59.         uint8_t W25X_ReadStatusReg=0x05;
    + B0 P: A: O% \1 b0 ]  Y/ s8 b
  60.         uint8_t temp;
    % Q' [4 L2 g- b# E
  61.         
    : G& @) X$ `$ \, w6 b
  62.         NSS_LOW();//选择FLASH:NSS低电平; c0 ]2 X! y* f2 q, t
  63.         
    " e& r( S& O% ~5 v& s
  64.         HAL_SPI_Transmit(&hspi1,&W25X_ReadStatusReg,1,10);//发送指令
    , I9 {8 t# F( D) Y( N
  65.         do
    5 j5 V# o6 o1 v% q
  66.         {3 O1 i% |& m: z& I6 f
  67.                 HAL_SPI_Receive(&hspi1,&temp,1,10);// 读取 FLASH 芯片的状态寄存器, Q  n, C1 {/ K  b/ e) e* f
  68.         }% \! B+ A! @3 C$ U- R; [  z
  69.         while((temp&0x01)==1);! b' T8 U1 |  {+ }
  70.         
    ) G+ q+ l1 \- m
  71.         NSS_HIGH();//停止信号 FLASH:NSS高电平9 R0 U! }$ ^- B0 A' X/ U( b& h7 ?
  72. }) F7 R  J2 M2 m% i# V( r7 L4 O' I8 C

  73. " a* E9 l0 h  A4 h9 Z7 D
  74. /*在FLASH的一个写循环中可以写多个字节,但一次写入
    7 Z4 D7 x2 G/ h. \% X* J
  75. 的字节数不能超过FLASH页的大小,W25Q64每页有256个字节*/5 W7 e( C4 I/ a+ p
  76. void SPI_FLASH_PageWrite(uint8_t * pBuffer, uint32_t WriteAddr,uint16_t NumByteToWrite); Q8 f2 Q" L1 ]
  77. {
    " a7 e9 ], c7 a( }
  78.         uint8_t W25X_PageProgram=0x02;; u% \! l# ~5 @: t
  79.         uint8_t temp1,temp2,temp3;
    ( [  `, k' M0 o' t' I* Q  n
  80.         * v) k1 Z: A! y5 a! a( I
  81.         SPI_FLASH_WriteEnable();//写使能
    - I; L* B# g# h9 i0 ^
  82.         $ n- _0 z1 m* r8 x9 F+ S
  83.         NSS_LOW();//选择FLASH:NSS低电平2 P! z3 [0 B6 o4 f. d
  84.         4 E4 \& [  p8 D$ a" |& b/ e
  85.         HAL_SPI_Transmit(&hspi1,&W25X_PageProgram,1,10);//发送指令: B! |7 T4 Z1 ^5 e7 V3 v% H' J
  86.         , Q- r- }* ]2 K0 ~' G9 t/ l& o( ]
  87.         temp1=(WriteAddr&(0xFF0000))>>16;//发送地址
    0 I# ]% m6 k; h2 y6 Z9 t
  88.         HAL_SPI_Transmit(&hspi1,&temp1,1,10);
    % V( m" \5 a* C
  89.         
    , V5 A* [$ X9 |7 t) x
  90.         temp2=(WriteAddr&(0x00FF00))>>8;3 x7 r3 y. B$ |7 E; y( V0 m3 n3 L3 @
  91.         HAL_SPI_Transmit(&hspi1,&temp2,1,10);7 z( f/ G9 j# J7 S& k8 ?7 d; }
  92.         
    4 T; E5 `0 h. U$ A
  93.         temp3=(WriteAddr&(0x0000FF));
    ) a2 m# ^& I- [; C5 y% b
  94.         HAL_SPI_Transmit(&hspi1,&temp3,1,10);
    / k7 S7 y2 m/ F) x. `) R" _
  95.         
    0 f+ {; u1 b) D. R, w# @
  96.         HAL_SPI_Transmit(&hspi1,pBuffer,NumByteToWrite,10);//发送数据2 @- H3 I4 o; `" a
  97.         
    ( y1 l8 [8 ]: ^
  98.         NSS_HIGH();//停止信号 FLASH:NSS高电平
    ) z% q2 V1 e. ]
  99.         ! K: j! B1 @4 S5 O) F. b; l
  100.         SPI_FLASH_WaitForWriteEnd();//等待写入完毕
    ) G5 w1 e# i( }% @8 i9 n- i6 }/ N, E
  101. }, p/ u% ^" ?# P* P% E! c' t  d8 @

  102. 3 A% ?% Y. L/ Y' Q" O* u/ M
  103. /*不定量数据写入*/* X+ V, b" {5 \4 P: A' g' M
  104. void SPI_FLASH_BufferWrite(uint8_t * pBuffer, uint32_t WriteAddr,uint16_t NumByteToWrite)  i1 {& m9 K' F2 Y
  105. {$ R4 B+ U+ g; z
  106.   uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;- W+ w% B( u% k' {

  107. 9 Q' `  {4 R3 V. \
  108.   Addr = WriteAddr % FLASH_PAGESIZE;//判断写入的首地址是否与EEPROM页的首地址对齐,0为对齐
    : d7 s4 ]* S( B8 A9 E
  109.   count = FLASH_PAGESIZE - Addr;//计算从写入的首地址需要写多少数据才能填满当前页. H; v% g# P; {* n. ?
  110.   NumOfPage =  NumByteToWrite / FLASH_PAGESIZE;//计算写入数据需要写几个完整页(地址对齐的情况)
    5 X) g, v, r4 }0 e/ R8 b2 n: t
  111.   NumOfSingle = NumByteToWrite % FLASH_PAGESIZE;//计算写完完整页剩下的数据个数(地址对齐的情况)
    ) O, y* t# i/ Z" V0 L: X/ _. Q

  112. . s) n* W. A. |" S* B: m9 b
  113.   if(Addr == 0) //判断写入的首地址是否与页地址对齐( s: e/ I0 K) N- W& Q
  114.   {/ C* a2 R) M' G* _& Q& k
  115.     if(NumOfPage == 0) //如果页对齐,判断数据是否不满一页
    3 |+ g  z8 y$ e
  116.     {
    ; G2 O/ Q$ ~6 y* a; z
  117.       SPI_FLASH_PageWrite(pBuffer,WriteAddr,NumOfSingle);//如果不满一页,直接写入数据' m+ P0 O" `0 J* r
  118.     }6 O% w: E3 q9 k$ T+ o: d
  119.     else  //在数据满一页的情况下,通过地址自增方式,循环写入数据(页写入的形式)
    * S6 c; ~5 G: R% _. T; B2 m
  120.     {6 e# D& z7 T  m( {- I
  121.       while(NumOfPage--)//循环写入数据:先写入完整页1 I" o: l) ^0 z& X, W
  122.       {
    , X' Y+ o8 n% O
  123.                                 SPI_FLASH_PageWrite(pBuffer, WriteAddr, FLASH_PAGESIZE);
    : Y0 |% ?; O4 M) l% f
  124.         WriteAddr +=  FLASH_PAGESIZE;
    , C  u5 [) P# V' U
  125.         pBuffer += FLASH_PAGESIZE;
    % u1 c, R0 d4 P* m/ F+ e9 N" E
  126.       }
    ' b$ {* @& e) s4 ?. q0 G7 v

  127. : S" @& p: t2 P# b- _
  128.       if(NumOfSingle!=0)//循环写入数据:再写入不满一页的数据
    . h7 q/ ~4 Y6 Y" S: W) m( C1 `5 C
  129.       {
    6 R" t; h3 H% O
  130.                                 SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle); 1 D( m2 c7 R8 c$ `& G
  131.       }
    * k7 {7 C' ^7 B8 |" j2 `
  132.     }6 e9 {) e: O9 T2 s: @
  133.   }' D/ @0 D+ b% H( S* `% T
  134.   else
    . A* ^- N& h! ?8 X7 r; U/ [( z
  135.   {/ v  Y4 ~# M3 ~! W4 x  K- i
  136.     if(NumOfPage== 0) //如果页不对齐,判断数据是否不满一页
    % o1 d6 p6 G1 |  x* q6 V5 E3 a
  137.     {
    ' W+ Q1 S5 p; Y1 ^
  138.                         if(NumOfSingle<=count)//如果不满一页,判断数据是否跨页
    9 E3 L# ]5 a3 q9 K
  139.                         {         8 \& U1 t% Z, }5 k6 K* ^& U0 ]
  140.                                 SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);//如果不跨页,直接写入数据+ |/ O# H4 Q5 O+ X9 \; @4 H; o
  141.                         }
    . i& X7 b' C) L' l5 @
  142.                         else
    & X1 l) {* _# R' X( E& W
  143.                         {
    9 u4 b& \7 U' m7 }8 H' R5 l
  144.                                 SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);//如果跨页,先写首页数据,再写次页数据: d5 q; m9 V* z- M
  145.                                 SPI_FLASH_PageWrite(pBuffer+count, WriteAddr+count, NumOfSingle-count);- n. n3 w7 S8 N, `* Z% ]% F1 D
  146.                         }9 D5 d8 t& T% k6 W) u3 L$ D0 Q
  147.     }
    - [" L+ A# U' L( s
  148. # g7 _; @5 z0 n- H3 a  T
  149.     else+ W) R3 _; F3 H8 T% V
  150.     {5 g- S' U% Y5 q4 E
  151.                         /*如果数据满一页,对数据进行分离*/
    3 Z- V0 Q" G" p! U( x& b6 Y
  152.       NumByteToWrite -= count;//扣除第一页数据个数
    * b9 V) ]- o) W& S0 M( @
  153.       NumOfPage =  NumByteToWrite / FLASH_PAGESIZE;//计算写入数据需要写几个完整页0 y) D+ X' W& p0 o; V% U( |4 o
  154.       NumOfSingle = NumByteToWrite % FLASH_PAGESIZE;        //计算写完完整页剩下的数据个数
    6 R2 y% f5 P5 o" b9 t' N
  155. 9 }8 w4 z6 D0 P& j  I, B' l
  156.       if(count != 0)
    + r2 W3 P0 p: V7 U5 r8 Q- P% O: @
  157.       {  3 n$ E1 g& B; N  Y' q' C$ X" r4 W
  158.         SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);//写入首页数据7 U$ Q5 h& X) |2 s1 n
  159.         WriteAddr += count;//写地址自增  O. G3 Y6 R4 ^2 f0 n
  160.         pBuffer += count;//缓冲区指针自增
    # o. J( R. q/ v# _$ K. B
  161.       }
    2 W: ]0 `' ^$ ?- t' O$ B$ I1 r. K

  162. ; H; v' P3 Q: v- \
  163.       while(NumOfPage--)//依次写入完整页的数据
    ! S# ^) ^( q- X
  164.       {
    + P. `! {( b3 B9 `/ {& ^( b- O0 n. W4 Y
  165.                                 SPI_FLASH_PageWrite(pBuffer, WriteAddr, FLASH_PAGESIZE);
    3 c: x& j& b- M' e
  166.         WriteAddr +=  FLASH_PAGESIZE;//写地址自增2 X- y: I5 w7 q; }2 W# Z
  167.         pBuffer += FLASH_PAGESIZE; //缓冲区指针自增
    / {8 Y, m. q/ T4 n  k
  168.       }
    0 k( k, m0 k) B! M2 r- S1 c
  169.       if(NumOfSingle != 0)//判断最后一页的数据是否是填满完整一页的# t# p0 t8 I) |( b) E8 U; Y
  170.       {1 l0 l7 H( n. b. n
  171.                                 SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);//写入最后一页的数据2 R# }0 W, I3 u# K4 K
  172.       }2 @/ `  {. v% [$ L' c
  173.     }
    + s0 E( F. x/ W
  174.   }  - f, J# s- j- ?$ @+ ]* X5 O& I
  175. }  }+ [& e) i) w+ L, s
  176. - Q# T0 m+ }1 d) @
  177. /*读取FLASH数据,读取的数据量没有限制*/
    9 C$ F" j! c/ ]$ q/ ]
  178. void SPI_FLASH_BufferRead(uint8_t * pBuffer, uint32_t  ReadAddr,uint16_t NumByteToWrite)1 |; z2 n: X2 e6 L$ O% p
  179. {/ I# d, h$ y7 E) f) n1 d
  180.         uint8_t W25X_ReadData=0x03;
    , _; p" U' l( i3 Z# F4 L( B8 d
  181.         uint8_t temp1,temp2,temp3;
    - S7 `1 L$ `2 y6 f1 M1 C% M
  182.         9 y+ ?0 c/ w" W7 E1 p
  183.         SPI_FLASH_WriteEnable();//写使能% G0 N% I: o0 S" C  l6 s
  184.         
    ( B- `) l: r9 m+ s" k2 Z- T/ T
  185.         NSS_LOW();//选择FLASH:NSS低电平) l6 T7 G+ L% H9 S1 N
  186.         ; G, x. Y: ~8 y: D& {; l1 A
  187.         HAL_SPI_Transmit(&hspi1,&W25X_ReadData,1,10);//发送指令
    , x5 n6 \2 o' k/ K/ t9 D) g
  188.         
    & [- D4 W0 Z. K$ i6 q
  189.         temp1=(ReadAddr&(0xFF0000))>>16;//发送地址9 K0 w: `0 y) H2 F* v/ u* n
  190.         HAL_SPI_Transmit(&hspi1,&temp1,1,10);6 f! G' }6 ~2 v% K% g
  191.         , o& `  u7 Z+ T  r3 \
  192.         temp2=(ReadAddr&(0x00FF00))>>8;8 B% X9 ?" W. L; ^' Z) U# q
  193.         HAL_SPI_Transmit(&hspi1,&temp2,1,10);8 ?% |3 u9 n6 Z# y3 T1 B
  194.         
    $ u. K6 q  d" Q( Y
  195.         temp3=(ReadAddr&(0x0000FF));
    / N: h( O! H: Q" H% {
  196.         HAL_SPI_Transmit(&hspi1,&temp3,1,10);
    1 h0 O( L  n3 X! T2 }' Y& v. }# r) d
  197.         + r7 k3 k' s6 K6 g5 ~! V
  198.         HAL_SPI_Receive(&hspi1,pBuffer,NumByteToWrite, 10);//这里超时时间不能设置为1,否则会读取错误
    ' z" z) k. \" P: j* l- A
  199.         
    9 w$ i4 |" ?9 V+ M4 z! I
  200.         NSS_HIGH();//停止信号 FLASH:NSS高电平
    2 y; c1 }) m* q; u$ n
  201. }+ x( ]9 F* H% `$ c# l* m, i
复制代码

4 n6 H: ~+ ^& E5 z. q 3、最后在main.c文件中,编写测试代码进行验证。2 L7 W, `: D$ \; J" \

% c1 U# M* B  U1 o9 _
  1. /*测试代码涉及到变量,宏定义*/
    - F2 }1 m- B* |. e! _3 V
  2. /* 获取缓冲区的长度 */! K9 w, d# v/ G

  3. * S# P% K" \) e0 L# p
  4. /*sizeof():数组占用字节除以数组类型所占字节,结果为数组元素个数*/& |3 Z8 [3 |' {( t5 ^) a2 r; y
  5. /*使用方法:sizeof(数组名)/ sizeof(数组类型名) */
    ! J; V1 B3 E: a1 u1 g2 G0 ?
  6. #define countof(a)      (sizeof(a) / sizeof(*(a)))! [$ L* {. ^2 b" r
  7. #define  BufferSize (countof(Tx_Buffer)-1)
    + h6 H2 S1 D8 M8 `

  8. ) A. b8 ~  G& G
  9. #define  FLASH_WriteAddress     0x000006( h* F3 u8 P9 W5 Z. e# U8 {9 S
  10. #define  FLASH_ReadAddress      FLASH_WriteAddress
    , [  t9 R; ?& A& W; J
  11. #define  FLASH_SectorToErase    FLASH_WriteAddress3 ^3 e. c' t3 h' T) U2 z

  12. ; R. r" B$ t* m7 z0 q1 F0 w
  13. uint8_t Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength);
    - {& i: ]0 N, n% C% s
  14.   c) S7 B( j  a8 d% s+ o- ^
  15. uint32_t Flash_ID;
    , H- ]7 j( X  X' K" H: x  M
  16. uint8_t CMP_RES;
    6 i5 u" J$ a! A4 G
  17. uint8_t Tx_Buffer[] =
    # l4 X7 {# [* `/ {9 Z# o' R( u6 |
  18. "采薇采薇,薇亦作止。曰归曰归,岁亦莫止。靡室靡家,猃狁之故。不遑启居,猃狁之故。\
    % q+ v& }1 l$ u/ n
  19. 采薇采薇,薇亦柔止。曰归曰归,心亦忧止。忧心烈烈,载饥载渴。我戍未定,靡使归聘。\0 e. [5 s8 |) n
  20. 采薇采薇,薇亦刚止。曰归曰归,岁亦阳止。王事靡盬,不遑启处。忧心孔疚,我行不来!\# H% H" n- N3 _1 e0 Q/ E/ d
  21. 彼尔维何?维常之华。彼路斯何?君子之车。戎车既驾,四牡业业。岂敢定居?一月三捷。\% s8 K2 ~1 x- X" l  F6 W6 p0 r
  22. 驾彼四牡,四牡骙骙。君子所依,小人所腓。四牡翼翼,象弭鱼服。岂不日戒?猃狁孔棘!\
    3 @' r8 u5 W7 p) J) e9 d
  23. 昔我往矣,杨柳依依。今我来思,雨雪霏霏。行道迟迟,载渴载饥。我心伤悲,莫知我哀!";
    5 x# p! j6 a( q: V' c
  24. uint8_t Rx_Buffer[BufferSize];
    ! h. s% h; q9 K7 C
复制代码
  1. /*比较函数,用于比较写入和读取的数据是否一致*/
    / _! `9 m/ K) o# `
  2. uint8_t Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength)1 G* k% H- \# i: d5 [# U9 Z
  3. {, c8 ]( }6 U  W4 `% c" k, j; R( T
  4.   while(BufferLength--): \! j/ o1 E" _2 R! r4 I
  5.   {
      o: X* X: w! j" x4 O$ e7 M
  6.     if(*pBuffer1 != *pBuffer2)
    5 k" q9 c: S$ u" z; @& o0 y" {
  7.     {
    . h: h( P5 q# S$ o- Z1 k/ }
  8.       return 0;
    9 K% _( q  u$ d: {& n9 v5 |5 S
  9.     }* i8 j3 w6 c: J& [7 P) i

  10. 5 I0 V) w7 c0 C- L$ k+ [) f1 W
  11.     pBuffer1++;! j( `: i. a7 J9 d
  12.     pBuffer2++;( f7 A( k7 M) q. H6 k  ~/ w
  13.   }
    2 L+ u! h! Y; r. `' y0 n* B+ I
  14.   return 1;
    4 t2 A- i4 Z# Z+ l& O
  15. }
    ' m. N! G6 I2 ?2 r
  16. /*在主函数编写测试代码:ID验证,读写比较*/
    6 E6 t# |: ?6 s
  17. Flash_ID=SPI_FLASH_ReadID();//读取Flash ID
    7 m1 i' j0 m8 P. Q0 t
  18.         if (Flash_ID == _Flash_ID) //判断读取的ID和数据手册的ID是否一致! s5 o9 h: m7 B: G+ H- A- |( }! M
  19.         {        % `) s# h5 c' @! y3 L7 L4 W
  20.                 printf("\r\n系统检测到SPI FLASH W25Q64 ! FlashID:0x%X\r\n",Flash_ID);
    3 g& I/ H. q; j6 n8 W! E3 _
  21.                
    4 ]/ u- C5 N- k
  22.                 /* 擦除将要写入的 SPI FLASH 扇区,FLASH写入前要先擦除 */
    " j1 E. x5 X# E. h- F: Q
  23.                 SPI_FLASH_SectorErase(FLASH_SectorToErase);                  
    , T+ w0 c, m8 x
  24.                 ' m2 i& r! J1 W# A! M( R. Q
  25.                 /* 将发送缓冲区的数据写到flash中 *///& Z; M3 u% O$ U4 q7 [
  26.                 SPI_FLASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize);9 Z0 [2 x  r* n: f; \: O9 C. ~' h
  27.                 printf("\r\n写入的数据为:\r\n%s", Tx_Buffer);
    7 Z7 p- U8 \2 k, _( \7 ^' L
  28.                 7 I# r( @+ Z: E
  29.                 /* 将刚刚写入的数据读出来放到接收缓冲区中 */9 F( n& Q6 O: W- G  {4 d
  30.                 SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);
    , R" N0 @4 @2 m5 R
  31.                 printf("\r\n读出的数据为:\r\n%s", Rx_Buffer);
    1 v0 n2 j' i3 Z
  32.                
    4 r7 Y% W! d; O$ V
  33.                 /* 检查写入的数据与读出的数据是否相等 */% m9 u3 V. A8 _% i2 B
  34.                 CMP_RES= Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);$ C0 [' a* W2 B0 h% ~, j" _
  35.                 " H/ m; i' J8 W+ ~! ?. M/ X4 `) L
  36.                 if(CMP_RES)& a" m. w1 V1 D3 d
  37.                 {   
    ; r% D  c+ ?. g# t
  38.                         printf("\r\n16M串行flash(W25Q64)测试成功!\n\r");
      q  C: H) f5 s4 ^9 M* c! o; X
  39.                 }
    5 Y& Y- n  \5 ~$ a
  40.                 else
    . v$ ?" w& M5 j
  41.                 {          m5 D2 J6 k4 p" E' w
  42.                         printf("\r\n16M串行flash(W25Q64)测试失败!\n\r");
    * a/ R$ h" m# k% e1 _% g- P+ d
  43.                 }* J# q' |, y! ?. S. y, ^
  44.         }// if (FlashID == sFLASH_ID)% a5 @, X' E! n) a4 X" g7 b
  45.         else( D) {( L" O6 R  w2 ^6 i
  46.         {    ' a5 c. }& g6 e( P( H
  47.                 printf("\r\n获取不到 W25Q64 ID!\n\r");
    0 F" M8 D) d$ U( f, @9 F2 i
  48.         }                        
复制代码
$ p. K* {7 o/ }; V+ s. P$ w% [6 E

/ W+ j$ o  D! ?  ~调试过程碰到几个问题,也是比较无脑的。  D- e6 t3 N# y; m6 c
1、编写页写入的时候,常规流程:写使能->NSS拉低->发送页编程指令->发送地址->写入数据。0 t, D, j7 r1 {2 l
我忘记写发送页编程指令,导致了每次读取的时候,数据前面总是多出一些空白。(数据内容:" K; M% w& C* Y5 R/ m3 R- ^. _% L, j
空格(发送内容越多,空格越多)+数据)。刚开始在论坛看到了,一个网友也出现类似情况,评论区,都是说等待函数加个延时就可以了,试了下还是不行,后来才发现是指令忘记写了。+ }2 |% Z# x! ~- L
3 X8 p0 \( M, u/ ~: |. B
  1. HAL_SPI_Transmit(&hspi1,&W25X_PageProgram,1,10);//发送指令
复制代码
; }+ W# f6 A7 y& q) Z& w9 ^
2、还出现一个问题,读取大量数据的时候(超过一页),后面的内容总是读不全。刚开始以为是自己本身写入就有问题,后来用了例程读取我写入的数据,可以正常读取。基本可以锁定是读取函数的问题,看了一下整个读取的流程没有问题,唯一需要更改就是库函数自带的接收函数的参数:超时时间的设置。果然把原先设置的1改成10就可以正常读写了。(因为这个原因,我索性把发送的超时时间也都设置为10,直接设置为1也会正常的就是了。)
9 I& T: T. b8 _7 @, v4 y
1 g2 b. y; k' ]" @6 k! W
  1. HAL_SPI_Receive(&hspi1,pBuffer,NumByteToWrite, 10);
复制代码
, t6 B% i; \: ]  w7 k
! H  P/ Y. ]% Z0 D! q

# B3 N& j; T) O1 Q: X) J3 G# S
[)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管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版