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

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

[复制链接]
STMCU小助手 发布时间:2023-1-13 19:00
   一、开发板平台简介:
- q  }% {' R* H/ A1、开发板资源简介

' l. i1 j# A, d4 z$ Q& b9 s(1)开发板主芯片型号:STM32L431RCT6; f0 _5 x7 u5 E4 b7 B  A

4 q2 [3 ]3 o: W: [. ?- w* i) ^(2)开发板主芯片封装:LQFP-64_10x10x05P
& W8 V( R9 g, y, f9 a8 c
. F1 r8 r$ ~2 @* d(3)开发板主芯片内核:ARM® Cortex®-M41 s9 h  d5 A5 c; q2 \3 ^

9 b# [3 [' R% @$ l' r) V  ~8 D(4)开发板主芯片主频:80MHz- \/ _+ k5 g, R! |* x" G6 B

- `% T- i! k6 O' l9 M(5)开发板主芯片Flash大小:256KB
3 u  k9 d5 `9 w' \  I6 c
/ u! E& p% U' |2 ?
(6)开发板主芯片RAM大小:64KB9 h" m# z8 A5 g* P. U
( E5 t7 n+ W" l* t/ k0 m
bb91345227434ee7a43a34bf3fe366e6.png 6 w0 n# [7 Y0 i7 ^, v$ q4 O. D

0 c& F' b, K# X 929c0cce04aa4115a8623703982bf3e1.png
4 |; \4 m, d! C4 ?
6 F% u! D. C% j! i& E
2、LED灯资源9 ^. G% N& O5 [" j" J# m2 S) b
(1) STM32L431RCT6开发板共5个LED灯资源,其中一个红色LED为系统指示灯,指示开发板供电系统是否正常,如供电系统正常,红色LED为上电常亮状态,硬件原理图如下图所示:
1 U& V+ C/ Z0 i, z6 l1 Z' M! L7 X
0f177691c5814f16a508bcfa747f87ac.png
1 V4 `. O, K( b1 r5 S
+ C9 X! K- u. N8 X# h7 r(2)其他四个LED灯为黄绿色可控LED,高电平点亮、低电平熄灭,计划用LED常亮验证看门狗的作用,硬件原理图如下图所示:
- }) F* X! f6 F  N2 B
' i4 C6 x/ J. |8 m( U
e923ad767b8448b7ac37c4ed693724bd.png
% w4 \. y4 ^) O/ |. M2 H7 m1 @* l5 E& {- H5 _
​3、串口printf打印工作原理
) r7 W  |7 }) n. B) b 串口全称为串行通讯接口,即数据在通信线上一次传输一位,按先后一定顺序传输。我们通常所说的单片机串口准确来说应该是串行异步收发传输器(Universal Asynchronous Receiver/Transmitter,UART),使用TTL电平,串口需要RXD、TXD、GND三根线进行通信。
4 u4 a! H/ z! T' @# M- y# y* T' u
6 Z+ Y' c' e9 v4 A/ q5 s* c
     (1)我们选用的STM32L431RCT6开发板串口1已通过USB转TLL串口芯片CH340G引出,使用时,只需要用公对公USB线连接电脑即可(注意也得需要安装CH340G驱动),后期验证试验也可使用该串口作为debug串口。1 ?& o% d. D4 m7 T) m* c  c

, N' W+ ?* w$ h% l: A" t/ c  O     (2)开发板上的其他串口已通过排针引出,为TTL电平,通信的时候需要注意选择对应的电平模块,如USB转TTL串口模块等。5 l- J1 q  ^5 F9 o  k. i. m5 m

* o& u) a' O& z3 ~3 z. T( |TTL转CH340串口,硬件原理图如下所示:" `: X. P( n" S" p- j6 `" k

1 F( d# z, x" M, m; i8 s4 C# c" J3 C
4df1b489cb944c26b55013001061675d.png
( I+ G  ~6 J+ t1 d0 {$ |) x. ?# b. t# G% Z+ j) g7 c! ?
4、SPI Flash简介7 Z* g! O! m* [7 k- ^2 x2 y) W
(1)SPI总线介绍$ Y8 j7 ?% l! X& N3 ?( p6 S
SPI分为主从工作模式,通常有一个主设备和一个或多个从设备,本文中MCU为主机,W25Q16为从机。
, S5 U( {' i) P2 I; F, t- X

1 j7 v8 u; ^9 t6 A9 j; s( k, `SPI通信有以下四根线:) I5 x( b# q; ^4 S1 `, |
MISO:主设备数据输入,从设备数据输出。, E# v$ a9 z/ o1 _% o" L
MOSI:主设备数据输出,从设备数据输入。
$ J$ q. r8 j$ c7 CSCLK:时钟信号,由主设备产生。
6 w+ s9 b" V9 M/ W" D
  o0 M4 Q- b1 M5 f* d! T
CS:从设备片选信号,由主设备控制,低电平为选中。
& x! s, Q, b4 m# M8 v1 g: s" M' B) N. k0 ~" P# ]5 Q- r0 F
(2)SPI的特点:( v& |5 L* S% W7 |4 Y0 b
高位先发送,共有四种工作模式。( Z# T9 @0 `7 k# \7 F
CPOL(时钟极性):规定了SCK时钟信号空闲状态的电平(0-低电平,1-高电平)
( Z7 y4 c8 r: a# @; W! L' {, ECPHA(时钟相位):规定了数据是在SCK时钟的上升沿还是下降沿被采样(0-第一个时钟边沿开始采样,1-第二个时钟边沿开始采样)8 {$ d/ L- l' C0 }8 p
9 @% \! `2 Y  R4 @7 {
模式0:CPOL=0,CPHA =0  SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据)
( W/ c% _6 C5 a  |8 L模式1:CPOL=0,CPHA =1  SCK空闲为低电平,数据在SCK的下降沿被采样(提取数据)
. C& f6 K1 P" H9 F' s4 U! j) U; _模式2:CPOL=1,CPHA =0  SCK空闲为高电平,数据在SCK的下降沿被采样(提取数据)
7 i4 \1 K1 Y' M9 p模式3:CPOL=1,CPHA =1  SCK空闲为高电平,数据在SCK的上升沿被采样(提取数据)
: K6 ~( \1 |: h$ I1 j) ~! |
+ i/ U8 Y5 X- h; G6 ^(3)开发板 SPI Flash原理图如下:1 e  \' g& s5 v* A
* O  D+ ?' r7 v& A2 i5 K+ _: \( A
da82f3d13fef48dd8d90aa170757e360.png , C  Y1 W8 h8 l* N

5 g/ k; J8 T( `' B+ _9 x9 E+ {( n; P/ p+ R' v8 U7 u
  二、串口printf实验过程
5 v% j3 T; p* l+ Q% n0 J! A0 i1、新建STM32CubeMX基础工程
7 ^" Y4 x9 Y2 N' |9 Q
(1)打开STM32CubeMX,点击“File”-->"New Project"
. x$ x6 ~& s3 W8 y: r
( h5 ~) ]- I3 G! R( B1 d
2399170347904e959c855fefd5877f84.png
- l2 o  o- l+ ^3 n

( D( x1 i1 T* \) V* y(2)等待打开主芯片选项界面(大约1分钟时间)。
8 x3 r: u2 [4 C0 t; @; z4 I% K% q# G9 L: K1 a
d74d2fdc60c9440483e2b7cf8d928e7f.png
0 y- ?/ P* H5 V) ~, O- ?  X( d+ o2 S( z$ K. P  K8 W3 b
(3)昨天搜索框中输入(或选择)所需的主芯片型号(因为我们用的是STM32L431RCT6开发板,所以此处选择STM32L431RC),然后在右下角选择STM32L431RCTx(因为开发板主芯片是STM32L431RCT6),左键双击即可打开新建的项目。
# l' a# H( u7 r( V
$ S4 ], y& M6 k! D6 Q0 P3 V  |& x
7b80345238d74bea82ce70e1a348f7b4.png 5 g1 N; l7 ~4 S( W- f* ?$ }
/ O0 v& Z& i; E1 e1 S
(4)选择时钟源。6 u; |9 N; ?8 B$ j% [
(1)因为开发板上有8M外部时钟,硬件原理图如下所示,所以此处选择使用外部高速时钟(HSE)。( m$ P4 D0 j- o. b. M% |

. P7 G1 }+ A! t( H  O/ u
3f65707eca104663a3dede6d25dfa961.png 0 ~  _5 |7 ^0 s) p3 j8 q. `5 G. A
( J5 W7 e. _0 ^: ~% v3 N$ J+ S
(2)因为我们没有用到外部低速时钟(LSE),此处不做处理,如下图所示。
# h3 r# U8 n0 i3 b' I, Y, h1 S
: O/ Q0 E9 G/ v
72119b971f62410fa8344f7f9fb9f389.png 6 N1 C, _+ K5 V/ p' x2 S
7 O% n, S' j, o% I* `* e8 a
2、配置GPIO控制LED& D1 f% x- J+ c+ g# x
(1)查开发板原理图得,LED1、LED2、LED3、LED4的控制引脚分别为:
# ?6 s* ^/ {* Q( e, [; U# B1 B2 ZLED1——PC0; F- m/ h$ T# n2 O  o% r, V  X; E
LED2——PC11 m: K" D# }" Y! g$ V& G" V
LED3——PC2
9 a- a* G) `" g1 \! xLED4——PC3
# s1 b$ _) t# T, v
! M% _# k1 G( U% [8 Q4 V; @
(2)配置LED的控制引脚为输出,输出频率、输出方式默认即可。7 \; E3 H7 J/ n% \' f
鼠标左键点击PC0,选择“GPIO_Output”,表示设置该引脚为输出模式。
. n3 X7 s2 E  P0 w* }8 U& k- R2 X鼠标左键点击PC1,选择“GPIO_Output”,表示设置该引脚为输出模式。+ ~: P: g0 p1 P: a9 ?+ W& [
鼠标左键点击PC2,选择“GPIO_Output”,表示设置该引脚为输出模式。
) g! Z% p8 l4 w/ D2 K鼠标左键点击PC3,选择“GPIO_Output”,表示设置该引脚为输出模式。: c7 G  k* B' T) r! z
" _/ f+ }+ x' {6 S
492907c1e71149819adbaee4516a2af4.png
+ \0 z3 Q  J7 @* {1 x5 J. s# l1 [3 G) v# w, h' a6 C
c75d3f10ba2e427d9c1a10fad9bf5471.png 6 ]$ z6 A5 J5 E# p

0 j$ H3 g, j0 N4 u& n& K2 @4 o2 K1 ^(3)也根据自己的需求配置GPIO的参数,如输出方式、输出频率、上拉下拉等。因为GPIO控制LED的要求比较低,此处采用默认参数即可,不用修改。! p: k9 G4 d% ^+ R5 |) \% o( x+ K: t5 R

9 v0 r$ |. j  G, k8 c0 E1 h
38ff4b80e1c5495ba3076a5158fd91fa.png 8 D$ D( @. w9 J3 R+ _4 u1 C1 R
0 o% X) p( @! ?8 r/ d( o; f
​​3、配置PA9、PA10为串口
) u; R9 H- c  i5 J$ w! e0 p查原理图得知,串口1使用STM32L431RCT6引脚为PA9-USART1_TX,PA10-USART1_RX,引脚设置如下:& w! T3 ^7 m' [2 v8 A# V
( |* H8 s: J3 c4 z2 Y5 V8 b
497b8ee0243e446784e64a3c31e8a26f.png % j6 L( p  f2 B4 U. N
* u& K7 M/ k7 R; `
(1)序号1用来设置串口收发引脚的选择。
- F$ s, x7 y& l1 V2 T3 A% \(2)序号2-3-4-5-6设置串口参数,如波特率115200、8位、NONE无奇偶校验等。; e% G2 w* k& }8 c& `& x: |( s
3 x. ~1 ?7 O. Q% D. V
4、配置SPI Flash接口
/ H! m8 Y; T# D! _(1)查看STM32L431RCT6开发板原理图得知,芯片原理图如下:
) S2 U* u# e* H. P3 a  ]: _3 _5 w5 X  q: y+ q
11aaba64f3d34f9ea1e4cbbfc0fdc102.png 8 p+ M! u6 F0 N' M3 S! ~4 M8 I

# |) G" f) o9 e4 c1 T; V5 Y0 t (2)SPI Flash接口对应芯片的PB12、 PB13、 PB14  PB15,芯片引脚配置如下:1 j0 G' S% w9 O( @' _
PB12:SPI2_NSS,此处设置普通输出IO即可。不能配置成NSS8 W/ ^9 t8 Y$ Z# O: z7 o
PB13:SPI2_SCK
* j9 O( x# N" B% |PB14:SPI2_MISO
8 A7 y; z( p! C0 MPB15:SPI_MOSI; v9 [0 ^9 X( e. H" A' g  D

# d, @7 X' e) Y( {
ade62d1658174af99a93e3928b62849c.png
+ w0 W2 S- c3 n; |
% P. F6 B6 w, \5 t (3)设置SPI引脚参数,并选择 Full-Duplex Master 全双工主模式,此处不开启 NSS 即不使用硬件片选信号。
6 F% o( y2 }3 n6 I- b" W& y. y  F* z3 D
c932367c299e46979e55a3e1a05d2c7e.png 3 J9 q% o- R" y5 Q- {+ R( b; e

/ V; ?8 w$ S! O! m& Y# s(4)设置SPI基础参数以及时钟。
3 q4 o. G) Z, D1 |+ `, Z- c: S; s5 U$ }' Q
e5af1c8c0b0b4ac9a07d740b955028c0.png . Y- X, ]+ {; w. h7 Z# _

" o% J8 x3 w: W6 ?9 P- z! S" U
dcc8d73ddcbe4082a09165905ac71cf1.png
+ p$ M* P" z4 A4 d- f: e6 e9 D0 ?6 Y* U
5、配置项目工程参数: i( g' e* z8 }$ `1 |
(1)配置时钟树,用于系统内部时钟,以及各个外设时钟等。此处选择外部8M晶振作为主时钟频率,内部最大倍频80MHz。
4 q* d& a( f3 \; l: s5 G# R7 F% _) ^9 B
f3ecedd9323f4463b69539906b3c1199.png
! S; w7 }. t1 Q5 n- G% G( c* F8 q0 y5 O
(2)完成配置工程。
' R) N2 P3 W3 a7 m备注:需要注意代码生成过程中的继承关系,如图所示:需要保留开发者自己编写的代码时,请根据配置设置,不然生成代码后会删除自己编写的代码(从这个方面也可以看出开发者备份自己的代码是多么的重要。)
- a( F4 z+ h! r  w6 `
+ d2 ^5 q; M+ |% |
efdb616174f54925b6eac31109f227b5.png ) R; m2 X; T/ \
​​
7 t/ A% c8 r( T1 y/ \! Y 41c3f2716e4e4eda8f62fa3a1d871322.png
7 b7 }2 L& N, E8 b% d
2 K- l: R0 @! r: D+ S3 H, f, P% o/ N
(3)生成代码。
$ T3 D9 p9 ]8 e5 K( r5 u; }4 Q备注:使用Generate CODE生成工程代码前,请确保文件路径无中文,否则会生成项目失败。
' J: L  Z3 a6 P8 F5 v4 ?; Y. M, G3 a4 U
8eaa7d0babea40ee93f158481a6ee471.png
8 e3 L3 r6 ]8 l. x& G1 W0 l7 |* o: Y5 a7 K( l( w; C  M- M
(4)工程代码生成成功。
' L! a% x6 C1 }5 g. |( S# o2 p; C( n1 R! N. g( p% h- P
e8f46d59a1fb4af589feb613fdff17b4.png
7 O4 g4 u3 ]. i7 }6 u* E5 Y& p" H+ c0 s

! p: d& t( C. F' g6 g 三、在KEIL 5中编写代码
0 l% O2 n1 ~7 o, C! Z7 \8 Q7 m5 ]! V
1、使用KEIL 5(MDK)打开项目工程文件
/ t. P! t' a3 Q7 G  E2 I源码使用说明:使用前必须把项目工程复制到无中文路径的文件夹下使用。
( X6 c5 B" i7 j( w5 f4 N/ o: }(1)找到刚才新建工程的存储路径,安装项目名称,打开项目工程.uvprojx。/ g4 X* q! z8 r# E
# B$ [. k6 T5 D' s. g9 L3 c
37170661111547238cfcd51f580aab01.png ! q# c/ d2 Z+ y; k' B2 C+ k# G8 V
! i+ q$ D' N# g. |5 G9 u
2、添加SPI Flash读写验证程序4 y8 L' G/ N. A! r, @- ?' {
(1)main.c文件中,初始化LED1、LED2、LED3、LED4默认为点亮,并在while循环中添加控制程序,如下所示:实现每隔500ms后LED1、LED2、LED3、LED4点亮和熄灭之间反转切换,并且串口每隔500ms打印一次。
2 x$ X. N4 y" a* f1 x) Y8 ?) o& w9 c
备注:自己添加的代码需要在 /* USER CODE BEGIN 3 */和 /* USER CODE END 3 */之间添加,否则STM32CubeMX更新代码时,会造成自己添加的代码丢失。9 ~% \+ T+ Z8 C6 d
, c3 L1 A6 D) K
d1194af353cd4b0b88cd9208b1ac7db1.png
, \, D, I* k; n. m  X
# }1 q1 M# V) p# [; L6 v
3c771cb9b6094461aa8ec2befc9d12dc.png ; C9 L, b5 y, C7 U7 X

5 W) p% P7 c: k% [8 K% r/ H
cd59e19f79524446a278fe4f5fd7b81a.png
1 k: @( @% \% [' n, ?$ D. e5 v7 R' _% ]0 c, S/ o: T/ [3 x
(2)main.c函数添加初始化代码和读取flash的代码,如下所示:
1 r; ^* K# P) i
  1. int main(void)0 n+ S. [: J' G, q% S
  2. {
    9 ^, c1 c! n/ L
  3.     /* USER CODE BEGIN 1 */  b" {2 T+ `+ m3 n# s! a6 g
  4.     uint32_t w25q_chip_id=0;                                                                                //读取芯片ID
    3 e, M1 {- O' w
  5.     uint8_t  onebyte_read[8]= {0};                                                                                //读取的字节内容
    # a7 E% k& S* i0 p
  6.     uint8_t  onebyte_write[8]= {0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80};                                //写入的字节内容/ r) \6 J0 V2 ^4 p
  7.     /* USER CODE END 1 */. T2 G. p0 Z/ n' T
  8. ! M3 G4 G0 u% i" y
  9.     /* MCU Configuration--------------------------------------------------------*/
    7 U$ c0 u2 ^( y3 s* F3 u; x: I& N5 z
  10. ; V. i, c4 |. O# p- |7 ?! P% z
  11.     /* Reset of all peripherals, Initializes the Flash interface and the Systick. */( [$ A3 g3 c7 w' r9 U1 a
  12.     HAL_Init();% ?" b2 i% |4 b; V; D* C) N+ L

  13. ) q) Y0 l) }% s% p
  14.     /* USER CODE BEGIN Init */
    / ~; X0 k  s9 H& j2 @5 s
  15. 3 _' s1 u9 F; D; ~6 z2 y. u' R! c# g
  16.     /* USER CODE END Init */) `# o8 U# T' w7 Z

  17. - p7 m2 \) i- a
  18.     /* Configure the system clock */
    , {, y! I4 ^+ O# p
  19.     SystemClock_Config();
    0 w; _# y" t" k' u1 @- v+ v+ P( S

  20. ! j4 `+ e0 p* _% J
  21.     /* USER CODE BEGIN SysInit */
    5 j) {" ]0 v) J' A

  22. + \' w1 C' F9 d$ v% X
  23.     /* USER CODE END SysInit */9 N6 B$ e# W; q3 Z

  24. 1 B! `$ S" n/ ^4 h2 ~8 S5 {: u
  25.     /* Initialize all configured peripherals */& M( ^1 v/ t- W+ R" e1 N
  26.     MX_GPIO_Init();% p) T1 S* M5 h( W* |: c
  27.     MX_DMA_Init();
    ; M$ G! `& o1 S7 W/ M- ], y" o
  28.     MX_USART1_UART_Init();, |0 B# Q% p: b' S& ^
  29.     MX_SPI2_Init();4 a8 w% R8 }6 P* Q$ O- c* F7 C. o
  30.     /* USER CODE BEGIN 2 */' ?9 ^" m3 X0 Q. C8 C" o$ z" N& G, ^
  31.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮7 P+ {8 L" h1 w$ q* a, d& z
  32.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮
    & h. S# f% u3 f5 U3 v0 g3 f' S
  33.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮) }0 N, t  j* @; i. k$ v# I% j3 t
  34.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_3,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮
    0 x1 c5 v7 x6 \$ D
  35.     w25q_chip_id=spi_flash_read_ID();                                                                                                        //读取flash芯片ID8 X3 d+ Z1 ?6 ~
  36.     HAL_Delay(100);
    # m4 l4 ]; E: C9 E. v
  37.     printf("hello world,spi_flash_read_ID==0x%0x!\r\n",w25q_chip_id);                    //printf 芯片ID
    : x% z8 G8 c, J- @
  38.     spi2_flash_sector_erase(0x0);                                                                                                                 //写之前需要先擦除扇区
    8 B) p7 `' Q! D* y  W# x
  39.     spi2_flash_write(0x0,onebyte_write,8);                                                                                                 //写八个字节! L/ a4 a6 ~6 v( L3 G
  40.     spi2_flash_read(0x00,onebyte_read,8);                                                                                                 //读取写入的字节
    0 l: J% r* M! G+ }4 _$ ]# W2 M
  41.     /* USER CODE END 2 */; ~. }# W7 E, k- F
  42. ( K* i- |) R' F, U0 N
  43.     /* Infinite loop */
    2 M/ Q1 n2 x" a5 x2 N
  44.     /* USER CODE BEGIN WHILE */& x! |; l1 H( q
  45.     while (1), w/ Y* W; e7 B- g1 Y6 q
  46.     {
    5 r% `8 H1 l  l& W3 O) U
  47.         /* USER CODE END WHILE *// X$ R) K8 V2 G
  48. 3 |5 t4 Q" A' x! P$ k( b
  49.         /* USER CODE BEGIN 3 */3 P& f/ w1 P! T5 ?/ s2 w
  50.         HAL_Delay(500);
    % t+ L2 q/ o, ]4 U
  51.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值5 d/ H) w. p, i4 {1 Y7 l7 _
  52.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_1);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值
    ! v* H" _4 q7 P& @! |; f/ O
  53.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_2);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值
    ) c) x2 Y  G. @
  54.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_3);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值+ R/ A! ~! Q! y5 ^; p8 H
  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],\
    ; g$ n1 T( O' P
  56.         onebyte_read[2],onebyte_read[3],onebyte_read[4],onebyte_read[5],onebyte_read[6],onebyte_read[7]);   //printf打印flash区域的值
      Q. {1 E: T+ ]1 B

  57. . e( B1 ?  V4 ~% e
  58.     }0 F- W$ i- C9 A7 x# a
  59.     /* USER CODE END 3 */: O5 O4 j: j% A7 j; C
  60. }
复制代码
! {+ c' J8 h3 _4 p
(3)新建w25qxx.c文件,并添加驱动层函数,如下所示:; U0 e/ E$ b, r1 p& |
  1. #include "main.h"
    6 y; v& W, F' l- h$ A5 n! i/ i9 C
  2. #include "spi.h"
    1 R. s" I- R  X7 s8 Z6 v& k9 l
  3. #include "w25qxx.h"
    ; S8 F6 R# N% f* ~; q1 d: o& x6 w
  4. /**
    * i& l; g1 M9 |! N0 y( `
  5.   * @brief SPI1 读一个字节
    8 Q4 F  o1 P) a* F/ M
  6.   * @param None+ x% r8 j7 s& `8 n
  7.   * @retval None
    ( K0 n/ }2 b. G, r7 n% d# m6 Y) o
  8.   */, c; O5 j5 @6 t# ~
  9. static uint8_t spi2_flash_read_byte(void)
    ) C& y8 W9 V: @1 C6 y' P, R# X
  10. {
    6 H7 [1 C1 e$ P  K/ \/ K- U1 p
  11.     uint8_t t_data, r_data;: Y4 p$ [& n; c/ _

  12. " g, X1 V7 n3 ]' e" G5 v' j
  13.     if(HAL_SPI_TransmitReceive(&hspi2, &t_data, &r_data, 1, 0xFFFFFF) != HAL_OK)
    9 A6 Y# ?: x* z) b$ C
  14.     {- \9 x9 y# ?" W9 N2 r' `& D( h# Q
  15.         r_data = 0xff;6 c# i; Y4 @* A3 E8 D+ h8 G
  16.     }
    ( F2 B& O! a, h1 B  O. b: o
  17.     return r_data;2 x3 o) }; c, o5 Z4 ?3 p
  18. }
    ! ]/ W0 \- ?, v
  19. /**
    ' X+ g4 M( o9 K: \$ o: ?% V
  20.   * @brief SPI1 写一个字节3 @0 b6 R8 j" \2 D8 `* W$ y! G
  21.   * @param byte 写入的字节1 D# D! h3 E8 U
  22.   * @retval 写状态 0成功 1失败
    % X1 I% o" o3 D" F# V& t: J
  23.   */
    : A9 \& B6 R2 Y, p/ ]
  24. static uint8_t spi2_flash_send_byte(uint8_t byte)
    + U7 e( @" r0 X) j6 b3 V9 E
  25. {9 O  J6 z. h9 F5 O% G( Z
  26.     uint8_t r_data;
    % o  g* ]" X. J1 l/ [
  27. 6 H, M: |+ H: H+ w: ~
  28.     if(HAL_SPI_TransmitReceive(&hspi2, &byte, &r_data, 1, 0xFFFFFF) != HAL_OK)
    9 ?; w, k" M( I/ G9 j, `0 k
  29.     {2 q! B# h$ K8 ^9 e; }! f
  30.         return 1;
    & C; L- p# M* l) b/ D: o; u
  31.     }
    ( \( L4 A1 B& x( X/ C! |
  32.     return 0;
    : d( }; r+ g4 K5 A
  33. }
    ( O5 J2 b$ w+ `7 Q: o) J
  34. /**) u2 |) ~$ R. O
  35.   * @brief FLASH 写使能
      E8 M9 R9 {9 s0 }9 m/ l
  36.   * @param None
    ' |$ E2 ?- O' Q" f! F! z2 U) S
  37.   * @retval None
    / v% b! s' O# m0 @
  38.   */+ x6 I/ Q) w2 F; j5 U
  39. static void spi2_flash_write_enable(void)1 ~. p+ d9 y+ I+ Z8 L
  40. {
    + s8 J; n6 j9 y7 h  n8 t
  41.     SPI_FLASH_CS_LOW();
    4 L& b7 o( M% u% z% \9 t
  42.     spi2_flash_send_byte(W25X_WriteEnable);' i5 \( j3 b' \1 u; r9 L1 {
  43.     SPI_FLASH_CS_HIGH();
    1 l5 u' [. r! w7 d% W" h, J- \
  44. }! r0 `% k$ N" w! E. n
  45. /**
    9 r# d" L7 N, {
  46.   * @brief FLASH 等待写结束& E9 \  w6 G' R. R% A- K. u6 s! c& V
  47.   * @param None6 H/ N# Y" Z, ]+ P: W- R
  48.   * @retval None
    : o* `; Q& F$ h4 x; I: U$ M
  49.   */1 F% U$ z5 l9 E# i: N2 h
  50. static void spi2_flash_wait_for_write_end(void)" r' [; V8 A# W" r' ^* m
  51. {+ U+ F2 O1 g& l
  52.     uint8_t state = 0;. W  X2 @. m) Q: D: x
  53. ( F% s% w3 ~8 Y* x& t& D. L
  54.     SPI_FLASH_CS_LOW();; I; L+ j4 m# o3 d

  55. 8 T' L" A$ _  M0 r- \7 V8 U: M
  56.     spi2_flash_send_byte(W25X_ReadStatusReg);, M6 s$ m, G: u; @( w* B  _' I
  57. * ]( h( l, t( M  H- D* }8 y
  58.     do: a# g, T% ?! h* W8 V
  59.     {
    . h. _( I$ M# Q8 p6 L. |
  60.         state = spi2_flash_read_byte();* b% e/ M, f! |( }1 i$ M
  61.     }
    1 q7 \2 m/ E1 @$ @( g9 `" u2 s
  62.     while((state & 0x01) == SET);
    8 ]; y/ V$ F4 z$ Y. a

  63. + Q. }, Q6 o8 _) c9 l6 D; m
  64.     SPI_FLASH_CS_HIGH();* K, l  d8 k! q# z& J4 b& c
  65. }
    - _9 z5 P5 X8 z% z! f, c' W
  66. /**
    # _" j# E( V4 |5 u" g
  67.   * @brief FLASH 读ID
    1 n8 \! U  ?1 k
  68.   * @param None
    , Y5 E# t% D3 m& L+ Z+ F
  69.   * @retval None
    ) k1 S6 L1 j, q0 P, n- p4 n: [% V
  70.   */
    " {5 X; H; ?; V- l2 q; O5 E4 Z
  71. uint32_t spi_flash_read_ID(void)- f& p, d! e" @' H$ O! x8 a
  72. {
    ! l# h# D( I5 F4 Q. h' f" K7 ~- i
  73.     uint32_t temp, temp0, temp1, temp2;5 X& p7 }- c* p% R! h" G8 @

  74. ! E+ w0 q8 Z% l) j8 n3 Y  C% V' V5 d
  75.     SPI_FLASH_CS_LOW();
    / C# x# a1 H' p$ M" a1 T+ O
  76.     spi2_flash_send_byte(W25X_JedecDeviceID);
    $ ^; e7 `, Q' ^1 h7 l, @

  77. : P! B3 b! Y: {9 Y& A5 ?1 U! f
  78.     temp0 = spi2_flash_read_byte();
      }8 c4 J* q; d) r2 e, }' r* Y
  79.     temp1 = spi2_flash_read_byte();, o1 i8 E/ n5 j$ I3 A: O! B+ D
  80.     temp2 = spi2_flash_read_byte();. E' f# R2 |% U
  81. ' \6 w+ O8 X8 M9 C2 p' m: r" m
  82.     SPI_FLASH_CS_HIGH();
    5 L% s7 ?/ t" Y  m9 _, H

  83. 5 o. P0 U0 C9 E! e0 ^) N8 z9 Q; C+ [
  84.     temp = (temp0 << 16) | (temp1 << 8) | temp2;
      o9 Y$ z. f% ~* c
  85. 8 e) l' R8 l/ Q& b, a7 V
  86.     return temp;( f7 V# m9 j( H% R3 l: q) x
  87. }% t, s0 B' G0 k4 E) r2 r3 r
  88. /**, w, e; G( A. a  Q6 ~  L# E
  89.   * @brief 读FLASH( F- e1 `( ~# n
  90.   * @param addr 读flash的起始地址
    ' m/ ?# U$ f6 U; I  ^+ Q
  91.   * @param pdata 读到的数据存放起始地址3 A3 r+ R/ W( r, r2 I, H
  92.   * pdata size 读数据大小. m8 k% K7 i8 v) q
  93.   * @retval None; e2 r/ }- V- `: g
  94.   */( m' m, X4 h  _3 Y
  95. void spi2_flash_read(uint32_t addr,uint8_t *pdata, uint16_t size)% V" k( Y( J6 M% r3 D7 E0 H1 c
  96. {
    - S7 c  m8 Y+ ]4 g+ T
  97.     SPI_FLASH_CS_LOW();
    1 g( p& B3 I7 \5 Y* o6 r

  98. ' B# @9 [2 o' k( V+ ?# q& e
  99.     spi2_flash_send_byte(W25X_ReadData);
    2 b: @& t; e$ L/ `! D6 p1 O

  100. / \$ \& d0 A1 E1 J( o+ x
  101.     spi2_flash_send_byte((addr & 0xFF0000) >> 16);$ X1 f$ b! h/ C! S7 j* O8 y
  102.     spi2_flash_send_byte((addr & 0xFF00) >> 8);
    # U5 h# N. H2 Z, x/ _* }* ?
  103.     spi2_flash_send_byte(addr  & 0xFF);
    - U/ ^4 }+ _6 W6 b2 N  x

  104. 1 v& @( K: Z. M' v: g) Q, x
  105.     while (size--)8 s  d+ ^5 L! Q( r) K, [# N
  106.     {3 b% k  N+ @6 h
  107.         *pdata = spi2_flash_read_byte();9 G% [* U7 t, @7 k/ `
  108.         pdata++;
    1 h' z* [  ]! M% S. ?+ I1 ?( s
  109.     }
    % K9 N( n- Q2 _) y! S  i4 w
  110. 3 {# z9 q# ^% U5 |. V/ R8 |
  111.     SPI_FLASH_CS_HIGH();& z+ A0 T% v+ Y, c4 s
  112. }: u7 H  C" H3 W8 Q0 S% Y& t
  113. /**" _# H( K5 |0 \  M/ g* E6 v8 n
  114.   * @brief 按页写FLASH6 q4 x) M% e$ c% D# Z3 `
  115.   * @param addr 写入flash的起始地址+ L7 ~& v( o$ d& `
  116.   * @param pdata 写入数据的起始地址
    9 @+ d+ ?3 }. h) A+ Y, f
  117.   * pdata size 写数据大小7 ~8 }  J5 d5 D: N! P- K2 r/ z* l
  118.   * @retval None
    % M1 w5 \& c3 R+ Z) ?2 P
  119.   */3 j6 I3 l5 @; P: u; `
  120. void spi2_flash_page_write(uint32_t addr, uint8_t *pdata, uint16_t size)6 K( d& l2 `/ k9 n' |
  121. {
    ( {; j  y/ O, A, n8 }* G- ^' p
  122.     uint16_t i;
    2 m. H  ?: O. `# a
  123. ' a! f& a+ D& I! F
  124.     spi2_flash_write_enable();8 _7 a' \* v/ h5 |6 D: c% i2 i0 e
  125. " x/ b4 ]' p; ?7 n6 U" |; O
  126.     SPI_FLASH_CS_LOW();& m3 J. G' ^7 c. G& X3 ]

  127. 7 Q9 p9 g# c1 K9 y6 M' H8 W$ Q7 X
  128.     spi2_flash_send_byte(W25X_PageProgram);
    8 ?6 _" a! F8 O8 O
  129.     spi2_flash_send_byte((uint8_t)((addr)>>16));
    ( h6 C4 r5 V! U8 X* U$ ~
  130.     spi2_flash_send_byte((uint8_t)((addr)>>8));2 u6 o9 E$ O2 r3 [* D+ ?
  131.     spi2_flash_send_byte((uint8_t)addr);. a. R- L7 O6 B6 f9 }% |, `% G

  132. 2 e& L2 O. w; z0 D% h+ L
  133.     for(i = 0; i < size; i++)* C+ T- d% F# F: ~5 J
  134.     {
    3 c/ d5 I1 i+ M5 u0 G; N
  135.         spi2_flash_send_byte(pdata[i]);$ p' `5 e4 _! I9 Y* H$ w9 }% n
  136.     }- y/ u% J) b  K
  137. 2 V" c9 Q. L, s" F" T- Y; f
  138.     SPI_FLASH_CS_HIGH();
    0 u: b- J, d  m# O6 f/ @
  139.     spi2_flash_wait_for_write_end();
    9 e1 M* b* J; w7 U4 s6 b3 Q
  140. }/ _+ @# f# t( z  o1 t7 J7 N
  141. /**+ ]8 P8 w8 q; W0 X7 j; }- v
  142.   * @brief 写FLASH
    & p, d. U" q' S7 k
  143.   * @param addr 写入flash的起始地址4 r# k* ^/ a3 B. d$ _) u
  144.   * @param pdata 写入数据的起始地址2 j* q7 ]) _" ?$ x3 q1 J
  145.   * pdata size 写数据大小, E5 Y4 @8 ^( d% `" O' w6 |
  146.   * @retval None3 \+ o( g  U1 u5 }# O% W
  147.   *// t/ S' A0 k% U+ u0 c
  148. void spi2_flash_write(uint32_t addr, uint8_t *pdata, uint32_t size)
    % @3 G" k- q/ h( S
  149. {
    ! F6 r, K- Q, z; S5 x. z3 H
  150.     uint32_t page_remain;) ^4 O* O: z! S! P+ v! N

  151. + u: }, b. p9 S6 ?2 x
  152.     page_remain = 256 - addr%256;, F# @; D" ^3 s% Q+ J
  153. / o5 t+ }: \4 g
  154.     if(size <= page_remain)  W( U/ z5 g/ f3 v! _: l
  155.     {
    * {% h& `+ {2 M' R$ n
  156.         page_remain = size;
    ; ~/ t7 v7 `! M8 P. W) k$ S
  157.     }  G5 e- Q& T% T' q9 \2 y6 ^
  158.     while(1)
    7 w  y; W2 |+ j
  159.     {/ E- I$ T5 l. K, K4 s% G  U
  160.         spi2_flash_page_write(addr, pdata, page_remain);
    ) _9 v0 m+ A, h5 G1 e

  161. + m( S; c. U' U  Z
  162.         if(size == page_remain), G) O* W* p* C0 ?. z
  163.             break;
    + o7 P  m2 l3 S* e
  164.         else
    ) J2 j& d& F9 ]6 h" T
  165.         {8 K1 u" }  l% p! {9 c3 [
  166.             pdata += page_remain;3 n0 Y  e6 V- S. S/ s( E% l# a
  167.             addr += page_remain;* s1 B# r. }; Y, m
  168. : i9 G# g2 ?! Z
  169.             size -= page_remain;* r9 P% S2 b  t3 h" V! P+ ^  y
  170.             if(size > 256)7 \" |( |9 P" R+ ?3 }  h* c2 v
  171.                 page_remain = 256;8 i# u0 u7 Y) n3 V  s$ K* Z; {6 u$ X2 H
  172.             else9 d! H8 D$ [& E/ i9 p# C" N
  173.                 page_remain = size;
    3 e" I% a1 n/ Y+ T# {# g
  174.         }4 e3 Z; ]- ?  U2 s% w" Q
  175.     }
    4 c6 i& E; R/ M2 Z1 U
  176. }9 B$ f, `9 E( ?9 S9 B
  177. /**8 m" Q" A* a  C8 W
  178.   * @brief 擦除FLASH扇区: `2 G  g5 q. i' U/ @& d
  179.   * @param sector_addr 扇区的起始地址
    * S+ K6 w  x$ t+ I- a
  180.   * @retval None
    , C" t& S- m5 e# _6 ?
  181.   */
    ) ~: y# X6 a4 i- n; D0 F8 U1 \" R
  182. void spi2_flash_sector_erase(uint32_t sector_addr)6 L7 [8 ^4 D7 C5 ^  H
  183. {
    0 k1 V+ W/ ~# y2 B' W7 _. ^; I
  184.     spi2_flash_write_enable();
    , k2 o+ x2 |8 g* h" S2 H8 n
  185.     spi2_flash_wait_for_write_end();; t; {8 o- w+ m! V2 M
  186. ! w' m' H/ h1 o
  187.     SPI_FLASH_CS_LOW();
    8 o; X7 h0 L5 W" i: Q) Z
  188.     spi2_flash_send_byte(W25X_SectorErase);
    ! Y- s/ {- v  B3 R
  189.     spi2_flash_send_byte((sector_addr & 0xFF0000) >> 16);
    8 z) q; J8 G; L1 H; Y  n
  190.     spi2_flash_send_byte((sector_addr & 0xFF00) >> 8);
    8 l* _) b6 C. i% W: g" n* K5 m) H. `
  191.     spi2_flash_send_byte(sector_addr & 0xFF);
    & v6 h$ Z1 Y& {. \, G* ^

  192. * R* `- Y5 v/ y$ P2 K
  193.     SPI_FLASH_CS_HIGH();6 c. I+ D' W. e% z0 A
  194. 8 h- B2 [9 }" e2 _0 T& R: w, I
  195.     spi2_flash_wait_for_write_end();( A6 L' j- A8 |( {" u+ x# p
  196. }
    - _' v2 s1 B: T) A2 x2 z' T: u
  197. 3 E1 l$ l! K4 s; ]" y4 e
  198. /**; s6 s/ N* M( l% n( i
  199.   * @brief 擦除FLASH块
    # `7 y0 B9 I7 y4 v( ?/ Z
  200.   * @param None- C9 a+ c  [/ P3 b8 k: J) @" w
  201.   * @retval None
    % u7 g7 u" X; X5 G% [, y( ]( z6 t
  202.   */6 E% A& t9 m& I" y- g
  203. void spi2_flash_block_erase(void)3 u* f4 {8 }% L8 {
  204. {. @  D8 ?9 I8 `3 i: I
  205.     spi2_flash_write_enable();( T: P) A; n& N. u

  206. 1 h1 X" }7 v' U1 x1 a
  207.     SPI_FLASH_CS_LOW();$ C) f1 B4 k3 w) M
  208.     spi2_flash_send_byte(W25X_ChipErase);
    + S3 C8 f6 g: y* [
  209.     SPI_FLASH_CS_HIGH();5 \8 ^) S8 u$ r5 V$ ]. T( z; s, e

  210. 0 M) ~3 ?5 B9 W% p
  211.     spi2_flash_wait_for_write_end();
    6 K! F% r4 Y: E: l: g" M) f0 B- j/ @" p
  212. }/ \5 [/ q0 X  r/ i
复制代码

! y' m6 l; e6 R# F3、设置编程仿真下载模式
* Y2 h- U7 G! M" Y! I/ w# {(1)选择Options for target ...>>Debug>>J-Link/J-JTRACE Cortex,点击Settings>>选择Port(SW),可以看到搜索成功SW Device,表示芯片可用,可以下载。/ u* M+ q* |" \3 E% s8 L* `

7 w# b: l" Z+ W% y- K/ p
edefb6b9a2a14be3b1221ad6d5c7a8a2.png
+ R% \4 A5 {+ t" l, L0 @" K​​) c8 a! B2 p9 ~& T5 t  p
(2)点击编译,完成后提示“0 error(s),0 warning(s)”。# |$ u; H* Z9 y, @  d( B1 H) i
! r1 Q: P  A0 D  o" C
e0b7e3e353b345a7987fe5f821de2dda.png
/ J3 }# Q7 ~6 U" L) }2 I
# }& ?  y4 N. ](3)点击Download(或者快捷键F8),即可下载程序。9 R0 T% u8 y4 h$ e6 d+ |
9 ?; J: y' Z; C/ {( W5 v
eb3f250f19e44862a7df80c2492fcd01.png 1 w; a; s2 @5 O
​​* Z6 E: m% l& R/ I0 }2 n! e6 Y: M
( f. b, f+ V4 @0 s7 y
(4) 如果下载程序后,没有看到LED1、LED2、LED3、LED4闪烁,可以按下述方式设置一下(Reset and run表示下载后自动复位和重启运行)。或者重新彻底断电再次上电(或按开发板的Reset按键复位MCU即可)。  i$ `/ T. k8 H; [: `, t9 u

, x1 f, e" s. q3 J  v6 w3 A: t
9f8aa1a4e8f04041bcf5cb183facd100.png + n" R) s; G7 G, N4 R7 P
​
) Y0 M3 T- R2 V0 l1 }9 G8 J! N

" m/ I# C7 P; G- m4、SPI Flash实验效果展示8 h) d7 G; a! _: @; ^( k7 S) [
      (1)  程序烧录到开发板后,即可看到LED1、LED2、LED3、LED4初始化后每隔500ms闪烁一次,并且打开串口助手后(串口参数:波特率115200、N、8、1),可以看到printf每隔500ms打印一次log数据。, }" q" Q$ I! p2 R

6 Y: ]; u1 F- b6 c/ ?' I1 H" U
954468e85b9f44299916e9c6b3440ad2.png
- `* ~- n3 G; `" w; N3 @. ?, t# ]
6 w$ j  Q( }! A5 q! h (2)屏蔽写入flash的语句,可以看到此时串口log打印的全为FF,则证明SPI Flash读写有效。
4 W8 d* A4 L' [* o! n! P
% Z' z! r/ _  u& z* G' V' r
b37dd10545e442c3924a75955386b09c.png
$ `8 `  Q: [1 m) B7 q: q, M5 B" ~. H" m( W3 P- D, a
d36c42cb706f4a2d8103858b70c8d803.png ' V. o2 j! s7 j- d' ?
0 {1 @' a: V- C) k
————————————————
* w3 b. V9 M  L7 l" ]: w1 L0 j版权声明:智能小屋ZYXC! Q8 Q2 Y2 j; D5 C
0 N' H0 Y1 U# n

3 M: N, ]! v* C: g' m# {
赞 收藏 评论0 发布时间:2023-1-13 19:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版