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