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

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

[复制链接]
STMCU小助手 发布时间:2023-1-13 19:00
   一、开发板平台简介:
. E! I+ \( `5 o4 |1、开发板资源简介
0 g$ j7 e4 m8 k  x3 e
(1)开发板主芯片型号:STM32L431RCT65 n( x; X8 ~: `1 y. n
% Q% T" J' h8 G- H$ S
(2)开发板主芯片封装:LQFP-64_10x10x05P1 Q. `) p2 }) t! Q

9 U0 M% |/ B9 J* X3 P(3)开发板主芯片内核:ARM® Cortex®-M4. ]. P* p- q. w
) e3 }6 R% _2 a+ U- T! j5 c
(4)开发板主芯片主频:80MHz
7 i" I$ [+ T3 l  s' r. N; R8 y& b) l* K7 s6 z1 @
(5)开发板主芯片Flash大小:256KB
, G/ p+ E2 f0 R7 u# p. U
' n; n, C0 J' M5 y
(6)开发板主芯片RAM大小:64KB; _& r# ?. F0 }

9 r- ], {! T, u0 Y6 n( c' a bb91345227434ee7a43a34bf3fe366e6.png
9 ~6 u$ {' M* f8 X3 W

( R7 q: ^) }! |$ b 929c0cce04aa4115a8623703982bf3e1.png 9 G* ^, l' A; l# \! l7 G

: B& q% _. Z4 S5 `- c6 W: m' [2、LED灯资源
+ o; p; k- V  T(1) STM32L431RCT6开发板共5个LED灯资源,其中一个红色LED为系统指示灯,指示开发板供电系统是否正常,如供电系统正常,红色LED为上电常亮状态,硬件原理图如下图所示:# }7 {. x! r% B5 u) l0 Z; h
. ?6 J2 r1 _" m0 q0 E
0f177691c5814f16a508bcfa747f87ac.png
. N" }6 u( P4 J5 Z- M4 o  o" |- y+ y% s6 e$ k
(2)其他四个LED灯为黄绿色可控LED,高电平点亮、低电平熄灭,计划用LED常亮验证看门狗的作用,硬件原理图如下图所示:
& y# q7 M* O  X) W" n7 f  o
7 G5 }4 O7 }% R, N3 w" g3 G
e923ad767b8448b7ac37c4ed693724bd.png 0 p& O2 P8 D& B# K- f9 x5 d

% y8 {: u# W- t​3、串口printf打印工作原理
- q/ \* H+ r: Z1 P0 C2 K 串口全称为串行通讯接口,即数据在通信线上一次传输一位,按先后一定顺序传输。我们通常所说的单片机串口准确来说应该是串行异步收发传输器(Universal Asynchronous Receiver/Transmitter,UART),使用TTL电平,串口需要RXD、TXD、GND三根线进行通信。
# w6 c! R9 L( g% O0 \3 t1 o
: z0 |1 u5 A9 V2 S- v6 W
     (1)我们选用的STM32L431RCT6开发板串口1已通过USB转TLL串口芯片CH340G引出,使用时,只需要用公对公USB线连接电脑即可(注意也得需要安装CH340G驱动),后期验证试验也可使用该串口作为debug串口。
% v( Z5 Y/ h0 _. m' J
5 |" G( o. l" x1 ]5 c     (2)开发板上的其他串口已通过排针引出,为TTL电平,通信的时候需要注意选择对应的电平模块,如USB转TTL串口模块等。
/ i2 N! c1 k7 X- W6 a. \

1 D5 U3 e0 |" r( m9 ?5 Q# yTTL转CH340串口,硬件原理图如下所示:) \& y2 B; V/ u
: p$ O9 j) O' c8 X8 Q3 _
4df1b489cb944c26b55013001061675d.png + Z% l7 T7 h; {% r* Z; H
$ ~& b) n8 C  Y( K+ l" p# v
4、SPI Flash简介
8 T  t3 [3 ~" r0 O/ s) {, j(1)SPI总线介绍, J% ^# L5 A( o& Y5 ]
SPI分为主从工作模式,通常有一个主设备和一个或多个从设备,本文中MCU为主机,W25Q16为从机。
# i$ w; c$ Q" C2 I. Y4 i$ X7 k2 I

1 M4 S9 e& h, r/ oSPI通信有以下四根线:
0 F; Y  z. L4 D5 }3 t: [9 r0 VMISO:主设备数据输入,从设备数据输出。
/ v$ m1 H. K6 u" [7 {MOSI:主设备数据输出,从设备数据输入。
" s4 H* g& W2 U0 T' d/ q2 i( `SCLK:时钟信号,由主设备产生。
4 ?# U) M8 T+ V; Z- ^( p/ }
5 g- M3 K0 B3 t$ C$ Q
CS:从设备片选信号,由主设备控制,低电平为选中。
. Y: M+ S0 _3 R# b6 R* [" k% l% u) p/ c$ G
(2)SPI的特点:, m) T. N, P1 J" D% v" @
高位先发送,共有四种工作模式。3 ?/ \/ S, Y' r! f  d, z3 ]5 |
CPOL(时钟极性):规定了SCK时钟信号空闲状态的电平(0-低电平,1-高电平), s- J6 Q- F; _: H$ _; l( |# o
CPHA(时钟相位):规定了数据是在SCK时钟的上升沿还是下降沿被采样(0-第一个时钟边沿开始采样,1-第二个时钟边沿开始采样)3 Q2 q' h" v* J9 z7 W# \
2 w4 Q% C* ?; _
模式0:CPOL=0,CPHA =0  SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据)5 _4 e7 a8 e( v; U
模式1:CPOL=0,CPHA =1  SCK空闲为低电平,数据在SCK的下降沿被采样(提取数据)% Q! A/ {: A/ R: h
模式2:CPOL=1,CPHA =0  SCK空闲为高电平,数据在SCK的下降沿被采样(提取数据), a4 n& ?1 N0 M; [8 S8 x0 E- S
模式3:CPOL=1,CPHA =1  SCK空闲为高电平,数据在SCK的上升沿被采样(提取数据)' F6 p1 X, g" G! |4 Y
# @5 ]! j9 O- O$ L2 h: S% @
(3)开发板 SPI Flash原理图如下:0 H4 q% J$ H6 D9 m# L, h( l6 {
" o7 _3 ?6 U! Z. ]3 `
da82f3d13fef48dd8d90aa170757e360.png
3 Z& h; y1 [8 j0 ]
& g$ b  r+ b! ^3 E+ ?/ K9 G/ _, ?* l$ O
  二、串口printf实验过程
5 @8 n4 N, i+ w1、新建STM32CubeMX基础工程
* u/ G( W5 b: x/ z- d6 H
(1)打开STM32CubeMX,点击“File”-->"New Project"% k1 ?/ s  P* f: \2 E7 @* L
; h" l: f5 H8 J) k+ a
2399170347904e959c855fefd5877f84.png
* M# \- D" u0 m! }

0 u7 a9 h) y7 ~( q/ H  s- i( b(2)等待打开主芯片选项界面(大约1分钟时间)。( ~( ]" K- R6 l6 u( a" i
6 {* F1 j5 n* c+ z
d74d2fdc60c9440483e2b7cf8d928e7f.png 4 p' Q9 g. `( j* K6 p6 j3 P

8 z4 w6 K  w/ c9 m5 t' S. o. u" y(3)昨天搜索框中输入(或选择)所需的主芯片型号(因为我们用的是STM32L431RCT6开发板,所以此处选择STM32L431RC),然后在右下角选择STM32L431RCTx(因为开发板主芯片是STM32L431RCT6),左键双击即可打开新建的项目。
. s* U! h9 h. M# S- w; G! D) T
7b80345238d74bea82ce70e1a348f7b4.png
% d3 J! N0 {9 b5 V$ y9 \, }" `- c' J
4 a( Q: l, o3 L5 G(4)选择时钟源。
. M5 d$ y2 d8 Q0 O+ T% p(1)因为开发板上有8M外部时钟,硬件原理图如下所示,所以此处选择使用外部高速时钟(HSE)。4 E3 Q: |# S6 B8 E+ T8 o) V. C
+ E$ `1 g; k0 k. K! f: U1 O
3f65707eca104663a3dede6d25dfa961.png
- N2 z( ~  ?6 c7 ~, I
9 T5 ^6 }( M% f' Z(2)因为我们没有用到外部低速时钟(LSE),此处不做处理,如下图所示。7 H" H( b5 q6 o9 F1 p
. v) g( `0 s( |9 i1 V9 o' R
72119b971f62410fa8344f7f9fb9f389.png , d, t  y) u5 `6 @1 i

- O' Q/ W, \6 Y" j; ?% w2、配置GPIO控制LED/ X& g( \, ~- x% U# N8 p8 |
(1)查开发板原理图得,LED1、LED2、LED3、LED4的控制引脚分别为:- |7 i9 a/ K. I% O
LED1——PC0
" h9 y9 H' P+ T* H8 CLED2——PC1  ?4 N5 t; u4 p
LED3——PC2
7 g0 O3 @& m* b; ALED4——PC3/ J: f$ h" v7 ?# c& p* y! K" ~
# G- j9 m% d0 T3 h
(2)配置LED的控制引脚为输出,输出频率、输出方式默认即可。( i, }; {5 A& D, i( j+ c" I6 l8 T
鼠标左键点击PC0,选择“GPIO_Output”,表示设置该引脚为输出模式。* B9 U& W; Y# i
鼠标左键点击PC1,选择“GPIO_Output”,表示设置该引脚为输出模式。6 P8 w. m9 \: D# |4 `8 I3 C3 O2 ~7 X
鼠标左键点击PC2,选择“GPIO_Output”,表示设置该引脚为输出模式。
5 R7 T6 {6 E/ T2 \鼠标左键点击PC3,选择“GPIO_Output”,表示设置该引脚为输出模式。% P5 F4 X# V8 H

5 z- Q9 U: \( b! g5 b5 K# Z1 o2 x
492907c1e71149819adbaee4516a2af4.png : c: n7 e% i0 z: M) h; H% W' z' Y
) g" z: a& O% p  c6 v& ^
c75d3f10ba2e427d9c1a10fad9bf5471.png 3 I2 E" ?2 s4 S. b" J7 V* F

7 ~6 f, Y2 j5 }! v8 p; N(3)也根据自己的需求配置GPIO的参数,如输出方式、输出频率、上拉下拉等。因为GPIO控制LED的要求比较低,此处采用默认参数即可,不用修改。9 `" a) f3 Q& N& |: T' B

. _+ `" \/ {1 ^4 k
38ff4b80e1c5495ba3076a5158fd91fa.png
5 J7 Q; J+ E# B& P& D
1 J6 A; t9 c. o​​3、配置PA9、PA10为串口
, c; U8 q) b# p6 v2 ~查原理图得知,串口1使用STM32L431RCT6引脚为PA9-USART1_TX,PA10-USART1_RX,引脚设置如下:7 Z( l6 i1 K; f/ ~* S/ c

  N6 P1 H+ F) A' v1 E
497b8ee0243e446784e64a3c31e8a26f.png / Z- }' t6 V, p& C; Y
. }7 H4 f0 ~( Y1 _9 H5 V0 h- n
(1)序号1用来设置串口收发引脚的选择。
# V( O/ K% P& h, S/ y2 |6 @; F(2)序号2-3-4-5-6设置串口参数,如波特率115200、8位、NONE无奇偶校验等。
' a5 g' z2 \5 f  Y7 P& R- z( X" M
2 Z8 e) \" F! F5 S& L3 x 4、配置SPI Flash接口
3 `; J: o: ?# T' E0 e(1)查看STM32L431RCT6开发板原理图得知,芯片原理图如下:5 l0 z: P. i' j9 A5 A( q( O( i( e
0 p+ D. |+ o* Y0 G% V! K* e
11aaba64f3d34f9ea1e4cbbfc0fdc102.png 8 i. s! [5 F& o( Q
$ D8 o* s" ^" h5 ]2 W
(2)SPI Flash接口对应芯片的PB12、 PB13、 PB14  PB15,芯片引脚配置如下:+ x$ Y8 l0 q6 n$ ~" @
PB12:SPI2_NSS,此处设置普通输出IO即可。不能配置成NSS" {9 ]0 B4 y2 s0 R
PB13:SPI2_SCK
# V; F# Y) z. v9 JPB14:SPI2_MISO! E  w% C4 t3 U4 k0 |7 i
PB15:SPI_MOSI6 n! C! s- B( ^% @
! u3 [  @" |) D; B- Q
ade62d1658174af99a93e3928b62849c.png / C1 |1 T9 n! e) ^6 x, Z
5 e8 Z" x; u( {! X9 v+ X  k  L
(3)设置SPI引脚参数,并选择 Full-Duplex Master 全双工主模式,此处不开启 NSS 即不使用硬件片选信号。& C, P( M  o& L% v5 j2 v
3 b! i+ p" e( i& B1 {
c932367c299e46979e55a3e1a05d2c7e.png
8 Y5 u% e/ b- F$ N9 ?7 O! h% e6 V2 G

& A( I' p' L. R/ f* H: D(4)设置SPI基础参数以及时钟。2 r6 T' v: m- \' \2 I' \( Z

( W: M  c5 g) D
e5af1c8c0b0b4ac9a07d740b955028c0.png . l& O/ k$ H" ^& y8 H4 j  y
6 v/ B3 l1 Y9 K; K2 R
dcc8d73ddcbe4082a09165905ac71cf1.png
& N9 r9 [; N) R
7 A5 W8 r5 t; @; | 5、配置项目工程参数
% G* J. L1 @; R6 Y0 u9 j(1)配置时钟树,用于系统内部时钟,以及各个外设时钟等。此处选择外部8M晶振作为主时钟频率,内部最大倍频80MHz。' h  R/ D. z. H

3 Y( B( H) _' `9 G% h
f3ecedd9323f4463b69539906b3c1199.png
3 P1 K) l1 E7 F( w( e; y4 S* R+ R& y, `* k, p5 J' W7 W4 \
(2)完成配置工程。
+ N0 U  C# e( U6 k# {" a备注:需要注意代码生成过程中的继承关系,如图所示:需要保留开发者自己编写的代码时,请根据配置设置,不然生成代码后会删除自己编写的代码(从这个方面也可以看出开发者备份自己的代码是多么的重要。)5 t9 R/ t/ p9 T# O) `
) v4 y, v1 q' z5 Q% K& D. d
efdb616174f54925b6eac31109f227b5.png
% |' _! |6 D5 C5 k( ?​​
! I' [2 ~- k/ ]' M) ]2 X' t 41c3f2716e4e4eda8f62fa3a1d871322.png
: _4 k" o! S/ D( I) w% C! S* `

4 A% y$ L$ _1 l/ N+ F8 s8 X(3)生成代码。' C( X+ q' z/ d$ a! @% d; ]% C
备注:使用Generate CODE生成工程代码前,请确保文件路径无中文,否则会生成项目失败。1 @. i4 T1 v& G8 V- s. |
. i( N1 I: g# G7 M# ?
8eaa7d0babea40ee93f158481a6ee471.png
1 ]2 l9 C% j0 c; b+ ?2 I  O" _3 ^0 h4 K( U+ ?/ s
(4)工程代码生成成功。  b7 a; v$ l* g, w' m4 R4 h; ]

7 V; k# n1 c; d6 e- d5 x7 i1 Y
e8f46d59a1fb4af589feb613fdff17b4.png 0 `2 T& {% v* f4 U( q& ?7 N

5 f/ o! N2 x+ V: R9 a# e1 f6 W9 k+ g3 [; J4 n8 v5 y' `9 C
三、在KEIL 5中编写代码
$ z; v! H' G* y. O: F: \
1、使用KEIL 5(MDK)打开项目工程文件
0 ^# l8 h) Q( }! o源码使用说明:使用前必须把项目工程复制到无中文路径的文件夹下使用。
1 _0 J  ]8 ]8 [% J( @# ^2 v8 m! a% p" K(1)找到刚才新建工程的存储路径,安装项目名称,打开项目工程.uvprojx。' Z- g8 x8 N# J. @

$ ]- c! l1 h& v0 p4 w. G
37170661111547238cfcd51f580aab01.png
9 ^" F' C' X6 E  q/ E0 t! r
! k3 ~2 v3 W3 I: i, t7 s$ M& ^2、添加SPI Flash读写验证程序5 h. L% d8 E* O1 }/ Q/ Q6 P1 g
(1)main.c文件中,初始化LED1、LED2、LED3、LED4默认为点亮,并在while循环中添加控制程序,如下所示:实现每隔500ms后LED1、LED2、LED3、LED4点亮和熄灭之间反转切换,并且串口每隔500ms打印一次。; a- @4 w6 l  t' M6 J0 W+ L: G( V

6 r% N6 m* [$ M1 a" f备注:自己添加的代码需要在 /* USER CODE BEGIN 3 */和 /* USER CODE END 3 */之间添加,否则STM32CubeMX更新代码时,会造成自己添加的代码丢失。2 z$ t4 c/ j) K$ Q9 P
4 D3 x1 q% J5 d5 c9 u, n
d1194af353cd4b0b88cd9208b1ac7db1.png : Q: w. u* T2 ]7 v# @  O0 u

! v" S# D: \% `4 h% Y. l8 ?2 v# m
3c771cb9b6094461aa8ec2befc9d12dc.png # D: B/ b  J% q( ~- s

. q3 h4 c3 I# ?
cd59e19f79524446a278fe4f5fd7b81a.png , `& T0 F- Z0 ~7 x$ Z5 {

- W2 v" S: m6 [6 M7 A/ P8 f(2)main.c函数添加初始化代码和读取flash的代码,如下所示:
+ Z2 u0 R+ r! i- L. v  C
  1. int main(void)
    & J6 H- d  O4 ~% @$ x6 q6 y5 u# l
  2. {3 W0 S) [0 F0 ?" t  ^* C  l: l+ x
  3.     /* USER CODE BEGIN 1 */
    ; Y* i- K% R) n+ F2 |, S
  4.     uint32_t w25q_chip_id=0;                                                                                //读取芯片ID
    : ]% g& r" r* b9 `
  5.     uint8_t  onebyte_read[8]= {0};                                                                                //读取的字节内容
    . a' ]! L- o1 I( u: P
  6.     uint8_t  onebyte_write[8]= {0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80};                                //写入的字节内容
    * N0 \: J+ V& w9 d+ x4 l: U0 \
  7.     /* USER CODE END 1 */
    * `" p/ X5 L% s4 Y' t

  8. 7 z) a& b7 b4 [
  9.     /* MCU Configuration--------------------------------------------------------*/# m7 v* n1 x4 J$ n9 c" K; o! `

  10. - g: H& Z+ Q' r5 [: v7 C5 K
  11.     /* Reset of all peripherals, Initializes the Flash interface and the Systick. */$ M6 g; j% n( x
  12.     HAL_Init();  d& O/ B  Z# R* W' |  r4 h. v

  13. ( [2 x" b- u! p- y2 @/ z5 u
  14.     /* USER CODE BEGIN Init */
    . S) l8 ~* w+ k  t( \6 F
  15. # j/ X4 i# |$ \$ |6 W
  16.     /* USER CODE END Init */& e! S, Y0 B0 p
  17. $ M9 O0 [& g$ Z1 r
  18.     /* Configure the system clock */$ k: Y$ d: D; W! |0 t
  19.     SystemClock_Config();& g0 X2 `/ w$ C- {8 x
  20. " R  a: K) c+ \8 Y8 v
  21.     /* USER CODE BEGIN SysInit */" s/ ]# P- A9 F( X
  22. ) H  @8 @! u4 `
  23.     /* USER CODE END SysInit */
    " z* |1 a. ?( Q9 g7 ?' r1 x
  24. 0 K& k+ l1 {1 L# P5 d
  25.     /* Initialize all configured peripherals */
    " i3 V3 m6 }  b& Q# |& F
  26.     MX_GPIO_Init();
    0 c: s/ ~5 {" w) K* _0 P5 X
  27.     MX_DMA_Init();- y: C" O3 [/ ?) _7 N6 Q
  28.     MX_USART1_UART_Init();
      Y+ ^3 ^6 Z0 n
  29.     MX_SPI2_Init();6 k. S# \9 f+ ]" B6 ^3 z5 m& e& [5 Z5 ~
  30.     /* USER CODE BEGIN 2 */
    ! |+ x2 d1 d0 K
  31.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮
    " P/ L& N3 e. \5 |3 j2 x, W
  32.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮
    2 K" w7 D$ ~8 x3 x2 e6 g
  33.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮
    3 B! T. j, _7 H1 L
  34.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_3,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮
    % G4 h1 \  h! F; _# f& P1 U
  35.     w25q_chip_id=spi_flash_read_ID();                                                                                                        //读取flash芯片ID
    . _* q$ u4 \2 ?
  36.     HAL_Delay(100);
    $ g) a0 P7 v* J& D* A$ Z: H
  37.     printf("hello world,spi_flash_read_ID==0x%0x!\r\n",w25q_chip_id);                    //printf 芯片ID
    # Z% }" }5 t+ d9 \
  38.     spi2_flash_sector_erase(0x0);                                                                                                                 //写之前需要先擦除扇区+ f! ?( i& r- X1 _
  39.     spi2_flash_write(0x0,onebyte_write,8);                                                                                                 //写八个字节
    5 S1 ~% C$ `0 m. |6 k5 V3 K
  40.     spi2_flash_read(0x00,onebyte_read,8);                                                                                                 //读取写入的字节+ T1 w: n  j1 h- w9 ]& X
  41.     /* USER CODE END 2 */
    1 t. y. O( \" C1 t5 _- E# L1 H

  42. 9 Y3 U8 t2 s% P
  43.     /* Infinite loop */# B! k& N; D. v6 r; E/ @
  44.     /* USER CODE BEGIN WHILE */
    + A1 c: ^, ]5 G
  45.     while (1)
    , O0 p' M6 R+ v- C) Y9 E, d! i. p
  46.     {
    + {# c! {1 r  p, ?
  47.         /* USER CODE END WHILE */
    " B  n% N. K) d9 U8 ^2 Q

  48. % i8 X4 L8 h) j
  49.         /* USER CODE BEGIN 3 */- l/ q# J+ K; Y
  50.         HAL_Delay(500);# r3 m" K% o% S
  51.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值! Z1 }* r( z3 n' G) L6 F4 G
  52.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_1);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值- l# P/ ^, ?9 A
  53.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_2);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值" ?. \  ]: M" z- F7 a
  54.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_3);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值
    . D4 X2 b2 v# m% e
  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],\! L# U) h% r: f
  56.         onebyte_read[2],onebyte_read[3],onebyte_read[4],onebyte_read[5],onebyte_read[6],onebyte_read[7]);   //printf打印flash区域的值! _$ X; F! Z1 |1 w6 |2 R
  57. & L: p5 y9 d: H8 T
  58.     }
    1 _6 f9 a+ ]( e6 j, [2 S, [
  59.     /* USER CODE END 3 */
    # s3 f* |: Z- t/ v
  60. }
复制代码

; a3 F# v3 \8 h2 n  v# b(3)新建w25qxx.c文件,并添加驱动层函数,如下所示:% v: Y) D& Q2 L- R# O# Z
  1. #include "main.h"' j' g' d! j. S  q" {' t
  2. #include "spi.h"3 |  \1 ]9 w9 P4 |
  3. #include "w25qxx.h"
    ; i# n4 M5 j" E0 W8 G
  4. /**2 T2 C' G6 w4 X4 e# t8 u
  5.   * @brief SPI1 读一个字节8 o) Y" g  w4 q2 P
  6.   * @param None
    4 f# B& W: s! v$ `
  7.   * @retval None
    - e9 f* w; X! G% c* ]
  8.   */
    7 M9 p2 _& z; t# i  e
  9. static uint8_t spi2_flash_read_byte(void)
    0 l; v* b6 r6 ~$ a& {
  10. {' a9 N$ U+ `5 I1 l& z
  11.     uint8_t t_data, r_data;
    0 Q. I1 t4 P8 Q4 N

  12.   {0 u$ `. M$ K% d+ T
  13.     if(HAL_SPI_TransmitReceive(&hspi2, &t_data, &r_data, 1, 0xFFFFFF) != HAL_OK)
    9 U+ d; U8 n8 C; D
  14.     {
    $ d/ q! M, a1 c! P7 r
  15.         r_data = 0xff;
    . t/ N! P$ @9 J
  16.     }" K& F9 _' H: [" k6 [
  17.     return r_data;
    7 M1 n: V) \! L8 `, u
  18. }
    9 r6 Q7 }: |4 K' w
  19. /**7 L0 E1 d* K8 ~' U$ g
  20.   * @brief SPI1 写一个字节
    7 B2 Z& o% s% @- \
  21.   * @param byte 写入的字节" ]1 I6 t$ n' s) f+ z1 ~4 f
  22.   * @retval 写状态 0成功 1失败% F3 |5 k' v" g9 I
  23.   */
    $ F; B1 Z. }' n
  24. static uint8_t spi2_flash_send_byte(uint8_t byte)
    6 f6 o, L; Q9 A4 A0 R/ ~, {- d9 }
  25. {6 E) N! W+ s$ x$ n* T
  26.     uint8_t r_data;9 m% a" R7 j  D' b9 v3 I0 W+ q% J

  27. : X1 a* k+ r8 K
  28.     if(HAL_SPI_TransmitReceive(&hspi2, &byte, &r_data, 1, 0xFFFFFF) != HAL_OK)" }" G# S2 e2 {+ _, D
  29.     {
    . x5 _7 w* N7 p. J, D  S
  30.         return 1;6 _$ K$ x0 N6 Q4 ?
  31.     }$ b3 r+ H" X2 J  [" Q+ x
  32.     return 0;  Z6 v3 N- {: k1 G
  33. }- \# s% B( t* _" Q6 e& Q& ^
  34. /**
    4 u0 K* M3 F4 p/ s2 i
  35.   * @brief FLASH 写使能
    . k3 \  U+ j. B
  36.   * @param None, O) ?6 _. Z/ D2 u7 f
  37.   * @retval None. V0 T1 _' n* A8 _7 x
  38.   */8 T: X5 h- A& g
  39. static void spi2_flash_write_enable(void)
    . y( n: @9 J1 t1 X
  40. {. {+ O) v" R* e- f; o* o5 i
  41.     SPI_FLASH_CS_LOW();% X; ^; h2 M4 I2 }& Q* R' Z
  42.     spi2_flash_send_byte(W25X_WriteEnable);9 }2 O! F# T( ]( ]; z" s
  43.     SPI_FLASH_CS_HIGH();9 W+ X$ p& I/ b) I- x$ e
  44. }
    ) m6 J9 [) d8 n9 W" n1 g  i, L
  45. /**
    0 D4 M0 o1 S) J: K1 K$ t/ J) d8 T2 J; V
  46.   * @brief FLASH 等待写结束  O9 g7 H% K% U* J  m, e' @1 u7 @
  47.   * @param None
    8 p# g& Q% O2 Q
  48.   * @retval None  p  P! ]% W4 X$ C
  49.   */3 y: s, {+ f2 e+ k2 H
  50. static void spi2_flash_wait_for_write_end(void)* X# g% |0 k" G% `2 z' Q
  51. {& ?% ?. s, y- ?' A; m3 i- c
  52.     uint8_t state = 0;4 x$ M  F3 o  b4 y8 E2 w% _. K7 e

  53. - G6 z3 T( J- C, ?( R
  54.     SPI_FLASH_CS_LOW();& f; g" p. |; X+ ^' K7 v' m

  55. 1 @6 \. [1 R+ R+ ^6 u( P2 D6 V
  56.     spi2_flash_send_byte(W25X_ReadStatusReg);0 s, Y  m3 h5 T$ [* W# ^+ k
  57. ; [. c. X, H- Z0 Z
  58.     do  I# ]/ h4 P1 Z9 }) l9 y; A
  59.     {4 m' u( n) O; `% s, s" c
  60.         state = spi2_flash_read_byte();( d0 N+ Y7 k$ J, L# H9 L3 _
  61.     }
    9 Y. ]- N1 T$ f% a- P* Y
  62.     while((state & 0x01) == SET);3 j. s# T( X) S( r6 g( P+ m' {- H9 N

  63. 4 y. l/ i0 z; m' [0 t3 i
  64.     SPI_FLASH_CS_HIGH();! m7 c7 N, g" L, u
  65. }$ H& y, \+ U0 B8 Y* t9 E
  66. /**
    % `) w! y/ a% I, `
  67.   * @brief FLASH 读ID
    8 \2 l* I: ?3 F* n
  68.   * @param None/ \2 ^/ l  L  W& f" P
  69.   * @retval None
    + i; |7 Y2 ~) V, W! Z: a
  70.   */
    6 j# s# ]' ^- q2 a
  71. uint32_t spi_flash_read_ID(void), X! e7 a$ }( B
  72. {
    5 N$ J8 [# V. N: x9 d
  73.     uint32_t temp, temp0, temp1, temp2;
    ( m1 w+ z' [, Y# H3 g2 @
  74. ' p5 L* e& W" a3 }3 U8 l' [- y; b
  75.     SPI_FLASH_CS_LOW();4 a' X, x# V- b
  76.     spi2_flash_send_byte(W25X_JedecDeviceID);
    ) N6 T; x/ T4 L* v7 j

  77. / U# b( z$ }  y) H. r
  78.     temp0 = spi2_flash_read_byte();
    & o% }0 y" F% ]! V" g( D2 [
  79.     temp1 = spi2_flash_read_byte();: j9 @4 b3 w2 o6 {. o* ]
  80.     temp2 = spi2_flash_read_byte();) }& B0 G: @  Q  y! l& F
  81. # F* a; r# M* V1 _
  82.     SPI_FLASH_CS_HIGH();
    % n! O& H7 X1 N0 S1 C

  83. ! L8 S3 L. {# v3 q; I% [
  84.     temp = (temp0 << 16) | (temp1 << 8) | temp2;4 d; E( B- |: {/ T

  85. 1 Z/ Y- q3 N9 d$ ?8 Y! i  ?& j  p; a
  86.     return temp;/ ~' M: f& N+ Q% ~" P. ?8 e
  87. }
    8 A/ p) J2 S7 i! |( E! Z0 J
  88. /**$ l% k. @: c* A1 S7 a2 n# R$ {
  89.   * @brief 读FLASH& p% _) E) ]0 b0 f* S6 G/ P
  90.   * @param addr 读flash的起始地址2 n; i4 @' d- i! c# _) }' v- K
  91.   * @param pdata 读到的数据存放起始地址" }7 l! W5 @; O) h" b, T
  92.   * pdata size 读数据大小. x; g  [, Y. [2 M. N/ `5 i
  93.   * @retval None# m7 s2 E  y# `0 _7 G. v
  94.   */- w' x) b$ V7 b5 Y
  95. void spi2_flash_read(uint32_t addr,uint8_t *pdata, uint16_t size)2 w: D9 P! R" u  s0 x5 j4 @' C
  96. {- D/ o% C8 J6 N* I9 ~: E- v
  97.     SPI_FLASH_CS_LOW();+ H2 P# x3 [! c" ~/ O  w
  98. 6 m/ Q2 w& [5 q! z/ L: D! i
  99.     spi2_flash_send_byte(W25X_ReadData);  U9 y( E2 l! a& I5 T/ X

  100. ! [. Z( X/ {& J. Z" P
  101.     spi2_flash_send_byte((addr & 0xFF0000) >> 16);4 s2 j- P$ f) n  W) b: U1 W) W
  102.     spi2_flash_send_byte((addr & 0xFF00) >> 8);
    7 u& Y9 ]- \# h- D
  103.     spi2_flash_send_byte(addr  & 0xFF);, g  M6 w% r$ v- q. u! C) M8 X0 F3 K
  104. * W; R$ w5 L. M0 f
  105.     while (size--)- S& A3 I8 O: _6 r3 @+ ~9 e! {6 n/ T
  106.     {8 P7 T1 a6 V3 h2 b9 p
  107.         *pdata = spi2_flash_read_byte();
    0 G# h+ `" x; ^
  108.         pdata++;8 M0 F1 T" H) s& c% t5 z5 R
  109.     }
    . S& Q9 u8 f  i$ V( r& y& m
  110. $ A$ C* X' u, F  E
  111.     SPI_FLASH_CS_HIGH();
    * b3 I+ W/ U* W
  112. }
    3 y1 m& ?0 K- z; C4 l5 }! y
  113. /**
    7 ^; |9 @( P( E- z' W
  114.   * @brief 按页写FLASH
    1 D: S4 N6 }$ n% t
  115.   * @param addr 写入flash的起始地址5 f1 d: R7 y+ l6 _) Z
  116.   * @param pdata 写入数据的起始地址/ s' m% i1 @+ K0 Z
  117.   * pdata size 写数据大小* e) m! ^& w: d7 N8 n
  118.   * @retval None
    1 p0 H6 p! M( d: J1 M8 z" V
  119.   */( o7 G/ z0 [6 g! w/ f  }- t
  120. void spi2_flash_page_write(uint32_t addr, uint8_t *pdata, uint16_t size)
    % r: G% h) x9 Q2 Y% r) F
  121. {1 d5 ?9 q+ w- ]. b8 C; D9 |0 ]# F) O
  122.     uint16_t i;
      l$ ]7 y% H1 r, m

  123.   [" D9 S2 v. n- Y! F
  124.     spi2_flash_write_enable();; f. m; O9 d- W7 T% k6 S  E' C
  125. ; n  j, N2 [" A) h& p+ g
  126.     SPI_FLASH_CS_LOW();
    * ^" E; [" X) Z
  127. " S$ N) Y3 L6 m/ T1 X
  128.     spi2_flash_send_byte(W25X_PageProgram);
    : T8 [& b% p+ b7 a( D$ r
  129.     spi2_flash_send_byte((uint8_t)((addr)>>16));
    6 o$ ?# k& p1 }2 c' m
  130.     spi2_flash_send_byte((uint8_t)((addr)>>8));7 U! y. F& h) ]- B3 o) K) m
  131.     spi2_flash_send_byte((uint8_t)addr);0 i. U! J- w* t9 B% \* L, r
  132. 1 q* M9 S$ @0 l. H! N
  133.     for(i = 0; i < size; i++)
    2 c. P- R. b0 I( H1 b2 g0 o+ W3 Q
  134.     {
    6 r* t6 p+ x$ [
  135.         spi2_flash_send_byte(pdata[i]);
    2 ]  ~# R- Q* a8 U
  136.     }
    $ R  L& M& W: e" p5 Z8 h

  137. & r) Y1 B# ?2 Y! w2 p
  138.     SPI_FLASH_CS_HIGH();
    8 T/ `* f. F, a  }
  139.     spi2_flash_wait_for_write_end();
    # V" h9 t4 k5 Q3 D* s/ \0 i& K* ?
  140. }
    - `0 E8 w' e% W/ S, S0 j( ^4 R
  141. /**
    : J+ H" P5 [, ]2 j  T. }
  142.   * @brief 写FLASH
    + c, o# i0 `% v% L2 H- q
  143.   * @param addr 写入flash的起始地址4 G( H4 w7 |- Z, `3 ~
  144.   * @param pdata 写入数据的起始地址" w- d4 I- q: u0 @3 f+ V+ l
  145.   * pdata size 写数据大小% S5 b' F3 Z' B. w  r# N
  146.   * @retval None
    6 J: @* z& Q4 w1 |* d4 E0 q* x
  147.   */; c9 C. o' m4 c, B
  148. void spi2_flash_write(uint32_t addr, uint8_t *pdata, uint32_t size)
    6 c! y$ L" `6 A* G
  149. {$ f! T4 q. Q. _' ]4 P, t
  150.     uint32_t page_remain;4 C  [+ ^! B5 e; K+ [1 @8 P

  151. ) H8 B1 x" p- C: z6 U2 d
  152.     page_remain = 256 - addr%256;/ I. r% {" ?/ }* a
  153. ; e- R- k& h. v3 m  a
  154.     if(size <= page_remain)) k7 |5 N* h& k6 _! X! z
  155.     {
    , c: y9 H/ K( ?  z8 A0 Y
  156.         page_remain = size;
    , t: K0 h* n  X) ]& d1 o6 I
  157.     }
    ' z7 h4 p9 O" }, j7 E( V
  158.     while(1)
    , J- t! U9 h2 V; M+ \' W
  159.     {
    / Y/ d  T6 {6 g: X3 i
  160.         spi2_flash_page_write(addr, pdata, page_remain);
    0 |- X: A" X  N* `
  161. & r/ J/ d, n6 [: i7 y2 T3 P
  162.         if(size == page_remain)8 \% \2 t$ r$ ?+ G, d" C
  163.             break;
    " }. R. x' W# W) o: }# b6 R
  164.         else
    & R. P& m; t/ j5 g
  165.         {5 s3 i' T! q7 M- ?
  166.             pdata += page_remain;
    - s  P  q0 ^6 G9 l  D# y" k
  167.             addr += page_remain;+ b' g. F0 [) {8 Y. I" v, \" W2 F
  168. 3 a; A  v. |+ A: g! H; B0 X+ s6 b2 c
  169.             size -= page_remain;
    / A4 M9 c+ Z6 W9 H# N2 {! U9 U9 W
  170.             if(size > 256)- H6 w$ ~& Z, E9 h6 Z
  171.                 page_remain = 256;
    / M& A, I. Y6 p4 `" \+ L- i0 B% F
  172.             else
    9 p. q0 p6 t$ D5 `, x5 v- `8 m* ?. g
  173.                 page_remain = size;
    ; k, v& F; V  M( a
  174.         }
    4 C/ S* T9 F/ I  ~5 T9 Z
  175.     }) P0 p4 h$ r" H: M/ }' d! E
  176. }
    + ]4 r" ]* a- _6 t3 ?# N5 ~
  177. /**
    . L7 A, P2 R5 W  q2 \7 I
  178.   * @brief 擦除FLASH扇区$ ^0 [' {) T  f8 q' {' q
  179.   * @param sector_addr 扇区的起始地址
    + h6 l1 {0 g& J3 I7 E  ]& B
  180.   * @retval None; Y* u# ]  O5 x* ], U
  181.   */
    1 p4 I7 |: e9 o
  182. void spi2_flash_sector_erase(uint32_t sector_addr)4 ]8 l( L% x8 F! T0 M
  183. {
    ( C# ^6 O4 ^" L* Q1 M
  184.     spi2_flash_write_enable();
    8 f; q: g/ ?  ?: S
  185.     spi2_flash_wait_for_write_end();
    ! i/ H" l7 S$ i- D. ]0 Y  ^
  186. * {- p# w( [9 m+ B* e" u# G
  187.     SPI_FLASH_CS_LOW();7 @/ I' J# }6 w3 A! k2 t
  188.     spi2_flash_send_byte(W25X_SectorErase);3 e/ |6 `) u2 ?3 u
  189.     spi2_flash_send_byte((sector_addr & 0xFF0000) >> 16);" `2 x" h! ~9 m
  190.     spi2_flash_send_byte((sector_addr & 0xFF00) >> 8);0 p4 c$ v1 X/ P' V! }% f  l) Q' ]; w
  191.     spi2_flash_send_byte(sector_addr & 0xFF);5 z+ i* V$ ?2 F3 J) s8 C
  192. ; ^2 u. \/ _, E" c
  193.     SPI_FLASH_CS_HIGH();
    ; F# F& \; i* u# s% j) J: J/ b

  194. * F) H( c% c) s  v
  195.     spi2_flash_wait_for_write_end();. w- w! L) f  s1 Y6 a, m' ], P
  196. }( U( \! a+ |  H* V
  197. / L% y/ C  c( i4 }1 U; M6 K% `
  198. /**
    ) N0 K$ }2 |% I3 x
  199.   * @brief 擦除FLASH块* d+ c- g* B% w
  200.   * @param None9 A- ?% g, @- e# E2 S+ v. Y1 w! ?
  201.   * @retval None9 [" l+ W7 W* A1 i) E  X; B
  202.   */
    $ c% b- s1 d) M% t
  203. void spi2_flash_block_erase(void)
    $ K6 O, w) E, J$ R; t  c
  204. {
    ( F, L& y( N7 K- \/ r+ Z9 V
  205.     spi2_flash_write_enable();
    0 R. n% D5 }5 I3 j- D+ L
  206. : @) o+ K' v3 B' p4 i$ _
  207.     SPI_FLASH_CS_LOW();8 s7 Z3 O# g4 T& x! x
  208.     spi2_flash_send_byte(W25X_ChipErase);9 ~, P& Y3 l, p; ~6 Q2 O+ u2 N5 s
  209.     SPI_FLASH_CS_HIGH();
      ^3 L/ K0 ^1 Z7 g
  210. " u( B3 l  E+ c% O6 T5 u
  211.     spi2_flash_wait_for_write_end();# |8 ]/ Z8 {: L1 f: M% @+ w0 N
  212. }  j: ~" \$ x0 N: D; }  t
复制代码

" @& G- Z3 k$ P! I3、设置编程仿真下载模式: D) ^# y2 P3 O: E3 S
(1)选择Options for target ...>>Debug>>J-Link/J-JTRACE Cortex,点击Settings>>选择Port(SW),可以看到搜索成功SW Device,表示芯片可用,可以下载。- D0 J6 ?8 j; G' Q, |. L% u

; P3 v5 O' T. p9 b0 z2 q% D
edefb6b9a2a14be3b1221ad6d5c7a8a2.png
) F" @! P) l) s8 t​​
' ~, D0 @5 T! ^, T* h& W(2)点击编译,完成后提示“0 error(s),0 warning(s)”。4 k9 O7 A, w% H/ |) H
! \  a) Q. R4 B4 V- n, r0 G- N3 t
e0b7e3e353b345a7987fe5f821de2dda.png
6 _5 D( X$ i3 [9 v4 ?+ X3 ^: Q: S  x  t/ e/ u1 T5 n
(3)点击Download(或者快捷键F8),即可下载程序。
9 E' ~; s0 I, A' o; ^9 B  B3 F$ K6 P& a3 F; Z
eb3f250f19e44862a7df80c2492fcd01.png
2 r1 _3 \+ X% K) l( P( B0 n​​
) _$ K- _. [" H1 @: q5 B( E' A+ l# W- b
(4) 如果下载程序后,没有看到LED1、LED2、LED3、LED4闪烁,可以按下述方式设置一下(Reset and run表示下载后自动复位和重启运行)。或者重新彻底断电再次上电(或按开发板的Reset按键复位MCU即可)。
& z' U  ~8 v, e3 Z5 r5 r
( Q1 w3 M3 e6 J1 U7 C
9f8aa1a4e8f04041bcf5cb183facd100.png 7 T; q) w5 S' x/ Z. w

3 {) z' ~- v0 H, y& P, }

0 D! f5 v" ?1 x, c1 Y5 z4、SPI Flash实验效果展示
; U) N2 z& O7 J% g3 c  g      (1)  程序烧录到开发板后,即可看到LED1、LED2、LED3、LED4初始化后每隔500ms闪烁一次,并且打开串口助手后(串口参数:波特率115200、N、8、1),可以看到printf每隔500ms打印一次log数据。3 \  N' L+ a0 h+ [4 v5 S
8 T7 f( Z" l" Q  e" O
954468e85b9f44299916e9c6b3440ad2.png 2 b  R  o, ^1 @1 I& O8 Q* Y

" a/ h* u. w' R( b (2)屏蔽写入flash的语句,可以看到此时串口log打印的全为FF,则证明SPI Flash读写有效。
; L1 |2 X: g% v( |# r
0 m1 n. r$ k. p
b37dd10545e442c3924a75955386b09c.png
: U# f+ A% E/ }3 q5 p0 u- f6 H+ d3 T! _5 n
d36c42cb706f4a2d8103858b70c8d803.png
3 ?  t0 U: d1 G( R2 u8 ~& H. _% _! z+ h% e! I
————————————————. @0 L4 X: z/ p% S) r1 l; G
版权声明:智能小屋ZYXC' c& E+ B" @/ _+ [# H( }: Y
" G% J5 b: `$ D+ j5 F
. `/ @* ^9 V* w
收藏 评论0 发布时间:2023-1-13 19:00

举报

0个回答

所属标签

相似分享

官网相关资源

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