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

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

[复制链接]
STMCU小助手 发布时间:2023-1-13 19:00
   一、开发板平台简介:* }" v2 N+ x: L0 ?' b
1、开发板资源简介
9 i9 |  F( ?: u/ E" a0 w7 X
(1)开发板主芯片型号:STM32L431RCT6
% j/ ?8 _. t2 \! J% `9 A

) I+ Y/ |$ ]' D(2)开发板主芯片封装:LQFP-64_10x10x05P
! w: }) z# [* ]: Q7 p; N. \% P! Z: ?+ f% [* J# l
(3)开发板主芯片内核:ARM® Cortex®-M4
( S- n( l7 _+ d/ a0 a4 C) j6 ]( j9 K7 |! u9 w7 z& N' f- }6 A
(4)开发板主芯片主频:80MHz
& z6 ^5 y& q2 T, M4 F6 @/ D' [9 j
(5)开发板主芯片Flash大小:256KB
. L9 L' g6 l2 ?, @, W

7 I+ J8 Y, I* T- O(6)开发板主芯片RAM大小:64KB
+ ]" b( B- H. \, A9 Z

0 x" B: n, i8 Q& M$ e, ] bb91345227434ee7a43a34bf3fe366e6.png ; ~! \. v' Z2 m1 r- K: D& \8 H! ?$ O
+ I2 W- o1 y( r3 {
929c0cce04aa4115a8623703982bf3e1.png : r. d0 y/ W: |( G/ k; \: ~$ E

0 F( B4 a. ]0 @  ^- h9 d3 ]2、LED灯资源
/ \$ w6 [4 Q; y7 f3 o) k+ |, L(1) STM32L431RCT6开发板共5个LED灯资源,其中一个红色LED为系统指示灯,指示开发板供电系统是否正常,如供电系统正常,红色LED为上电常亮状态,硬件原理图如下图所示:0 l+ T7 y; B5 z2 t

* F, [5 [: i( A0 j  Q4 O% F1 k
0f177691c5814f16a508bcfa747f87ac.png
0 \0 u( K8 w+ ]0 \6 d$ a+ o1 i+ d& y
(2)其他四个LED灯为黄绿色可控LED,高电平点亮、低电平熄灭,计划用LED常亮验证看门狗的作用,硬件原理图如下图所示:- T/ T! d7 }0 l6 J! I
/ O# V% W5 m5 t( _  O! ]
e923ad767b8448b7ac37c4ed693724bd.png 1 ?! ]6 R/ y$ N4 \; a$ `: O0 A

+ h, H9 _; O+ c1 _​3、串口printf打印工作原理0 d! R" \: I/ [& F* l. |
 串口全称为串行通讯接口,即数据在通信线上一次传输一位,按先后一定顺序传输。我们通常所说的单片机串口准确来说应该是串行异步收发传输器(Universal Asynchronous Receiver/Transmitter,UART),使用TTL电平,串口需要RXD、TXD、GND三根线进行通信。
9 M8 U) |: G' q! H, S/ L
5 ~0 ]0 {& n3 @8 m0 K7 ]- r
     (1)我们选用的STM32L431RCT6开发板串口1已通过USB转TLL串口芯片CH340G引出,使用时,只需要用公对公USB线连接电脑即可(注意也得需要安装CH340G驱动),后期验证试验也可使用该串口作为debug串口。7 D; J3 E4 f, u( q% l4 C6 X5 C* M

5 O! c+ K/ C4 L( n  Y4 Q( d: N; D  i     (2)开发板上的其他串口已通过排针引出,为TTL电平,通信的时候需要注意选择对应的电平模块,如USB转TTL串口模块等。
! F9 \; l3 |# ~
* \0 P9 p" s5 J: h+ g
TTL转CH340串口,硬件原理图如下所示:
$ r, P2 T9 I( k( t+ \) F: ~' p& K/ t" H
4df1b489cb944c26b55013001061675d.png 6 a  ?% U: V5 u4 N
. U4 Y# C# ]( Y6 o/ p
4、SPI Flash简介( z: k/ Z! d7 c$ L
(1)SPI总线介绍
; g8 W5 u4 N+ u4 t& r( `1 tSPI分为主从工作模式,通常有一个主设备和一个或多个从设备,本文中MCU为主机,W25Q16为从机。
! ~& ?! b( ], \4 n. i4 [$ ^

- G! Y) L3 l. |SPI通信有以下四根线:& }% A( P9 X$ `- M  e* \8 T
MISO:主设备数据输入,从设备数据输出。
7 ]2 p! Z) y" J  t4 ^) xMOSI:主设备数据输出,从设备数据输入。& d5 c' J0 i/ j
SCLK:时钟信号,由主设备产生。9 @1 g. g( H' [6 m. `
1 y' ?/ T/ [1 w% m* l
CS:从设备片选信号,由主设备控制,低电平为选中。% a$ W$ L' S5 a( D/ |3 _1 [

& r8 K" f1 _. s' m0 x6 U% i' I0 \1 e; y(2)SPI的特点:, u( y/ j! N& D
高位先发送,共有四种工作模式。
, R* C" L' D2 ?* r6 F6 r6 TCPOL(时钟极性):规定了SCK时钟信号空闲状态的电平(0-低电平,1-高电平)  m) n' [8 `4 \
CPHA(时钟相位):规定了数据是在SCK时钟的上升沿还是下降沿被采样(0-第一个时钟边沿开始采样,1-第二个时钟边沿开始采样)
$ Q4 d3 L5 I3 J* Q
+ ?. @6 o1 |( _5 y) L8 r# _8 ^4 B+ v模式0:CPOL=0,CPHA =0  SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据)! }2 E  \& }3 @! B0 ~) X" L
模式1:CPOL=0,CPHA =1  SCK空闲为低电平,数据在SCK的下降沿被采样(提取数据)
  g; Y/ g- h; y. B7 g# t( ?$ V$ o模式2:CPOL=1,CPHA =0  SCK空闲为高电平,数据在SCK的下降沿被采样(提取数据)5 f9 m  p. y: ?/ a' G# J! P+ c
模式3:CPOL=1,CPHA =1  SCK空闲为高电平,数据在SCK的上升沿被采样(提取数据)- E/ J# F, \" c8 W8 O4 k
! s, q* m8 `, T1 U5 F
(3)开发板 SPI Flash原理图如下:
) A' B4 {& X' L% S3 Q/ t4 o% \
7 K. j( p* L) B5 s( C
da82f3d13fef48dd8d90aa170757e360.png ( x; N! }8 X1 L$ u+ N

/ y& K1 d0 I2 e' @$ S8 S- j( K8 H$ }3 Q, N5 p9 ^
  二、串口printf实验过程
8 Q7 \* S9 O3 {9 G2 W( G: U1、新建STM32CubeMX基础工程
1 Q0 d7 j+ `7 N- h/ M
(1)打开STM32CubeMX,点击“File”-->"New Project"" G' |2 K1 ^. U# D7 Y4 Z/ v4 C/ I& R
" r5 R3 b' p7 e
2399170347904e959c855fefd5877f84.png
3 W7 A9 {8 b' ~0 d, X) u! E$ S' o9 W
4 X2 N% Y8 p9 K0 I
(2)等待打开主芯片选项界面(大约1分钟时间)。
; x0 o, C7 N/ I. {. i! d- }# Z
# \4 W" K8 ]# X
d74d2fdc60c9440483e2b7cf8d928e7f.png & x- s# v  C. S0 O( H6 r

! }" q0 P+ U, }) b1 k( b( t, R(3)昨天搜索框中输入(或选择)所需的主芯片型号(因为我们用的是STM32L431RCT6开发板,所以此处选择STM32L431RC),然后在右下角选择STM32L431RCTx(因为开发板主芯片是STM32L431RCT6),左键双击即可打开新建的项目。
" r. j' O# M8 @  `; y  {3 ]" `- t' `& o9 E0 U+ L, |/ s
7b80345238d74bea82ce70e1a348f7b4.png
: w& b! _! m5 o8 N: y* u- V8 R7 `4 V% O! n7 y5 w* z9 j2 y) i
(4)选择时钟源。4 S* f2 ]+ g# I
(1)因为开发板上有8M外部时钟,硬件原理图如下所示,所以此处选择使用外部高速时钟(HSE)。2 D4 a4 m6 H( O, P
" }5 V' Y/ |! U, H, s
3f65707eca104663a3dede6d25dfa961.png & W0 ]* l8 f7 A8 Z  u

( _& _9 k6 _1 }- M(2)因为我们没有用到外部低速时钟(LSE),此处不做处理,如下图所示。
# C, ~" v# ~# q$ M( ^" N4 P* P+ x# r
72119b971f62410fa8344f7f9fb9f389.png
5 K& s( v3 O6 Q2 i# q$ n& j3 S& H6 q
2、配置GPIO控制LED4 H2 R. M* z8 \! ?
(1)查开发板原理图得,LED1、LED2、LED3、LED4的控制引脚分别为:
- K0 c- o( e3 V, U* O* q/ m& sLED1——PC0& [* M0 X8 ~6 p* D" k$ M9 J
LED2——PC11 s& Z5 [2 D1 `1 X. p1 }
LED3——PC2
! T; ^. D; \0 kLED4——PC34 ]2 Z/ N1 W: M) {

0 U6 k$ N3 X# H$ x# s3 Z(2)配置LED的控制引脚为输出,输出频率、输出方式默认即可。
! L9 b9 W. p: _9 _& t鼠标左键点击PC0,选择“GPIO_Output”,表示设置该引脚为输出模式。2 `" l5 @( }+ v0 E
鼠标左键点击PC1,选择“GPIO_Output”,表示设置该引脚为输出模式。- o- Z- x2 l" v+ j3 t' w
鼠标左键点击PC2,选择“GPIO_Output”,表示设置该引脚为输出模式。6 j7 J2 H- x( Y4 z1 P8 ]) M
鼠标左键点击PC3,选择“GPIO_Output”,表示设置该引脚为输出模式。9 t7 g+ F2 Y  y1 H6 [% U, X
2 a. Y+ {1 N% X9 V, Q) D. f6 z" c
492907c1e71149819adbaee4516a2af4.png $ P/ c) ]( F& V4 b" I7 N

6 u# c( q2 x1 f' c% s, D7 E
c75d3f10ba2e427d9c1a10fad9bf5471.png
% R/ j/ H$ B$ y* r1 o2 K

8 [: d; W0 e  u- M5 E' L(3)也根据自己的需求配置GPIO的参数,如输出方式、输出频率、上拉下拉等。因为GPIO控制LED的要求比较低,此处采用默认参数即可,不用修改。# M" W1 ~0 O9 W" c3 D4 v7 [/ s

  R) c4 P0 u  ^! {$ y4 i, L
38ff4b80e1c5495ba3076a5158fd91fa.png
( R/ X5 b+ i; @' Y- d3 J% u( j5 A# }2 m( u, j% ~
​​3、配置PA9、PA10为串口& W# e6 v4 x5 u
查原理图得知,串口1使用STM32L431RCT6引脚为PA9-USART1_TX,PA10-USART1_RX,引脚设置如下:
; n, F6 q2 A4 d3 E0 A: g3 n
& q  C; K1 G" q) v; N) S
497b8ee0243e446784e64a3c31e8a26f.png / V& U" U: N( N! z7 [% s
' Z0 `6 P4 c3 E7 }0 |2 u
(1)序号1用来设置串口收发引脚的选择。9 ^% a1 r9 @" V+ v2 @0 t9 p
(2)序号2-3-4-5-6设置串口参数,如波特率115200、8位、NONE无奇偶校验等。$ E) K. P" k! c/ Z( R0 O

( u( ]+ P* s! ^7 N; \ 4、配置SPI Flash接口
5 q' y, L4 I  p9 w" p8 z(1)查看STM32L431RCT6开发板原理图得知,芯片原理图如下:
) b$ G, L1 B9 ?9 g% j7 T4 ^( |3 u0 z* H3 q
11aaba64f3d34f9ea1e4cbbfc0fdc102.png - K' ?/ w+ x" U1 W- @6 T

) e, {" C# S/ }7 Y (2)SPI Flash接口对应芯片的PB12、 PB13、 PB14  PB15,芯片引脚配置如下:1 e1 z) O3 r9 S# B; a' Y" v1 W
PB12:SPI2_NSS,此处设置普通输出IO即可。不能配置成NSS
9 {) V  `/ T3 X, dPB13:SPI2_SCK
; C) B2 b' {2 u3 Q. XPB14:SPI2_MISO
  V" E; R% z% U& W1 x; G7 Y3 R* v$ ~9 PPB15:SPI_MOSI# c( b6 q# ^" S4 O

% Z$ D8 p. @* u6 e3 e- |5 Y1 x- z
ade62d1658174af99a93e3928b62849c.png
# @7 R# t& |) r% |$ |1 Q1 o* d6 c* T5 \( C7 o  B2 m
(3)设置SPI引脚参数,并选择 Full-Duplex Master 全双工主模式,此处不开启 NSS 即不使用硬件片选信号。/ G2 Y" R, h3 U* ]$ ^- c

- I3 |" ]' t8 t% N$ j
c932367c299e46979e55a3e1a05d2c7e.png # S+ m- J+ _5 F. z# d2 O3 x1 v

0 z$ A; f% L: Y* c(4)设置SPI基础参数以及时钟。
0 g7 T) M! G* x8 m1 I8 u# N
' q0 Y1 d# i- w  h5 X  H
e5af1c8c0b0b4ac9a07d740b955028c0.png
' }# z, s6 f, q+ V: c+ ?* v( x/ G3 H
dcc8d73ddcbe4082a09165905ac71cf1.png ( ^  E0 z% m, V5 G# H. y! y

* a  q9 e$ k0 w# L( ] 5、配置项目工程参数, B8 s" V; l5 u$ G) }# u
(1)配置时钟树,用于系统内部时钟,以及各个外设时钟等。此处选择外部8M晶振作为主时钟频率,内部最大倍频80MHz。
9 ^* K2 f. k! x9 m) m. p
6 H  R5 a- r7 g- A: K5 R$ |5 [
f3ecedd9323f4463b69539906b3c1199.png ' P. q6 M. l, F  l- F; [$ A
  k/ {6 B" W+ v( |
(2)完成配置工程。
; G; M- T/ c( V( m- t9 v备注:需要注意代码生成过程中的继承关系,如图所示:需要保留开发者自己编写的代码时,请根据配置设置,不然生成代码后会删除自己编写的代码(从这个方面也可以看出开发者备份自己的代码是多么的重要。)  C7 k* E. c5 j

" t3 Q' S; r8 K2 B) ?+ L
efdb616174f54925b6eac31109f227b5.png # t* ?7 m3 z. k" U8 i
​​
- h" _$ I3 ]; ]% ~1 E1 b 41c3f2716e4e4eda8f62fa3a1d871322.png 2 Z* ]2 g& a8 f7 ?

( A7 f/ A& s3 R4 \, J$ j7 {(3)生成代码。3 j8 o% K+ Z. m4 z/ q
备注:使用Generate CODE生成工程代码前,请确保文件路径无中文,否则会生成项目失败。: X2 @7 f" G* W& a" u; D- `
/ m1 @$ M9 Q* R
8eaa7d0babea40ee93f158481a6ee471.png
! o' V$ `1 ~5 K+ K$ H2 E  O% ^' s: ]  b, J) J6 S$ w+ |6 G' ?
(4)工程代码生成成功。0 a! j' R9 O8 V0 c! Y  T

  J/ u  \- I7 J4 M, v6 y$ P1 y% {
e8f46d59a1fb4af589feb613fdff17b4.png
8 g; A' K, Z* D2 u8 B( a
" c1 {/ n  I- ^; o3 k2 n4 _
! f, Z0 ]3 l" w3 g" U 三、在KEIL 5中编写代码

: g- B* c8 ]1 _; l% F; V7 s1、使用KEIL 5(MDK)打开项目工程文件
, r& {3 [' F# g源码使用说明:使用前必须把项目工程复制到无中文路径的文件夹下使用。4 y2 }+ m. E6 t/ y) L! z/ J  a
(1)找到刚才新建工程的存储路径,安装项目名称,打开项目工程.uvprojx。
% |! B+ n% z- d8 D1 U; M8 B+ v! x- ~
37170661111547238cfcd51f580aab01.png
: Z- I4 g% N- m- Q9 Y' X+ R7 i/ P: [
2、添加SPI Flash读写验证程序' m" ]) v5 A+ U" H- V, F4 m
(1)main.c文件中,初始化LED1、LED2、LED3、LED4默认为点亮,并在while循环中添加控制程序,如下所示:实现每隔500ms后LED1、LED2、LED3、LED4点亮和熄灭之间反转切换,并且串口每隔500ms打印一次。
. d5 B3 {5 _/ n) X4 Y1 d% ?: U
. ~& c" y; @3 V+ j8 u3 c4 ]4 @备注:自己添加的代码需要在 /* USER CODE BEGIN 3 */和 /* USER CODE END 3 */之间添加,否则STM32CubeMX更新代码时,会造成自己添加的代码丢失。4 W- P' n5 {. K  M1 V) C; }! D

' |0 p6 [+ X8 u2 A
d1194af353cd4b0b88cd9208b1ac7db1.png $ `3 K& r) j% d  n( x
  y1 y' {: {# f3 D1 ^* Y% O* h. [  S
3c771cb9b6094461aa8ec2befc9d12dc.png / a; @3 V8 S: z: e  i. t- B
, y; J$ ]" j; u2 A3 f- _
cd59e19f79524446a278fe4f5fd7b81a.png
/ I9 _% o' \; n( h' n6 D( t" }3 p( j! Z4 E6 B5 \1 H4 O
(2)main.c函数添加初始化代码和读取flash的代码,如下所示:8 b: e1 R( Q/ W5 M" i
  1. int main(void)
    " C9 S# J4 w1 H# h* {% f
  2. {
    3 a' [+ M) u( w6 @
  3.     /* USER CODE BEGIN 1 */
    - Q. }. O1 t+ M- _$ j* K
  4.     uint32_t w25q_chip_id=0;                                                                                //读取芯片ID
    % p( h- `* q9 M: p6 d2 p
  5.     uint8_t  onebyte_read[8]= {0};                                                                                //读取的字节内容
    , ]3 w  a- N- \. D) Y' i4 e: h4 P. C
  6.     uint8_t  onebyte_write[8]= {0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80};                                //写入的字节内容
    0 A+ U2 B. f" b2 a( @" Y$ n- G
  7.     /* USER CODE END 1 */
    2 i+ f# ^' X# R4 p( X4 B+ w
  8. 3 W0 o. R& q5 }. ^
  9.     /* MCU Configuration--------------------------------------------------------*/- `! d/ E% {( p, r7 ^2 ?

  10. ; e) {! f+ V' z; m  h& {/ {
  11.     /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    ' l7 ~% T( x- j7 G
  12.     HAL_Init();3 ~5 C! s0 b: }
  13. , D) a+ Q' N3 p. L4 Q; a* t
  14.     /* USER CODE BEGIN Init */
    & x- p% a5 u2 A- ?" J

  15. 1 u0 W  p! P2 g. i
  16.     /* USER CODE END Init */: c  i2 L3 T; k4 E$ r( I# p

  17. 4 w# R7 _8 Q# [, b# F% ~
  18.     /* Configure the system clock */% ?. n0 k; I& t  B% z* L
  19.     SystemClock_Config();
    3 Z% v8 P5 P) w( ?! g

  20. 2 @1 ]9 B6 ~2 V% {
  21.     /* USER CODE BEGIN SysInit */+ |; K: v' A) i, m* }8 X

  22. % [7 {1 Y; |. V# T
  23.     /* USER CODE END SysInit */$ S/ q, `3 e9 d4 {( |+ A

  24. 9 R& k' F: a: K' J+ I& j# ]7 p
  25.     /* Initialize all configured peripherals */4 F, f" }$ u4 Y" Z% {* \/ M/ I
  26.     MX_GPIO_Init();
    8 E- c3 x  z6 Z
  27.     MX_DMA_Init();% W& g, D/ n4 M$ k& n/ M
  28.     MX_USART1_UART_Init();, M4 G) g. m2 \
  29.     MX_SPI2_Init();2 Z% d; d# U5 J' Q* u
  30.     /* USER CODE BEGIN 2 */
    % A1 Q' ~7 L$ x7 ]1 |8 b
  31.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮8 b0 }4 G" F- E4 b
  32.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮
    # ]( s+ b+ B8 ], t! B
  33.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮
    6 K' Y! q$ V5 i0 i+ P. |3 p
  34.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_3,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮& T9 O7 d& N2 X2 X7 S
  35.     w25q_chip_id=spi_flash_read_ID();                                                                                                        //读取flash芯片ID
    & b) }) G8 Y- F* V
  36.     HAL_Delay(100);
    4 r- D4 m' @+ ?" h: _  Y
  37.     printf("hello world,spi_flash_read_ID==0x%0x!\r\n",w25q_chip_id);                    //printf 芯片ID# K9 K! s. w3 U5 W. J9 W, A
  38.     spi2_flash_sector_erase(0x0);                                                                                                                 //写之前需要先擦除扇区' u8 @5 s9 z9 V% y+ {5 c; F
  39.     spi2_flash_write(0x0,onebyte_write,8);                                                                                                 //写八个字节' H8 G6 W0 x2 r, Z2 w) j
  40.     spi2_flash_read(0x00,onebyte_read,8);                                                                                                 //读取写入的字节% Z4 ?. w2 w) k- |! S3 s) N
  41.     /* USER CODE END 2 */
    / m- V! q+ [9 u1 b
  42. - R+ p: L( R  d. T8 c1 V( _9 m
  43.     /* Infinite loop */
    # H3 _! }  [  O5 S) ^" u* m5 }
  44.     /* USER CODE BEGIN WHILE */  @4 r6 Q5 U1 ~& [# k; {( r3 a
  45.     while (1)( C1 A5 Z4 n$ w& r8 P' D
  46.     {- b3 v9 l# i7 g* a$ o, t9 V1 s' w
  47.         /* USER CODE END WHILE */
    7 o% B& R! ~: W4 j4 \/ P
  48. ; S6 x" f/ `# S, G, d( D& P
  49.         /* USER CODE BEGIN 3 */
    . F( w% E" ^8 H- m3 N
  50.         HAL_Delay(500);' x7 j2 H0 L: ~. u/ f$ m. C: m. ?
  51.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值
    ' E; w. y! H) a7 ~* D
  52.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_1);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值- t' K8 l3 H1 Z9 t1 M
  53.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_2);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值
    # U3 `8 _% o3 }. n, l
  54.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_3);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值4 t# C6 v2 A7 }! }0 z; c: 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],\
    2 R( ^' t6 A4 \9 f  z
  56.         onebyte_read[2],onebyte_read[3],onebyte_read[4],onebyte_read[5],onebyte_read[6],onebyte_read[7]);   //printf打印flash区域的值
    % l, W+ J  [" g) Z) `2 V% ~

  57. ! W4 e1 }9 h9 _! V3 Z
  58.     }6 t: ^& o1 Z4 D5 x! @# K
  59.     /* USER CODE END 3 */' c2 C2 x7 b9 Z$ n& \
  60. }
复制代码
( g: q+ V' d, Y; Y& W' }
(3)新建w25qxx.c文件,并添加驱动层函数,如下所示:3 X: X' D5 `& ^0 M) i8 v
  1. #include "main.h"
    4 @: D6 x' U, b4 \( }5 q5 _
  2. #include "spi.h"7 o9 L4 f- P& w; [' [
  3. #include "w25qxx.h"
    2 W( i3 j6 g* [/ k& I: U
  4. /**
    3 r, ^0 y% Y% ^' q2 I- \
  5.   * @brief SPI1 读一个字节0 f. G; v5 e/ q8 w8 E  ]
  6.   * @param None
    5 X& J" t& ?: X$ H, Q! g) Z! M
  7.   * @retval None
      @& n- D8 f* k+ Y$ s% o- m# I
  8.   */8 t4 u4 `, g# _: B/ b5 N+ C
  9. static uint8_t spi2_flash_read_byte(void)
    8 y  c4 h$ y" G6 k2 l; ~# R  A7 W
  10. {+ V; w# ~, o" C5 l
  11.     uint8_t t_data, r_data;- j# _, I0 I& g% i2 ^: m8 T( g+ R
  12. - O) T8 b$ Y3 I# g- ?8 ?# c
  13.     if(HAL_SPI_TransmitReceive(&hspi2, &t_data, &r_data, 1, 0xFFFFFF) != HAL_OK)
    7 {8 D8 j( n! X5 a/ E
  14.     {3 L5 m" k1 r4 R0 g4 g
  15.         r_data = 0xff;2 \5 x2 E" u2 T
  16.     }
    + j6 f7 x3 G' t/ s
  17.     return r_data;& Q& O8 M5 q: o6 M
  18. }
    # ?& \4 {8 c) j1 W
  19. /**' f, K6 q2 s* @: `2 J
  20.   * @brief SPI1 写一个字节
    + ?5 T* G! Q! e' |2 X! b  o4 w* L
  21.   * @param byte 写入的字节9 S9 E5 E/ [% ^
  22.   * @retval 写状态 0成功 1失败, o5 Y; \7 P" j2 l' ?
  23.   */' D0 |2 G  K3 }( R' a" r# D
  24. static uint8_t spi2_flash_send_byte(uint8_t byte). k. k# B% u5 P
  25. {
    . d7 l9 @5 b" ]9 L  T
  26.     uint8_t r_data;
    2 {5 x5 V) f3 K- q5 w, s4 n0 ~

  27. ) I9 b& }- _' z, g. G( J0 y. `
  28.     if(HAL_SPI_TransmitReceive(&hspi2, &byte, &r_data, 1, 0xFFFFFF) != HAL_OK)# a/ a' k  m( Z9 N1 p8 D
  29.     {/ T7 p1 A5 J+ z' b* D' y* J, S' ?
  30.         return 1;
      O( P. z$ T/ t6 ]% T9 x: t- n; J
  31.     }
    , o$ m: Z/ g9 t
  32.     return 0;
    1 W8 O# S/ I' i) v4 |- T
  33. }& m" H5 f& {+ D5 y+ F+ `
  34. /**; y( a; F3 o8 x8 Z& x; Y$ E* y
  35.   * @brief FLASH 写使能
    9 `$ Y4 f' B5 G# b' {
  36.   * @param None
    ' C% L' F& k" k0 D$ X
  37.   * @retval None
    . O. C. u1 a" b; m3 ?$ Z* s, Y( \
  38.   */
    8 u) o& k7 B9 T! z( R3 |0 i: U# `) Q
  39. static void spi2_flash_write_enable(void)) n& v" T5 K' I2 C" z  }
  40. {% k( o  ^8 Z: S9 Q$ \% ~, [
  41.     SPI_FLASH_CS_LOW();  \( L, A2 n) v4 Y( W
  42.     spi2_flash_send_byte(W25X_WriteEnable);
    " g  e/ O: m- S' ~5 C9 `8 E2 {
  43.     SPI_FLASH_CS_HIGH();3 n+ [& I5 y3 N1 v' W# o# R
  44. }% M# D$ W$ u% G. _% P. L
  45. /**
      h" ]( a0 I5 G
  46.   * @brief FLASH 等待写结束: S3 p1 D# p6 E
  47.   * @param None/ Y6 Y  ^) M! j  k8 E
  48.   * @retval None
    1 f( E- S; A- o+ s0 N7 _* ?/ L/ b1 D& ]# D1 k
  49.   */5 {# c& ]$ V# E% i2 Q! q3 H
  50. static void spi2_flash_wait_for_write_end(void)3 D: u' U2 P8 E8 h
  51. {9 R4 Q6 n, V+ Y. @+ S6 e4 r
  52.     uint8_t state = 0;
    . l8 A# L1 J/ _% L) N6 F  s
  53. + l. I) ~; q) E  n
  54.     SPI_FLASH_CS_LOW();  ^9 ~% W7 ?7 ?( p. m" D
  55. $ \; K7 n! c1 n% P
  56.     spi2_flash_send_byte(W25X_ReadStatusReg);  G! J& n/ y8 Q. |3 b1 W/ a6 S" g

  57. # Z, }1 O, j" N
  58.     do; L* c* m2 u7 i4 k$ P# B6 T* m/ f
  59.     {
    " n! J0 ^( Z0 g# s/ D, F8 F
  60.         state = spi2_flash_read_byte();. u5 f3 H5 W+ Q: ]/ x
  61.     }" Q8 B# ?0 Z; j. D: g' H! y, ]. g
  62.     while((state & 0x01) == SET);
      X; M, n+ E) Q9 b$ l1 b0 y% }

  63. & r0 r; u* ^: ~
  64.     SPI_FLASH_CS_HIGH();
    7 q7 o9 Q! l0 V/ J# m( r1 W
  65. }+ V9 C; q; J* a; V+ t  D& R
  66. /**% Z6 b  s; M% S* M1 E
  67.   * @brief FLASH 读ID/ {7 q( i% i: S
  68.   * @param None! ]( d) E9 R; o% J
  69.   * @retval None
    , w/ e8 V, M5 [, S; J1 |
  70.   */
    ( w, f7 t5 s" P( l2 f5 N
  71. uint32_t spi_flash_read_ID(void)* m0 F" l" X1 |0 T, Z1 s
  72. {& C; w) u" T" |+ [2 N- F  r
  73.     uint32_t temp, temp0, temp1, temp2;0 ]6 x5 Z2 ^" T1 L$ s5 {5 N; S: L+ c
  74. 2 v/ }+ d- {/ [7 C6 @7 @5 {
  75.     SPI_FLASH_CS_LOW();1 C6 l! E$ @8 ?* u% ~
  76.     spi2_flash_send_byte(W25X_JedecDeviceID);8 p) V! ~. ?. f& g4 [' l: f
  77. ) B" i  G. m, f8 J
  78.     temp0 = spi2_flash_read_byte();
    * G2 B( [: b) k. N) o! O. W- E& \
  79.     temp1 = spi2_flash_read_byte();
    - w0 z) [& j& k* ?/ W- {+ m
  80.     temp2 = spi2_flash_read_byte();
    % N5 w& c) C5 }
  81. 5 D4 \" x. A. ?4 `4 w7 [1 ]
  82.     SPI_FLASH_CS_HIGH();, b$ u, A* M$ I1 W" I

  83. 7 ?- n# v# G; Q/ ?) a4 W, q; ?& U
  84.     temp = (temp0 << 16) | (temp1 << 8) | temp2;
    8 K, O8 k1 ^, q; A* [# B
  85. + {- t6 e8 |9 f" g9 \3 @
  86.     return temp;
    " y* Z6 Q5 A$ R; c! b6 i
  87. }) N* ]3 o% e$ q) ]; ~
  88. /**
    & B: s; w/ [) N9 t+ E  Y
  89.   * @brief 读FLASH: q$ x! A! }3 d
  90.   * @param addr 读flash的起始地址+ c8 a( q( {& h
  91.   * @param pdata 读到的数据存放起始地址
    * W. a4 ]9 G/ [: U# x# N7 P: c6 x
  92.   * pdata size 读数据大小
    9 @1 d& S+ m( ~9 w1 y# `6 f
  93.   * @retval None
    " N! r# |8 o# ^* W. e8 a$ v" Z/ C
  94.   */+ T. N- l( S, I% Y# J$ T
  95. void spi2_flash_read(uint32_t addr,uint8_t *pdata, uint16_t size)
    : A( y* v/ ~2 D! h4 w7 e
  96. {/ p/ E& d) `& O9 g/ C
  97.     SPI_FLASH_CS_LOW();9 P! G- w  A) Q3 [
  98. 3 H+ t9 \2 j1 Y- s
  99.     spi2_flash_send_byte(W25X_ReadData);; {0 Q! q' C0 y5 q, l
  100. 2 p$ w$ q7 t3 C0 w- P) v
  101.     spi2_flash_send_byte((addr & 0xFF0000) >> 16);
    ( r- v1 `! w9 l- C: z1 Y
  102.     spi2_flash_send_byte((addr & 0xFF00) >> 8);
      ]8 I9 \; e* d, j# I) m7 f6 ^
  103.     spi2_flash_send_byte(addr  & 0xFF);3 ?3 n+ U  U2 \& s% i: X

  104. ' [7 i6 m6 U+ b
  105.     while (size--)8 K& x' P' K/ o2 Z
  106.     {
    $ {6 R2 ^% {# z* @) x
  107.         *pdata = spi2_flash_read_byte();6 z# `( o9 K3 [: e; x& `+ X
  108.         pdata++;6 H+ R$ H' {# z+ I$ [
  109.     }: E4 }+ h( c) H- p3 J. t" t; X9 a
  110. # F! n2 O. c1 }; M3 g0 |
  111.     SPI_FLASH_CS_HIGH();
    ( M- m! r- a5 K( z, T% _0 U& E
  112. }  D9 D; i6 T9 i" m+ C' {
  113. /**
    : E4 b/ w$ z  O( l# `1 A0 L
  114.   * @brief 按页写FLASH
    " V  s2 m+ |1 O+ s' s
  115.   * @param addr 写入flash的起始地址
    2 A( b5 b- {/ D& F' B0 ?
  116.   * @param pdata 写入数据的起始地址% ^  Q0 |% m; D& I& [
  117.   * pdata size 写数据大小
    % y' ]/ Y& d' R( C
  118.   * @retval None& {+ K2 p. Z& K2 x
  119.   */" z8 F9 O- m: O5 @7 `; t
  120. void spi2_flash_page_write(uint32_t addr, uint8_t *pdata, uint16_t size): ~+ j- p" r+ G' Z
  121. {
    9 o6 s+ O9 y: I% w. Z/ `2 j
  122.     uint16_t i;
    . f) O- }& \& y! `
  123. 6 D  y4 [, l! u$ q2 m
  124.     spi2_flash_write_enable();
    ( E: D) h$ }! b# R+ [# M$ L: O# v
  125. 3 j3 k; c4 W) w2 B; H% B
  126.     SPI_FLASH_CS_LOW();# P( y' J2 w! V5 F2 O7 |

  127. , \4 w' q) [+ @5 ]6 J
  128.     spi2_flash_send_byte(W25X_PageProgram);
    : k% |: q% P% v' S
  129.     spi2_flash_send_byte((uint8_t)((addr)>>16));
    4 [$ M- x% e: W) W8 V/ A. K
  130.     spi2_flash_send_byte((uint8_t)((addr)>>8));1 M: ^& z2 f- V* \+ u9 g0 a
  131.     spi2_flash_send_byte((uint8_t)addr);
    7 b' O" C* z: y7 ~& Q
  132. , A& ^, D) e8 I5 K: X9 O% _" h3 `
  133.     for(i = 0; i < size; i++)6 V7 ?2 i7 t5 o
  134.     {
    6 O7 s3 d9 u4 F- c* L) r; U
  135.         spi2_flash_send_byte(pdata[i]);
    7 G$ ]' V2 g  X! d0 ?4 K" m
  136.     }3 d. _2 G) I- U4 s( `4 j
  137. $ ?" @& j9 y) `' z3 B0 R3 h7 V& l
  138.     SPI_FLASH_CS_HIGH();
    ! N; E9 P7 }) u/ u0 Q' ?6 h
  139.     spi2_flash_wait_for_write_end();
    % |7 Y9 Q4 P) G# E4 ~' T* t& e
  140. }: l7 N& A4 B1 @8 G9 E$ t8 b  b
  141. /**; x& X8 C+ R1 [$ x4 T. u
  142.   * @brief 写FLASH
    ) s9 h' z/ H. E3 x; b" U- }
  143.   * @param addr 写入flash的起始地址. X8 x: H0 n" o; }4 K1 f, J
  144.   * @param pdata 写入数据的起始地址
    ' b# Y+ }% m& o4 H0 l) w! b/ `3 s
  145.   * pdata size 写数据大小
    % d. B6 G9 r. n  {7 ]9 [
  146.   * @retval None
    6 L4 k7 X# z* g- c# z, R
  147.   */$ ]* Y0 P& T) A0 m; {. _
  148. void spi2_flash_write(uint32_t addr, uint8_t *pdata, uint32_t size)8 m; G- T7 E0 _, f/ o  D* M
  149. {% r  }* ~5 a# }: S2 s
  150.     uint32_t page_remain;$ [& y7 v# q! X2 r" B
  151. ' S& J- e) c. f- _# Q' O
  152.     page_remain = 256 - addr%256;& M2 a2 @1 M% P, W2 R" V7 E$ s
  153. , L; j2 m+ u! m8 N4 O1 z9 f
  154.     if(size <= page_remain)
    $ X) q: A$ f% ~! S. r2 T
  155.     {
    4 r8 A" B9 ^0 R) T
  156.         page_remain = size;6 D9 Y; ]8 E- E* u8 C/ S: {, G, z  C
  157.     }
    * B" i8 m  ?8 z
  158.     while(1)
    6 Z4 T$ h, G3 j  W( R+ B5 C
  159.     {
    % W5 E. H, l4 k
  160.         spi2_flash_page_write(addr, pdata, page_remain);" B2 H3 I) ~! n: L6 Y
  161. ) Q* a  u3 N5 u. }' i) v; q+ Y0 I* [/ }
  162.         if(size == page_remain)+ j# w8 K: A  H8 `
  163.             break;
    ( ?" i+ P) q% M" ~' L& u/ e( q/ D/ Q
  164.         else  i. T- r* K* f8 |2 a- y/ N# |
  165.         {6 O, x  [7 u  h# a. s5 z) k/ {$ o
  166.             pdata += page_remain;6 j3 s  b% h3 S8 H: |; [4 M$ R. J7 p
  167.             addr += page_remain;4 S/ |9 b8 c/ r7 |. C; D. G8 a! n

  168. " |2 \, W, b, N* w3 b: Z( }
  169.             size -= page_remain;
    : {3 Z6 J1 E0 a  e8 K  X! `1 n
  170.             if(size > 256)
    " _0 o" l3 w) v+ u" Q
  171.                 page_remain = 256;
    9 ]0 K9 R% E: E- w( j1 R" c- I, T; [( T
  172.             else
    : R* Y. n, I5 D' k' {- ^- L! l
  173.                 page_remain = size;
    9 c6 X' S& W: G# h+ {( c; J$ `' J
  174.         }% s; M5 o) k7 v7 z( c; {9 T
  175.     }
    $ l& P- L! j6 D0 p& H
  176. }: r( X" m0 t! }, Q6 t( y) T
  177. /**
    2 N  D: _% V) W! y5 y
  178.   * @brief 擦除FLASH扇区0 ?$ m; p" O& A1 e3 K& ?* R
  179.   * @param sector_addr 扇区的起始地址
    7 l0 R7 y, N  _
  180.   * @retval None4 `9 q  L3 q  }9 x
  181.   */! y7 N4 ^  [- u+ b
  182. void spi2_flash_sector_erase(uint32_t sector_addr)7 j. v4 f0 Y& B  [; A9 w: r
  183. {( g/ y7 n6 U$ Q) p; x7 W
  184.     spi2_flash_write_enable();
    4 d, g3 N: Z/ O# q/ b# Q
  185.     spi2_flash_wait_for_write_end();/ u8 N$ E6 ?/ l6 d5 H2 D( y; `
  186. # B! T) E$ `* W8 k5 c& _" M
  187.     SPI_FLASH_CS_LOW();$ u" n# `! d/ D3 F+ g$ }4 u
  188.     spi2_flash_send_byte(W25X_SectorErase);7 N% _( l6 z7 u6 [* A. J9 i* Y
  189.     spi2_flash_send_byte((sector_addr & 0xFF0000) >> 16);
      Q! r$ _& m0 H1 c+ q* j
  190.     spi2_flash_send_byte((sector_addr & 0xFF00) >> 8);
    * E( z1 t; J; U" ^/ [& P
  191.     spi2_flash_send_byte(sector_addr & 0xFF);5 n6 `5 \' @+ E
  192. 6 e$ s( I3 C1 q' D
  193.     SPI_FLASH_CS_HIGH();3 a+ O' t* n5 q3 Z; ?# S! S0 {
  194. 1 T  K0 {' ~- s* X4 q$ }
  195.     spi2_flash_wait_for_write_end();2 p) f; ^9 Z! ^
  196. }
    # G! o) n0 c! k. _! b. e1 B2 J9 ?
  197. 0 f5 X7 b) ~: G/ s8 z* j
  198. /**; o+ i) _& ~3 c4 f/ O  J5 p9 n. J
  199.   * @brief 擦除FLASH块
    8 b$ N" E% b) i
  200.   * @param None
    & i  P" {9 f/ ]( h  g/ t0 g
  201.   * @retval None
    . B, N9 z/ \- b/ [- P8 }9 }
  202.   */
    * @* `( h% x4 @7 o6 X
  203. void spi2_flash_block_erase(void)
    ) c6 b" c7 p" U
  204. {2 p* W9 |. q7 a" B2 ~. ?' q5 e
  205.     spi2_flash_write_enable();
    3 r% b. U6 `- ?  o: h0 t, H
  206. 3 M! r/ ^# J& y0 ?$ h. n
  207.     SPI_FLASH_CS_LOW();
    ) s* j1 j) o# F; G7 \) M
  208.     spi2_flash_send_byte(W25X_ChipErase);+ E/ u7 N4 R% A- C
  209.     SPI_FLASH_CS_HIGH();4 Z4 ~2 I, _) V- h0 p% E- x# u# j
  210. 4 [4 R! t6 n) N+ ~6 Y. t  x! k& n
  211.     spi2_flash_wait_for_write_end();
    # [# A3 P/ j7 ^* Y5 }) G
  212. }
    3 D6 E5 B4 p; ^& ], ]0 H2 j- s
复制代码
+ l. k; V; n' ~$ o
3、设置编程仿真下载模式% l) d; n; b" P* V1 s6 r0 h
(1)选择Options for target ...>>Debug>>J-Link/J-JTRACE Cortex,点击Settings>>选择Port(SW),可以看到搜索成功SW Device,表示芯片可用,可以下载。
1 ^  K* C* R7 N2 z+ [6 f: }
9 j& H0 i4 ?0 N
edefb6b9a2a14be3b1221ad6d5c7a8a2.png
  {: ?. R4 \( J) r' K5 I​​
1 v. z  e( D. M; x+ i2 s# `+ `(2)点击编译,完成后提示“0 error(s),0 warning(s)”。4 ?/ z% R' O2 U+ |
. ]5 A& T  L0 _6 O  ]/ j5 I/ h
e0b7e3e353b345a7987fe5f821de2dda.png 0 `* q+ ^$ [& Q# Y& ?+ |

8 D' m$ r, x, B! X% y(3)点击Download(或者快捷键F8),即可下载程序。
* L3 \6 y: }* k; A8 m9 c) X; l
eb3f250f19e44862a7df80c2492fcd01.png
! c  I1 `0 R# M3 y* D​​% e4 Q; p/ X) _3 y/ F) u, A
/ |; ?6 V/ h5 ?; @1 z4 P
(4) 如果下载程序后,没有看到LED1、LED2、LED3、LED4闪烁,可以按下述方式设置一下(Reset and run表示下载后自动复位和重启运行)。或者重新彻底断电再次上电(或按开发板的Reset按键复位MCU即可)。3 k6 E# D" n3 }* q4 O
3 b! m6 D; Y+ `& D1 f( C' @9 l
9f8aa1a4e8f04041bcf5cb183facd100.png
9 c! W, k8 u0 X6 I( V1 X0 I+ e& q/ j- r; |7 V

: v( v& e* D- f/ ?6 ]5 s; Y) ^# m; x& _4、SPI Flash实验效果展示
& f9 ]- t. [' d1 u" b% J& M6 _      (1)  程序烧录到开发板后,即可看到LED1、LED2、LED3、LED4初始化后每隔500ms闪烁一次,并且打开串口助手后(串口参数:波特率115200、N、8、1),可以看到printf每隔500ms打印一次log数据。# \0 F8 l+ d5 Q$ y' W( h. i

) W  R8 j* h5 {; s
954468e85b9f44299916e9c6b3440ad2.png 7 u# H5 }. }: O  X

6 ^1 T- j2 ]8 s+ S (2)屏蔽写入flash的语句,可以看到此时串口log打印的全为FF,则证明SPI Flash读写有效。5 X3 c6 X9 p) p* z0 c$ I$ v7 L" [9 [

% g2 \: \9 A" A. I$ r3 [. n" X$ b
b37dd10545e442c3924a75955386b09c.png
+ j2 Y1 w1 I1 N8 W; x  a, U( J; G  J! `( L
d36c42cb706f4a2d8103858b70c8d803.png   r( s  }% y6 |! c7 |; E( D7 ?2 u
. {8 B- _5 ~+ b3 a: W7 n
————————————————
  k: _/ C1 E9 v3 C3 _; b! F/ P版权声明:智能小屋ZYXC
4 J7 s8 l3 s" z* P5 M# Q# {; B& n4 S! L# j, L5 |3 ]
1 M$ _+ J" j% p2 |
收藏 评论0 发布时间:2023-1-13 19:00

举报

0个回答

所属标签

相似分享

官网相关资源

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