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

【实测教程】STM32L431之SPI Flash读写实验

[复制链接]
STMCU小助手 发布时间:2023-1-13 19:00
   一、开发板平台简介:
+ T3 Y0 r7 p8 e1 [, r: g; w1、开发板资源简介

0 L' b: y, e7 [6 y$ k( {4 m( C(1)开发板主芯片型号:STM32L431RCT6: r: K3 ~# Y- y; O6 h  P

) X. e) T: G# P(2)开发板主芯片封装:LQFP-64_10x10x05P( ]5 R/ K( B9 q1 ^3 y3 L

' G) M0 b3 g! |/ U(3)开发板主芯片内核:ARM® Cortex®-M4
3 ?7 b: x" m. A. U
& _1 k; [. p+ j(4)开发板主芯片主频:80MHz
' Z. x, D' K! q7 V8 X3 n$ o- C4 @+ S+ R$ d4 B9 d  C
(5)开发板主芯片Flash大小:256KB* c. A4 ^0 ^$ u8 \- a  n2 Z+ m: `
$ d3 Q! n' e/ T; }6 v
(6)开发板主芯片RAM大小:64KB
: I3 L( a/ d9 E, S

* R- Q' e& o9 C bb91345227434ee7a43a34bf3fe366e6.png * M  |- {8 ^, E; ^$ c$ @2 I8 V

% o9 R( u) ~2 n) L0 X 929c0cce04aa4115a8623703982bf3e1.png / T: N2 X9 u1 x$ Z! \" p9 @

! [0 M4 c' v$ R+ N( w# e: Z0 w2、LED灯资源
8 C  l9 k& f/ D  `( N+ T% m3 Y1 Q(1) STM32L431RCT6开发板共5个LED灯资源,其中一个红色LED为系统指示灯,指示开发板供电系统是否正常,如供电系统正常,红色LED为上电常亮状态,硬件原理图如下图所示:
3 [& V3 V7 A# T' O: k' E' }- Z7 O* L  f- `) N: d7 n) S
0f177691c5814f16a508bcfa747f87ac.png ) I9 K& D7 u1 Y3 ?$ {+ v

. n% G. h4 s8 ?(2)其他四个LED灯为黄绿色可控LED,高电平点亮、低电平熄灭,计划用LED常亮验证看门狗的作用,硬件原理图如下图所示:
( ^8 W" p4 B+ q4 N! O: Z
& @9 ^! U* e5 @; V2 G/ X
e923ad767b8448b7ac37c4ed693724bd.png
. ^8 G7 s; A3 ?# S1 ^5 y3 Y' \( p) B: ?. O, J" \2 u9 t4 T  H- ]
​3、串口printf打印工作原理
# V+ b8 ]7 F  S, i/ M  u 串口全称为串行通讯接口,即数据在通信线上一次传输一位,按先后一定顺序传输。我们通常所说的单片机串口准确来说应该是串行异步收发传输器(Universal Asynchronous Receiver/Transmitter,UART),使用TTL电平,串口需要RXD、TXD、GND三根线进行通信。
# q! o. \9 n% ]& d1 d& I

" \1 G% u) c& \+ V% \: k8 x. [     (1)我们选用的STM32L431RCT6开发板串口1已通过USB转TLL串口芯片CH340G引出,使用时,只需要用公对公USB线连接电脑即可(注意也得需要安装CH340G驱动),后期验证试验也可使用该串口作为debug串口。
. y" w5 i9 q1 k7 S/ s" \: @' m: D. _, C( `: b  G3 L. {0 X
     (2)开发板上的其他串口已通过排针引出,为TTL电平,通信的时候需要注意选择对应的电平模块,如USB转TTL串口模块等。* _' a# t! M5 O  J  h5 ]3 [

# @# D* B0 _- \; G  i6 `* x0 tTTL转CH340串口,硬件原理图如下所示:' p$ Z; L$ O; p- g
+ b7 {8 j0 h1 Q! W/ }1 d
4df1b489cb944c26b55013001061675d.png * f0 W0 M* u- W, `2 [

- I' v7 V7 q; @* g3 O8 ?! @4、SPI Flash简介0 _& @; v- U' C
(1)SPI总线介绍1 C" ?1 Y( {% h" y9 u
SPI分为主从工作模式,通常有一个主设备和一个或多个从设备,本文中MCU为主机,W25Q16为从机。2 q. I6 X* @. Y) x1 [

0 s2 m: j$ F$ F" O9 c' i6 ^SPI通信有以下四根线:/ P7 E1 o$ _$ \
MISO:主设备数据输入,从设备数据输出。
/ o5 }' t  ~* HMOSI:主设备数据输出,从设备数据输入。
- v, M, W: L# ^5 [9 KSCLK:时钟信号,由主设备产生。
  j3 H' z0 g3 f: n
' d+ Y3 J1 s% ]4 H
CS:从设备片选信号,由主设备控制,低电平为选中。
4 b4 ~5 I- _5 m$ U) `
% W6 i$ p5 g, o. s0 D; q$ G! Z(2)SPI的特点:
. V# ^, v3 [: Y2 Y; M高位先发送,共有四种工作模式。
( |6 v7 v' n. c! }5 f9 oCPOL(时钟极性):规定了SCK时钟信号空闲状态的电平(0-低电平,1-高电平)! l# x1 l$ K: v! f
CPHA(时钟相位):规定了数据是在SCK时钟的上升沿还是下降沿被采样(0-第一个时钟边沿开始采样,1-第二个时钟边沿开始采样)
8 Y; K: W: ^4 m2 a8 q& G' s9 D$ }7 e7 V4 Z/ B% o
模式0:CPOL=0,CPHA =0  SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据)
8 f6 Z( |, P6 F+ g+ B模式1:CPOL=0,CPHA =1  SCK空闲为低电平,数据在SCK的下降沿被采样(提取数据): N5 f( V5 R" y) ^, ?3 V& Z- |. Y
模式2:CPOL=1,CPHA =0  SCK空闲为高电平,数据在SCK的下降沿被采样(提取数据)
( ], q" J3 O4 k+ q模式3:CPOL=1,CPHA =1  SCK空闲为高电平,数据在SCK的上升沿被采样(提取数据)1 D2 w: j0 K6 D& R
7 w) A/ u: S7 e* C  Z
(3)开发板 SPI Flash原理图如下:- A$ G1 \& ]. Z& Y5 m* f& q3 Z
: G- V6 |7 H2 O7 l1 d
da82f3d13fef48dd8d90aa170757e360.png ; l- F7 p: e. K" ^* c; ~

# @* h1 z2 x8 m! Z3 \. r! ^7 M" _7 m1 ^
  二、串口printf实验过程: F8 j. }/ b4 x/ ?( q4 x5 N
1、新建STM32CubeMX基础工程

: `- C' K% K7 l* M( b(1)打开STM32CubeMX,点击“File”-->"New Project"
7 ]! P3 }# {: @- h; q2 P) S$ T9 l4 F4 e! B; {$ V
2399170347904e959c855fefd5877f84.png 9 Z$ A8 B. W- t  t9 U. [
& k8 `' {5 R: l' q9 Z1 p& A9 t
(2)等待打开主芯片选项界面(大约1分钟时间)。
' v9 l8 d3 u, S# V3 }# i5 W; T% ]4 r" g- }/ M
d74d2fdc60c9440483e2b7cf8d928e7f.png 5 W& E1 e& H3 X5 Y1 _

0 R8 m1 Z1 t" G- s4 p+ k7 ^/ q(3)昨天搜索框中输入(或选择)所需的主芯片型号(因为我们用的是STM32L431RCT6开发板,所以此处选择STM32L431RC),然后在右下角选择STM32L431RCTx(因为开发板主芯片是STM32L431RCT6),左键双击即可打开新建的项目。
7 [+ g3 J. m( G& k9 L2 L0 A9 u% t8 x# I
7b80345238d74bea82ce70e1a348f7b4.png % j1 N" ~% u2 n5 R9 H9 Y

+ f: V- z6 |5 t" ?7 `  L(4)选择时钟源。
! X& R* ~0 _( z6 M4 [* o(1)因为开发板上有8M外部时钟,硬件原理图如下所示,所以此处选择使用外部高速时钟(HSE)。
& c3 `% T# a) ?5 A$ v, s
# F9 |  |. V$ b. @; v. I
3f65707eca104663a3dede6d25dfa961.png
4 E8 e* U% J$ |6 V! M! T* X; I, ~) k, g7 m9 `
(2)因为我们没有用到外部低速时钟(LSE),此处不做处理,如下图所示。+ r1 O) d" y' c1 n+ ~

2 {0 G2 i- c+ ^+ a% d; h
72119b971f62410fa8344f7f9fb9f389.png ) o6 B& L* \  }! k7 f6 G# _0 y
5 J, G6 y8 h8 w5 ~2 E
2、配置GPIO控制LED1 \5 ^( V& J6 |  ]7 D
(1)查开发板原理图得,LED1、LED2、LED3、LED4的控制引脚分别为:5 `8 e; q# K" Z4 v0 K
LED1——PC0. y. {! a& T: J/ F( e# N! n
LED2——PC1
2 l, `; l3 g( ]* `$ I* w4 `4 SLED3——PC20 W, @/ f! H: n% N
LED4——PC39 y5 T* r6 g8 P. m7 x. m! w- T: X

- M% D0 b9 {* J(2)配置LED的控制引脚为输出,输出频率、输出方式默认即可。
; P" z( k, }, f鼠标左键点击PC0,选择“GPIO_Output”,表示设置该引脚为输出模式。6 Y6 C$ Y( o! n% R: V
鼠标左键点击PC1,选择“GPIO_Output”,表示设置该引脚为输出模式。0 [* p7 w% k# v9 r3 S( l% x
鼠标左键点击PC2,选择“GPIO_Output”,表示设置该引脚为输出模式。$ C2 N. C$ F  Q1 u
鼠标左键点击PC3,选择“GPIO_Output”,表示设置该引脚为输出模式。9 ^$ c5 b9 r* h% G9 z) F6 D
7 L# n+ Y/ e- s% o+ N" x
492907c1e71149819adbaee4516a2af4.png $ `0 O8 ~+ @, g8 t1 J9 o3 Q4 F; `
* |: R) T, k9 m' r  v; A
c75d3f10ba2e427d9c1a10fad9bf5471.png 4 c: ^/ ~. l' s# E

: L. u. Q) z6 C3 J' M* k(3)也根据自己的需求配置GPIO的参数,如输出方式、输出频率、上拉下拉等。因为GPIO控制LED的要求比较低,此处采用默认参数即可,不用修改。" w' o: m6 T3 a  O

! x5 s8 }: i6 s( L0 l
38ff4b80e1c5495ba3076a5158fd91fa.png ! h1 T8 w; ]. ?7 t' i9 I
4 j8 r( x. c: w6 {' j, X, ]* ~- c9 t
​​3、配置PA9、PA10为串口5 }& u, \" @, u2 i* p- R+ T
查原理图得知,串口1使用STM32L431RCT6引脚为PA9-USART1_TX,PA10-USART1_RX,引脚设置如下:  b0 Q2 \7 @6 m/ p( e# @7 y8 N

+ ]- f8 e; t, e7 y1 q4 w4 O
497b8ee0243e446784e64a3c31e8a26f.png
1 C! z  L4 w0 r; F8 O

' s+ n- r' L  r" d: y (1)序号1用来设置串口收发引脚的选择。# g6 {% W: X: H9 C
(2)序号2-3-4-5-6设置串口参数,如波特率115200、8位、NONE无奇偶校验等。  `! i+ s8 k9 \( _! a

7 F$ P: m! s, F* T 4、配置SPI Flash接口( [# E& {. n) C8 }7 i" ]
(1)查看STM32L431RCT6开发板原理图得知,芯片原理图如下:- w$ Q' Q5 a' F+ C1 U3 e

5 \* c7 F  P3 r! t8 N+ h
11aaba64f3d34f9ea1e4cbbfc0fdc102.png / @$ b) U" o) j6 V7 s+ M

+ N$ Y( w+ T8 K' N (2)SPI Flash接口对应芯片的PB12、 PB13、 PB14  PB15,芯片引脚配置如下:' \$ }3 g. t( X4 H
PB12:SPI2_NSS,此处设置普通输出IO即可。不能配置成NSS' n; b/ V9 H7 m9 W$ F' n! A
PB13:SPI2_SCK
2 c3 k* v" Y& xPB14:SPI2_MISO& i$ G4 ^' @& p
PB15:SPI_MOSI
. g/ w; V( x6 e" f& _
; N! A" K& s5 \$ b
ade62d1658174af99a93e3928b62849c.png
3 R0 Q' T, f; s) q' F3 z2 g$ x
/ j' D4 I- ^2 O" b6 T (3)设置SPI引脚参数,并选择 Full-Duplex Master 全双工主模式,此处不开启 NSS 即不使用硬件片选信号。
1 z- [# G1 x7 m' v. L& ^" m5 P* ?0 n4 Y+ I( G! s# e
c932367c299e46979e55a3e1a05d2c7e.png
9 m0 g6 V$ i+ z0 S7 G

" J/ @! D, k+ ?5 a  m(4)设置SPI基础参数以及时钟。
# Y6 M# ^, Y: ^; m2 z" X: c% U/ U. S
8 v  a# Y3 ?4 V( N, h* a, Y1 G
e5af1c8c0b0b4ac9a07d740b955028c0.png
7 p7 O7 e0 X7 V/ m, o) @$ ?, _: W
5 |2 |: f" o. v. U8 Z
dcc8d73ddcbe4082a09165905ac71cf1.png 7 M8 v6 S4 a) Z9 e' M, q" e
; q- A+ _  w3 Y& j
5、配置项目工程参数
7 _* I1 n+ N8 k(1)配置时钟树,用于系统内部时钟,以及各个外设时钟等。此处选择外部8M晶振作为主时钟频率,内部最大倍频80MHz。
/ W; T" L; C7 k  P8 {' I0 L% b. ?6 j" d. q$ q: {
f3ecedd9323f4463b69539906b3c1199.png
( h: @' Q8 o' H7 O4 b/ _+ t
% p7 ~; s+ x. f" l# p% ?(2)完成配置工程。
. N& ?, t# L( h) \备注:需要注意代码生成过程中的继承关系,如图所示:需要保留开发者自己编写的代码时,请根据配置设置,不然生成代码后会删除自己编写的代码(从这个方面也可以看出开发者备份自己的代码是多么的重要。)
& w- r( l1 t( i* Q" ]( @8 G" _: k6 {9 a. e: Q
efdb616174f54925b6eac31109f227b5.png * \6 j, _" c; W0 Z
​​
: o+ g5 |  |# T5 Q+ |9 I$ j 41c3f2716e4e4eda8f62fa3a1d871322.png
, A7 ?6 i/ a  `& B  a
( T' a  T" s! P, E. C
(3)生成代码。
& [  p' f# g4 O' j' a7 y  o2 _备注:使用Generate CODE生成工程代码前,请确保文件路径无中文,否则会生成项目失败。
7 Z2 w- e7 A8 Z! Q: V' T' @0 U" o. t. ~7 m8 A! v
8eaa7d0babea40ee93f158481a6ee471.png - o% U5 t5 k/ A
( E, O: w! L5 H- S; I( P
(4)工程代码生成成功。5 q" P/ l6 z) G7 [6 p* h/ o6 w

' h- n; T9 A' e- l
e8f46d59a1fb4af589feb613fdff17b4.png . Q3 w) T) w( k% b* L
8 ^9 t. z  h4 S/ ]& d  C

9 h/ M9 ^7 Y; W8 C 三、在KEIL 5中编写代码
0 J, {8 Q2 F- ?4 D
1、使用KEIL 5(MDK)打开项目工程文件( S: E0 R* s9 m4 R
源码使用说明:使用前必须把项目工程复制到无中文路径的文件夹下使用。
, f0 D' ]) l5 ^6 @8 |! E2 M' m8 ]/ {(1)找到刚才新建工程的存储路径,安装项目名称,打开项目工程.uvprojx。' h' F  Y) G- q$ e% }8 C0 D3 s# t

3 \/ S+ A0 |/ p1 ^& x: p
37170661111547238cfcd51f580aab01.png
6 Z- g3 b3 e) d+ l+ p$ {* Q% W5 l. ?/ k) F
2、添加SPI Flash读写验证程序* f2 w& m% o' a1 @! o
(1)main.c文件中,初始化LED1、LED2、LED3、LED4默认为点亮,并在while循环中添加控制程序,如下所示:实现每隔500ms后LED1、LED2、LED3、LED4点亮和熄灭之间反转切换,并且串口每隔500ms打印一次。
' h: @# j, Q- a* J7 p  q' Z7 _+ u: h
备注:自己添加的代码需要在 /* USER CODE BEGIN 3 */和 /* USER CODE END 3 */之间添加,否则STM32CubeMX更新代码时,会造成自己添加的代码丢失。$ x* Z- Z- _* W6 m+ @2 \5 g
" P4 Z5 M0 c! k/ X
d1194af353cd4b0b88cd9208b1ac7db1.png
% G4 o# s. p% q# d" J6 k& w. \7 V% w  N/ q3 n: z5 v
3c771cb9b6094461aa8ec2befc9d12dc.png
! T/ D5 {# S% h" n9 T: E/ z+ M) B6 t. Q
cd59e19f79524446a278fe4f5fd7b81a.png
( n4 l: |, c1 \* S8 S* p
3 D0 i- Q" L8 w: a% V: Y7 a; K(2)main.c函数添加初始化代码和读取flash的代码,如下所示:7 T: K( M. k! a% ^
  1. int main(void)
    % x% i& n) Y* y2 l+ T
  2. {5 X9 G/ Z" X' J( }
  3.     /* USER CODE BEGIN 1 */. g$ n8 R* `1 J, \  d
  4.     uint32_t w25q_chip_id=0;                                                                                //读取芯片ID
    # T) P7 }7 f+ j4 \
  5.     uint8_t  onebyte_read[8]= {0};                                                                                //读取的字节内容
    ( c% t& d+ b, z. _- n" Y7 ]# F
  6.     uint8_t  onebyte_write[8]= {0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80};                                //写入的字节内容* T% d  B$ s! i5 I0 O( ]
  7.     /* USER CODE END 1 */
    : ~- w# O# E7 ~5 ^$ ^: ]

  8. $ J2 m# w- t% I7 j& S
  9.     /* MCU Configuration--------------------------------------------------------*// c, G" o4 z) ?" s( s
  10. ( z, b; g# K+ S  i" E3 k1 r2 M
  11.     /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    ; w& s8 \3 H6 `3 p
  12.     HAL_Init();! f9 E' ~; R) J5 ]5 n1 d& R
  13. " ~: F; y( v; h  a' b
  14.     /* USER CODE BEGIN Init */6 Z: a4 V$ `/ m

  15. 5 R  g. l7 m, S% m7 X; k
  16.     /* USER CODE END Init */
      \- d. Q' K; s# z4 D0 R. ^$ X1 p! [

  17. # ?' M& v+ U2 I6 d* r# s9 x
  18.     /* Configure the system clock */, r9 A# o' B: A6 q- @& ^& ~: N: I# t) G
  19.     SystemClock_Config();
    3 W2 N0 w0 G( D4 y3 s9 i7 t

  20. 8 @, {0 p+ G( E' Q8 L4 t: _
  21.     /* USER CODE BEGIN SysInit */
    * [1 }: M* N! F5 A! r8 Y
  22. 3 y: g% B: v9 n& @7 Q5 @9 I
  23.     /* USER CODE END SysInit */
    : Z0 q# K+ b# S$ _
  24. ; i& J- u2 d% T( O  E" k; h: W
  25.     /* Initialize all configured peripherals */
    0 t& C2 C; f- z' {, v' ]& D
  26.     MX_GPIO_Init();" A; F* b: x, `- m2 v( O
  27.     MX_DMA_Init();
    7 d: g( S" U9 k6 i) o
  28.     MX_USART1_UART_Init();
    4 n# z' D( l3 G6 k' {
  29.     MX_SPI2_Init();
    ( {- ~4 \; K% N" c1 f
  30.     /* USER CODE BEGIN 2 */
    5 E# x( {. q' p  q+ t
  31.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮0 h& F4 S4 N" x
  32.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮# F; V1 {4 x$ A$ P" M( p' N
  33.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮
    5 @, t# Z9 A/ v, |) h
  34.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_3,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮0 h# H- `" L, N1 @' `
  35.     w25q_chip_id=spi_flash_read_ID();                                                                                                        //读取flash芯片ID  h+ R; ?+ ^! C2 M2 H  M+ I
  36.     HAL_Delay(100);0 J; u. B0 _) S; [6 H: n0 V
  37.     printf("hello world,spi_flash_read_ID==0x%0x!\r\n",w25q_chip_id);                    //printf 芯片ID$ V$ }4 \6 [" o. E$ ^
  38.     spi2_flash_sector_erase(0x0);                                                                                                                 //写之前需要先擦除扇区, o; h" Q" v8 D1 J
  39.     spi2_flash_write(0x0,onebyte_write,8);                                                                                                 //写八个字节& Z4 q) p& {8 D# O. C0 ^1 t
  40.     spi2_flash_read(0x00,onebyte_read,8);                                                                                                 //读取写入的字节
    : h& @) `8 I! z, s
  41.     /* USER CODE END 2 */
    ) s9 l: a' q# O; S

  42. * W& I& r6 M8 t6 {+ n, h7 ^3 v, I
  43.     /* Infinite loop */
    ( h# v+ R6 |5 b% v) F2 |
  44.     /* USER CODE BEGIN WHILE */
    5 z2 x6 f2 D8 k% }4 F+ }; i2 D
  45.     while (1)
    , Z% X: P8 `' F9 I
  46.     {# @" b' t2 p& v0 l
  47.         /* USER CODE END WHILE */
    / f5 q4 ^. |( u6 u
  48. 0 }0 c; T/ }! {, ?
  49.         /* USER CODE BEGIN 3 */
    ! ~# \; J) W5 r. i) v7 j
  50.         HAL_Delay(500);
    ' T* F) C$ S( d- c( v1 f& @( M: z
  51.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值
    * i/ A% s- R, J. g' \
  52.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_1);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值% a9 U" Y( t! I% m" s8 E% r
  53.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_2);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值6 R+ u" f3 }- |; a* d' u( N
  54.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_3);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值
    2 L* O7 \+ u7 v9 J3 p
  55.         printf("hello world,spi2_flash_read =0x%0x,0x%0x,0x%0x,0x%0x,0x%0x,0x%0x,0x%0x,0x%0x\r\n",onebyte_read[0],onebyte_read[1],\
    # i0 }2 H; I. b) U  i
  56.         onebyte_read[2],onebyte_read[3],onebyte_read[4],onebyte_read[5],onebyte_read[6],onebyte_read[7]);   //printf打印flash区域的值
    9 T7 G0 }6 \5 P4 z4 k7 u9 A7 R
  57. 7 r2 O8 c7 Z7 f4 H! @
  58.     }9 C! K# z9 g' Y! v( e  G
  59.     /* USER CODE END 3 */$ r& @+ g! }, H) R  I# z
  60. }
复制代码
( u) {8 y1 C8 w- f' v- T
(3)新建w25qxx.c文件,并添加驱动层函数,如下所示:) w% {  ?  k2 Y
  1. #include "main.h"
    3 f8 z: I+ t6 Q+ R: t
  2. #include "spi.h"% o- G) U4 w* J2 F! d: {4 T
  3. #include "w25qxx.h"
    3 j# k  j5 W4 M/ t6 P( ~9 ~
  4. /**
    1 |: m7 A/ x- Z8 N- \
  5.   * @brief SPI1 读一个字节6 _0 l( ^% D+ G- a
  6.   * @param None  A1 z) f8 `, x1 Y4 A  x3 G$ B
  7.   * @retval None8 X7 H2 T; q- i2 h
  8.   */! Z$ w; [3 J: O& E) ~! f9 ^; W7 j+ r" A- e
  9. static uint8_t spi2_flash_read_byte(void)+ U* q7 c) ~4 }5 O
  10. {
    5 ~3 o/ z7 `; L/ Q0 A: j
  11.     uint8_t t_data, r_data;
    ( D2 w2 ~, z( v4 o0 D4 u7 {# ^; C
  12. 6 O7 @) z# B+ d  _$ i( L
  13.     if(HAL_SPI_TransmitReceive(&hspi2, &t_data, &r_data, 1, 0xFFFFFF) != HAL_OK)
    - J& d* D* g2 R$ E
  14.     {. _$ k" [8 D7 J
  15.         r_data = 0xff;. F6 m% W* j+ M; o( k$ u" H5 M
  16.     }9 n% |$ z4 o; i1 f1 G4 S
  17.     return r_data;6 ^6 i0 S; i- s* }& f, [
  18. }
    ! O- S* \5 n9 I) Q
  19. /**9 `5 W$ d+ d+ R! R1 L' g& R
  20.   * @brief SPI1 写一个字节  i& o" l( k! i4 `( m. @
  21.   * @param byte 写入的字节
    ) c0 u* s8 O; [: I  J/ |
  22.   * @retval 写状态 0成功 1失败; S9 i5 n% _' e" X+ @% p# d
  23.   */
    . K$ C9 P/ F8 ~5 M9 \# _
  24. static uint8_t spi2_flash_send_byte(uint8_t byte)
    % Z9 W2 ^+ s# a7 Q0 B* {3 t, k
  25. {
    8 \: @3 j, Q$ P$ ?
  26.     uint8_t r_data;
    ) q0 [! L) Z, J2 i. W
  27. * a& v! T5 J$ f$ h7 H  {
  28.     if(HAL_SPI_TransmitReceive(&hspi2, &byte, &r_data, 1, 0xFFFFFF) != HAL_OK)" u5 b  y7 L6 @  q
  29.     {: R" v# h5 u& U' n! j
  30.         return 1;, _1 T/ k5 o6 r" e& S6 x
  31.     }' V+ L' T9 S4 J; n' r' S3 v
  32.     return 0;
    5 N' U2 g( |/ \; M/ U6 N
  33. }
    8 d; g4 z; D: c& E
  34. /**
    / w* ~# S: n  t; A2 B
  35.   * @brief FLASH 写使能9 M: D5 Y" b4 G4 H, `
  36.   * @param None
    ! |! p; o* R  W
  37.   * @retval None
    : B; h! |) W" G6 }6 r& Q& t$ C* [) U) O
  38.   */$ I8 p1 r6 {% |* ^/ T3 K
  39. static void spi2_flash_write_enable(void)
    - Z) w5 V2 u! H6 e- U
  40. {
    " w2 R1 c) h% g  t3 K
  41.     SPI_FLASH_CS_LOW();' w8 q; Y" v! t4 r
  42.     spi2_flash_send_byte(W25X_WriteEnable);
    # ?; O$ _- k: K( j* E" o* [0 \
  43.     SPI_FLASH_CS_HIGH();
    1 J$ b  X0 Q% g/ I) R  I$ I; W# Q& o
  44. }
    ) }1 C8 n9 Z' K$ N
  45. /**
    ) ^: K" W( m" H/ `
  46.   * @brief FLASH 等待写结束
    1 x% ^, g- @7 V; F+ z
  47.   * @param None
    2 S1 C5 T, {) m1 Q0 N
  48.   * @retval None
    ) F/ Q1 ~' ~4 y
  49.   */
    7 u1 q; s5 P2 x4 N% }
  50. static void spi2_flash_wait_for_write_end(void)* d" o- s' W3 I& c9 ~: v1 K7 p
  51. {  j, b9 O* z* B. M+ P
  52.     uint8_t state = 0;" y" F! c$ h, d$ G

  53. + O. ]1 z1 j8 Q* m3 r
  54.     SPI_FLASH_CS_LOW();' [$ r$ J  x/ _0 m
  55. 3 P: Q/ Q; K: j- s( k, z  m4 L  |- d
  56.     spi2_flash_send_byte(W25X_ReadStatusReg);
    4 m7 r; h& O6 }9 R1 v. O: k
  57. # K8 {1 |) k5 {% J
  58.     do' o6 S0 g- @8 [, `' W" B
  59.     {  M4 Y5 j1 X% n# m
  60.         state = spi2_flash_read_byte();
    9 z7 y+ `: m) O0 ~" L
  61.     }
    % T# u3 c  H2 p: ^' z8 B
  62.     while((state & 0x01) == SET);1 G  F' v0 V  ~* V; Z1 ]( P
  63. 8 X5 Q5 M& n6 p
  64.     SPI_FLASH_CS_HIGH();
    ; E- t3 Z9 s! A3 Y- |5 o
  65. }# s" [; Z, ~8 [2 V3 G  \
  66. /**
    % Y; O" ]+ ~: P% X3 g* N' d
  67.   * @brief FLASH 读ID
    5 R3 t, b: }! L6 X$ t' `1 U* B% ?
  68.   * @param None
      n: ?( U; V  J. ]* l0 ]
  69.   * @retval None
    * d7 v$ l0 U. r6 y$ R- S
  70.   */2 S6 ]! s4 k/ J& g' ?1 n4 v
  71. uint32_t spi_flash_read_ID(void)8 d' c$ f) l3 Q& m/ ?8 S
  72. {
    8 q; x' n. B$ P6 {9 Y# }) M2 C7 j
  73.     uint32_t temp, temp0, temp1, temp2;
    ; p' ~- F) G. t- t  n+ e' Y

  74. ) s, C3 W9 O% Z  @4 R
  75.     SPI_FLASH_CS_LOW();
    # F4 r7 }4 N: B
  76.     spi2_flash_send_byte(W25X_JedecDeviceID);
    0 Q" }; W( ^  Z* O  q, B: T
  77. 1 f: z. p3 J5 U. @6 I0 Q4 `  o0 z
  78.     temp0 = spi2_flash_read_byte();
    / l: [- t1 |# T: u' M
  79.     temp1 = spi2_flash_read_byte();, `* w' Y) w: n! ]% `7 X- k& p# e* o
  80.     temp2 = spi2_flash_read_byte();) G3 r8 x* o6 \8 F7 Q  S! z8 ]

  81. ) U3 Z& G3 P) I& u' n; n2 t3 k
  82.     SPI_FLASH_CS_HIGH();
    . [4 s+ K' x" B: V. y5 R) g
  83. * H7 p' P. B0 T6 M0 ?
  84.     temp = (temp0 << 16) | (temp1 << 8) | temp2;- C* `5 I  L$ i8 _3 D8 i& c5 e- x
  85. : a% U; C6 ?$ O& \; Y# o
  86.     return temp;
    8 @+ J' n( H5 {3 z0 C, Y: W
  87. }; w, H. s3 X5 e
  88. /**. z9 e' n; t( ~, L  ?  w1 b
  89.   * @brief 读FLASH/ d$ N/ }- h+ q8 \
  90.   * @param addr 读flash的起始地址' d4 q2 n+ j! X! Q
  91.   * @param pdata 读到的数据存放起始地址
    2 z- t" v: {4 Z2 g. S. B; b
  92.   * pdata size 读数据大小
    1 l6 _' Q  R; o& t2 n/ l
  93.   * @retval None
      t" t" T5 F8 ]& p1 X, {; O+ C' ~
  94.   */! L/ ]5 Y' {' u1 X0 n( H
  95. void spi2_flash_read(uint32_t addr,uint8_t *pdata, uint16_t size)* q, [1 j6 R9 v( d8 _- h
  96. {
    * U* s2 b! u# s/ R. X$ X. \8 y
  97.     SPI_FLASH_CS_LOW();
    7 P7 I. I- W  x; @3 _8 M1 Q

  98. & H. j9 Y  Y4 T& W! D
  99.     spi2_flash_send_byte(W25X_ReadData);! J; d! E0 Y+ S

  100. 9 L5 q% _2 C* \- k+ J( c
  101.     spi2_flash_send_byte((addr & 0xFF0000) >> 16);
    & t2 l5 @  g  C0 |. U
  102.     spi2_flash_send_byte((addr & 0xFF00) >> 8);7 j, y! h; Z5 M5 e" l
  103.     spi2_flash_send_byte(addr  & 0xFF);
    $ C. y0 v( X8 O$ [8 ~* p
  104. . K( ]) g. d) Y, K) Y4 W/ s
  105.     while (size--)
    5 h6 }' h$ o$ [% E3 l8 g' d
  106.     {
    $ R" @5 ^3 C2 u0 ?
  107.         *pdata = spi2_flash_read_byte();4 K' V) J; R! P$ @8 U. L: J2 v
  108.         pdata++;
    : o% R: L3 Z2 W7 a% O
  109.     }8 d  @' D7 E% U* ~) u  y

  110. $ s; J; j/ m; ^0 |+ H
  111.     SPI_FLASH_CS_HIGH();/ f$ b# o4 J/ r7 g' `/ i) u/ i
  112. }
    3 m2 M. \! ]* o# Q6 S) _, E
  113. /**
    . x: o: z5 q" Q0 p( j& f
  114.   * @brief 按页写FLASH
    0 C/ s0 ~2 F9 a! n: M
  115.   * @param addr 写入flash的起始地址
    1 i3 O6 V3 B: |( u0 F: r
  116.   * @param pdata 写入数据的起始地址1 f2 L! D. b- h$ p4 a" o
  117.   * pdata size 写数据大小' z+ i5 B4 s9 D0 _* \
  118.   * @retval None
    " \6 m9 Z* U4 Y" Q  U1 U/ ]+ e
  119.   */
    $ W, B- P  V4 a2 y. n% i% W
  120. void spi2_flash_page_write(uint32_t addr, uint8_t *pdata, uint16_t size)
    3 L* V3 q2 O1 J: p) @3 C2 q
  121. {
    ) \: d7 w9 ]* y0 L( G
  122.     uint16_t i;
    8 }/ ~$ n! v  P2 I; d# j& ]" x; }

  123. ) w  T/ s: h4 }* l
  124.     spi2_flash_write_enable();
    8 W0 g7 N. I$ E$ J8 T: l, |6 x2 y

  125. 0 p( E7 ^/ r. Z8 B: }* g' @# S- E
  126.     SPI_FLASH_CS_LOW();( R8 u, \; z, W# Q1 Z

  127. 2 }. }5 ?8 \) L
  128.     spi2_flash_send_byte(W25X_PageProgram);
    " d+ \0 ^/ ~- F- |0 y$ }1 d
  129.     spi2_flash_send_byte((uint8_t)((addr)>>16));  B" [' ^  V2 H+ m) M. {6 e0 K$ }
  130.     spi2_flash_send_byte((uint8_t)((addr)>>8));& L7 x; [, T/ }1 z) d8 `  O
  131.     spi2_flash_send_byte((uint8_t)addr);( Z+ Z5 w# j1 ~
  132. 3 Y# `( y8 i$ Q9 ~
  133.     for(i = 0; i < size; i++)9 E, l3 t! o& M4 s. @7 b# L
  134.     {
    , e1 l6 c- r$ a% E: }, l1 L
  135.         spi2_flash_send_byte(pdata[i]);0 Q9 g; T7 d" ^4 E9 B+ m
  136.     }+ z/ N, M& B  g7 a2 I0 A

  137. , |& S" P  z( \+ o" I8 }# P
  138.     SPI_FLASH_CS_HIGH();4 M& H/ f+ c' s3 _' Z4 |
  139.     spi2_flash_wait_for_write_end();  Q( o, W8 |# k1 P6 ^1 R! ]- x
  140. }/ a( g  O* T* T6 x
  141. /**
    & p7 e+ E; M5 C: d# g/ n
  142.   * @brief 写FLASH. X8 o) j$ x5 s3 z( J
  143.   * @param addr 写入flash的起始地址' U$ f* a7 L6 Z
  144.   * @param pdata 写入数据的起始地址
    / o3 X* z! x# v6 _' T
  145.   * pdata size 写数据大小0 N# R  ]! t$ i+ K$ O
  146.   * @retval None8 @, Q4 G2 w  q* q1 L5 S1 ?
  147.   */% ?4 Q# i  N1 a3 R
  148. void spi2_flash_write(uint32_t addr, uint8_t *pdata, uint32_t size)/ Q( n' t# M7 }- t8 }
  149. {
    " X" Q6 Q  A" r8 N) y
  150.     uint32_t page_remain;
    8 v# y/ V% @! x  [& Z9 I

  151. * ~+ ]6 ~1 ?& C4 b$ |
  152.     page_remain = 256 - addr%256;' t0 I  Z0 T; E+ C
  153. - o: L: q- I! h# H
  154.     if(size <= page_remain)
    : _3 w  Q* Z7 c  F
  155.     {
    % {$ s+ f0 d( a$ I
  156.         page_remain = size;1 M( ~7 x% A! y3 b& ~
  157.     }
    / N  Z& |  e7 c5 y! v
  158.     while(1)( L6 h$ s7 p8 u+ Q* ^
  159.     {& {' B# X  `" ]; F
  160.         spi2_flash_page_write(addr, pdata, page_remain);7 d/ w, E5 _2 F1 `; r: Z: Z
  161. ; t, W7 ]+ I; K7 N+ D+ k/ `
  162.         if(size == page_remain). `( H9 \% S# L
  163.             break;3 Y' k  W% G" Q' a  l, ?3 g
  164.         else, `) q$ }1 d' O9 _3 z" L) I/ \  }
  165.         {4 w0 f* C( s; R
  166.             pdata += page_remain;4 d2 F  Z+ }7 K' t" n' O0 ], k
  167.             addr += page_remain;3 m, D% N0 O9 e( ]

  168. 9 T0 n" y* x  ]
  169.             size -= page_remain;7 ]7 Z* X2 w' l6 D# M
  170.             if(size > 256)
    ' i' Z* P/ _+ Z; M, D
  171.                 page_remain = 256;
    : k! s0 R& D$ }" E: T  V
  172.             else" C' z, Q. E, b6 J
  173.                 page_remain = size;
    6 S5 h9 e' W% x* |) W$ o. I% Z
  174.         }
    6 }8 P0 a4 L: T* W- d4 i
  175.     }
    + R- \, ?4 H1 ^7 |
  176. }
    6 J% c% h1 B; C
  177. /**" A2 I- W- u! A3 N" W7 N) J
  178.   * @brief 擦除FLASH扇区
    : {3 o* R+ k  y) Y8 J8 ~, C
  179.   * @param sector_addr 扇区的起始地址
    8 i7 f- z; H' {9 O- v1 M3 T
  180.   * @retval None+ x9 }# ~, q1 l" K# e
  181.   */
    : e1 @" w0 [& S
  182. void spi2_flash_sector_erase(uint32_t sector_addr)
    9 Q) Q3 J# ]4 e* Z; ~
  183. {% j" b+ j1 b( p/ O' X1 n
  184.     spi2_flash_write_enable();# [. x, c5 [# T, P% N
  185.     spi2_flash_wait_for_write_end();
    2 U; c  O+ @# q0 m" ?5 B$ @% e

  186. ' r; |+ B4 Y# x& S7 T
  187.     SPI_FLASH_CS_LOW();1 M4 L, x; p& p- M$ S& ^+ l
  188.     spi2_flash_send_byte(W25X_SectorErase);
    / Q& h$ E+ j5 \: u( m0 h
  189.     spi2_flash_send_byte((sector_addr & 0xFF0000) >> 16);. M. B5 G  ^; I$ o
  190.     spi2_flash_send_byte((sector_addr & 0xFF00) >> 8);; X& t. p4 c2 \' r* X* `
  191.     spi2_flash_send_byte(sector_addr & 0xFF);
    3 g' _" X3 B* k7 {9 F

  192. 6 Y8 |& [8 t# R; |
  193.     SPI_FLASH_CS_HIGH();
    9 O! g% \1 n' W- R
  194. % H& p6 [. h8 E7 ]$ E- j/ V+ u) Y
  195.     spi2_flash_wait_for_write_end();& Q$ {. U7 t6 a4 K1 \  x
  196. }% @+ Y+ r7 n8 K5 k
  197. , B0 Q6 z* T& d. U. ]5 Y) m
  198. /**
    4 ^6 N% C+ T0 Q- ^- d- Y9 f4 W
  199.   * @brief 擦除FLASH块3 R1 [( N2 m; T
  200.   * @param None/ ?6 M" k: ^/ U: U5 j+ T% k
  201.   * @retval None
    ) H; [- f) B4 F
  202.   */
    % G2 b) A2 a1 Y; l( s
  203. void spi2_flash_block_erase(void)
      O- V/ _* w$ v1 z# E, r) Q
  204. {
    ! u% B1 r3 j& W3 H
  205.     spi2_flash_write_enable();, j4 a) B! G2 E' s; T) m: c

  206. * ]; A# K3 r3 \" Y' Z8 O
  207.     SPI_FLASH_CS_LOW();& |7 ~& q: O9 m$ W
  208.     spi2_flash_send_byte(W25X_ChipErase);- {# B' o* a% T. V5 d* E: x& a
  209.     SPI_FLASH_CS_HIGH();
    8 `4 K# a. P7 v5 I6 b% X. c/ m

  210. 3 T2 @2 _* s7 ^
  211.     spi2_flash_wait_for_write_end();
    6 O; X% ^3 w8 R% ]
  212. }
    . w. N2 e7 ~8 b, i
复制代码

- }% Q5 X2 u" e# T# S2 }2 j3、设置编程仿真下载模式
) @' I9 `  Y! G: A(1)选择Options for target ...>>Debug>>J-Link/J-JTRACE Cortex,点击Settings>>选择Port(SW),可以看到搜索成功SW Device,表示芯片可用,可以下载。
) E: r5 [# F+ I) \  w4 f0 \
+ M% J( B9 P" n1 S5 _3 ?6 X
edefb6b9a2a14be3b1221ad6d5c7a8a2.png
7 ?" f+ D0 ]* t% F# z; f+ a  ^7 k  w( c0 I​​
3 d& o3 z* D2 q8 j- u  x(2)点击编译,完成后提示“0 error(s),0 warning(s)”。  P" l) G& v% C' a2 K
" H0 D6 `# [: b4 w. B% d3 S
e0b7e3e353b345a7987fe5f821de2dda.png 0 z4 d7 N  a( [6 \4 j$ `1 c1 v& @$ g1 V

% L/ p+ [2 c6 p6 d(3)点击Download(或者快捷键F8),即可下载程序。
8 @, C0 I! h4 n5 ]: D1 H0 N- `6 d7 T  D( R* S
eb3f250f19e44862a7df80c2492fcd01.png . f/ }- D. k5 P. s/ J# d
​​
: B  X# W: G' A' u, g; ]
0 e/ ?1 d8 }$ g& |+ ^3 E6 B6 S(4) 如果下载程序后,没有看到LED1、LED2、LED3、LED4闪烁,可以按下述方式设置一下(Reset and run表示下载后自动复位和重启运行)。或者重新彻底断电再次上电(或按开发板的Reset按键复位MCU即可)。
5 x$ A- C0 W. N4 S7 X! s3 J9 \5 M0 P$ N+ X8 l( u. `
9f8aa1a4e8f04041bcf5cb183facd100.png
& N4 `" x3 [3 v- i( k- ^- s
$ c- H- r+ H0 X! C( ?' a4 }
0 ^( Z) v- Q$ d  o1 `" i; ]
4、SPI Flash实验效果展示
1 d0 k& q' E% E; ~8 @6 s- T      (1)  程序烧录到开发板后,即可看到LED1、LED2、LED3、LED4初始化后每隔500ms闪烁一次,并且打开串口助手后(串口参数:波特率115200、N、8、1),可以看到printf每隔500ms打印一次log数据。+ W- Z  R2 _6 ^1 X# Q

4 x9 y" O  F6 z' Q  q9 E7 ?
954468e85b9f44299916e9c6b3440ad2.png
7 g8 h- H% u0 B+ s& t. G( E3 ~
/ J  f- x! p2 g: p (2)屏蔽写入flash的语句,可以看到此时串口log打印的全为FF,则证明SPI Flash读写有效。
2 _/ M: a( u* X8 N/ v# ~" m/ S# `# `2 V
b37dd10545e442c3924a75955386b09c.png / g. M) a; b( |' `  a

( f: J. f9 Q/ v) ~ d36c42cb706f4a2d8103858b70c8d803.png
' t: V1 y6 q7 |$ R- G3 ]* d; O0 g, A) Y1 M2 p* i+ Q' _5 ~
————————————————
+ G# a# G8 {+ L" E3 j版权声明:智能小屋ZYXC
1 E- m. P2 @% _# s; }  ]3 b+ F
, j( `: }% k2 Y0 w; c4 c2 @; ~' {5 \2 x& l& ?/ A7 w9 R# F
收藏 评论0 发布时间:2023-1-13 19:00

举报

0个回答

所属标签

相似分享

官网相关资源

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