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