一、开发板平台简介:
+ T3 Y0 r7 p8 e1 [, r: g; w1、开发板资源简介
0 L' b: y, e7 [6 y$ k( {4 m( C(1)开发板主芯片型号:STM32L431RCT6: r: K3 ~# Y- y; O6 h P
) X. e) T: G# P(2)开发板主芯片封装:LQFP-64_10x10x05P( ]5 R/ K( B9 q1 ^3 y3 L
' G) M0 b3 g! |/ U(3)开发板主芯片内核:ARM® Cortex®-M4
3 ?7 b: x" m. A. U
& _1 k; [. p+ j(4)开发板主芯片主频:80MHz
' Z. x, D' K! q7 V8 X3 n$ o- C4 @+ S+ R$ d4 B9 d C
(5)开发板主芯片Flash大小:256KB* c. A4 ^0 ^$ u8 \- a n2 Z+ m: `
$ d3 Q! n' e/ T; }6 v
(6)开发板主芯片RAM大小:64KB
: I3 L( a/ d9 E, S
* R- Q' e& o9 C
* M |- {8 ^, E; ^$ c$ @2 I8 V
% o9 R( u) ~2 n) L0 X
/ T: N2 X9 u1 x$ Z! \" p9 @
! [0 M4 c' v$ R+ N( w# e: Z0 w2、LED灯资源
8 C l9 k& f/ D `( N+ T% m3 Y1 Q(1) STM32L431RCT6开发板共5个LED灯资源,其中一个红色LED为系统指示灯,指示开发板供电系统是否正常,如供电系统正常,红色LED为上电常亮状态,硬件原理图如下图所示:
3 [& V3 V7 A# T' O: k' E' }- Z7 O* L f- `) N: d7 n) S
) I9 K& D7 u1 Y3 ?$ {+ v
. n% G. h4 s8 ?(2)其他四个LED灯为黄绿色可控LED,高电平点亮、低电平熄灭,计划用LED常亮验证看门狗的作用,硬件原理图如下图所示:
( ^8 W" p4 B+ q4 N! O: Z
& @9 ^! U* e5 @; V2 G/ X
. ^8 G7 s; A3 ?# S1 ^5 y3 Y' \( p) B: ?. O, J" \2 u9 t4 T H- ]
3、串口printf打印工作原理
# V+ b8 ]7 F S, i/ M u 串口全称为串行通讯接口,即数据在通信线上一次传输一位,按先后一定顺序传输。我们通常所说的单片机串口准确来说应该是串行异步收发传输器(Universal Asynchronous Receiver/Transmitter,UART),使用TTL电平,串口需要RXD、TXD、GND三根线进行通信。
# q! o. \9 n% ]& d1 d& I
" \1 G% u) c& \+ V% \: k8 x. [ (1)我们选用的STM32L431RCT6开发板串口1已通过USB转TLL串口芯片CH340G引出,使用时,只需要用公对公USB线连接电脑即可(注意也得需要安装CH340G驱动),后期验证试验也可使用该串口作为debug串口。
. y" w5 i9 q1 k7 S/ s" \: @' m: D. _, C( `: b G3 L. {0 X
(2)开发板上的其他串口已通过排针引出,为TTL电平,通信的时候需要注意选择对应的电平模块,如USB转TTL串口模块等。* _' a# t! M5 O J h5 ]3 [
# @# D* B0 _- \; G i6 `* x0 tTTL转CH340串口,硬件原理图如下所示:' p$ Z; L$ O; p- g
+ b7 {8 j0 h1 Q! W/ }1 d
* f0 W0 M* u- W, `2 [
- I' v7 V7 q; @* g3 O8 ?! @4、SPI Flash简介0 _& @; v- U' C
(1)SPI总线介绍1 C" ?1 Y( {% h" y9 u
SPI分为主从工作模式,通常有一个主设备和一个或多个从设备,本文中MCU为主机,W25Q16为从机。2 q. I6 X* @. Y) x1 [
0 s2 m: j$ F$ F" O9 c' i6 ^SPI通信有以下四根线:/ P7 E1 o$ _$ \
MISO:主设备数据输入,从设备数据输出。
/ o5 }' t ~* HMOSI:主设备数据输出,从设备数据输入。
- v, M, W: L# ^5 [9 KSCLK:时钟信号,由主设备产生。
j3 H' z0 g3 f: n
' d+ Y3 J1 s% ]4 HCS:从设备片选信号,由主设备控制,低电平为选中。
4 b4 ~5 I- _5 m$ U) `
% W6 i$ p5 g, o. s0 D; q$ G! Z(2)SPI的特点:
. V# ^, v3 [: Y2 Y; M高位先发送,共有四种工作模式。
( |6 v7 v' n. c! }5 f9 oCPOL(时钟极性):规定了SCK时钟信号空闲状态的电平(0-低电平,1-高电平)! l# x1 l$ K: v! f
CPHA(时钟相位):规定了数据是在SCK时钟的上升沿还是下降沿被采样(0-第一个时钟边沿开始采样,1-第二个时钟边沿开始采样)
8 Y; K: W: ^4 m2 a8 q& G' s9 D$ }7 e7 V4 Z/ B% o
模式0:CPOL=0,CPHA =0 SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据)
8 f6 Z( |, P6 F+ g+ B模式1:CPOL=0,CPHA =1 SCK空闲为低电平,数据在SCK的下降沿被采样(提取数据): N5 f( V5 R" y) ^, ?3 V& Z- |. Y
模式2:CPOL=1,CPHA =0 SCK空闲为高电平,数据在SCK的下降沿被采样(提取数据)
( ], q" J3 O4 k+ q模式3:CPOL=1,CPHA =1 SCK空闲为高电平,数据在SCK的上升沿被采样(提取数据)1 D2 w: j0 K6 D& R
7 w) A/ u: S7 e* C Z
(3)开发板 SPI Flash原理图如下:- A$ G1 \& ]. Z& Y5 m* f& q3 Z
: G- V6 |7 H2 O7 l1 d
; l- F7 p: e. K" ^* c; ~
# @* h1 z2 x8 m! Z3 \. r! ^7 M" _7 m1 ^
二、串口printf实验过程: F8 j. }/ b4 x/ ?( q4 x5 N
1、新建STM32CubeMX基础工程
: `- C' K% K7 l* M( b(1)打开STM32CubeMX,点击“File”-->"New Project"
7 ]! P3 }# {: @- h; q2 P) S$ T9 l4 F4 e! B; {$ V
9 Z$ A8 B. W- t t9 U. [
& k8 `' {5 R: l' q9 Z1 p& A9 t
(2)等待打开主芯片选项界面(大约1分钟时间)。
' v9 l8 d3 u, S# V3 }# i5 W; T% ]4 r" g- }/ M
5 W& E1 e& H3 X5 Y1 _
0 R8 m1 Z1 t" G- s4 p+ k7 ^/ q(3)昨天搜索框中输入(或选择)所需的主芯片型号(因为我们用的是STM32L431RCT6开发板,所以此处选择STM32L431RC),然后在右下角选择STM32L431RCTx(因为开发板主芯片是STM32L431RCT6),左键双击即可打开新建的项目。
7 [+ g3 J. m( G& k9 L2 L0 A9 u% t8 x# I
% j1 N" ~% u2 n5 R9 H9 Y
+ f: V- z6 |5 t" ?7 ` L(4)选择时钟源。
! X& R* ~0 _( z6 M4 [* o(1)因为开发板上有8M外部时钟,硬件原理图如下所示,所以此处选择使用外部高速时钟(HSE)。
& c3 `% T# a) ?5 A$ v, s
# F9 | |. V$ b. @; v. I
4 E8 e* U% J$ |6 V! M! T* X; I, ~) k, g7 m9 `
(2)因为我们没有用到外部低速时钟(LSE),此处不做处理,如下图所示。+ r1 O) d" y' c1 n+ ~
2 {0 G2 i- c+ ^+ a% d; h
) o6 B& L* \ }! k7 f6 G# _0 y
5 J, G6 y8 h8 w5 ~2 E
2、配置GPIO控制LED1 \5 ^( V& J6 | ]7 D
(1)查开发板原理图得,LED1、LED2、LED3、LED4的控制引脚分别为:5 `8 e; q# K" Z4 v0 K
LED1——PC0. y. {! a& T: J/ F( e# N! n
LED2——PC1
2 l, `; l3 g( ]* `$ I* w4 `4 SLED3——PC20 W, @/ f! H: n% N
LED4——PC39 y5 T* r6 g8 P. m7 x. m! w- T: X
- M% D0 b9 {* J(2)配置LED的控制引脚为输出,输出频率、输出方式默认即可。
; P" z( k, }, f鼠标左键点击PC0,选择“GPIO_Output”,表示设置该引脚为输出模式。6 Y6 C$ Y( o! n% R: V
鼠标左键点击PC1,选择“GPIO_Output”,表示设置该引脚为输出模式。0 [* p7 w% k# v9 r3 S( l% x
鼠标左键点击PC2,选择“GPIO_Output”,表示设置该引脚为输出模式。$ C2 N. C$ F Q1 u
鼠标左键点击PC3,选择“GPIO_Output”,表示设置该引脚为输出模式。9 ^$ c5 b9 r* h% G9 z) F6 D
7 L# n+ Y/ e- s% o+ N" x
$ `0 O8 ~+ @, g8 t1 J9 o3 Q4 F; `
* |: R) T, k9 m' r v; A
4 c: ^/ ~. l' s# E
: L. u. Q) z6 C3 J' M* k(3)也根据自己的需求配置GPIO的参数,如输出方式、输出频率、上拉下拉等。因为GPIO控制LED的要求比较低,此处采用默认参数即可,不用修改。" w' o: m6 T3 a O
! x5 s8 }: i6 s( L0 l
! h1 T8 w; ]. ?7 t' i9 I
4 j8 r( x. c: w6 {' j, X, ]* ~- c9 t
3、配置PA9、PA10为串口5 }& u, \" @, u2 i* p- R+ T
查原理图得知,串口1使用STM32L431RCT6引脚为PA9-USART1_TX,PA10-USART1_RX,引脚设置如下: b0 Q2 \7 @6 m/ p( e# @7 y8 N
+ ]- f8 e; t, e7 y1 q4 w4 O
1 C! z L4 w0 r; F8 O
' s+ n- r' L r" d: y (1)序号1用来设置串口收发引脚的选择。# g6 {% W: X: H9 C
(2)序号2-3-4-5-6设置串口参数,如波特率115200、8位、NONE无奇偶校验等。 `! i+ s8 k9 \( _! a
7 F$ P: m! s, F* T 4、配置SPI Flash接口( [# E& {. n) C8 }7 i" ]
(1)查看STM32L431RCT6开发板原理图得知,芯片原理图如下:- w$ Q' Q5 a' F+ C1 U3 e
5 \* c7 F P3 r! t8 N+ h
/ @$ b) U" o) j6 V7 s+ M
+ N$ Y( w+ T8 K' N (2)SPI Flash接口对应芯片的PB12、 PB13、 PB14 PB15,芯片引脚配置如下:' \$ }3 g. t( X4 H
PB12:SPI2_NSS,此处设置普通输出IO即可。不能配置成NSS' n; b/ V9 H7 m9 W$ F' n! A
PB13:SPI2_SCK
2 c3 k* v" Y& xPB14:SPI2_MISO& i$ G4 ^' @& p
PB15:SPI_MOSI
. g/ w; V( x6 e" f& _
; N! A" K& s5 \$ b
3 R0 Q' T, f; s) q' F3 z2 g$ x
/ j' D4 I- ^2 O" b6 T (3)设置SPI引脚参数,并选择 Full-Duplex Master 全双工主模式,此处不开启 NSS 即不使用硬件片选信号。
1 z- [# G1 x7 m' v. L& ^" m5 P* ?0 n4 Y+ I( G! s# e
9 m0 g6 V$ i+ z0 S7 G
" J/ @! D, k+ ?5 a m(4)设置SPI基础参数以及时钟。
# Y6 M# ^, Y: ^; m2 z" X: c% U/ U. S
8 v a# Y3 ?4 V( N, h* a, Y1 G
7 p7 O7 e0 X7 V/ m, o) @$ ?, _: W
5 |2 |: f" o. v. U8 Z
7 M8 v6 S4 a) Z9 e' M, q" e
; q- A+ _ w3 Y& j
5、配置项目工程参数
7 _* I1 n+ N8 k(1)配置时钟树,用于系统内部时钟,以及各个外设时钟等。此处选择外部8M晶振作为主时钟频率,内部最大倍频80MHz。
/ W; T" L; C7 k P8 {' I0 L% b. ?6 j" d. q$ q: {
( h: @' Q8 o' H7 O4 b/ _+ t
% p7 ~; s+ x. f" l# p% ?(2)完成配置工程。
. N& ?, t# L( h) \备注:需要注意代码生成过程中的继承关系,如图所示:需要保留开发者自己编写的代码时,请根据配置设置,不然生成代码后会删除自己编写的代码(从这个方面也可以看出开发者备份自己的代码是多么的重要。)
& w- r( l1 t( i* Q" ]( @8 G" _: k6 {9 a. e: Q
* \6 j, _" c; W0 Z
: o+ g5 | |# T5 Q+ |9 I$ j
, A7 ?6 i/ a `& B a( T' a T" s! P, E. C
(3)生成代码。
& [ p' f# g4 O' j' a7 y o2 _备注:使用Generate CODE生成工程代码前,请确保文件路径无中文,否则会生成项目失败。
7 Z2 w- e7 A8 Z! Q: V' T' @0 U" o. t. ~7 m8 A! v
- o% U5 t5 k/ A
( E, O: w! L5 H- S; I( P
(4)工程代码生成成功。5 q" P/ l6 z) G7 [6 p* h/ o6 w
' h- n; T9 A' e- l
. Q3 w) T) w( k% b* L
8 ^9 t. z h4 S/ ]& d C
9 h/ M9 ^7 Y; W8 C 三、在KEIL 5中编写代码0 J, {8 Q2 F- ?4 D
1、使用KEIL 5(MDK)打开项目工程文件( S: E0 R* s9 m4 R
源码使用说明:使用前必须把项目工程复制到无中文路径的文件夹下使用。
, f0 D' ]) l5 ^6 @8 |! E2 M' m8 ]/ {(1)找到刚才新建工程的存储路径,安装项目名称,打开项目工程.uvprojx。' h' F Y) G- q$ e% }8 C0 D3 s# t
3 \/ S+ A0 |/ p1 ^& x: p
6 Z- g3 b3 e) d+ l+ p$ {* Q% W5 l. ?/ k) F
2、添加SPI Flash读写验证程序* f2 w& m% o' a1 @! o
(1)main.c文件中,初始化LED1、LED2、LED3、LED4默认为点亮,并在while循环中添加控制程序,如下所示:实现每隔500ms后LED1、LED2、LED3、LED4点亮和熄灭之间反转切换,并且串口每隔500ms打印一次。
' h: @# j, Q- a* J7 p q' Z7 _+ u: h
备注:自己添加的代码需要在 /* USER CODE BEGIN 3 */和 /* USER CODE END 3 */之间添加,否则STM32CubeMX更新代码时,会造成自己添加的代码丢失。$ x* Z- Z- _* W6 m+ @2 \5 g
" P4 Z5 M0 c! k/ X
% G4 o# s. p% q# d" J6 k& w. \7 V% w N/ q3 n: z5 v
! T/ D5 {# S% h" n9 T: E/ z+ M) B6 t. Q
( n4 l: |, c1 \* S8 S* p
3 D0 i- Q" L8 w: a% V: Y7 a; K(2)main.c函数添加初始化代码和读取flash的代码,如下所示:7 T: K( M. k! a% ^
- int main(void)
% x% i& n) Y* y2 l+ T - {5 X9 G/ Z" X' J( }
- /* USER CODE BEGIN 1 */. g$ n8 R* `1 J, \ d
- uint32_t w25q_chip_id=0; //读取芯片ID
# T) P7 }7 f+ j4 \ - uint8_t onebyte_read[8]= {0}; //读取的字节内容
( c% t& d+ b, z. _- n" Y7 ]# F - uint8_t onebyte_write[8]= {0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80}; //写入的字节内容* T% d B$ s! i5 I0 O( ]
- /* USER CODE END 1 */
: ~- w# O# E7 ~5 ^$ ^: ] -
$ J2 m# w- t% I7 j& S - /* MCU Configuration--------------------------------------------------------*// c, G" o4 z) ?" s( s
- ( z, b; g# K+ S i" E3 k1 r2 M
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
; w& s8 \3 H6 `3 p - HAL_Init();! f9 E' ~; R) J5 ]5 n1 d& R
- " ~: F; y( v; h a' b
- /* USER CODE BEGIN Init */6 Z: a4 V$ `/ m
-
5 R g. l7 m, S% m7 X; k - /* USER CODE END Init */
\- d. Q' K; s# z4 D0 R. ^$ X1 p! [ -
# ?' M& v+ U2 I6 d* r# s9 x - /* Configure the system clock */, r9 A# o' B: A6 q- @& ^& ~: N: I# t) G
- SystemClock_Config();
3 W2 N0 w0 G( D4 y3 s9 i7 t -
8 @, {0 p+ G( E' Q8 L4 t: _ - /* USER CODE BEGIN SysInit */
* [1 }: M* N! F5 A! r8 Y - 3 y: g% B: v9 n& @7 Q5 @9 I
- /* USER CODE END SysInit */
: Z0 q# K+ b# S$ _ - ; i& J- u2 d% T( O E" k; h: W
- /* Initialize all configured peripherals */
0 t& C2 C; f- z' {, v' ]& D - MX_GPIO_Init();" A; F* b: x, `- m2 v( O
- MX_DMA_Init();
7 d: g( S" U9 k6 i) o - MX_USART1_UART_Init();
4 n# z' D( l3 G6 k' { - MX_SPI2_Init();
( {- ~4 \; K% N" c1 f - /* USER CODE BEGIN 2 */
5 E# x( {. q' p q+ t - HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,GPIO_PIN_SET); //初始化默认LED灯为点亮0 h& F4 S4 N" x
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_SET); //初始化默认LED灯为点亮# F; V1 {4 x$ A$ P" M( p' N
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_SET); //初始化默认LED灯为点亮
5 @, t# Z9 A/ v, |) h - HAL_GPIO_WritePin(GPIOC,GPIO_PIN_3,GPIO_PIN_SET); //初始化默认LED灯为点亮0 h# H- `" L, N1 @' `
- w25q_chip_id=spi_flash_read_ID(); //读取flash芯片ID h+ R; ?+ ^! C2 M2 H M+ I
- HAL_Delay(100);0 J; u. B0 _) S; [6 H: n0 V
- printf("hello world,spi_flash_read_ID==0x%0x!\r\n",w25q_chip_id); //printf 芯片ID$ V$ }4 \6 [" o. E$ ^
- spi2_flash_sector_erase(0x0); //写之前需要先擦除扇区, o; h" Q" v8 D1 J
- spi2_flash_write(0x0,onebyte_write,8); //写八个字节& Z4 q) p& {8 D# O. C0 ^1 t
- spi2_flash_read(0x00,onebyte_read,8); //读取写入的字节
: h& @) `8 I! z, s - /* USER CODE END 2 */
) s9 l: a' q# O; S -
* W& I& r6 M8 t6 {+ n, h7 ^3 v, I - /* Infinite loop */
( h# v+ R6 |5 b% v) F2 | - /* USER CODE BEGIN WHILE */
5 z2 x6 f2 D8 k% }4 F+ }; i2 D - while (1)
, Z% X: P8 `' F9 I - {# @" b' t2 p& v0 l
- /* USER CODE END WHILE */
/ f5 q4 ^. |( u6 u - 0 }0 c; T/ }! {, ?
- /* USER CODE BEGIN 3 */
! ~# \; J) W5 r. i) v7 j - HAL_Delay(500);
' T* F) C$ S( d- c( v1 f& @( M: z - HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0); //每隔500ms闪烁一次,并打印flash区域的值
* i/ A% s- R, J. g' \ - HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_1); //每隔500ms闪烁一次,并打印flash区域的值% a9 U" Y( t! I% m" s8 E% r
- HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_2); //每隔500ms闪烁一次,并打印flash区域的值6 R+ u" f3 }- |; a* d' u( N
- HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_3); //每隔500ms闪烁一次,并打印flash区域的值
2 L* O7 \+ u7 v9 J3 p - 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],\
# i0 }2 H; I. b) U i - onebyte_read[2],onebyte_read[3],onebyte_read[4],onebyte_read[5],onebyte_read[6],onebyte_read[7]); //printf打印flash区域的值
9 T7 G0 }6 \5 P4 z4 k7 u9 A7 R - 7 r2 O8 c7 Z7 f4 H! @
- }9 C! K# z9 g' Y! v( e G
- /* USER CODE END 3 */$ r& @+ g! }, H) R I# z
- }
复制代码 ( u) {8 y1 C8 w- f' v- T
(3)新建w25qxx.c文件,并添加驱动层函数,如下所示:) w% { ? k2 Y
- #include "main.h"
3 f8 z: I+ t6 Q+ R: t - #include "spi.h"% o- G) U4 w* J2 F! d: {4 T
- #include "w25qxx.h"
3 j# k j5 W4 M/ t6 P( ~9 ~ - /**
1 |: m7 A/ x- Z8 N- \ - * @brief SPI1 读一个字节6 _0 l( ^% D+ G- a
- * @param None A1 z) f8 `, x1 Y4 A x3 G$ B
- * @retval None8 X7 H2 T; q- i2 h
- */! Z$ w; [3 J: O& E) ~! f9 ^; W7 j+ r" A- e
- static uint8_t spi2_flash_read_byte(void)+ U* q7 c) ~4 }5 O
- {
5 ~3 o/ z7 `; L/ Q0 A: j - uint8_t t_data, r_data;
( D2 w2 ~, z( v4 o0 D4 u7 {# ^; C - 6 O7 @) z# B+ d _$ i( L
- if(HAL_SPI_TransmitReceive(&hspi2, &t_data, &r_data, 1, 0xFFFFFF) != HAL_OK)
- J& d* D* g2 R$ E - {. _$ k" [8 D7 J
- r_data = 0xff;. F6 m% W* j+ M; o( k$ u" H5 M
- }9 n% |$ z4 o; i1 f1 G4 S
- return r_data;6 ^6 i0 S; i- s* }& f, [
- }
! O- S* \5 n9 I) Q - /**9 `5 W$ d+ d+ R! R1 L' g& R
- * @brief SPI1 写一个字节 i& o" l( k! i4 `( m. @
- * @param byte 写入的字节
) c0 u* s8 O; [: I J/ | - * @retval 写状态 0成功 1失败; S9 i5 n% _' e" X+ @% p# d
- */
. K$ C9 P/ F8 ~5 M9 \# _ - static uint8_t spi2_flash_send_byte(uint8_t byte)
% Z9 W2 ^+ s# a7 Q0 B* {3 t, k - {
8 \: @3 j, Q$ P$ ? - uint8_t r_data;
) q0 [! L) Z, J2 i. W - * a& v! T5 J$ f$ h7 H {
- if(HAL_SPI_TransmitReceive(&hspi2, &byte, &r_data, 1, 0xFFFFFF) != HAL_OK)" u5 b y7 L6 @ q
- {: R" v# h5 u& U' n! j
- return 1;, _1 T/ k5 o6 r" e& S6 x
- }' V+ L' T9 S4 J; n' r' S3 v
- return 0;
5 N' U2 g( |/ \; M/ U6 N - }
8 d; g4 z; D: c& E - /**
/ w* ~# S: n t; A2 B - * @brief FLASH 写使能9 M: D5 Y" b4 G4 H, `
- * @param None
! |! p; o* R W - * @retval None
: B; h! |) W" G6 }6 r& Q& t$ C* [) U) O - */$ I8 p1 r6 {% |* ^/ T3 K
- static void spi2_flash_write_enable(void)
- Z) w5 V2 u! H6 e- U - {
" w2 R1 c) h% g t3 K - SPI_FLASH_CS_LOW();' w8 q; Y" v! t4 r
- spi2_flash_send_byte(W25X_WriteEnable);
# ?; O$ _- k: K( j* E" o* [0 \ - SPI_FLASH_CS_HIGH();
1 J$ b X0 Q% g/ I) R I$ I; W# Q& o - }
) }1 C8 n9 Z' K$ N - /**
) ^: K" W( m" H/ ` - * @brief FLASH 等待写结束
1 x% ^, g- @7 V; F+ z - * @param None
2 S1 C5 T, {) m1 Q0 N - * @retval None
) F/ Q1 ~' ~4 y - */
7 u1 q; s5 P2 x4 N% } - static void spi2_flash_wait_for_write_end(void)* d" o- s' W3 I& c9 ~: v1 K7 p
- { j, b9 O* z* B. M+ P
- uint8_t state = 0;" y" F! c$ h, d$ G
-
+ O. ]1 z1 j8 Q* m3 r - SPI_FLASH_CS_LOW();' [$ r$ J x/ _0 m
- 3 P: Q/ Q; K: j- s( k, z m4 L |- d
- spi2_flash_send_byte(W25X_ReadStatusReg);
4 m7 r; h& O6 }9 R1 v. O: k - # K8 {1 |) k5 {% J
- do' o6 S0 g- @8 [, `' W" B
- { M4 Y5 j1 X% n# m
- state = spi2_flash_read_byte();
9 z7 y+ `: m) O0 ~" L - }
% T# u3 c H2 p: ^' z8 B - while((state & 0x01) == SET);1 G F' v0 V ~* V; Z1 ]( P
- 8 X5 Q5 M& n6 p
- SPI_FLASH_CS_HIGH();
; E- t3 Z9 s! A3 Y- |5 o - }# s" [; Z, ~8 [2 V3 G \
- /**
% Y; O" ]+ ~: P% X3 g* N' d - * @brief FLASH 读ID
5 R3 t, b: }! L6 X$ t' `1 U* B% ? - * @param None
n: ?( U; V J. ]* l0 ] - * @retval None
* d7 v$ l0 U. r6 y$ R- S - */2 S6 ]! s4 k/ J& g' ?1 n4 v
- uint32_t spi_flash_read_ID(void)8 d' c$ f) l3 Q& m/ ?8 S
- {
8 q; x' n. B$ P6 {9 Y# }) M2 C7 j - uint32_t temp, temp0, temp1, temp2;
; p' ~- F) G. t- t n+ e' Y -
) s, C3 W9 O% Z @4 R - SPI_FLASH_CS_LOW();
# F4 r7 }4 N: B - spi2_flash_send_byte(W25X_JedecDeviceID);
0 Q" }; W( ^ Z* O q, B: T - 1 f: z. p3 J5 U. @6 I0 Q4 ` o0 z
- temp0 = spi2_flash_read_byte();
/ l: [- t1 |# T: u' M - temp1 = spi2_flash_read_byte();, `* w' Y) w: n! ]% `7 X- k& p# e* o
- temp2 = spi2_flash_read_byte();) G3 r8 x* o6 \8 F7 Q S! z8 ]
-
) U3 Z& G3 P) I& u' n; n2 t3 k - SPI_FLASH_CS_HIGH();
. [4 s+ K' x" B: V. y5 R) g - * H7 p' P. B0 T6 M0 ?
- temp = (temp0 << 16) | (temp1 << 8) | temp2;- C* `5 I L$ i8 _3 D8 i& c5 e- x
- : a% U; C6 ?$ O& \; Y# o
- return temp;
8 @+ J' n( H5 {3 z0 C, Y: W - }; w, H. s3 X5 e
- /**. z9 e' n; t( ~, L ? w1 b
- * @brief 读FLASH/ d$ N/ }- h+ q8 \
- * @param addr 读flash的起始地址' d4 q2 n+ j! X! Q
- * @param pdata 读到的数据存放起始地址
2 z- t" v: {4 Z2 g. S. B; b - * pdata size 读数据大小
1 l6 _' Q R; o& t2 n/ l - * @retval None
t" t" T5 F8 ]& p1 X, {; O+ C' ~ - */! L/ ]5 Y' {' u1 X0 n( H
- void spi2_flash_read(uint32_t addr,uint8_t *pdata, uint16_t size)* q, [1 j6 R9 v( d8 _- h
- {
* U* s2 b! u# s/ R. X$ X. \8 y - SPI_FLASH_CS_LOW();
7 P7 I. I- W x; @3 _8 M1 Q -
& H. j9 Y Y4 T& W! D - spi2_flash_send_byte(W25X_ReadData);! J; d! E0 Y+ S
-
9 L5 q% _2 C* \- k+ J( c - spi2_flash_send_byte((addr & 0xFF0000) >> 16);
& t2 l5 @ g C0 |. U - spi2_flash_send_byte((addr & 0xFF00) >> 8);7 j, y! h; Z5 M5 e" l
- spi2_flash_send_byte(addr & 0xFF);
$ C. y0 v( X8 O$ [8 ~* p - . K( ]) g. d) Y, K) Y4 W/ s
- while (size--)
5 h6 }' h$ o$ [% E3 l8 g' d - {
$ R" @5 ^3 C2 u0 ? - *pdata = spi2_flash_read_byte();4 K' V) J; R! P$ @8 U. L: J2 v
- pdata++;
: o% R: L3 Z2 W7 a% O - }8 d @' D7 E% U* ~) u y
-
$ s; J; j/ m; ^0 |+ H - SPI_FLASH_CS_HIGH();/ f$ b# o4 J/ r7 g' `/ i) u/ i
- }
3 m2 M. \! ]* o# Q6 S) _, E - /**
. x: o: z5 q" Q0 p( j& f - * @brief 按页写FLASH
0 C/ s0 ~2 F9 a! n: M - * @param addr 写入flash的起始地址
1 i3 O6 V3 B: |( u0 F: r - * @param pdata 写入数据的起始地址1 f2 L! D. b- h$ p4 a" o
- * pdata size 写数据大小' z+ i5 B4 s9 D0 _* \
- * @retval None
" \6 m9 Z* U4 Y" Q U1 U/ ]+ e - */
$ W, B- P V4 a2 y. n% i% W - void spi2_flash_page_write(uint32_t addr, uint8_t *pdata, uint16_t size)
3 L* V3 q2 O1 J: p) @3 C2 q - {
) \: d7 w9 ]* y0 L( G - uint16_t i;
8 }/ ~$ n! v P2 I; d# j& ]" x; } -
) w T/ s: h4 }* l - spi2_flash_write_enable();
8 W0 g7 N. I$ E$ J8 T: l, |6 x2 y -
0 p( E7 ^/ r. Z8 B: }* g' @# S- E - SPI_FLASH_CS_LOW();( R8 u, \; z, W# Q1 Z
-
2 }. }5 ?8 \) L - spi2_flash_send_byte(W25X_PageProgram);
" d+ \0 ^/ ~- F- |0 y$ }1 d - spi2_flash_send_byte((uint8_t)((addr)>>16)); B" [' ^ V2 H+ m) M. {6 e0 K$ }
- spi2_flash_send_byte((uint8_t)((addr)>>8));& L7 x; [, T/ }1 z) d8 ` O
- spi2_flash_send_byte((uint8_t)addr);( Z+ Z5 w# j1 ~
- 3 Y# `( y8 i$ Q9 ~
- for(i = 0; i < size; i++)9 E, l3 t! o& M4 s. @7 b# L
- {
, e1 l6 c- r$ a% E: }, l1 L - spi2_flash_send_byte(pdata[i]);0 Q9 g; T7 d" ^4 E9 B+ m
- }+ z/ N, M& B g7 a2 I0 A
-
, |& S" P z( \+ o" I8 }# P - SPI_FLASH_CS_HIGH();4 M& H/ f+ c' s3 _' Z4 |
- spi2_flash_wait_for_write_end(); Q( o, W8 |# k1 P6 ^1 R! ]- x
- }/ a( g O* T* T6 x
- /**
& p7 e+ E; M5 C: d# g/ n - * @brief 写FLASH. X8 o) j$ x5 s3 z( J
- * @param addr 写入flash的起始地址' U$ f* a7 L6 Z
- * @param pdata 写入数据的起始地址
/ o3 X* z! x# v6 _' T - * pdata size 写数据大小0 N# R ]! t$ i+ K$ O
- * @retval None8 @, Q4 G2 w q* q1 L5 S1 ?
- */% ?4 Q# i N1 a3 R
- void spi2_flash_write(uint32_t addr, uint8_t *pdata, uint32_t size)/ Q( n' t# M7 }- t8 }
- {
" X" Q6 Q A" r8 N) y - uint32_t page_remain;
8 v# y/ V% @! x [& Z9 I -
* ~+ ]6 ~1 ?& C4 b$ | - page_remain = 256 - addr%256;' t0 I Z0 T; E+ C
- - o: L: q- I! h# H
- if(size <= page_remain)
: _3 w Q* Z7 c F - {
% {$ s+ f0 d( a$ I - page_remain = size;1 M( ~7 x% A! y3 b& ~
- }
/ N Z& | e7 c5 y! v - while(1)( L6 h$ s7 p8 u+ Q* ^
- {& {' B# X `" ]; F
- spi2_flash_page_write(addr, pdata, page_remain);7 d/ w, E5 _2 F1 `; r: Z: Z
- ; t, W7 ]+ I; K7 N+ D+ k/ `
- if(size == page_remain). `( H9 \% S# L
- break;3 Y' k W% G" Q' a l, ?3 g
- else, `) q$ }1 d' O9 _3 z" L) I/ \ }
- {4 w0 f* C( s; R
- pdata += page_remain;4 d2 F Z+ }7 K' t" n' O0 ], k
- addr += page_remain;3 m, D% N0 O9 e( ]
-
9 T0 n" y* x ] - size -= page_remain;7 ]7 Z* X2 w' l6 D# M
- if(size > 256)
' i' Z* P/ _+ Z; M, D - page_remain = 256;
: k! s0 R& D$ }" E: T V - else" C' z, Q. E, b6 J
- page_remain = size;
6 S5 h9 e' W% x* |) W$ o. I% Z - }
6 }8 P0 a4 L: T* W- d4 i - }
+ R- \, ?4 H1 ^7 | - }
6 J% c% h1 B; C - /**" A2 I- W- u! A3 N" W7 N) J
- * @brief 擦除FLASH扇区
: {3 o* R+ k y) Y8 J8 ~, C - * @param sector_addr 扇区的起始地址
8 i7 f- z; H' {9 O- v1 M3 T - * @retval None+ x9 }# ~, q1 l" K# e
- */
: e1 @" w0 [& S - void spi2_flash_sector_erase(uint32_t sector_addr)
9 Q) Q3 J# ]4 e* Z; ~ - {% j" b+ j1 b( p/ O' X1 n
- spi2_flash_write_enable();# [. x, c5 [# T, P% N
- spi2_flash_wait_for_write_end();
2 U; c O+ @# q0 m" ?5 B$ @% e -
' r; |+ B4 Y# x& S7 T - SPI_FLASH_CS_LOW();1 M4 L, x; p& p- M$ S& ^+ l
- spi2_flash_send_byte(W25X_SectorErase);
/ Q& h$ E+ j5 \: u( m0 h - spi2_flash_send_byte((sector_addr & 0xFF0000) >> 16);. M. B5 G ^; I$ o
- spi2_flash_send_byte((sector_addr & 0xFF00) >> 8);; X& t. p4 c2 \' r* X* `
- spi2_flash_send_byte(sector_addr & 0xFF);
3 g' _" X3 B* k7 {9 F -
6 Y8 |& [8 t# R; | - SPI_FLASH_CS_HIGH();
9 O! g% \1 n' W- R - % H& p6 [. h8 E7 ]$ E- j/ V+ u) Y
- spi2_flash_wait_for_write_end();& Q$ {. U7 t6 a4 K1 \ x
- }% @+ Y+ r7 n8 K5 k
- , B0 Q6 z* T& d. U. ]5 Y) m
- /**
4 ^6 N% C+ T0 Q- ^- d- Y9 f4 W - * @brief 擦除FLASH块3 R1 [( N2 m; T
- * @param None/ ?6 M" k: ^/ U: U5 j+ T% k
- * @retval None
) H; [- f) B4 F - */
% G2 b) A2 a1 Y; l( s - void spi2_flash_block_erase(void)
O- V/ _* w$ v1 z# E, r) Q - {
! u% B1 r3 j& W3 H - spi2_flash_write_enable();, j4 a) B! G2 E' s; T) m: c
-
* ]; A# K3 r3 \" Y' Z8 O - SPI_FLASH_CS_LOW();& |7 ~& q: O9 m$ W
- spi2_flash_send_byte(W25X_ChipErase);- {# B' o* a% T. V5 d* E: x& a
- SPI_FLASH_CS_HIGH();
8 `4 K# a. P7 v5 I6 b% X. c/ m -
3 T2 @2 _* s7 ^ - spi2_flash_wait_for_write_end();
6 O; X% ^3 w8 R% ] - }
. w. N2 e7 ~8 b, i -
复制代码
- }% Q5 X2 u" e# T# S2 }2 j3、设置编程仿真下载模式
) @' I9 ` Y! G: A(1)选择Options for target ...>>Debug>>J-Link/J-JTRACE Cortex,点击Settings>>选择Port(SW),可以看到搜索成功SW Device,表示芯片可用,可以下载。
) E: r5 [# F+ I) \ w4 f0 \
+ M% J( B9 P" n1 S5 _3 ?6 X
7 ?" f+ D0 ]* t% F# z; f+ a ^7 k w( c0 I
3 d& o3 z* D2 q8 j- u x(2)点击编译,完成后提示“0 error(s),0 warning(s)”。 P" l) G& v% C' a2 K
" H0 D6 `# [: b4 w. B% d3 S
0 z4 d7 N a( [6 \4 j$ `1 c1 v& @$ g1 V
% L/ p+ [2 c6 p6 d(3)点击Download(或者快捷键F8),即可下载程序。
8 @, C0 I! h4 n5 ]: D1 H0 N- `6 d7 T D( R* S
. f/ }- D. k5 P. s/ J# d
: B X# W: G' A' u, g; ]
0 e/ ?1 d8 }$ g& |+ ^3 E6 B6 S(4) 如果下载程序后,没有看到LED1、LED2、LED3、LED4闪烁,可以按下述方式设置一下(Reset and run表示下载后自动复位和重启运行)。或者重新彻底断电再次上电(或按开发板的Reset按键复位MCU即可)。
5 x$ A- C0 W. N4 S7 X! s3 J9 \5 M0 P$ N+ X8 l( u. `
& N4 `" x3 [3 v- i( k- ^- s
$ c- H- r+ H0 X! C( ?' a4 }0 ^( Z) v- Q$ d o1 `" i; ]
4、SPI Flash实验效果展示
1 d0 k& q' E% E; ~8 @6 s- T (1) 程序烧录到开发板后,即可看到LED1、LED2、LED3、LED4初始化后每隔500ms闪烁一次,并且打开串口助手后(串口参数:波特率115200、N、8、1),可以看到printf每隔500ms打印一次log数据。+ W- Z R2 _6 ^1 X# Q
4 x9 y" O F6 z' Q q9 E7 ?
7 g8 h- H% u0 B+ s& t. G( E3 ~
/ J f- x! p2 g: p (2)屏蔽写入flash的语句,可以看到此时串口log打印的全为FF,则证明SPI Flash读写有效。
2 _/ M: a( u* X8 N/ v# ~" m/ S# `# `2 V
/ g. M) a; b( |' ` a
( f: J. f9 Q/ v) ~
' t: V1 y6 q7 |$ R- G3 ]* d; O0 g, A) Y1 M2 p* i+ Q' _5 ~
————————————————
+ G# a# G8 {+ L" E3 j版权声明:智能小屋ZYXC
1 E- m. P2 @% _# s; } ]3 b+ F
, j( `: }% k2 Y0 w; c4 c2 @; ~' {5 \2 x& l& ?/ A7 w9 R# F
|