一、开发板平台简介: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 j9 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" ^
9 n' G$ ]( a# i
1 E! g9 }% c* ^; o% O \
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
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
; r3 C2 D$ k8 ~0 _. Q. _9 q# \
1 }! \. b0 x9 Q S9 s3、串口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
' ~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 { U9 @, 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
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
( 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
! 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
, 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
. 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
; 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
# m5 ^* a1 M# N `; x- P$ O5 J3 p& d5 C- R' X( @5 d! [
# 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
- m& E# F: z4 Z7 G
# g5 K/ }! h% {# p3、配置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
) 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
% 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
: \" 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
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
9 f% F+ z, f" W6 ?2 A. d/ W
$ g7 _ Y! q1 J" j5 {( [
* 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
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
, E- V3 @. h) y- l" `; [" f+ H0 ~
+ j3 n8 z% k( o+ n) N
" ?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
( {" ?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, _
! 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
+ 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
# N! m0 N6 [4 j6 Q
% V8 ?) ~- R5 b1 a8 R7 l
7 D( n4 ^! U! W5 a2 _6 w; s( y' `9 z6 t! b6 a% E' O" ^7 i5 d
/ v1 P# M1 B& S2 ~& B' k! h: N8 G
9 Z& j: J$ u% ~6 ~
(2)main.c函数添加初始化代码和读取flash的代码,如下所示:+ G9 r# `% ~! e
- int main(void)1 q5 |# x( X, I _! y. {" Y
- {9 v7 j+ O) |7 V `
- /* USER CODE BEGIN 1 */
2 O4 ]/ n+ }* R) \* J - uint32_t w25q_chip_id=0; //读取芯片ID5 A& G3 L& Y5 c2 |
- uint8_t onebyte_read[8]= {0}; //读取的字节内容; C- r. _ f# [$ Z8 r9 u
- uint8_t onebyte_write[8]= {0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80}; //写入的字节内容
' A7 W& F) H: u9 O# q1 b3 M - /* USER CODE END 1 */, H$ Z3 a; @8 i1 o( L2 J
- + b- U: @0 ?& |& O N
- /* MCU Configuration--------------------------------------------------------*/
B: j- C9 T; P -
$ ^% N s/ S9 I s2 y - /* Reset of all peripherals, Initializes the Flash interface and the Systick. */# n" n. m: G, f7 |0 n: o
- HAL_Init();& }# e3 Q9 Z5 F: D! o8 W& h
- 1 I+ l1 J2 @6 E4 {. p9 c
- /* USER CODE BEGIN Init */5 |: N% ?, L, U( Z
- & U7 |3 A( L& _% @/ g4 ^8 i( E d
- /* USER CODE END Init */7 M# q, l& s3 G2 t$ ~, T
-
/ R# P+ A5 ^: i1 f* Q6 b& D - /* Configure the system clock */2 h' E0 }' e6 }6 {' X
- SystemClock_Config();
' b0 q' D! W4 H. N) f/ \ -
" W* ~$ v' c, o6 X y - /* USER CODE BEGIN SysInit */
" X5 M$ |5 c' [- D -
" D; y' q! [9 s3 G - /* USER CODE END SysInit */
2 M5 d* r2 k$ v0 p -
1 I6 O2 {1 P6 g( ~/ e - /* Initialize all configured peripherals */: q3 ]$ ^) p% K$ R% \' Z
- MX_GPIO_Init();8 @' J( b6 O& ?/ k- b4 \
- MX_DMA_Init();: H- n& p3 O: {4 a4 H$ z# P4 P
- MX_USART1_UART_Init();: F$ L- n# T; F8 @8 r
- MX_SPI2_Init();; z( g' N }+ g2 g1 i4 o) z
- /* USER CODE BEGIN 2 */
( d/ s- \/ w/ }5 K& e5 }: Z - HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,GPIO_PIN_SET); //初始化默认LED灯为点亮
* g& s. E5 o4 e% s: o; ], ^6 F - HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_SET); //初始化默认LED灯为点亮
# r3 A& e3 }0 s/ j+ }- k& ? - HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_SET); //初始化默认LED灯为点亮0 m0 ^! B0 {6 l- ?
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_3,GPIO_PIN_SET); //初始化默认LED灯为点亮
4 i* T u% A, J$ ^ - w25q_chip_id=spi_flash_read_ID(); //读取flash芯片ID
! u' p: b3 R* h - HAL_Delay(100);0 v, s, X2 w4 L( r. Y- ?
- printf("hello world,spi_flash_read_ID==0x%0x!\r\n",w25q_chip_id); //printf 芯片ID
9 a- e! h1 ^) S5 y& T* r - spi2_flash_sector_erase(0x0); //写之前需要先擦除扇区
% O& Y9 h" L0 q, Q+ Y1 A( n - spi2_flash_write(0x0,onebyte_write,8); //写八个字节
3 G. @* C, M. p+ X6 M: ^" [2 m - spi2_flash_read(0x00,onebyte_read,8); //读取写入的字节" n2 k1 ?/ y. P' n4 ~2 ?: V0 B
- /* USER CODE END 2 */" X& N' u$ g6 N$ T, `
-
4 C2 |8 q) s- H/ p% ~. v - /* Infinite loop */
9 r5 f& n: [; C0 }* H9 I6 ?! y1 e U - /* USER CODE BEGIN WHILE */
4 U8 ?- S, X2 i; F+ I' `( `2 P - while (1)$ x/ ^1 o& z7 w; N- u
- {/ \7 p' K+ N! g+ ]) O
- /* USER CODE END WHILE */
9 E. K% J$ ? u3 x. P5 V3 h - 0 x* B7 ?* v: s/ w2 J5 J1 }3 l! D
- /* USER CODE BEGIN 3 */; u* v- d V( n9 X: O' \4 H
- HAL_Delay(500);- i4 \% T- F: b6 c6 k0 R; x, {. l
- HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0); //每隔500ms闪烁一次,并打印flash区域的值0 @& f) u5 c' A5 p5 m; w
- HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_1); //每隔500ms闪烁一次,并打印flash区域的值, e( ]7 V* b( @8 D6 l! _: d
- HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_2); //每隔500ms闪烁一次,并打印flash区域的值
9 c( |3 \/ b3 H2 r8 O - HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_3); //每隔500ms闪烁一次,并打印flash区域的值
. |- C! ?9 a) h4 A0 y5 b! V, m - 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
- onebyte_read[2],onebyte_read[3],onebyte_read[4],onebyte_read[5],onebyte_read[6],onebyte_read[7]); //printf打印flash区域的值) `8 D2 |+ w! b
-
0 y2 w( g/ _6 ~# m: P: n/ K: e2 I - }
8 c* X/ v- }) V# S - /* USER CODE END 3 */# Y5 |( X, B) k, |6 I ~; f
- }
复制代码
7 v7 i, h# }6 f(3)新建w25qxx.c文件,并添加驱动层函数,如下所示:
( @5 V) ^0 v7 ]3 a4 F5 i+ y- #include "main.h"$ I/ _9 W2 j o3 F0 v- E
- #include "spi.h"
9 d) O/ ~0 i" X - #include "w25qxx.h"
, g. m3 }2 L' O. q2 l - /**1 \2 L0 Y6 e7 h- H3 }
- * @brief SPI1 读一个字节
% `8 @/ |- h3 T! L( e+ C - * @param None& c: I1 G! q7 W! E2 u% |
- * @retval None, _' T( G- I* d2 Y, `1 L8 ]2 M
- */
7 N1 Q" p, k, z+ m9 q# J; y. S8 i7 x - static uint8_t spi2_flash_read_byte(void)
3 m; u! H. q5 d" R- } - {2 ~ I8 k. G: z/ R: c
- uint8_t t_data, r_data;0 p$ e+ D ]8 d% c2 Z
- ) Q5 c5 o6 C6 d5 N& B
- if(HAL_SPI_TransmitReceive(&hspi2, &t_data, &r_data, 1, 0xFFFFFF) != HAL_OK) U- `! W$ T; X( e
- {
* V o) Z, E7 a4 z - r_data = 0xff;8 V! v$ D7 M1 K3 H
- }
1 y3 o5 \3 D8 \, F$ N - return r_data;& L! `5 j: ~: F/ W% [
- }
; m" h* v# s" H J7 c& J# v) @/ }8 h# g - /**
8 u' a* `* D! d; j# e+ h; u4 h( y - * @brief SPI1 写一个字节/ s% P, a& ?& v4 n' N
- * @param byte 写入的字节
) L/ J' A) q1 ^. n& f - * @retval 写状态 0成功 1失败
) ]7 N% _" ^8 E, m7 i - */
- K+ X; r0 }& B - static uint8_t spi2_flash_send_byte(uint8_t byte)2 x! K9 ^( H! `% i
- {
' O; D2 q( F" u! x& F/ y! }6 B - uint8_t r_data;' q' p/ P" j+ u- E3 U
- 1 d, r: G9 Z: T4 y
- if(HAL_SPI_TransmitReceive(&hspi2, &byte, &r_data, 1, 0xFFFFFF) != HAL_OK). q: @6 N0 X3 d
- {- O$ q/ C2 T8 ?+ X
- return 1;
8 H0 ^; b2 k( e: t - }
5 A9 B) {* E3 Y) G' ]- G$ H, z - return 0;! l5 o- y' G: O( @- | Z6 i
- }
% v* U9 `& [6 i - /**% p/ j2 K) I! M8 }
- * @brief FLASH 写使能& T5 V h) @- ?% P$ D1 o& \0 k- M% i
- * @param None
1 n2 g2 B3 ?! K& W- f i- U - * @retval None- |9 H7 ]: g! T! J1 x6 l4 e
- */
& a# P* D, d% W& y8 D$ K' M4 c% g - static void spi2_flash_write_enable(void)
/ h1 c- r7 k3 D1 o - {
$ F# X' @" R! t: H( K - SPI_FLASH_CS_LOW();: U" ?6 g, C$ ^
- spi2_flash_send_byte(W25X_WriteEnable);) G6 e X% @/ ]( x
- SPI_FLASH_CS_HIGH();5 u, f. o; s$ X* P
- }' N( L: Q- @* e, G
- /**" m, g% N1 s; Q; J
- * @brief FLASH 等待写结束; T5 Q/ P) c$ b/ r3 a
- * @param None
. `- ]6 X1 H! E) ~5 R - * @retval None$ C) H8 O; P+ j% @) {) H: a$ A
- */# g' F) O0 b! R$ N( F% @3 G( k
- static void spi2_flash_wait_for_write_end(void)5 w- I3 Y) I: ~/ I
- {
3 p, ~! H% ]' t8 S: C - uint8_t state = 0;$ \2 k& ^% F% m" g7 z# _ y1 }! c3 `
-
' R% {8 y. O+ M! l - SPI_FLASH_CS_LOW();& k8 [4 G% M7 P" ~
-
. _" R% N0 B: b! _9 X; X - spi2_flash_send_byte(W25X_ReadStatusReg);1 [+ Y- m! c' ]/ U( J( B
-
8 x: s/ V/ b/ N8 C# d. }- a - do- D& k: |) R5 O1 _0 s
- {
. u$ X# y& \( S9 w) e d; l - state = spi2_flash_read_byte();0 J/ W) N* H$ A6 w
- }
y+ o- l1 j) |0 s! [ - while((state & 0x01) == SET);; n' V+ O# N8 O; R( c* r0 Z
- a) u, `& O9 u3 x" w' N- D* _
- SPI_FLASH_CS_HIGH();, I K5 ?% p$ t5 p
- }3 N& e: k8 M0 h/ q; \$ s4 b
- /**: K: [" I& n$ {1 C' E0 w' C! w
- * @brief FLASH 读ID
. b2 k/ _. n6 }$ i7 `. Z - * @param None+ P3 D% s% c9 v- V4 R& b- Q5 A. k2 Y
- * @retval None
4 A) k0 S! i6 P `4 z - */
7 P% K. p) `% b" u - uint32_t spi_flash_read_ID(void)
% I! Y5 x# h, a8 R4 d$ {, n+ \ - {
) \3 T+ v, m1 {% `: m( P8 [9 g - uint32_t temp, temp0, temp1, temp2;1 }! x& M6 V1 s: ^# S6 y
-
+ }) P" h4 F+ m# i: G! O - SPI_FLASH_CS_LOW();' u/ m" R+ p1 L4 L7 |! H3 S7 W
- spi2_flash_send_byte(W25X_JedecDeviceID);
, @) I& S& Y8 j: p - , |% a2 p; M/ I- j$ s
- temp0 = spi2_flash_read_byte();
`6 `7 O7 y/ q - temp1 = spi2_flash_read_byte();! i9 E; j3 t) J$ t
- temp2 = spi2_flash_read_byte();3 Q- A2 j2 [% L2 {3 I) ]0 K: a) {& F
- $ _$ [- t! E# H4 D/ ~
- SPI_FLASH_CS_HIGH();5 [9 R) m, u9 K* F0 W
-
- ?9 \, m8 B+ z9 E3 I - temp = (temp0 << 16) | (temp1 << 8) | temp2;: ]! p: x. D, f$ ?+ j, q
-
% Y5 y x6 y% ]0 { - return temp;
+ O; U* O j" k2 C9 p3 r) ? - }/ l5 b7 }; o: d$ {$ ~: Z+ c
- /**8 A9 n$ f: t" \+ V3 ?; u" \
- * @brief 读FLASH
9 N% o$ v( H/ U - * @param addr 读flash的起始地址
. i8 V6 ^7 X' r - * @param pdata 读到的数据存放起始地址
$ u" C$ I' ^" j; v; M) }' B- p - * pdata size 读数据大小
" @4 M( y' q% N - * @retval None
2 c6 x9 J/ g, G& J, C - */
$ ?- p @8 Q; ]( F6 E! P - 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 - {
) Z$ y& [2 m& i' L% ^ - SPI_FLASH_CS_LOW();
% T3 z4 t9 \; w) {& \! n - 7 y, Y2 `, l) a' ^0 A u1 V3 L4 s
- spi2_flash_send_byte(W25X_ReadData); X0 j$ z( n9 n1 ?6 k
-
% H. o7 l9 o# _% R" c - spi2_flash_send_byte((addr & 0xFF0000) >> 16);
% z/ C7 @8 `- A' x( p. P - spi2_flash_send_byte((addr & 0xFF00) >> 8);
" n4 C1 t% U9 H- c7 L& C - spi2_flash_send_byte(addr & 0xFF);
/ j1 r- T9 P6 a: p4 X$ l - 6 {2 ~+ \6 y H) i1 w2 g+ r
- while (size--)
; k8 b& s9 G) P* [5 D- j/ U - {
& Q: K* y L, c" q6 W3 n+ t - *pdata = spi2_flash_read_byte();& D6 p. G! m, L6 L0 U
- pdata++;
: t* c( i2 b& M - }
5 \" U" E! _2 Q* R& N. S/ ~ - 4 d/ N' ?; D- i& \2 q
- SPI_FLASH_CS_HIGH();
; x; O' Q: i+ v+ H" c' L5 U3 x# N - }
8 \( h" V. T- p, B7 K - /**2 ~% m- t, j, g2 Q5 S4 w
- * @brief 按页写FLASH6 I& f& W! J" \
- * @param addr 写入flash的起始地址
' ]( U1 ^5 L) L. [ - * @param pdata 写入数据的起始地址
* O- Z0 f% k3 M, a. `2 h% | - * pdata size 写数据大小
3 D6 C U+ a# [ |; j( } - * @retval None
) [, {+ O7 Q0 e+ D" ` - */: M) Z* c7 _% h0 m5 z
- void spi2_flash_page_write(uint32_t addr, uint8_t *pdata, uint16_t size)
2 [6 c' I; k! I" M. l - {
% M: a' {( f; G3 W3 u$ u4 Q: M0 L$ v - uint16_t i;
3 j3 S8 ~. ^. ?& k+ f -
5 x9 V, b% R6 F7 }' I! | - spi2_flash_write_enable();
, \4 V& g( }- m# c" x" j! G/ @: [/ X - 1 J4 S4 V9 g/ ]6 Z, I1 X/ M
- SPI_FLASH_CS_LOW();! Z1 W" r6 q- A
- $ Z7 n' k' ?5 I+ q! |6 Y0 p
- spi2_flash_send_byte(W25X_PageProgram);
9 c$ l8 G1 b! c" ?$ ^4 B - spi2_flash_send_byte((uint8_t)((addr)>>16));/ q4 I* @ X; _7 c+ ]
- spi2_flash_send_byte((uint8_t)((addr)>>8));! ?0 V- I) h( T8 v
- spi2_flash_send_byte((uint8_t)addr);+ q& i5 T% f0 m: O B6 B% S
- 2 Q% o1 D: ]$ m4 n: @5 [
- for(i = 0; i < size; i++)
# Y" n3 u8 w% e) K. G2 t - {( V5 p' \) v! @; }1 Y
- spi2_flash_send_byte(pdata[i]); x. t# t; v. k+ P/ p5 I A8 L8 O
- }
/ U* b/ O8 m5 O* @8 Z -
( S9 l& F0 j# {0 @5 M5 [0 V% m. b - SPI_FLASH_CS_HIGH();
3 ]' O7 d7 w" _/ g - spi2_flash_wait_for_write_end();5 U! j! [) W8 f1 z3 c
- }
, U; D2 \; q( P8 x* x - /**
; s$ h1 R" e0 h% f9 M$ D - * @brief 写FLASH6 Q# D$ R J; f
- * @param addr 写入flash的起始地址
* W. ^8 R+ h7 g; g$ W - * @param pdata 写入数据的起始地址
/ D5 Q6 K( Q6 m" Z' @ - * pdata size 写数据大小1 N5 Q- P' K0 D$ @ X+ }
- * @retval None* s# K' d5 f7 `) G1 _! K
- */* u& z, ~8 w& h0 `3 \$ H
- void spi2_flash_write(uint32_t addr, uint8_t *pdata, uint32_t size)" Q! h! P4 _/ S, Y; u& |
- {
" f' t# F/ \8 s9 m - uint32_t page_remain;
& e8 x/ P1 {5 w -
6 m- Z$ M' Z; E% x+ p5 M' Z& ?3 A- ^0 w - page_remain = 256 - addr%256;
$ Z! g( n" m" Q2 | - 9 H+ I7 a$ I+ Z8 _ l
- if(size <= page_remain)9 w: M! Q% a" H {3 \! p" q2 X
- {
( s/ F3 {/ z' I/ o1 P - page_remain = size;
( C6 y5 y+ O9 _ g - }2 j' h p: a9 N8 u* \$ M$ i- R
- while(1)
0 ^) p! `" z4 b2 w5 f+ y; M( p* W - {
" X; ^. ?- b/ e: L0 B& L - spi2_flash_page_write(addr, pdata, page_remain);
) Z, m! ^& q0 j$ U -
6 J+ L* a3 `& U& {6 V - if(size == page_remain)+ J) g2 n* ?8 f
- break;2 W% Y; ~- [- ^6 j% ~: ^
- else2 X/ @! j8 G! X9 T( `3 Q: E) ]' h8 E
- {
! ?" r8 h2 f) Z7 @: x; D - pdata += page_remain;$ ^: `2 G" B* \% Y2 C/ O1 `4 N
- addr += page_remain;
! Y3 w) h( W' E1 Z" B- g -
% S, b$ N' c8 {2 z. T' o" ~# M7 ~( h - size -= page_remain;
7 G0 c! k, j" t0 @; J7 u - if(size > 256)
0 z# p( B* V4 C7 V0 r7 v - page_remain = 256;$ L& D3 \3 y: G9 W* D! U
- else
' e. N1 N; G, U% O+ q9 h* ?/ H - page_remain = size;: n' W( ~3 `) B* ?
- }; R6 Y. \5 r2 S. L
- }
K$ l4 d) X/ W* M b+ ?1 C - }# m" C! K; F/ c9 \: Y; L' e$ n
- /**" ^/ u7 M6 F! p
- * @brief 擦除FLASH扇区& s6 c; K, u! d' B; |
- * @param sector_addr 扇区的起始地址
$ C" d8 K. i; C - * @retval None5 c+ I' H7 w& Y! T( {4 i
- */
$ f+ ^5 g/ r7 n* x. }3 m/ z) p - void spi2_flash_sector_erase(uint32_t sector_addr)9 r ^$ a$ ?( W( f$ r2 m8 R
- {- m2 a+ Z1 }, Y8 \( o& ^
- spi2_flash_write_enable();* N: X) m& R0 z) ~$ r$ ?7 q
- spi2_flash_wait_for_write_end();
! s7 A8 S- p. y0 b% c) m# J6 U - 6 p/ }$ A6 O$ j; j7 L- ~0 a0 o
- SPI_FLASH_CS_LOW();
8 x$ L/ h: W3 M8 r - spi2_flash_send_byte(W25X_SectorErase);& s3 H% H" h7 n% ~$ K
- spi2_flash_send_byte((sector_addr & 0xFF0000) >> 16);
! ?9 r, L- O! ^% q+ i - spi2_flash_send_byte((sector_addr & 0xFF00) >> 8);
- i, t! \2 E! g) F - spi2_flash_send_byte(sector_addr & 0xFF);* A8 H1 b; a9 q5 x9 ^7 E
- 3 n( x; w3 a2 n* p! V6 o
- SPI_FLASH_CS_HIGH();
" \% f) l0 k" a - 1 R/ A, _' }6 L! ?
- spi2_flash_wait_for_write_end();
0 @+ Z/ z7 K- V: Y* f( B: N - }) j3 b& s% g# G. [/ l \8 _
-
; ~7 t! r4 D* G& F5 M, r/ w$ R2 H" D - /**
# s/ n+ S- {5 l# K: z% E7 ~ - * @brief 擦除FLASH块3 Z; T) f5 j. g
- * @param None8 s, D2 _& W) h3 ^; r: u1 v
- * @retval None
5 G' J, l5 k8 V3 M: A, J - */2 @5 b- U+ V- Y2 v, l
- void spi2_flash_block_erase(void)* {8 Z. d# z: [1 u6 a
- {
4 z- P3 d+ e% f - spi2_flash_write_enable();" w) {& f4 L4 Z8 b S$ } w% |
- 0 O( A5 s0 L' Q1 e8 T. ]6 D. W
- SPI_FLASH_CS_LOW();7 d/ ~8 W E8 i5 z& ~8 X! |1 @
- spi2_flash_send_byte(W25X_ChipErase);
1 H! U. n2 B& A9 D' d" _, N1 ~# b - SPI_FLASH_CS_HIGH();3 l+ A6 L5 M7 f2 a
-
) P: r) F F3 b1 c2 e+ C - spi2_flash_wait_for_write_end();3 d# G6 B6 \7 n+ ]/ ~* p& V' r
- }
$ 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
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
) 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
- z9 L2 L: [) k; {6 A M! j9 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
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
; 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
3 g. p( u: n" c8 X/ v: t& L
; l5 ^- ?! }* g5 G4 T
# 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 |