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

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

[复制链接]
STMCU小助手 发布时间:2023-1-13 19:00
   一、开发板平台简介:3 Y8 ]: v5 D% r1 ]
1、开发板资源简介

. d1 A4 \" v5 i% r$ |* A(1)开发板主芯片型号:STM32L431RCT6
- g8 Q- }- y8 k1 S
1 N% t% `- {3 }( J7 R6 ^
(2)开发板主芯片封装:LQFP-64_10x10x05P* d0 m. N7 ?( R2 S7 Q2 `

1 j5 g1 V% F1 A* `7 k(3)开发板主芯片内核:ARM® Cortex®-M4
% |2 ]7 L+ o  x8 Q  c1 O% S/ i4 M. x( Z  C! {: I. x. i4 Q+ f/ i' r
(4)开发板主芯片主频:80MHz6 {" p0 o' }# L& e
7 A. l- \: I4 p5 U
(5)开发板主芯片Flash大小:256KB
- K( \$ H/ B; f, V+ m( L

, l. \' E3 T# ~. |% J0 g0 `" P(6)开发板主芯片RAM大小:64KB) Y' U, z4 V- ~  w6 B  |
( e* Z# a9 Z3 B% p; M
bb91345227434ee7a43a34bf3fe366e6.png
6 s! R. q% t- }; `0 k

" q4 H( D1 R, D8 K' v 929c0cce04aa4115a8623703982bf3e1.png ' q+ U. T# w" {% R& w% m- b

4 [/ i1 v( o# `& p" I/ }2、LED灯资源
, v) t/ Z# K* z0 d7 s(1) STM32L431RCT6开发板共5个LED灯资源,其中一个红色LED为系统指示灯,指示开发板供电系统是否正常,如供电系统正常,红色LED为上电常亮状态,硬件原理图如下图所示:8 U, D* y  S/ m- l2 H# M/ j; b; _

8 w) ]) P0 F( K9 S
0f177691c5814f16a508bcfa747f87ac.png 9 y. }4 W1 p8 i; M' I0 U. j
( H' g/ F! u+ {+ X$ N, b
(2)其他四个LED灯为黄绿色可控LED,高电平点亮、低电平熄灭,计划用LED常亮验证看门狗的作用,硬件原理图如下图所示:
$ X6 T' r7 @4 t) I; P8 C0 u# I) ]3 O  I: X" M- T) Y% t6 V
e923ad767b8448b7ac37c4ed693724bd.png
; j$ P3 }* L7 j# ~1 q+ L- u, c9 E8 e9 P6 [1 e
​3、串口printf打印工作原理
& A4 c7 E) I6 B" l& H8 w5 o6 ^* s& m 串口全称为串行通讯接口,即数据在通信线上一次传输一位,按先后一定顺序传输。我们通常所说的单片机串口准确来说应该是串行异步收发传输器(Universal Asynchronous Receiver/Transmitter,UART),使用TTL电平,串口需要RXD、TXD、GND三根线进行通信。3 ~8 V9 F5 L+ G% t& e
/ M; t9 F3 ?& i) f+ p+ ^" w# ~7 P
     (1)我们选用的STM32L431RCT6开发板串口1已通过USB转TLL串口芯片CH340G引出,使用时,只需要用公对公USB线连接电脑即可(注意也得需要安装CH340G驱动),后期验证试验也可使用该串口作为debug串口。' R! b1 Q! s' m- ~; A

% Y4 f. q1 J$ V! {9 z4 u! @     (2)开发板上的其他串口已通过排针引出,为TTL电平,通信的时候需要注意选择对应的电平模块,如USB转TTL串口模块等。5 s' A3 S: |! K5 M( g' r! C

- b3 k& i# v( C5 yTTL转CH340串口,硬件原理图如下所示:
5 z7 [% F  C$ v" j% \+ w" `6 S0 q* {
4df1b489cb944c26b55013001061675d.png
1 V! g8 |9 e) [4 |* l4 p
: S/ |# N& K& t1 b8 Y4、SPI Flash简介
6 K: i7 ^" _# d; {3 W! _(1)SPI总线介绍
* z+ ]: a- Q2 @& ESPI分为主从工作模式,通常有一个主设备和一个或多个从设备,本文中MCU为主机,W25Q16为从机。9 N$ g& Q: p; N

% [* [+ D4 |  @# |# N2 jSPI通信有以下四根线:  n7 H0 Y. \% T$ w6 H
MISO:主设备数据输入,从设备数据输出。7 v$ s" g, C4 O4 r1 N6 r
MOSI:主设备数据输出,从设备数据输入。
$ L3 Y2 R5 ]* O+ q2 q2 Z. FSCLK:时钟信号,由主设备产生。
4 B2 D; v$ N$ J
7 ]9 x4 o3 ?) B  `: R
CS:从设备片选信号,由主设备控制,低电平为选中。, ?  h+ W$ Q$ P4 d6 X

; j* G' H; }1 J- a! {5 C3 M6 o(2)SPI的特点:  M3 N- m+ ^" V
高位先发送,共有四种工作模式。/ q) w) l0 ~1 `" k( e; }" J
CPOL(时钟极性):规定了SCK时钟信号空闲状态的电平(0-低电平,1-高电平)
4 f. e% u2 T4 X. m8 Z: ECPHA(时钟相位):规定了数据是在SCK时钟的上升沿还是下降沿被采样(0-第一个时钟边沿开始采样,1-第二个时钟边沿开始采样)8 X' l4 J- P" J) O
& J) d' e$ ]4 M$ ^
模式0:CPOL=0,CPHA =0  SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据)
' r; F, M2 S  e, i模式1:CPOL=0,CPHA =1  SCK空闲为低电平,数据在SCK的下降沿被采样(提取数据)
/ W8 G6 m8 r9 C3 N模式2:CPOL=1,CPHA =0  SCK空闲为高电平,数据在SCK的下降沿被采样(提取数据)  S, J! h9 k' X" ^  T# v4 b/ _% p
模式3:CPOL=1,CPHA =1  SCK空闲为高电平,数据在SCK的上升沿被采样(提取数据)
& ]9 X7 N3 |/ P4 k6 ?" Z" O: E- x+ P0 ]. e/ }4 c! {
(3)开发板 SPI Flash原理图如下:9 O0 u" E. |8 o3 @$ h3 P) a
  R7 K3 T4 e$ e# d0 `
da82f3d13fef48dd8d90aa170757e360.png % l9 x  f" h; H' H0 m: Z6 m

/ a* e- ~" \- @5 R/ _7 I% B, A& z
9 J) r: N/ x9 x1 \* {$ h  二、串口printf实验过程
8 F; M9 f4 S( d, v6 U! j% }. L" Z7 U1、新建STM32CubeMX基础工程

7 X  `8 C; Y& h4 Q: q! N7 F(1)打开STM32CubeMX,点击“File”-->"New Project"
9 c! v* i, Q5 r
1 O3 B- t0 v' r$ z( \
2399170347904e959c855fefd5877f84.png 2 S1 J! `9 E7 W' R2 `. x7 b
1 ^: F2 p+ j- U3 N
(2)等待打开主芯片选项界面(大约1分钟时间)。
+ m7 i! s0 H- e
+ n% b0 \: A! ]: _
d74d2fdc60c9440483e2b7cf8d928e7f.png
. C5 L$ r9 a2 R) |8 \& h$ R" g
7 H  z( o) @* i# r4 r(3)昨天搜索框中输入(或选择)所需的主芯片型号(因为我们用的是STM32L431RCT6开发板,所以此处选择STM32L431RC),然后在右下角选择STM32L431RCTx(因为开发板主芯片是STM32L431RCT6),左键双击即可打开新建的项目。; J: k4 ^1 V/ @" h
7 r' d$ G$ ~7 v1 v. e# ~9 _
7b80345238d74bea82ce70e1a348f7b4.png
' c, X$ O. t' y" ?
' t3 W; `8 O0 T' o(4)选择时钟源。, q7 @7 `: V$ q; g# L( n8 e
(1)因为开发板上有8M外部时钟,硬件原理图如下所示,所以此处选择使用外部高速时钟(HSE)。( M* i/ [) w% J, O1 k' f* g
+ `# g3 p) Z5 S3 ~
3f65707eca104663a3dede6d25dfa961.png
2 U! M- S6 b! e5 N
2 B. N3 d, e9 D6 S(2)因为我们没有用到外部低速时钟(LSE),此处不做处理,如下图所示。: a. G! {( D3 o% ~/ x- r

/ o- j1 i. D3 t1 [, v4 X
72119b971f62410fa8344f7f9fb9f389.png
( v2 `0 P) N: i8 ?5 f
2 u" j/ i6 U& B5 y, t2、配置GPIO控制LED
% F& W5 a- H# p. C) t+ Y0 x: f(1)查开发板原理图得,LED1、LED2、LED3、LED4的控制引脚分别为:, j2 r! v/ u, ?" J8 N
LED1——PC0& y" i0 C* n2 V2 Z
LED2——PC1
8 _  `( I7 H4 ?) r: j8 H# i* |# r, K. TLED3——PC2' V7 W7 y3 u1 V
LED4——PC3; c. ^. n  ?$ j! Q4 ~  }- @

+ A8 a% d1 N% n9 k(2)配置LED的控制引脚为输出,输出频率、输出方式默认即可。
9 m9 ^0 @: a& g/ @鼠标左键点击PC0,选择“GPIO_Output”,表示设置该引脚为输出模式。% F, }4 g0 S4 y/ X0 N3 _
鼠标左键点击PC1,选择“GPIO_Output”,表示设置该引脚为输出模式。$ E0 ?6 h4 G) h1 E0 t& @% ?
鼠标左键点击PC2,选择“GPIO_Output”,表示设置该引脚为输出模式。
+ i5 o' @9 N7 m2 y鼠标左键点击PC3,选择“GPIO_Output”,表示设置该引脚为输出模式。2 _3 E) W9 V8 b. ^0 _
- n8 {5 _: i: M) Z1 h0 k+ g; T
492907c1e71149819adbaee4516a2af4.png 0 a1 c" L2 P% @6 Q" ]
/ |- F: J# M" T) k0 M3 {/ i$ c1 {& s
c75d3f10ba2e427d9c1a10fad9bf5471.png
3 P. T. j2 M. t: L' f% z: a6 \

5 _) L" M# d, k& ](3)也根据自己的需求配置GPIO的参数,如输出方式、输出频率、上拉下拉等。因为GPIO控制LED的要求比较低,此处采用默认参数即可,不用修改。6 ^$ q* |& }& j) L. Z" z) q) m- ^

/ B, i  i, `/ n: f/ V& Q
38ff4b80e1c5495ba3076a5158fd91fa.png
  k7 d  i0 o) o/ ^% l1 ~
* l8 C/ X  b* I& O​​3、配置PA9、PA10为串口
( V1 K- n' q; w查原理图得知,串口1使用STM32L431RCT6引脚为PA9-USART1_TX,PA10-USART1_RX,引脚设置如下:- [- E3 e% ?& x2 Q* `4 j$ o/ d' F, n
& @. G2 Z, O' e9 z
497b8ee0243e446784e64a3c31e8a26f.png 9 k' h- ]$ A! q. U& J/ y
+ E1 p) I/ B. d$ E
(1)序号1用来设置串口收发引脚的选择。% }4 H8 |1 s/ t8 H' J/ I$ h
(2)序号2-3-4-5-6设置串口参数,如波特率115200、8位、NONE无奇偶校验等。
7 U4 l, n3 v. s
  ?4 F4 e2 ]! w1 k$ s( S1 z/ n" C 4、配置SPI Flash接口$ k" H( n: X3 C; \  f' K$ C5 T
(1)查看STM32L431RCT6开发板原理图得知,芯片原理图如下:
" g5 I" _- T: l& s# Q7 v0 N9 @8 K) |5 n/ x
11aaba64f3d34f9ea1e4cbbfc0fdc102.png ; U& \% a% k0 V3 F8 a  U

7 u5 G+ X$ l0 Y; x+ } (2)SPI Flash接口对应芯片的PB12、 PB13、 PB14  PB15,芯片引脚配置如下:" T/ |, Z' U9 H7 T( r
PB12:SPI2_NSS,此处设置普通输出IO即可。不能配置成NSS; ?' @- j4 \* o0 N3 V
PB13:SPI2_SCK1 c7 p! [9 _; g& L6 Y2 A5 J; D
PB14:SPI2_MISO
- ~/ l8 c2 t! o3 xPB15:SPI_MOSI) V! {1 _. i4 J% X

' m5 J/ N& J) B7 F& M& }& C
ade62d1658174af99a93e3928b62849c.png 5 ]" l* ^' h4 A' e
; w) u3 M) H' a0 f/ `- A
(3)设置SPI引脚参数,并选择 Full-Duplex Master 全双工主模式,此处不开启 NSS 即不使用硬件片选信号。
4 C. c0 l. O4 R6 i* i% d
) E( }8 |9 K: X' H
c932367c299e46979e55a3e1a05d2c7e.png , J3 @: Z) A; s# b

) r1 d5 _7 ]- ?! I, ?0 v; @+ z(4)设置SPI基础参数以及时钟。
' q$ j1 G8 F$ h$ _3 y
0 n7 A( s$ `. K3 p1 Z
e5af1c8c0b0b4ac9a07d740b955028c0.png & E1 c9 J+ G- m# K1 k

9 C/ J- [6 T3 T
dcc8d73ddcbe4082a09165905ac71cf1.png 5 s1 a: h0 [# Z" c7 E! E9 N

+ R& t$ R+ e* ]+ [9 U2 i& {! G 5、配置项目工程参数
& m* G: f; A! }7 }2 f(1)配置时钟树,用于系统内部时钟,以及各个外设时钟等。此处选择外部8M晶振作为主时钟频率,内部最大倍频80MHz。
# F9 j$ s; C' X! M# k0 V) z* f9 k& d+ U3 K- l, P
f3ecedd9323f4463b69539906b3c1199.png ; e. o" X- \7 k" s5 `$ E  U" A
$ ^- W0 P: \2 y
(2)完成配置工程。$ w+ I' {- [" D5 B
备注:需要注意代码生成过程中的继承关系,如图所示:需要保留开发者自己编写的代码时,请根据配置设置,不然生成代码后会删除自己编写的代码(从这个方面也可以看出开发者备份自己的代码是多么的重要。)
( {7 Y& Y2 z  w8 I, i- K  t
. t# E1 ~# k/ p/ N
efdb616174f54925b6eac31109f227b5.png / Y8 \0 Z* Q9 z4 L9 g
​​+ P# n7 j9 `. E2 o2 q7 U4 @
41c3f2716e4e4eda8f62fa3a1d871322.png $ i6 d. b: i( E
, c5 g7 g2 i7 U  \8 u
(3)生成代码。0 n4 [- x9 D& U5 c# F% _
备注:使用Generate CODE生成工程代码前,请确保文件路径无中文,否则会生成项目失败。
6 o/ ?" ]" m) Z, F9 m1 ]( e; D7 W0 d+ N. |% }
8eaa7d0babea40ee93f158481a6ee471.png
2 e! `* q# ~' f! E8 K% d
( `) N+ @+ ?  ~- p7 J+ k; ?2 S(4)工程代码生成成功。
% B6 U& i7 _% i- s1 J  v4 n8 L3 i, Y5 B7 A5 P) S
e8f46d59a1fb4af589feb613fdff17b4.png
. m6 K: K: L8 x+ g; j2 ^7 U& [/ k* Z; |- k$ P' P. s5 D3 V0 }9 C6 k

% X6 K' G' E6 C6 ^; k3 v: z 三、在KEIL 5中编写代码
5 p) v5 }' l: k6 x5 q4 C
1、使用KEIL 5(MDK)打开项目工程文件
9 u$ T% h3 I. f4 Z/ }6 h9 O源码使用说明:使用前必须把项目工程复制到无中文路径的文件夹下使用。7 n* {  B0 l' o7 h" S
(1)找到刚才新建工程的存储路径,安装项目名称,打开项目工程.uvprojx。
4 j7 e( j& H" P
+ z$ D, V- b0 [3 r& T
37170661111547238cfcd51f580aab01.png 1 R2 ?$ p! }  I# J$ c: h2 @3 k. u$ u8 F

' P/ ~" X; d3 s  \2、添加SPI Flash读写验证程序
: R! W6 i( ~1 S' r0 I(1)main.c文件中,初始化LED1、LED2、LED3、LED4默认为点亮,并在while循环中添加控制程序,如下所示:实现每隔500ms后LED1、LED2、LED3、LED4点亮和熄灭之间反转切换,并且串口每隔500ms打印一次。
) ?) r' O& f' j# ]$ v
3 V# p' G1 G$ s% Y4 {; Y4 N3 m备注:自己添加的代码需要在 /* USER CODE BEGIN 3 */和 /* USER CODE END 3 */之间添加,否则STM32CubeMX更新代码时,会造成自己添加的代码丢失。/ y* H6 y) E/ k) y+ O6 G1 U
+ C# s. z+ `5 a# A6 x. I
d1194af353cd4b0b88cd9208b1ac7db1.png
5 f: h8 |; K+ @$ j7 b$ C: s- q+ _; {) o% K! H8 D' e. J: A
3c771cb9b6094461aa8ec2befc9d12dc.png
' l% B% [' F: _4 v6 W. b% a
0 a# {- ~' m+ O, e. I# k# k
cd59e19f79524446a278fe4f5fd7b81a.png
; f0 }8 Q7 @4 \3 v1 [; s- j+ c
9 _) P+ X1 D- X5 F5 T(2)main.c函数添加初始化代码和读取flash的代码,如下所示:
0 L! r9 f: y- B0 t# h
  1. int main(void)* D- K" A# I" y) N1 T! p6 G
  2. {
    0 L& ^. j5 Y6 A) r( q+ g& s
  3.     /* USER CODE BEGIN 1 */+ L( r4 T. W1 M6 l# t! F# {
  4.     uint32_t w25q_chip_id=0;                                                                                //读取芯片ID6 ~  }  h% J4 t+ r+ u  R8 V; R
  5.     uint8_t  onebyte_read[8]= {0};                                                                                //读取的字节内容
      W9 Z: ^( E! v, b( `1 `; x
  6.     uint8_t  onebyte_write[8]= {0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80};                                //写入的字节内容; l. j! `' J, j9 g2 {6 F
  7.     /* USER CODE END 1 */
    , w5 Y. |1 w6 x: s, g3 D
  8. # p* C+ N5 u' y, z# f
  9.     /* MCU Configuration--------------------------------------------------------*/
    1 _; @: G, ^# A/ n- Y; }
  10. 3 n6 u" {" E: c* p+ \) k
  11.     /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    8 X5 p8 P* `% b
  12.     HAL_Init();
    + }, _% I5 `( l, C
  13. 3 q; F8 F$ `: E# Q6 g- V4 N! U
  14.     /* USER CODE BEGIN Init */* t5 G. k7 P. L3 ?7 m: ^

  15. / c7 C: S6 c, m* m
  16.     /* USER CODE END Init */" H6 ^8 f+ m0 o; }( r, j1 X" j
  17. 7 J% D# e) F0 H% S+ E8 N- o$ K
  18.     /* Configure the system clock */6 Q" s$ j/ Y* \/ _- m
  19.     SystemClock_Config();% E  H, o1 V& j) M( H

  20. 7 k4 x% @8 \9 j4 W1 O8 h
  21.     /* USER CODE BEGIN SysInit */
    % G3 S! w; z+ }0 w# n) y
  22. 6 v! q: g; R7 A8 A3 o% W
  23.     /* USER CODE END SysInit */5 v, i" x  l3 _3 A

  24. 0 s7 p9 n- y! y) r4 H% q5 K
  25.     /* Initialize all configured peripherals */
    . f/ ~# j& t; T, L  v: F2 l' ?$ ?
  26.     MX_GPIO_Init();
    : M8 j8 K% N5 [0 z  ]
  27.     MX_DMA_Init();
    4 f5 a, F" w$ t; Q. |% f
  28.     MX_USART1_UART_Init();" Q+ M9 @% b  _; i; ^6 m/ @
  29.     MX_SPI2_Init();/ }: a# j' D2 m, E
  30.     /* USER CODE BEGIN 2 */; G. k5 A$ f6 c6 g" i
  31.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮
    ! d- l+ e: i- ^" f/ y' J; b& _. X
  32.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮
    " x/ x, ?: \) q8 f3 W& X" a
  33.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮
    $ |: H' C+ N+ {7 A6 T" |
  34.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_3,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮# W/ F# i* ?; T- A, V, ?  X7 F
  35.     w25q_chip_id=spi_flash_read_ID();                                                                                                        //读取flash芯片ID
    ! j3 B" c( K8 N% P) I5 j1 m
  36.     HAL_Delay(100);
    , u# W" p- _5 O" [1 R4 E
  37.     printf("hello world,spi_flash_read_ID==0x%0x!\r\n",w25q_chip_id);                    //printf 芯片ID, ^1 C: @1 A+ n( ^3 C' d/ U# e
  38.     spi2_flash_sector_erase(0x0);                                                                                                                 //写之前需要先擦除扇区
    # O* X; \/ h  J7 H0 v/ I$ O
  39.     spi2_flash_write(0x0,onebyte_write,8);                                                                                                 //写八个字节
    1 u5 B' d' T! g  j/ w) J0 \# T
  40.     spi2_flash_read(0x00,onebyte_read,8);                                                                                                 //读取写入的字节# J7 M7 [, k+ p: N$ x( I; T
  41.     /* USER CODE END 2 */
    * Z" D# Z, Q8 r' g

  42. " e* F3 L) @/ O; I2 [) M9 O" u
  43.     /* Infinite loop */. w5 R5 C7 P% h0 ]# O9 J
  44.     /* USER CODE BEGIN WHILE */0 m) }3 X) D/ G3 H( b8 \# c5 p. p
  45.     while (1)
    $ H, m2 H6 H* R
  46.     {8 a+ K- s, D8 C
  47.         /* USER CODE END WHILE */
    7 N/ U  Q$ K$ F, J  @
  48. 8 K& U  h* H+ u7 M% ?6 c
  49.         /* USER CODE BEGIN 3 */1 ^. h, x" s. q9 m9 A' O
  50.         HAL_Delay(500);
    % c# O! ^6 d+ S9 K( q8 Z/ Q1 Z! E
  51.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值
    * r0 U9 o3 s' r
  52.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_1);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值' o# l, Z/ `' v6 w+ m, t- Z
  53.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_2);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值+ D! \% w% I0 a4 h0 _
  54.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_3);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值
    & b* }; y+ l4 J1 g2 J) h$ @5 C
  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],\
    ( Z/ I& a; ], q, i. N
  56.         onebyte_read[2],onebyte_read[3],onebyte_read[4],onebyte_read[5],onebyte_read[6],onebyte_read[7]);   //printf打印flash区域的值8 f- C* u8 q, q, W0 ?

  57. 4 ^' Y8 u- O6 D9 E) o/ h$ g' R# p
  58.     }
    , {+ R2 F7 a' s" P7 l6 z) K
  59.     /* USER CODE END 3 */
    $ U7 v6 t! s( O0 o% W
  60. }
复制代码
8 S' r4 U- G* v# b; n9 B: k2 N
(3)新建w25qxx.c文件,并添加驱动层函数,如下所示:
4 H  f/ I: N+ W! w/ K; [6 M9 K. q& H# l
  1. #include "main.h"
    - ^- O! v  S3 l% l2 d5 O* l
  2. #include "spi.h"2 @7 Y# z6 ]8 M
  3. #include "w25qxx.h"7 P2 |. n* {( p2 T. a0 h
  4. /**0 ~3 [" q! @. a+ ]% z1 w( {
  5.   * @brief SPI1 读一个字节
    ! y* ?1 y. ?% H& @. G/ R
  6.   * @param None
    . d( }/ M+ W: {- t3 ]7 ?( _
  7.   * @retval None
    ; {9 u( S8 S- W% y5 T. k
  8.   */& i5 q& r% u  A: s" A$ K) o
  9. static uint8_t spi2_flash_read_byte(void)' H3 ?6 H( S+ ~& v6 _
  10. {0 `6 B( n5 A, p3 }1 I, j
  11.     uint8_t t_data, r_data;6 A* p" `, @4 t" a4 r
  12. - c/ N/ m+ d' L5 ?) S8 N% Z9 |
  13.     if(HAL_SPI_TransmitReceive(&hspi2, &t_data, &r_data, 1, 0xFFFFFF) != HAL_OK)
    8 l& m  e* d0 u5 ~3 O: J
  14.     {( `& l6 m/ t" ]/ R% i6 a* |
  15.         r_data = 0xff;& X. ?' [$ M' w2 F
  16.     }# P/ n" w% Y/ l3 y  {7 f
  17.     return r_data;
    " x% i. B2 w4 Q, p
  18. }5 m8 k3 Q' P. u5 A+ m$ z; H1 ~% r2 L
  19. /**0 {0 P  U7 a: @& t  U/ {; Z
  20.   * @brief SPI1 写一个字节
    " V+ B3 N* l2 c
  21.   * @param byte 写入的字节2 h2 G, r2 V+ {
  22.   * @retval 写状态 0成功 1失败9 r1 f( m9 [9 ?, p* y, ~4 ^5 `
  23.   */
    * [  l2 o; B0 x3 y3 d2 }; S! z' G
  24. static uint8_t spi2_flash_send_byte(uint8_t byte)
    . P! K3 n. C: Y; z
  25. {5 j' w/ R7 K. I% c' B: Z
  26.     uint8_t r_data;
    7 t/ x/ B% p  }5 p. Z& Y' T

  27. 5 c+ i9 A  R; I. k
  28.     if(HAL_SPI_TransmitReceive(&hspi2, &byte, &r_data, 1, 0xFFFFFF) != HAL_OK)
    + J# H1 ^+ E7 T  t, }
  29.     {
    " y# H7 y1 M0 o7 \5 _6 X
  30.         return 1;
    % l9 T; s, z7 v& q, C, U
  31.     }
    ; w9 f# e( F9 D9 O8 ]
  32.     return 0;3 p  {6 K) q* `0 ~% K- J! D
  33. }
      e9 w0 A5 u7 {: ?4 Y) Z. @
  34. /**
    $ Z& g4 L) ^0 r9 x' z+ A
  35.   * @brief FLASH 写使能2 W8 `! ~) j- H5 u( r& A
  36.   * @param None
    8 z2 ], B4 k. Z  {5 ?, o7 A
  37.   * @retval None
    + }* F" x9 ?: C3 K3 o9 [' t" j
  38.   */* h& I8 x' X: G1 b8 v5 R! o
  39. static void spi2_flash_write_enable(void)
    - ~$ U* ~- b6 H) S) }7 p: u
  40. {
    * L* j# V# x! y/ i+ ]
  41.     SPI_FLASH_CS_LOW();& t4 P* i! h5 G6 ]
  42.     spi2_flash_send_byte(W25X_WriteEnable);% U9 O0 Q7 Z! U
  43.     SPI_FLASH_CS_HIGH();5 G5 ^1 x. W; O# c. ^
  44. }
    . B* w5 A6 q1 X$ P) Y5 {+ Z
  45. /**
    + D- h" X7 O* K0 O
  46.   * @brief FLASH 等待写结束
    , z) e7 ^% H3 f
  47.   * @param None, J4 P0 |4 l% G! a
  48.   * @retval None
    ! q+ T6 h5 n5 s& r+ x
  49.   */
    / g/ [0 ?; B/ A) J! C4 G
  50. static void spi2_flash_wait_for_write_end(void)7 x# [; N9 k# l' h) E2 S
  51. {
    1 I; Z' @& D" d' l
  52.     uint8_t state = 0;
    ; n% \& h% E$ v; Z1 E' M0 a8 A

  53. & ]* a# g, r( Z9 Z3 G: Y7 [3 N
  54.     SPI_FLASH_CS_LOW();
    3 f6 J0 |$ o3 H5 C% ~2 Z, }

  55. . d3 }6 M4 S8 w. [; L, L. C
  56.     spi2_flash_send_byte(W25X_ReadStatusReg);4 D( {& q& e7 S4 Q% F' v. `

  57. - n! ^: r& V6 q: A
  58.     do* W; V4 _1 z, J3 H  I* Q0 C
  59.     {
    + v8 y* G, V* @' Y! M* p6 c; H
  60.         state = spi2_flash_read_byte();
    % N9 }( M) `9 @% p  ?" Q
  61.     }
    / V* `% k) G" I1 w+ V- O
  62.     while((state & 0x01) == SET);$ g( U2 }: l: l
  63. 6 g- I. Z0 B0 t7 ?2 y
  64.     SPI_FLASH_CS_HIGH();2 O- `8 ?9 P4 M; {
  65. }
    ; s$ P/ k4 q1 i. V: r  [& K
  66. /**' {2 @9 W* z7 j7 [$ T% R
  67.   * @brief FLASH 读ID
    , V. ]# j' }2 w; L( a
  68.   * @param None3 m$ g, R5 J- c7 m7 p4 Z
  69.   * @retval None
    % a, ?! d/ w' b. \5 _
  70.   */
    2 @6 a) g4 W0 i
  71. uint32_t spi_flash_read_ID(void)4 ?) G) ^: N4 F# b6 U
  72. {
    ( S. I; u/ t$ d6 N- R
  73.     uint32_t temp, temp0, temp1, temp2;6 t9 E" \1 p2 \7 z# E) x' o

  74. , M) ^* M; A( Q1 `! W
  75.     SPI_FLASH_CS_LOW();
    4 ?$ {# Y6 u8 {" ]" v5 ]  s
  76.     spi2_flash_send_byte(W25X_JedecDeviceID);: g  z& m- \; Z8 y9 P9 P
  77. ! T0 P# i/ g: c
  78.     temp0 = spi2_flash_read_byte();
    % ^' A( Z* w6 Z
  79.     temp1 = spi2_flash_read_byte();
    / R4 v0 q" M: Z7 j
  80.     temp2 = spi2_flash_read_byte();
    7 }2 N" b! }8 t1 Q" I

  81. / r6 K. |7 {+ y  m6 K
  82.     SPI_FLASH_CS_HIGH();
    0 z) S: N% l9 A

  83. , z1 W% s$ K% Z" R, r+ ~( W( K; n
  84.     temp = (temp0 << 16) | (temp1 << 8) | temp2;
    2 L, |5 R/ |6 W4 J

  85. 5 S, \4 M. b) {5 F1 _% {0 y$ U
  86.     return temp;
    ; F7 b3 Y9 |4 }6 I
  87. }* }# m& u* ~4 U
  88. /**
    2 j6 ~8 \1 c5 x( c" R
  89.   * @brief 读FLASH5 ]; o3 R* b( D, p% W2 Z: b$ _# j5 T+ S, K
  90.   * @param addr 读flash的起始地址
    ; D& Y# n2 B" Z
  91.   * @param pdata 读到的数据存放起始地址
    * g: s6 T7 I  e2 Q4 w
  92.   * pdata size 读数据大小3 y/ P3 C' g, Z: c* ]7 Z* @8 @
  93.   * @retval None
    : [/ i1 f- Z) Z( Q* E
  94.   */6 Y2 F. h; ~8 x" A( g% j
  95. void spi2_flash_read(uint32_t addr,uint8_t *pdata, uint16_t size)! ?1 \4 U- u; B8 R6 X) `- s3 ]0 r7 I+ H! \
  96. {7 k  ^# Z& l  w& l8 G
  97.     SPI_FLASH_CS_LOW();3 O+ i; E' w. Y5 }$ f& B

  98. 0 d% a+ ?) C, ]* m* z
  99.     spi2_flash_send_byte(W25X_ReadData);
    & S. ^- A0 a9 K2 e# B, u* E

  100. ' T0 _4 {8 D& |, [% O% `
  101.     spi2_flash_send_byte((addr & 0xFF0000) >> 16);
    + v6 o* W3 q0 }' |: C+ z
  102.     spi2_flash_send_byte((addr & 0xFF00) >> 8);
    * \  Z5 f- ]) n6 I) B& m* z: @% C
  103.     spi2_flash_send_byte(addr  & 0xFF);
    3 S: a" Q- D- d6 ^5 b

  104. 6 U% y1 K- e4 H6 e/ _: I8 V. o
  105.     while (size--)
    & w1 N/ [* [8 m/ O5 v$ |+ ^( n
  106.     {% U; E3 K' `, Q  w4 j& j# a
  107.         *pdata = spi2_flash_read_byte();) Z( @% H* R$ [' |3 B
  108.         pdata++;) [; n$ c" M* z# ~  m
  109.     }
    + C3 q) e: V8 @& C6 a' ~
  110. 9 [1 P4 @& T0 y
  111.     SPI_FLASH_CS_HIGH();
    9 e6 K: H% C( o  m" D
  112. }. C2 Q/ u3 c8 {: g2 ^
  113. /**/ n, ?# ]: b4 v' ^8 H; E9 ^
  114.   * @brief 按页写FLASH7 d$ M4 A: N( z: s
  115.   * @param addr 写入flash的起始地址5 O- a, h) _$ l: u# s
  116.   * @param pdata 写入数据的起始地址
    # s0 i9 k; v% D0 p- `. Z, m; j
  117.   * pdata size 写数据大小
    & A! a0 s( E% [
  118.   * @retval None1 g) {! O: g1 G! z
  119.   */# {0 l+ P# _; X/ r% N) G
  120. void spi2_flash_page_write(uint32_t addr, uint8_t *pdata, uint16_t size)
    7 _9 o! E' \# j' c, j, }/ D
  121. {0 v) C* T* n3 [% _, D( @
  122.     uint16_t i;. r8 k  f6 _$ T$ V1 D) C/ y2 v

  123. $ P" n/ O/ C+ b4 ~, B) z
  124.     spi2_flash_write_enable();7 F, C# i: a, w7 ?' p% R# Z. ~7 J
  125. 4 q" @% L9 q! _
  126.     SPI_FLASH_CS_LOW();! t0 X+ _0 I& h' G( \/ @6 |

  127. 2 H6 ?. {7 X! h- B
  128.     spi2_flash_send_byte(W25X_PageProgram);! a4 ?$ k0 C* R
  129.     spi2_flash_send_byte((uint8_t)((addr)>>16));# n- q9 Z! @: F# O" X/ t  X0 ^9 G
  130.     spi2_flash_send_byte((uint8_t)((addr)>>8));
    $ H, d6 [/ s# Y' e. K5 ]2 |
  131.     spi2_flash_send_byte((uint8_t)addr);: r) O4 `+ l9 N+ b! d# N3 z

  132. ) l- ]6 l4 L9 [' l
  133.     for(i = 0; i < size; i++)4 @4 B# Z3 D  {2 A# y: k# s4 F
  134.     {
    % E' S8 \6 Z, v5 T" Z+ T
  135.         spi2_flash_send_byte(pdata[i]);
    : v4 Q+ A0 `: a( y% ?5 J/ u! y
  136.     }
      o5 r. l6 r0 s/ n0 X

  137. 9 y+ a( M, x- B- j! `. W) u
  138.     SPI_FLASH_CS_HIGH();
    ( W- z* O$ ]$ Z( P: u; o
  139.     spi2_flash_wait_for_write_end();
    4 X- I% F* c4 K" [. |- O1 `! U
  140. }8 \: F8 B: ^! n; t6 ~. {* L# t
  141. /**
    2 C2 U+ h5 y. r; k% `: N
  142.   * @brief 写FLASH
    ' c7 l( P$ j+ @; ]# \- N, b
  143.   * @param addr 写入flash的起始地址& r$ F2 N$ [5 @& r' W( ?
  144.   * @param pdata 写入数据的起始地址* Z; C' h9 T% e1 \& q
  145.   * pdata size 写数据大小
    8 o1 u4 c; p  R, t
  146.   * @retval None( V# t- C( h8 y5 y) x
  147.   */
    + y9 c6 E' ~, O& ~) k2 E7 ^
  148. void spi2_flash_write(uint32_t addr, uint8_t *pdata, uint32_t size)
    ( C/ ]# r) e' I, w  `5 h
  149. {
    ; T# s0 j. Z; i- W+ ]' {8 P( D% I' I
  150.     uint32_t page_remain;
    ' Y- B, s6 m) p: d- M% X5 D0 Q

  151. & w! n- H0 l0 B0 m; ^3 t: }6 W! D1 A2 w" s
  152.     page_remain = 256 - addr%256;
    . Z# c5 d3 |0 l+ k
  153. + Q  W* n" \) @4 i8 z& I
  154.     if(size <= page_remain)
    6 x1 }9 @1 ~+ t6 m3 u1 k3 [
  155.     {
    , O2 {" B8 p; P! k
  156.         page_remain = size;* t6 x* w+ Z4 U0 @# Y6 s, H+ i
  157.     }
    1 e  i5 F+ D' s. z; p
  158.     while(1)1 y7 t2 e' v) X4 x
  159.     {# V$ F! ^4 w2 U3 u6 t
  160.         spi2_flash_page_write(addr, pdata, page_remain);1 F3 r$ M# v0 J% {$ k
  161. / I% }& E" q/ i# A
  162.         if(size == page_remain)
    2 Z0 X2 |; b7 _: k" r
  163.             break;
    8 K2 j1 a* j& A7 I& C
  164.         else
    * @' S  o( U5 X7 g$ z; r
  165.         {5 ^: i+ n6 Q$ n, `% V
  166.             pdata += page_remain;" E, O, [' V- \& r  m
  167.             addr += page_remain;& |2 ?. |% U# j) w' e
  168. : z# E* R9 u) ?1 y) Z0 {
  169.             size -= page_remain;
    1 X/ u9 m7 _, [4 A" _) V
  170.             if(size > 256), W7 z. A! D. q+ }- J: x+ p
  171.                 page_remain = 256;9 w; g, ~* y0 t
  172.             else1 d% n& X. o- g2 Y6 Z) p
  173.                 page_remain = size;
    * G, O/ |% ?  ^
  174.         }
    + k% h! \, L# s# k, b7 e, q
  175.     }
    + x% I: ~5 W1 V
  176. }
    ' y0 r8 p# R/ N9 B. a
  177. /**' q" Z; x* H( Y5 {2 |  P
  178.   * @brief 擦除FLASH扇区3 ~: q6 N4 H7 b5 D
  179.   * @param sector_addr 扇区的起始地址  w/ `: E6 j# ~1 Z
  180.   * @retval None
    % I3 M, I& n( J- Q! v2 S9 {
  181.   */
    , O$ n# m9 q' F3 v: d
  182. void spi2_flash_sector_erase(uint32_t sector_addr)3 @1 T- w1 o) S: g& x! F* a* h
  183. {: p( j  |* ~* P1 z7 _% M0 S" @
  184.     spi2_flash_write_enable();
    ) E2 @$ a& N8 r* ]4 g; U: \2 M* D
  185.     spi2_flash_wait_for_write_end();) {; g  V9 h* n5 n8 I) Z3 F
  186. 8 _) Y8 L( i: c
  187.     SPI_FLASH_CS_LOW();; X) }2 j1 s6 c6 {# C# i/ U" ?
  188.     spi2_flash_send_byte(W25X_SectorErase);2 Z/ x! u; R; D1 ]2 g! W
  189.     spi2_flash_send_byte((sector_addr & 0xFF0000) >> 16);
    ' n3 i8 f- y  ~, ~( D( l% z
  190.     spi2_flash_send_byte((sector_addr & 0xFF00) >> 8);. f: c1 ^% s2 b2 F( j
  191.     spi2_flash_send_byte(sector_addr & 0xFF);
    # Z- M; a1 t, o
  192. . Y0 ^8 y4 }1 o9 v
  193.     SPI_FLASH_CS_HIGH();% y/ T( X3 {' a$ V, G+ }4 v8 ^9 d  b2 H
  194. * F; o7 t  V  U* h9 Q" K5 W% b
  195.     spi2_flash_wait_for_write_end();! T. q) x! H* L$ A  ?
  196. }
    * C- f5 Q3 ~' y

  197. 4 [5 Q8 Q0 l* @3 ^; i
  198. /**
    ! U! r; m4 T% @& H! L4 o4 J
  199.   * @brief 擦除FLASH块
    4 j) v' x. g: t( a; \% f
  200.   * @param None
    ( w& c5 H3 m& j5 e# g0 v
  201.   * @retval None
    0 Q' y2 m# j4 l, _) S
  202.   */" P$ O: g4 F0 F! _$ s
  203. void spi2_flash_block_erase(void)& \0 @' ~; Q" b5 E8 Z
  204. {, C# _  d( x, m% j( X  @6 w' A
  205.     spi2_flash_write_enable();8 K) a# D; `' N/ M! I. m
  206. & e; G4 H+ c' W) m0 G! ?2 y( [/ e
  207.     SPI_FLASH_CS_LOW();, Z" R! n; t% e* e4 {
  208.     spi2_flash_send_byte(W25X_ChipErase);
    6 {( T8 V' x. N& K- f- v& @5 @' Y
  209.     SPI_FLASH_CS_HIGH();  x" v3 _0 h; ^  l$ d9 t& R
  210. * U+ U' E7 L+ ]. @- E( [/ U
  211.     spi2_flash_wait_for_write_end();$ {$ u  k+ `8 _
  212. }
    5 b( q& F; X2 ?, Q
复制代码

) B6 {4 ]) C% u* t2 \3 R8 G2 {; }3、设置编程仿真下载模式, X: o6 L" {# q
(1)选择Options for target ...>>Debug>>J-Link/J-JTRACE Cortex,点击Settings>>选择Port(SW),可以看到搜索成功SW Device,表示芯片可用,可以下载。
  v" F  U: f( L6 U/ c  g' Y$ ~8 \% z7 P5 c. S* I, ^( Q
edefb6b9a2a14be3b1221ad6d5c7a8a2.png
7 X2 }8 w: M. M* R: I​​
9 G, j- z; j7 b- C7 u, v5 P(2)点击编译,完成后提示“0 error(s),0 warning(s)”。" U* R7 M1 B! ]8 ], y
8 f6 a9 x+ J0 p! B
e0b7e3e353b345a7987fe5f821de2dda.png ; x+ A: J7 l3 W+ c2 L( u

  {9 G' c9 F% k1 W(3)点击Download(或者快捷键F8),即可下载程序。0 e4 @+ F4 C" p' \: b5 F! W

5 U8 L; \2 C& u# T, r8 y# V" [
eb3f250f19e44862a7df80c2492fcd01.png
1 E# j$ f/ T: }6 O& M2 g  S​​
7 G2 @9 ~3 ^6 L% [3 |# K- K- {" Z1 n9 x# P# y) S
(4) 如果下载程序后,没有看到LED1、LED2、LED3、LED4闪烁,可以按下述方式设置一下(Reset and run表示下载后自动复位和重启运行)。或者重新彻底断电再次上电(或按开发板的Reset按键复位MCU即可)。
6 |5 Q- W, M' ]9 {% B2 h- M% Y3 ]5 V- D, A
9f8aa1a4e8f04041bcf5cb183facd100.png . w# D. Y3 V" i( C1 H
- i' {5 f# V# K/ ^' g4 W

4 F% J4 F. N2 d: H: V% m( a# S4、SPI Flash实验效果展示
/ q- ?# D( ?2 n; \6 Z/ i      (1)  程序烧录到开发板后,即可看到LED1、LED2、LED3、LED4初始化后每隔500ms闪烁一次,并且打开串口助手后(串口参数:波特率115200、N、8、1),可以看到printf每隔500ms打印一次log数据。* C! K( y. k. x6 O4 a

' W  r; {$ X# F# e/ v. _
954468e85b9f44299916e9c6b3440ad2.png " r' w$ H  O, U  k! d  G8 j5 M. Q

4 y5 t- t0 k5 x, D6 Y (2)屏蔽写入flash的语句,可以看到此时串口log打印的全为FF,则证明SPI Flash读写有效。
: B+ [9 Y% R  E
4 _, ]+ R2 u& p8 J, E. c7 L5 K' P
b37dd10545e442c3924a75955386b09c.png
+ j2 Q. t8 A- L1 z* O7 t& S
) z& |1 a  _& w- r+ G  v5 M$ c8 M  O( \ d36c42cb706f4a2d8103858b70c8d803.png $ N& h3 R4 W4 x8 ~: a2 A
6 U( N  h. _) ?0 V: s
————————————————6 u2 u% |# Q4 i5 m& U
版权声明:智能小屋ZYXC
) W( H2 X8 u. R' A8 o$ p/ {5 a% ^+ z7 [$ }

9 Y0 g# ]4 U& a$ k
收藏 评论0 发布时间:2023-1-13 19:00

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版