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

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

[复制链接]
STMCU小助手 发布时间:2023-1-13 19:00
   一、开发板平台简介:1 G+ d. v7 K2 d1 ]- O+ I1 j
1、开发板资源简介

3 E6 \8 s) K9 C( Y9 v(1)开发板主芯片型号:STM32L431RCT6
; p1 A6 S8 c  w2 m: z5 j
9 p8 }$ D- q, J' H. q
(2)开发板主芯片封装:LQFP-64_10x10x05P
9 }5 y+ p: O3 l( k3 K( z& S) M4 w$ q
(3)开发板主芯片内核:ARM® Cortex®-M40 T! _6 X. w# M

* W$ n! U4 I$ H% t3 M$ e$ T  g/ ?(4)开发板主芯片主频:80MHz) ?& p, b1 [2 H
2 [, c% E9 e, ]
(5)开发板主芯片Flash大小:256KB
+ l$ {) Y5 ?" h) z

7 c$ Q& N9 B$ I2 J4 U(6)开发板主芯片RAM大小:64KB" T& Z+ L9 g' U3 _1 ^

' ^$ x. v9 V  K# y+ H# P" ^ bb91345227434ee7a43a34bf3fe366e6.png 9 n' G$ ]( a# i

1 E! g9 }% c* ^; o% O  \ 929c0cce04aa4115a8623703982bf3e1.png
8 A& c. d' _! F! r' z1 _$ W/ w3 Y

/ F, ]* H; _, h1 I2、LED灯资源1 r! r9 `8 n% d7 \7 O6 ]& D
(1) STM32L431RCT6开发板共5个LED灯资源,其中一个红色LED为系统指示灯,指示开发板供电系统是否正常,如供电系统正常,红色LED为上电常亮状态,硬件原理图如下图所示:1 T8 w7 G8 P: u# j7 d1 ~$ ^1 b

' [" I. G0 y/ w' K; ^. ?' ]8 S
0f177691c5814f16a508bcfa747f87ac.png 7 G+ p7 n$ a# J' E5 D6 c' N
% t+ Q8 Q; {$ b; ~
(2)其他四个LED灯为黄绿色可控LED,高电平点亮、低电平熄灭,计划用LED常亮验证看门狗的作用,硬件原理图如下图所示:
7 |- ?* E6 L6 R" R, ?7 w& z
! u7 A3 w' c& w3 e/ m& Q" C
e923ad767b8448b7ac37c4ed693724bd.png ; r3 C2 D$ k8 ~0 _. Q. _9 q# \

1 }! \. b0 x9 Q  S9 s​3、串口printf打印工作原理9 a, _( F, N& }0 V! t6 P+ k
 串口全称为串行通讯接口,即数据在通信线上一次传输一位,按先后一定顺序传输。我们通常所说的单片机串口准确来说应该是串行异步收发传输器(Universal Asynchronous Receiver/Transmitter,UART),使用TTL电平,串口需要RXD、TXD、GND三根线进行通信。1 h; Q; D3 u& M

# \* R4 N3 T) [, o     (1)我们选用的STM32L431RCT6开发板串口1已通过USB转TLL串口芯片CH340G引出,使用时,只需要用公对公USB线连接电脑即可(注意也得需要安装CH340G驱动),后期验证试验也可使用该串口作为debug串口。
! \: \* \3 n* U* B  C: y5 G- e- {/ E, K* p  Z- r" a
     (2)开发板上的其他串口已通过排针引出,为TTL电平,通信的时候需要注意选择对应的电平模块,如USB转TTL串口模块等。! Z1 M- \; R5 g
$ O, v2 @2 ^! ?# o9 i
TTL转CH340串口,硬件原理图如下所示:' I2 A5 |1 o' ?, n! m

9 R# R! x( ?" M" O$ E3 U. X4 h
4df1b489cb944c26b55013001061675d.png
' ~6 Y' D* U& j6 k5 |% u
, _: H8 q( c- U! d$ J4、SPI Flash简介8 N, `; b0 ?0 k' s9 k! P
(1)SPI总线介绍" P; X+ Y7 r) m5 v$ B
SPI分为主从工作模式,通常有一个主设备和一个或多个从设备,本文中MCU为主机,W25Q16为从机。
' @& v! w2 s( M) Z- [& U+ G: }6 {  U
9 @, d+ q/ }, E5 M* _: h- Z! r+ g5 U
SPI通信有以下四根线:' k$ G6 d0 y5 o1 V8 S' \3 G
MISO:主设备数据输入,从设备数据输出。# _' ?+ n; L0 l) Z$ F2 W
MOSI:主设备数据输出,从设备数据输入。6 w& j7 e! R* `3 Y. y% k% h
SCLK:时钟信号,由主设备产生。
7 e* l4 @. p9 ~% P0 m3 Q( M! D/ A, E6 {+ U* d" e3 _2 f  b- ~0 x
CS:从设备片选信号,由主设备控制,低电平为选中。$ v$ a0 X# P# j# m3 N- h  ~. {( T9 Y

- n& |8 W0 e/ w* A(2)SPI的特点:
; |6 s' p, E! q( C+ ]4 Q$ n+ t2 B高位先发送,共有四种工作模式。
; |, j0 S& @  }+ v' [CPOL(时钟极性):规定了SCK时钟信号空闲状态的电平(0-低电平,1-高电平)9 ?! ?& t, S1 n9 g2 f  ~% j
CPHA(时钟相位):规定了数据是在SCK时钟的上升沿还是下降沿被采样(0-第一个时钟边沿开始采样,1-第二个时钟边沿开始采样)
/ f4 _( e* l, r
6 @: ~& a2 a# j2 w! y模式0:CPOL=0,CPHA =0  SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据), X# X$ r0 A1 u8 x/ j
模式1:CPOL=0,CPHA =1  SCK空闲为低电平,数据在SCK的下降沿被采样(提取数据)
. F0 {/ R( z' D/ {* j模式2:CPOL=1,CPHA =0  SCK空闲为高电平,数据在SCK的下降沿被采样(提取数据)
  g6 c' u/ R- S/ ]/ N模式3:CPOL=1,CPHA =1  SCK空闲为高电平,数据在SCK的上升沿被采样(提取数据)
; u. t. w/ X1 k  z, ^& i8 Y2 O& x. \4 q: D# }4 A4 K/ ?' k' v
(3)开发板 SPI Flash原理图如下:) R8 z" P5 n" P* [

  g* X9 z' n5 Z! J1 y
da82f3d13fef48dd8d90aa170757e360.png
7 P5 g4 c) u' A/ H. P- h' X1 n5 E
- s4 J5 j% e  |0 S& p
5 H2 b4 r' ]0 M1 v& N' d, R$ a% w  二、串口printf实验过程; A. p- x9 Z7 u5 \
1、新建STM32CubeMX基础工程

4 d% d% h4 f* r* U3 F(1)打开STM32CubeMX,点击“File”-->"New Project"% f" }8 `, [' |' P9 z# o, P; E2 K( T
. o; k3 u& W; Y+ l6 r# c
2399170347904e959c855fefd5877f84.png
( q5 T/ c0 y0 @8 c4 N+ B

" w( Z* [$ a1 D, H9 M(2)等待打开主芯片选项界面(大约1分钟时间)。
% ^/ K* c% E' O! f) L+ S4 W+ Q
5 f1 b; C6 w1 |) V( o* |  B  _+ O
d74d2fdc60c9440483e2b7cf8d928e7f.png ! J, s9 N7 x& a5 B# V9 E9 u4 C

1 V) r: |2 v0 w% L8 d. R' I' O9 R" {(3)昨天搜索框中输入(或选择)所需的主芯片型号(因为我们用的是STM32L431RCT6开发板,所以此处选择STM32L431RC),然后在右下角选择STM32L431RCTx(因为开发板主芯片是STM32L431RCT6),左键双击即可打开新建的项目。+ M/ S1 ^$ E4 {8 ^
6 t% U; v5 x, x# Y+ s) d. Y
7b80345238d74bea82ce70e1a348f7b4.png
, q- Q: U# e& j% }
, t$ n- k+ S) K* Q* j(4)选择时钟源。' {$ P: _: ]. Y' E' q
(1)因为开发板上有8M外部时钟,硬件原理图如下所示,所以此处选择使用外部高速时钟(HSE)。1 f4 ?) H9 R, K/ c) X5 r

8 L9 q9 U7 H, Q1 V
3f65707eca104663a3dede6d25dfa961.png . A" p! J# v9 _+ `
! l9 Q' m! p! f# ^
(2)因为我们没有用到外部低速时钟(LSE),此处不做处理,如下图所示。. ]( Y" @6 R$ R6 R9 W. N
# D6 ?. M% {5 p: z8 z+ u
72119b971f62410fa8344f7f9fb9f389.png
; N' E. l/ o1 {; p: y
8 f) Y: N: |  k  Y# T) R* I2、配置GPIO控制LED
5 w4 }/ |) q/ N+ M$ [4 L(1)查开发板原理图得,LED1、LED2、LED3、LED4的控制引脚分别为:- {2 x4 ~  p  s4 a
LED1——PC0
$ S* N, s' e& G$ s) d; _LED2——PC1
% G* O- j2 H* M* i$ vLED3——PC2; _( G$ w" {: e; {! b: w+ p. L
LED4——PC37 K* [1 c7 k! H. N% k
' d; _- {- f$ R; O4 C
(2)配置LED的控制引脚为输出,输出频率、输出方式默认即可。1 x1 s( X/ X% s7 J7 n: i
鼠标左键点击PC0,选择“GPIO_Output”,表示设置该引脚为输出模式。
+ C) i1 S8 Z. }$ M" b鼠标左键点击PC1,选择“GPIO_Output”,表示设置该引脚为输出模式。
& R3 D8 \3 i3 A$ s0 Y鼠标左键点击PC2,选择“GPIO_Output”,表示设置该引脚为输出模式。
% Q  S, R* ^+ \+ t8 L0 M鼠标左键点击PC3,选择“GPIO_Output”,表示设置该引脚为输出模式。# c6 U# W3 J" f! ~3 U/ z

# O. C! r4 V8 e8 a
492907c1e71149819adbaee4516a2af4.png
# m5 ^* a1 M# N  `; x- P$ O5 J3 p& d5 C- R' X( @5 d! [
c75d3f10ba2e427d9c1a10fad9bf5471.png # D) }- e8 v/ n

5 H% t$ c0 t+ S* _(3)也根据自己的需求配置GPIO的参数,如输出方式、输出频率、上拉下拉等。因为GPIO控制LED的要求比较低,此处采用默认参数即可,不用修改。
, _7 L' R3 Y% `2 a* `( f/ |; V" u) `& k, C5 G
38ff4b80e1c5495ba3076a5158fd91fa.png
- m& E# F: z4 Z7 G
# g5 K/ }! h% {# p​​3、配置PA9、PA10为串口8 Q9 T% B! |1 J# z2 z# R) H
查原理图得知,串口1使用STM32L431RCT6引脚为PA9-USART1_TX,PA10-USART1_RX,引脚设置如下:% q: s% X2 ?5 H4 e# m
) g0 m5 m, @, m, L
497b8ee0243e446784e64a3c31e8a26f.png ) x6 ^$ m* @* j5 O0 c
& q! a: C) r" w0 J: V
(1)序号1用来设置串口收发引脚的选择。0 k4 Z# y4 W  ^* A  J& i0 y; @
(2)序号2-3-4-5-6设置串口参数,如波特率115200、8位、NONE无奇偶校验等。9 R' u& W1 O& a2 J/ B
- q' x+ I/ P7 m% n, W
4、配置SPI Flash接口8 G9 q+ `) o* f. I
(1)查看STM32L431RCT6开发板原理图得知,芯片原理图如下:3 [( f4 L7 `5 [! z
' V/ P1 u' z. n/ S) O$ Z, p
11aaba64f3d34f9ea1e4cbbfc0fdc102.png
% R" s5 P" |. z& Y8 k( m

. L9 I" u, p" U; t) i: v (2)SPI Flash接口对应芯片的PB12、 PB13、 PB14  PB15,芯片引脚配置如下:) W& h7 V8 w! B# w! i% C7 X
PB12:SPI2_NSS,此处设置普通输出IO即可。不能配置成NSS
: r5 o9 X% X8 @" gPB13:SPI2_SCK& ~4 L  q( y+ ~, @: z/ e% ?' C) ~; W
PB14:SPI2_MISO
7 V1 r4 L8 f- E7 S! U$ iPB15:SPI_MOSI
# ^6 {0 T/ p" W, s6 {4 ]+ E4 B6 i
ade62d1658174af99a93e3928b62849c.png : \" q3 J; c- F9 [+ J

: j' q- s% i, n" P (3)设置SPI引脚参数,并选择 Full-Duplex Master 全双工主模式,此处不开启 NSS 即不使用硬件片选信号。
* L/ T* k- U7 g. [
  Z, m* A/ }5 G
c932367c299e46979e55a3e1a05d2c7e.png
0 `, R; K6 N# l1 F3 y& x; O# i3 t

9 ?3 l1 y3 t+ p- X* L(4)设置SPI基础参数以及时钟。$ b) C9 l2 g0 o$ {* B
, Y& I. T% T" f
e5af1c8c0b0b4ac9a07d740b955028c0.png 9 f% F+ z, f" W6 ?2 A. d/ W

$ g7 _  Y! q1 J" j5 {( [
dcc8d73ddcbe4082a09165905ac71cf1.png * b, Y' r+ G& ~  B
- g5 F' T" y  ~& ?. n! A' |
5、配置项目工程参数: R* \* a* B8 E& ?( ?
(1)配置时钟树,用于系统内部时钟,以及各个外设时钟等。此处选择外部8M晶振作为主时钟频率,内部最大倍频80MHz。
; s7 K& S" Q; q2 V$ m4 X6 L+ Y' D' d
7 I9 y# y7 E; B( W$ r
f3ecedd9323f4463b69539906b3c1199.png
1 z$ d+ z4 R# l: G
+ W! R, D2 p' G9 X(2)完成配置工程。
9 G7 `# P* K+ m' o# b) ?备注:需要注意代码生成过程中的继承关系,如图所示:需要保留开发者自己编写的代码时,请根据配置设置,不然生成代码后会删除自己编写的代码(从这个方面也可以看出开发者备份自己的代码是多么的重要。): j4 ?" `8 R' Y
4 a1 s8 g$ u+ N8 p* Y9 ~# ]: S& e8 ]7 l
efdb616174f54925b6eac31109f227b5.png , E- V3 @. h) y- l" `; [" f+ H0 ~
​​+ j3 n8 z% k( o+ n) N
41c3f2716e4e4eda8f62fa3a1d871322.png
" ?4 l3 _9 h7 w( v* \
& I( X8 ]: T2 Q4 u0 T3 [
(3)生成代码。1 h7 Z# u8 b: t+ Z( A9 B  v/ q, i# a
备注:使用Generate CODE生成工程代码前,请确保文件路径无中文,否则会生成项目失败。
0 _) A: R( J) [+ _0 |
0 Z& y) v. @6 f! ~' D
8eaa7d0babea40ee93f158481a6ee471.png
( {" ?0 B7 G7 M6 _$ ?5 F) x. H+ B$ w6 w# a% T0 q. N
(4)工程代码生成成功。3 n, c' E* F& \' M
9 n! {- d, Q* I6 w, _
e8f46d59a1fb4af589feb613fdff17b4.png ! l4 ~7 U0 H# O5 r$ H6 q7 @

+ J+ {9 G# z' I  L0 h. I0 j* K; ^" C- v) ?
三、在KEIL 5中编写代码

+ Q6 A& ?3 v/ R& a+ J- x1、使用KEIL 5(MDK)打开项目工程文件
" ~  ~) A# p# l源码使用说明:使用前必须把项目工程复制到无中文路径的文件夹下使用。  |) P6 A" K" S- Y3 |4 a! ^
(1)找到刚才新建工程的存储路径,安装项目名称,打开项目工程.uvprojx。
5 }8 `% r9 }- E! s( W) N
' l' l$ a( }% T, k
37170661111547238cfcd51f580aab01.png
+ z; t+ L0 q; h7 |6 q3 f  g' w9 b5 t; ~
2、添加SPI Flash读写验证程序/ ^  p& L' e$ V7 o
(1)main.c文件中,初始化LED1、LED2、LED3、LED4默认为点亮,并在while循环中添加控制程序,如下所示:实现每隔500ms后LED1、LED2、LED3、LED4点亮和熄灭之间反转切换,并且串口每隔500ms打印一次。8 q: ?/ v! ^+ c$ T
/ [( w- t( [( ^" j; o2 c3 ^4 d
备注:自己添加的代码需要在 /* USER CODE BEGIN 3 */和 /* USER CODE END 3 */之间添加,否则STM32CubeMX更新代码时,会造成自己添加的代码丢失。8 c& T: S/ }0 H! A% U

% J' f0 a# {9 f8 U$ e* V2 M
d1194af353cd4b0b88cd9208b1ac7db1.png # N! m0 N6 [4 j6 Q

% V8 ?) ~- R5 b1 a8 R7 l
3c771cb9b6094461aa8ec2befc9d12dc.png
7 D( n4 ^! U! W5 a2 _6 w; s( y' `9 z6 t! b6 a% E' O" ^7 i5 d
cd59e19f79524446a278fe4f5fd7b81a.png / v1 P# M1 B& S2 ~& B' k! h: N8 G
9 Z& j: J$ u% ~6 ~
(2)main.c函数添加初始化代码和读取flash的代码,如下所示:+ G9 r# `% ~! e
  1. int main(void)1 q5 |# x( X, I  _! y. {" Y
  2. {9 v7 j+ O) |7 V  `
  3.     /* USER CODE BEGIN 1 */
    2 O4 ]/ n+ }* R) \* J
  4.     uint32_t w25q_chip_id=0;                                                                                //读取芯片ID5 A& G3 L& Y5 c2 |
  5.     uint8_t  onebyte_read[8]= {0};                                                                                //读取的字节内容; C- r. _  f# [$ Z8 r9 u
  6.     uint8_t  onebyte_write[8]= {0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80};                                //写入的字节内容
    ' A7 W& F) H: u9 O# q1 b3 M
  7.     /* USER CODE END 1 */, H$ Z3 a; @8 i1 o( L2 J
  8. + b- U: @0 ?& |& O  N
  9.     /* MCU Configuration--------------------------------------------------------*/
      B: j- C9 T; P

  10. $ ^% N  s/ S9 I  s2 y
  11.     /* Reset of all peripherals, Initializes the Flash interface and the Systick. */# n" n. m: G, f7 |0 n: o
  12.     HAL_Init();& }# e3 Q9 Z5 F: D! o8 W& h
  13. 1 I+ l1 J2 @6 E4 {. p9 c
  14.     /* USER CODE BEGIN Init */5 |: N% ?, L, U( Z
  15. & U7 |3 A( L& _% @/ g4 ^8 i( E  d
  16.     /* USER CODE END Init */7 M# q, l& s3 G2 t$ ~, T

  17. / R# P+ A5 ^: i1 f* Q6 b& D
  18.     /* Configure the system clock */2 h' E0 }' e6 }6 {' X
  19.     SystemClock_Config();
    ' b0 q' D! W4 H. N) f/ \

  20. " W* ~$ v' c, o6 X  y
  21.     /* USER CODE BEGIN SysInit */
    " X5 M$ |5 c' [- D

  22. " D; y' q! [9 s3 G
  23.     /* USER CODE END SysInit */
    2 M5 d* r2 k$ v0 p

  24. 1 I6 O2 {1 P6 g( ~/ e
  25.     /* Initialize all configured peripherals */: q3 ]$ ^) p% K$ R% \' Z
  26.     MX_GPIO_Init();8 @' J( b6 O& ?/ k- b4 \
  27.     MX_DMA_Init();: H- n& p3 O: {4 a4 H$ z# P4 P
  28.     MX_USART1_UART_Init();: F$ L- n# T; F8 @8 r
  29.     MX_SPI2_Init();; z( g' N  }+ g2 g1 i4 o) z
  30.     /* USER CODE BEGIN 2 */
    ( d/ s- \/ w/ }5 K& e5 }: Z
  31.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮
    * g& s. E5 o4 e% s: o; ], ^6 F
  32.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮
    # r3 A& e3 }0 s/ j+ }- k& ?
  33.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮0 m0 ^! B0 {6 l- ?
  34.     HAL_GPIO_WritePin(GPIOC,GPIO_PIN_3,GPIO_PIN_SET);                                                                    //初始化默认LED灯为点亮
    4 i* T  u% A, J$ ^
  35.     w25q_chip_id=spi_flash_read_ID();                                                                                                        //读取flash芯片ID
    ! u' p: b3 R* h
  36.     HAL_Delay(100);0 v, s, X2 w4 L( r. Y- ?
  37.     printf("hello world,spi_flash_read_ID==0x%0x!\r\n",w25q_chip_id);                    //printf 芯片ID
    9 a- e! h1 ^) S5 y& T* r
  38.     spi2_flash_sector_erase(0x0);                                                                                                                 //写之前需要先擦除扇区
    % O& Y9 h" L0 q, Q+ Y1 A( n
  39.     spi2_flash_write(0x0,onebyte_write,8);                                                                                                 //写八个字节
    3 G. @* C, M. p+ X6 M: ^" [2 m
  40.     spi2_flash_read(0x00,onebyte_read,8);                                                                                                 //读取写入的字节" n2 k1 ?/ y. P' n4 ~2 ?: V0 B
  41.     /* USER CODE END 2 */" X& N' u$ g6 N$ T, `

  42. 4 C2 |8 q) s- H/ p% ~. v
  43.     /* Infinite loop */
    9 r5 f& n: [; C0 }* H9 I6 ?! y1 e  U
  44.     /* USER CODE BEGIN WHILE */
    4 U8 ?- S, X2 i; F+ I' `( `2 P
  45.     while (1)$ x/ ^1 o& z7 w; N- u
  46.     {/ \7 p' K+ N! g+ ]) O
  47.         /* USER CODE END WHILE */
    9 E. K% J$ ?  u3 x. P5 V3 h
  48. 0 x* B7 ?* v: s/ w2 J5 J1 }3 l! D
  49.         /* USER CODE BEGIN 3 */; u* v- d  V( n9 X: O' \4 H
  50.         HAL_Delay(500);- i4 \% T- F: b6 c6 k0 R; x, {. l
  51.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值0 @& f) u5 c' A5 p5 m; w
  52.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_1);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值, e( ]7 V* b( @8 D6 l! _: d
  53.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_2);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值
    9 c( |3 \/ b3 H2 r8 O
  54.         HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_3);                                                                                 //每隔500ms闪烁一次,并打印flash区域的值
    . |- C! ?9 a) h4 A0 y5 b! V, m
  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],\4 U5 ]* T5 w4 v
  56.         onebyte_read[2],onebyte_read[3],onebyte_read[4],onebyte_read[5],onebyte_read[6],onebyte_read[7]);   //printf打印flash区域的值) `8 D2 |+ w! b

  57. 0 y2 w( g/ _6 ~# m: P: n/ K: e2 I
  58.     }
    8 c* X/ v- }) V# S
  59.     /* USER CODE END 3 */# Y5 |( X, B) k, |6 I  ~; f
  60. }
复制代码

7 v7 i, h# }6 f(3)新建w25qxx.c文件,并添加驱动层函数,如下所示:
( @5 V) ^0 v7 ]3 a4 F5 i+ y
  1. #include "main.h"$ I/ _9 W2 j  o3 F0 v- E
  2. #include "spi.h"
    9 d) O/ ~0 i" X
  3. #include "w25qxx.h"
    , g. m3 }2 L' O. q2 l
  4. /**1 \2 L0 Y6 e7 h- H3 }
  5.   * @brief SPI1 读一个字节
    % `8 @/ |- h3 T! L( e+ C
  6.   * @param None& c: I1 G! q7 W! E2 u% |
  7.   * @retval None, _' T( G- I* d2 Y, `1 L8 ]2 M
  8.   */
    7 N1 Q" p, k, z+ m9 q# J; y. S8 i7 x
  9. static uint8_t spi2_flash_read_byte(void)
    3 m; u! H. q5 d" R- }
  10. {2 ~  I8 k. G: z/ R: c
  11.     uint8_t t_data, r_data;0 p$ e+ D  ]8 d% c2 Z
  12. ) Q5 c5 o6 C6 d5 N& B
  13.     if(HAL_SPI_TransmitReceive(&hspi2, &t_data, &r_data, 1, 0xFFFFFF) != HAL_OK)  U- `! W$ T; X( e
  14.     {
    * V  o) Z, E7 a4 z
  15.         r_data = 0xff;8 V! v$ D7 M1 K3 H
  16.     }
    1 y3 o5 \3 D8 \, F$ N
  17.     return r_data;& L! `5 j: ~: F/ W% [
  18. }
    ; m" h* v# s" H  J7 c& J# v) @/ }8 h# g
  19. /**
    8 u' a* `* D! d; j# e+ h; u4 h( y
  20.   * @brief SPI1 写一个字节/ s% P, a& ?& v4 n' N
  21.   * @param byte 写入的字节
    ) L/ J' A) q1 ^. n& f
  22.   * @retval 写状态 0成功 1失败
    ) ]7 N% _" ^8 E, m7 i
  23.   */
    - K+ X; r0 }& B
  24. static uint8_t spi2_flash_send_byte(uint8_t byte)2 x! K9 ^( H! `% i
  25. {
    ' O; D2 q( F" u! x& F/ y! }6 B
  26.     uint8_t r_data;' q' p/ P" j+ u- E3 U
  27. 1 d, r: G9 Z: T4 y
  28.     if(HAL_SPI_TransmitReceive(&hspi2, &byte, &r_data, 1, 0xFFFFFF) != HAL_OK). q: @6 N0 X3 d
  29.     {- O$ q/ C2 T8 ?+ X
  30.         return 1;
    8 H0 ^; b2 k( e: t
  31.     }
    5 A9 B) {* E3 Y) G' ]- G$ H, z
  32.     return 0;! l5 o- y' G: O( @- |  Z6 i
  33. }
    % v* U9 `& [6 i
  34. /**% p/ j2 K) I! M8 }
  35.   * @brief FLASH 写使能& T5 V  h) @- ?% P$ D1 o& \0 k- M% i
  36.   * @param None
    1 n2 g2 B3 ?! K& W- f  i- U
  37.   * @retval None- |9 H7 ]: g! T! J1 x6 l4 e
  38.   */
    & a# P* D, d% W& y8 D$ K' M4 c% g
  39. static void spi2_flash_write_enable(void)
    / h1 c- r7 k3 D1 o
  40. {
    $ F# X' @" R! t: H( K
  41.     SPI_FLASH_CS_LOW();: U" ?6 g, C$ ^
  42.     spi2_flash_send_byte(W25X_WriteEnable);) G6 e  X% @/ ]( x
  43.     SPI_FLASH_CS_HIGH();5 u, f. o; s$ X* P
  44. }' N( L: Q- @* e, G
  45. /**" m, g% N1 s; Q; J
  46.   * @brief FLASH 等待写结束; T5 Q/ P) c$ b/ r3 a
  47.   * @param None
    . `- ]6 X1 H! E) ~5 R
  48.   * @retval None$ C) H8 O; P+ j% @) {) H: a$ A
  49.   */# g' F) O0 b! R$ N( F% @3 G( k
  50. static void spi2_flash_wait_for_write_end(void)5 w- I3 Y) I: ~/ I
  51. {
    3 p, ~! H% ]' t8 S: C
  52.     uint8_t state = 0;$ \2 k& ^% F% m" g7 z# _  y1 }! c3 `

  53. ' R% {8 y. O+ M! l
  54.     SPI_FLASH_CS_LOW();& k8 [4 G% M7 P" ~

  55. . _" R% N0 B: b! _9 X; X
  56.     spi2_flash_send_byte(W25X_ReadStatusReg);1 [+ Y- m! c' ]/ U( J( B

  57. 8 x: s/ V/ b/ N8 C# d. }- a
  58.     do- D& k: |) R5 O1 _0 s
  59.     {
    . u$ X# y& \( S9 w) e  d; l
  60.         state = spi2_flash_read_byte();0 J/ W) N* H$ A6 w
  61.     }
      y+ o- l1 j) |0 s! [
  62.     while((state & 0x01) == SET);; n' V+ O# N8 O; R( c* r0 Z
  63.   a) u, `& O9 u3 x" w' N- D* _
  64.     SPI_FLASH_CS_HIGH();, I  K5 ?% p$ t5 p
  65. }3 N& e: k8 M0 h/ q; \$ s4 b
  66. /**: K: [" I& n$ {1 C' E0 w' C! w
  67.   * @brief FLASH 读ID
    . b2 k/ _. n6 }$ i7 `. Z
  68.   * @param None+ P3 D% s% c9 v- V4 R& b- Q5 A. k2 Y
  69.   * @retval None
    4 A) k0 S! i6 P  `4 z
  70.   */
    7 P% K. p) `% b" u
  71. uint32_t spi_flash_read_ID(void)
    % I! Y5 x# h, a8 R4 d$ {, n+ \
  72. {
    ) \3 T+ v, m1 {% `: m( P8 [9 g
  73.     uint32_t temp, temp0, temp1, temp2;1 }! x& M6 V1 s: ^# S6 y

  74. + }) P" h4 F+ m# i: G! O
  75.     SPI_FLASH_CS_LOW();' u/ m" R+ p1 L4 L7 |! H3 S7 W
  76.     spi2_flash_send_byte(W25X_JedecDeviceID);
    , @) I& S& Y8 j: p
  77. , |% a2 p; M/ I- j$ s
  78.     temp0 = spi2_flash_read_byte();
      `6 `7 O7 y/ q
  79.     temp1 = spi2_flash_read_byte();! i9 E; j3 t) J$ t
  80.     temp2 = spi2_flash_read_byte();3 Q- A2 j2 [% L2 {3 I) ]0 K: a) {& F
  81. $ _$ [- t! E# H4 D/ ~
  82.     SPI_FLASH_CS_HIGH();5 [9 R) m, u9 K* F0 W

  83. - ?9 \, m8 B+ z9 E3 I
  84.     temp = (temp0 << 16) | (temp1 << 8) | temp2;: ]! p: x. D, f$ ?+ j, q

  85. % Y5 y  x6 y% ]0 {
  86.     return temp;
    + O; U* O  j" k2 C9 p3 r) ?
  87. }/ l5 b7 }; o: d$ {$ ~: Z+ c
  88. /**8 A9 n$ f: t" \+ V3 ?; u" \
  89.   * @brief 读FLASH
    9 N% o$ v( H/ U
  90.   * @param addr 读flash的起始地址
    . i8 V6 ^7 X' r
  91.   * @param pdata 读到的数据存放起始地址
    $ u" C$ I' ^" j; v; M) }' B- p
  92.   * pdata size 读数据大小
    " @4 M( y' q% N
  93.   * @retval None
    2 c6 x9 J/ g, G& J, C
  94.   */
    $ ?- p  @8 Q; ]( F6 E! P
  95. void spi2_flash_read(uint32_t addr,uint8_t *pdata, uint16_t size)
    8 X& b7 V6 v8 e, A% z- J2 z/ k( ]8 u
  96. {
    ) Z$ y& [2 m& i' L% ^
  97.     SPI_FLASH_CS_LOW();
    % T3 z4 t9 \; w) {& \! n
  98. 7 y, Y2 `, l) a' ^0 A  u1 V3 L4 s
  99.     spi2_flash_send_byte(W25X_ReadData);  X0 j$ z( n9 n1 ?6 k

  100. % H. o7 l9 o# _% R" c
  101.     spi2_flash_send_byte((addr & 0xFF0000) >> 16);
    % z/ C7 @8 `- A' x( p. P
  102.     spi2_flash_send_byte((addr & 0xFF00) >> 8);
    " n4 C1 t% U9 H- c7 L& C
  103.     spi2_flash_send_byte(addr  & 0xFF);
    / j1 r- T9 P6 a: p4 X$ l
  104. 6 {2 ~+ \6 y  H) i1 w2 g+ r
  105.     while (size--)
    ; k8 b& s9 G) P* [5 D- j/ U
  106.     {
    & Q: K* y  L, c" q6 W3 n+ t
  107.         *pdata = spi2_flash_read_byte();& D6 p. G! m, L6 L0 U
  108.         pdata++;
    : t* c( i2 b& M
  109.     }
    5 \" U" E! _2 Q* R& N. S/ ~
  110. 4 d/ N' ?; D- i& \2 q
  111.     SPI_FLASH_CS_HIGH();
    ; x; O' Q: i+ v+ H" c' L5 U3 x# N
  112. }
    8 \( h" V. T- p, B7 K
  113. /**2 ~% m- t, j, g2 Q5 S4 w
  114.   * @brief 按页写FLASH6 I& f& W! J" \
  115.   * @param addr 写入flash的起始地址
    ' ]( U1 ^5 L) L. [
  116.   * @param pdata 写入数据的起始地址
    * O- Z0 f% k3 M, a. `2 h% |
  117.   * pdata size 写数据大小
    3 D6 C  U+ a# [  |; j( }
  118.   * @retval None
    ) [, {+ O7 Q0 e+ D" `
  119.   */: M) Z* c7 _% h0 m5 z
  120. void spi2_flash_page_write(uint32_t addr, uint8_t *pdata, uint16_t size)
    2 [6 c' I; k! I" M. l
  121. {
    % M: a' {( f; G3 W3 u$ u4 Q: M0 L$ v
  122.     uint16_t i;
    3 j3 S8 ~. ^. ?& k+ f

  123. 5 x9 V, b% R6 F7 }' I! |
  124.     spi2_flash_write_enable();
    , \4 V& g( }- m# c" x" j! G/ @: [/ X
  125. 1 J4 S4 V9 g/ ]6 Z, I1 X/ M
  126.     SPI_FLASH_CS_LOW();! Z1 W" r6 q- A
  127. $ Z7 n' k' ?5 I+ q! |6 Y0 p
  128.     spi2_flash_send_byte(W25X_PageProgram);
    9 c$ l8 G1 b! c" ?$ ^4 B
  129.     spi2_flash_send_byte((uint8_t)((addr)>>16));/ q4 I* @  X; _7 c+ ]
  130.     spi2_flash_send_byte((uint8_t)((addr)>>8));! ?0 V- I) h( T8 v
  131.     spi2_flash_send_byte((uint8_t)addr);+ q& i5 T% f0 m: O  B6 B% S
  132. 2 Q% o1 D: ]$ m4 n: @5 [
  133.     for(i = 0; i < size; i++)
    # Y" n3 u8 w% e) K. G2 t
  134.     {( V5 p' \) v! @; }1 Y
  135.         spi2_flash_send_byte(pdata[i]);  x. t# t; v. k+ P/ p5 I  A8 L8 O
  136.     }
    / U* b/ O8 m5 O* @8 Z

  137. ( S9 l& F0 j# {0 @5 M5 [0 V% m. b
  138.     SPI_FLASH_CS_HIGH();
    3 ]' O7 d7 w" _/ g
  139.     spi2_flash_wait_for_write_end();5 U! j! [) W8 f1 z3 c
  140. }
    , U; D2 \; q( P8 x* x
  141. /**
    ; s$ h1 R" e0 h% f9 M$ D
  142.   * @brief 写FLASH6 Q# D$ R  J; f
  143.   * @param addr 写入flash的起始地址
    * W. ^8 R+ h7 g; g$ W
  144.   * @param pdata 写入数据的起始地址
    / D5 Q6 K( Q6 m" Z' @
  145.   * pdata size 写数据大小1 N5 Q- P' K0 D$ @  X+ }
  146.   * @retval None* s# K' d5 f7 `) G1 _! K
  147.   */* u& z, ~8 w& h0 `3 \$ H
  148. void spi2_flash_write(uint32_t addr, uint8_t *pdata, uint32_t size)" Q! h! P4 _/ S, Y; u& |
  149. {
    " f' t# F/ \8 s9 m
  150.     uint32_t page_remain;
    & e8 x/ P1 {5 w

  151. 6 m- Z$ M' Z; E% x+ p5 M' Z& ?3 A- ^0 w
  152.     page_remain = 256 - addr%256;
    $ Z! g( n" m" Q2 |
  153. 9 H+ I7 a$ I+ Z8 _  l
  154.     if(size <= page_remain)9 w: M! Q% a" H  {3 \! p" q2 X
  155.     {
    ( s/ F3 {/ z' I/ o1 P
  156.         page_remain = size;
    ( C6 y5 y+ O9 _  g
  157.     }2 j' h  p: a9 N8 u* \$ M$ i- R
  158.     while(1)
    0 ^) p! `" z4 b2 w5 f+ y; M( p* W
  159.     {
    " X; ^. ?- b/ e: L0 B& L
  160.         spi2_flash_page_write(addr, pdata, page_remain);
    ) Z, m! ^& q0 j$ U

  161. 6 J+ L* a3 `& U& {6 V
  162.         if(size == page_remain)+ J) g2 n* ?8 f
  163.             break;2 W% Y; ~- [- ^6 j% ~: ^
  164.         else2 X/ @! j8 G! X9 T( `3 Q: E) ]' h8 E
  165.         {
    ! ?" r8 h2 f) Z7 @: x; D
  166.             pdata += page_remain;$ ^: `2 G" B* \% Y2 C/ O1 `4 N
  167.             addr += page_remain;
    ! Y3 w) h( W' E1 Z" B- g

  168. % S, b$ N' c8 {2 z. T' o" ~# M7 ~( h
  169.             size -= page_remain;
    7 G0 c! k, j" t0 @; J7 u
  170.             if(size > 256)
    0 z# p( B* V4 C7 V0 r7 v
  171.                 page_remain = 256;$ L& D3 \3 y: G9 W* D! U
  172.             else
    ' e. N1 N; G, U% O+ q9 h* ?/ H
  173.                 page_remain = size;: n' W( ~3 `) B* ?
  174.         }; R6 Y. \5 r2 S. L
  175.     }
      K$ l4 d) X/ W* M  b+ ?1 C
  176. }# m" C! K; F/ c9 \: Y; L' e$ n
  177. /**" ^/ u7 M6 F! p
  178.   * @brief 擦除FLASH扇区& s6 c; K, u! d' B; |
  179.   * @param sector_addr 扇区的起始地址
    $ C" d8 K. i; C
  180.   * @retval None5 c+ I' H7 w& Y! T( {4 i
  181.   */
    $ f+ ^5 g/ r7 n* x. }3 m/ z) p
  182. void spi2_flash_sector_erase(uint32_t sector_addr)9 r  ^$ a$ ?( W( f$ r2 m8 R
  183. {- m2 a+ Z1 }, Y8 \( o& ^
  184.     spi2_flash_write_enable();* N: X) m& R0 z) ~$ r$ ?7 q
  185.     spi2_flash_wait_for_write_end();
    ! s7 A8 S- p. y0 b% c) m# J6 U
  186. 6 p/ }$ A6 O$ j; j7 L- ~0 a0 o
  187.     SPI_FLASH_CS_LOW();
    8 x$ L/ h: W3 M8 r
  188.     spi2_flash_send_byte(W25X_SectorErase);& s3 H% H" h7 n% ~$ K
  189.     spi2_flash_send_byte((sector_addr & 0xFF0000) >> 16);
    ! ?9 r, L- O! ^% q+ i
  190.     spi2_flash_send_byte((sector_addr & 0xFF00) >> 8);
    - i, t! \2 E! g) F
  191.     spi2_flash_send_byte(sector_addr & 0xFF);* A8 H1 b; a9 q5 x9 ^7 E
  192. 3 n( x; w3 a2 n* p! V6 o
  193.     SPI_FLASH_CS_HIGH();
    " \% f) l0 k" a
  194. 1 R/ A, _' }6 L! ?
  195.     spi2_flash_wait_for_write_end();
    0 @+ Z/ z7 K- V: Y* f( B: N
  196. }) j3 b& s% g# G. [/ l  \8 _

  197. ; ~7 t! r4 D* G& F5 M, r/ w$ R2 H" D
  198. /**
    # s/ n+ S- {5 l# K: z% E7 ~
  199.   * @brief 擦除FLASH块3 Z; T) f5 j. g
  200.   * @param None8 s, D2 _& W) h3 ^; r: u1 v
  201.   * @retval None
    5 G' J, l5 k8 V3 M: A, J
  202.   */2 @5 b- U+ V- Y2 v, l
  203. void spi2_flash_block_erase(void)* {8 Z. d# z: [1 u6 a
  204. {
    4 z- P3 d+ e% f
  205.     spi2_flash_write_enable();" w) {& f4 L4 Z8 b  S$ }  w% |
  206. 0 O( A5 s0 L' Q1 e8 T. ]6 D. W
  207.     SPI_FLASH_CS_LOW();7 d/ ~8 W  E8 i5 z& ~8 X! |1 @
  208.     spi2_flash_send_byte(W25X_ChipErase);
    1 H! U. n2 B& A9 D' d" _, N1 ~# b
  209.     SPI_FLASH_CS_HIGH();3 l+ A6 L5 M7 f2 a

  210. ) P: r) F  F3 b1 c2 e+ C
  211.     spi2_flash_wait_for_write_end();3 d# G6 B6 \7 n+ ]/ ~* p& V' r
  212. }
    $ z. G% ^/ |0 L
复制代码

. z: [4 F1 L0 l! s; m1 w" B3、设置编程仿真下载模式* m4 j/ v; _& \. Z' v5 b# Y
(1)选择Options for target ...>>Debug>>J-Link/J-JTRACE Cortex,点击Settings>>选择Port(SW),可以看到搜索成功SW Device,表示芯片可用,可以下载。$ u' g9 ~) G3 K2 A
9 f) k6 O# ]9 g. O
edefb6b9a2a14be3b1221ad6d5c7a8a2.png 5 g% f4 c# i7 }  s( p* R0 v- i
​​6 ?6 o0 F) b- u- l( y
(2)点击编译,完成后提示“0 error(s),0 warning(s)”。
- d! |- e& M" k8 d0 a! P  o& o( x' P/ Q0 q( S' z
e0b7e3e353b345a7987fe5f821de2dda.png ) M( u) j) C' h* g( |8 {
4 l( X# y0 h! I( Z4 Q& W
(3)点击Download(或者快捷键F8),即可下载程序。9 `( h# X2 T* o$ M

6 N$ p# @* \( U0 [9 p7 g8 _& H8 a
eb3f250f19e44862a7df80c2492fcd01.png
- z9 L2 L: [) k; {6 A  M! j​​9 L9 O" y1 {$ ?. p" N
% G, K0 W4 K3 i$ {. u
(4) 如果下载程序后,没有看到LED1、LED2、LED3、LED4闪烁,可以按下述方式设置一下(Reset and run表示下载后自动复位和重启运行)。或者重新彻底断电再次上电(或按开发板的Reset按键复位MCU即可)。
* H3 R; n7 R. X& b: y# H% ~' S6 y$ j& @2 z8 l$ I, _/ I
9f8aa1a4e8f04041bcf5cb183facd100.png
2 |. U7 I1 d1 @; i5 D ​% s+ a. i2 K8 O! W6 ~# E
3 @1 }( N: R1 ?! B+ o
4、SPI Flash实验效果展示
- t! f& w  \* g0 P      (1)  程序烧录到开发板后,即可看到LED1、LED2、LED3、LED4初始化后每隔500ms闪烁一次,并且打开串口助手后(串口参数:波特率115200、N、8、1),可以看到printf每隔500ms打印一次log数据。3 s1 a$ R0 ^& D' D2 c2 s- g

& a2 ^# I& H& C8 `, L1 I
954468e85b9f44299916e9c6b3440ad2.png
; K! ?" k6 h$ g$ T
9 b" p- E* b/ S9 V (2)屏蔽写入flash的语句,可以看到此时串口log打印的全为FF,则证明SPI Flash读写有效。& r" c; {; B! A+ u& g: b
3 A! j! b2 g+ h0 f) O9 v" p3 G  c
b37dd10545e442c3924a75955386b09c.png
3 g. p( u: n" c8 X/ v: t& L
; l5 ^- ?! }* g5 G4 T d36c42cb706f4a2d8103858b70c8d803.png
# w" }6 ~0 i! u" H5 e
. O* |: {8 q4 W. E————————————————$ q4 |+ S' K# M8 T. b) F
版权声明:智能小屋ZYXC1 Y# O0 z: @1 I6 g' y' z. F
) K) I7 E1 t: a& H+ I$ s: b

9 ]( X6 `6 B1 V. {- Z: I" n
赞 收藏 评论0 发布时间:2023-1-13 19:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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