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