你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32心得:FLASH闪存编程原理与实验

[复制链接]
STMCU小助手 发布时间:2022-11-22 14:15
主要内容:
1 [2 @8 l) A% A1) STM32 Flash操作介绍;
7 @  p8 S! a- m6 N2 p2 @2) 寄存器和库函数介绍;
# p2 |' e- U& M) y3) 相关实验代码解读。
+ _' a% S! {9 i+ ~9 d# y0 {' p) w, a) M+ ?1 z
实验功能:利用 STM32 内部的 FLASH 来实现类似I2C通讯实验的效果(参考I2C通讯实验),不过这次是将数据直接存放在STM32内部,而不是存放在W25Q128。, c3 }5 j& S/ i' r
1. STM32 Flash操作介绍
/ M  e  B) m5 w- C& L1 }1.1 STM32编程方式
! j# Y1 ]" l1 Q5 D( k( O, ^( i, F1) 在线编程(ICP,In-Circuit Programming)
) ~" U) f1 @) z4 ?6 r通过JTAG/SWD协议(用JLINK或ST LINK下载程序)或者系统加载程序(Bootloader)下载用户应用程序(用串口下载)到微控制器中。
! k8 A1 C& M% [6 s$ ~* m2) 在程序中编程(IAP,In Application Programming)
: k% Y. ^5 ?8 B( g2 D; m通过任何一种通信接口(如IO端口,USB,CAN,UART,I2C,SPI等)下载程序或者应用数据到存储器中。也就是说,STM32允许用户在应用程序中重新烧写闪存存储器中的内容。然而,IAP需要至少有一部分程序已经使用ICP方式烧到闪存存储器中(Bootloader)(与系统加载程序的Bootloaer不同)。; e* S# v! I* @! W
3 |8 }7 Q, f- k
1.2 闪存模块存储器组织; ~. {/ |3 L2 y! j6 e- a. Z7 U
按照不同容量,存储器组织成32个1K字节/页(小容量),128个1K字节/页(中容量),256个2K字节/页(大容量)的主存储器和一些信息块等。战舰/精英板FLASH容量为512K,所以一共有256页(0-255)。( O, M; ~; X. x+ h9 H$ d) Y' X
9 h# ?8 z7 E; a5 T; k  w
20200915133520347.png 2 D& o3 G: ~( H* ?- m8 `$ V

- u$ g( E  `& N, ~6 z. OSTM32的闪存模块由:主存储器、信息块和闪存存储器接口寄存器等3部分组成。
3 {8 Y0 P  U: ?+ K' c3 |* R! e1 f) {1) 主存储器,该部分用来存放代码和数据常数(如const类型的数据)。对于大容量产品,其被划分为256页,每页2K字节。注意,小容量和中容量产品则每页只有1K字节。从上图可以看出主存储器的起始地址就是0X08000000, B0、B1都接GND的时候,就是从0X08000000开始运行代码的。
4 \& C) _2 i+ E# H5 n" ]/ H- n4 d2) 信息块,该部分分为2个小部分,其中启动程序代码(系统存储器),是用来存储ST自带的启动程序,用于串口下载代码,当B0接V3.3,B1接GND的时候,运行的就是这部分代码。用户选择字节,则一般用于配置写保护、读保护等功能。
$ H3 V0 T  y2 c, ?6 e+ ^$ n3) 闪存存储器接口寄存器,该部分用于控制闪存读写等,是整个闪存模块的控制机构。
) G% Z( B: B! C) {# A" [, z对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器(FPEC)管理。编程与擦除的高电压由内部产生。
5 T5 Y: _) f  F: ?& Z# a" S& b0 I6 B  R9 R( `  Z+ @
1.3 Flash闪存的读取
" V' \1 m( G, v% \2 O6 C内置闪存模块可以在通用地址空间直接寻址,任何32位数据的读操作都能访问闪存模块的内容并得到相应的数据。例如,我们要从地址addr,读取一个半字(半字为16位,1个字为32位),可以通过如下的语句读取:  m4 f4 ~. c0 F6 d

9 V( d8 O+ ?! V  M) w" d
  1. data=*(vu16*)addr;
复制代码

4 g- L3 d9 U6 ]7 P. S/ T+ L将addr强制转换为vu16指针,然后取该指针所指向的地址的值,即得到了addr地址的值。类似的,将上面的vu16改为vu8,即可读取指定地址的一个字节。5 G3 C- {! h8 h1 q* M8 g

, j8 k: @" t) P. @' `1.4 Flash闪存的编程(写)和擦除操作
9 M" ?% Y0 m+ PSTM32的闪存编程是由FPEC(闪存编程和擦除控制器)模块处理的,这个模块包含7个32位寄存器,他们分别是:; T1 F; w1 `9 i
1) FPEC键寄存器 (FLASH_KEYR)
4 i1 G1 ?$ l( M, S% Q8 f7 ?4 P0 I其中FPEC总共有3个键值:RDPRT键=0X000000A5、KEY1=0X45670123、KEY2=0XCDEF89AB。% j% `' ^: F2 b" q+ [4 i
2) 选择字节键寄存器 (FLASH_OPTKEYR)
& s# e1 ?* Q' t$ T2 O" ?, @3) 闪存控制寄存器 (FLASH_CR)- E2 K) y" i8 z! w. h3 e6 y/ V9 y
4) 闪存状态寄存器 (FLASH_SR)* ~4 h" R! Z9 O0 [; R* P
5) 闪存地址寄存器 (FLASH_AR)
  O" u) j. H* E' e8 ^, r: `% @6) 选择字节寄存器 (FLASH_OBR)
, y- u" [- [9 g$ B$ O7) 写保护寄存器 (FLASH_WRPR)
/ C: t! X7 s7 F% u( ~7 F5 m9 J6 w
1.5 Flash编程注意事项
& Q" @8 I5 H  k1 s8 R& _
1.5.1 STM32复位后,FPEC模块是被保护的,不能写入FLASH_CR寄存器;通过写入特定的序列到FLASH_KEYR寄存器可以打开FPEC模块(即写入KEY1和KEY2),只有在写保护被解除后,我们才能操作相关寄存器。 叫Unlock。键值不正确会产生总线错误。
8 V5 E5 R: f  _6 ]3 s+ P0 @1.5.2 STM32闪存的编程每次必须写入16位(不能单纯的写入8位数据!),当FLASH_CR寄存器的PG位为‘1’时,在一个闪存地址写入一个半字将启动一次编程。写入任何非半字的数据,FPEC都会产生总线错误。
1 x" K$ ~, T; d2 ?$ m1.5.3 在编程过程中(FALSH_SR的BSY位为‘1’),任何读写闪存的操作都会使CPU暂停,直到此次闪存编程结束。. E4 `2 J9 O+ \: _- P' h% J
1.5.4 STM32的FLASH在编程的时候,也必须要求其写入地址的FLASH是被擦除了的(也就是其值必须是0XFFFF),否则无法写入,在FLASH_SR寄存器的PGERR位将得到一个警告。
) W* J- Q8 _3 k' _/ F
0 W) |1 E! n5 s1.6 STM32 Falsh编程过程
8 @( d% O5 F, p, g  b1.6.1 检查FLASH_CR的LOCK是否解锁,如果没有则先解锁;
) m, r% Q- U9 i1.6.2 检查FLASH_SR寄存器的BSY位,以确认没有其他正在进行的编程操作;
( ?4 }! y" D2 D: ]6 G0 Q1.6.3 设置FLASH_CR寄存器的PG位为‘1’在指定的地址写入要编程的半字;
7 e" e# @" a& Q7 H: E1.6.4 等待BSY位变为‘0’;
- _+ I$ O; T% }8 e9 s$ i1.6.5 读出写入的地址并验证数据。  j9 r' p+ p$ a5 R$ Q( J

5 y' n! E, [4 \8 e+ l1.7 STM32 Flash擦除过程
1 j, s2 W  W5 o  fSTM32 FLASH编程的时候,要先判断所写地址是否被擦除了。闪存擦除分为两种:页擦除和整片擦除。
, q4 e  }, g) [* ?9 q1.7.1 Flash页擦除过程# h# I$ J. W% `4 F) s
1) 检查FLASH_CR的LOCK是否解锁,如果没有则先解锁;
5 ^+ `: r. J4 G1 m2) 检查FLASH_SR寄存器的BSY位,以确认没有其他正在进行的闪存操作;
: A7 R7 p6 a+ U: R; q7 M6 H4 }3) 设置FLASH_CR寄存器的PER位为‘1’;5 Q9 c9 J& e# ?! y7 X8 ?
4) 用FLASH_AR寄存器选择要擦除的页;
5 B! ?+ U/ m+ f- b" J5) 设置FLASH_CR寄存器的STRT位为‘1’;! W8 \5 j1 o% K: Z
6) 等待BSY位变为‘0’;
) y  o. w; P* S' e% J) Y/ J+ ]7) 读出被擦除的页并做验证。& G+ E# J) z9 L
1.7.2 Flash整片擦除过程( A; G% ?5 C% f
1) 检查FLASH_SR寄存器的BSY位,以确定没有其他操作在进行;8 B2 [4 H8 F0 @% u. n
2) 设置FLASH_CR寄存器的MER为1;
* j" n* {1 [: C3) 设置FLASH_CR寄存器的STRT为1;2 L- c6 j6 g' ^, t- A
4) 等待BSY为0;
  v; Z% K" V; P9 ^2 i5) 读出所有页并验证。0 S) {  }2 p6 B& ^
  z9 {# k; w; G5 {6 R
1.8 Flash操作相关寄存器
3 j7 q3 i- P, q$ b) S1) FPEC键寄存器(FLASH_KEYR);, c7 `/ a) q/ |

, k5 A5 E, W$ J 20200915134120600.png
, C1 B: @2 X! p( k5 V" b
; f: B/ R" V' ], Y9 h/ P* L- M2) 闪存控制寄存器(FLASH_CR);7 {2 B8 o: `  I" S9 P, s

3 u4 ]: s) y6 m! @: b& o 20200915134217432.png
" ?4 C1 b# U4 ~, S2 V3 U/ L- Y
5 i9 c  P- U9 p3) 闪存状态寄存器(FLASH_SR);
) |! s& y! Z! |+ R
* O7 `' |/ \  Z* j# q' C 20200915134306471.png ! B6 {/ }7 r( p/ e4 q% J  n! H! L

; w0 E+ T' |1 T- u8 X4) 闪存地址寄存器(FLASH_AR)。0 v$ Y& k3 F) j  Z) X: ~
) L* y0 S! c/ B  M: \3 x0 Y
20200915134348987.png 6 Z1 [5 h+ z" ^5 x+ \

& a' }: A9 G! D' }1 W2 X8 G2 Q3 i) s1.9 相关库函数
6 p# v7 `9 i* ?6 ?2 o% e官方给出的库函数位于:stm32f10x_flash.c/stm32f10x_flash.h。
- \6 f% f0 s' u+ L; H$ G+ X0 M; z5 O: x
1.9.1 闪存操作常用库函数. [; c1 }* m& i; j5 q9 t) _
  1. void FLASH_Unlock(void);                                                                                      //检查 FLASH_CR寄存器的LOCK位//
    + J5 @, A" M& {$ x* ^
  2. void FLASH_Lock(void);                                                                     //检查 FLASH_CR寄存器的LOCK位//& C0 O4 E; W& Z# v& S+ f, t. [
  3. FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);           //对主存储区编程//" d. @6 a: L5 s- l; D2 R
  4. FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);       //对主存储区编程//# G& F! e# d  z2 a; n2 M6 o# ^: ~' }
  5. FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);
    * T/ `, z/ u! l6 j9 e( J7 b
  6. FLASH_Status FLASH_ErasePage(uint32_t Page_Address);                       //对FLASH_AR寄存器进行操作//& K" k; y1 b2 Y: C& E7 ^
  7. FLASH_Status FLASH_EraseAllPages(void);
    ) |4 ~# n9 A$ J, l/ f/ p) F
  8. FLASH_Status FLASH_EraseOptionBytes(void);% H" G2 O& g# q; `, M+ u: q
  9. FLASH_Status FLASH_GetStatus(void);
    3 q& k: {# {. c3 M# g* D
  10. FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);                          //等待FLASH_SR寄存器的BSY位变0//
复制代码

3 K. B4 S1 K& C/ i5 f- V1.10 Flash操作总结
8 x* _5 e! d! w- J1.10.1 锁定解锁函数+ |" O, i7 Y& ?& d8 e3 }6 a" w
在对FLASH进行写操作前必须先解锁,解锁操作也就是必须在FLASH_KEYR寄存器写入特定的序列(KEY1和KEY2),固件库函数实现:
: A8 Z; V" G+ q( M% y% c5 _$ S) O
# a( ?# o5 m% m+ y( x/ e7 v; n
  1. void FLASH_Unlock(void);
复制代码
$ n: H( I* A% T+ v
同理,在对FLASH写操作完成之后,我们要锁定FLASH:! z& |" Z( V! i; o/ z+ m/ R- g
0 h$ U1 Q6 M" C2 H+ \/ P! p
  1. void FLASH_Lock(void);
复制代码

& y" J) M) i: L- P1.10.2 写操作函数
# M% e6 ^) V) o& O; m' N6 S固件库提供了三个FLASH写函数:- D- ~1 V( w: J) P' ]( A

7 {% t: D" {6 {' r
  1. FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);; r* c; C! D( M3 u+ E  y4 p) w6 a" h
  2. FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
    & c, A9 B$ F2 y8 _- t( i
  3. FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);
复制代码

* d# u& H5 k  D& l顾名思义:FLASH_ProgramWord为 32位字写入函数,其他分别为16位半字写入和用户选择字节写入函数。这里需要说明,32位字节写入实际上是写入的两次16位数据,写完第一次后地址+2,这与我们前面讲解的STM32闪存的编程每次必须写入16位并不矛盾。写入8位实际也是占用的两个地址了,跟写入16位基本上没啥区别。  R7 ]: e8 \# R4 m

+ D" k! c5 v9 Q& i5 w1.10.3 擦除函数8 J! d& x1 z/ l2 o2 k: ~
固件库提供三个FLASH擦除函数:% Z* Y' o1 P, Q% B
1 b+ G& N3 e1 X0 s' u  `
  1. FLASH_Status FLASH_ErasePage(uint32_t Page_Address);$ C8 L& p( W& q) m
  2. FLASH_Status FLASH_EraseAllPages(void);
    ) G2 A$ c1 A& Q. T9 g
  3. FLASH_Status FLASH_EraseOptionBytes(void);
复制代码
. F0 Q3 h$ a+ v, Z
顾名思义:第一个函数是页擦除函数,根据页地址擦除特定的页数据,第二个函数是擦除所有的页数据,第三个函数是擦除用户选择字节数据。这三个函数使用非常简单。7 W% a) x: K/ t' ^' s4 z; I
! R: U" f1 z4 z" _0 W
1.10.4 获取状态函数# M; c( J7 V1 m9 f4 h8 c/ K
主要是用的函数是:
% h& ^; ~$ f! F8 p2 n' w8 R5 g4 h% }* J6 b6 i
  1. FLASH_Status FLASH_GetStatus(void);
复制代码
) A6 k" o9 U% T
返回值是通过枚举类型定义的:1 J0 T7 `  G3 F% |# N( Y

6 h, K5 F+ |  D2 j* N8 L
  1. typedef enum
    : J, H) ~' f8 q. H
  2. {
    5 x" {7 T/ H0 b) i
  3. FLASH_BUSY = 1,    //忙2 D8 }# Q. Y& U& ?- [
  4. FLASH_ERROR_PG,    //编程错误8 u8 u  |  v4 j+ K
  5. FLASH_ERROR_WRP,   //写保护错误) {. z3 t% V4 O! W
  6. FLASH_COMPLETE,    //操作完成
    - t5 I' s8 J' ~9 \( E
  7. FLASH_TIMEOUT      //操作超时
    7 g( i  ^0 J9 s; Q1 ^
  8. }FLASH_Status;
复制代码
6 L$ \2 U5 g0 e( V
1.10.5 等待操作完成函数7 ^8 w2 C: H9 P! G6 r: |
在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。所以在每次操作之前,我们都要等待上一次操作完成这次操作才能开始。使用的函数是:
: s% ~9 f2 o0 a$ @4 l  [- U) w. _5 l7 N8 o4 f
  1. FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);
复制代码
% E" f, M1 a, c% D9 M. R$ A
入口参数为等待时间,返回值是FLASH的状态,这个很容易理解,这个函数本身我们在固件库中使用得不多,但是在固件库函数体中间可以多次看到。" q( x( q3 u, n- Q) r2 a; {

* a$ O  Y2 B; _  j3 [5 k1.10.6 读FLASH特定地址数据函数$ k% K6 p- P' r$ T
有写就必定有读,而读取FLASH指定地址的半字的函数固件库并没有给出来,这里我们自己写的一个函数:' N; N7 u+ O2 i! a; O3 u' N4 s7 M

) l' y! d" b" I8 q
  1. u16 STMFLASH_ReadHalfWord(u32 faddr)
    / x. K# @2 M( j
  2. {
    # O1 s7 d$ N; u" t
  3. return *(vu16*)faddr;
    7 g; [# t, g; F. `! p9 w3 W  ?+ m- o
  4. }
复制代码
+ k, V# @: R+ F4 K; O/ w1 a2 R& n7 ~- ~
2. 相关实验代码解读
( `( j, r! {3 m) i2.1 stmflash.h头文件代码解读

3 a4 H( i' Y% e0 [- p$ r( }
1 b% p5 B  B/ {0 x. S4 o1 j; i' n
  1. /**9 u* W4 A+ c' Q! b8 C
  2. ********************************  STM32F10x  *********************************
    . o/ p, k1 G8 I& k3 m8 B/ J
  3. * @文件名称: STMFLASH.h
    6 j$ o5 t  z6 I2 F1 e0 M
  4. * @作者名称: -----
    7 M) R8 x5 |, I! h- |6 e
  5. * @库版本号: V3.5.0" y% O1 _- h4 D. p4 z) \4 x# Z
  6. * @工程版本: V1.0.0# P* G0 Q$ q& K/ r9 l: K; _
  7. * @开发日期: 2020年9月15日! G6 L& k8 i- g) r" O6 u
  8. * @摘要简述: STMFLASH头文件
    . V6 z) k8 N0 q5 y! J. X
  9. ******************************************************************************/* H9 f9 L) H: B3 d& F
  10. /*----------------------------------------------------------------------------' ^; V$ b0 @8 x1 d9 Z
  11. * @更新日志:
    6 @. j1 p( H8 a) S7 g! z
  12. * @无3 \8 \4 K! ^/ D4 A( b. U
  13. * ---------------------------------------------------------------------------*/0 `8 n- }2 ~7 {( d5 S! d
  14. #ifndef __STMFLASH_H__
      t# k  l  ^. S4 M. H) c; n
  15. #define __STMFLASH_H__
    8 h- w' |) ?* U; g% o( A$ e7 _
  16. /* 包含的头文件 ---------------------------------------------------------------*/' u7 K: m  c7 L' w- R
  17. #include "stm32f10x.h"
    : J* d9 {- Q9 }3 P) f% a( p  g+ w
  18. /* 常量 ----------------------------------------------------------------------*/
    : r/ H0 c2 W( K, v5 @; `: C6 |0 I/ N3 J+ o
  19. #define STM32_FLASH_SIZE 512                    /* STM32F103ZET6的FLASH是512K */
    ! `0 J" |% A' H3 {$ K( }  v
  20. #define STM32_FLASH_WREN 1                 /* 使能FLASH写入(0,不是能;1,使能) */
    1 {$ h8 o+ t  b6 z
  21. #define STM32_FLASH_BASE 0x08000000                  /* STM32 FLASH的起始地址 */7 k& x  V- j" f$ w. e
  22. /* 函数申明 ------------------------------------------------------------------*/4 @/ T$ F# p9 o8 ]6 }1 V. s# B
  23. u16  STMFLASH_ReadHalfWord(u32 faddr);                                 /* 读出半字 */  
    * B1 I5 ]- a& V# ^: n
  24. u32  STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len);                       /* 指定地址开始读取指定长度数据 */
    - a0 U0 h7 h& `
  25. void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len);     /* 指定地址开始写入指定长度的数据 */0 r6 l% @0 Q' [% y3 o. [. c( r1 Q
  26. void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite);        /* 从指定地址开始写入指定长度的数据 */* ]; [+ D* r: C! k7 T- N0 C
  27. void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead);           /* 从指定地址开始读出指定长度的数据 */   
    % M# S; `# g& c
  28. #endif /* __STMFLASH_H */
    + E6 @8 f& V' B. y0 X4 @  t" `
  29. /****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/
复制代码
4 K2 B! Z( J. U$ ]: a: N
2.2 stmflash.c文件代码解读
# O! Y' ^, Y9 r& r
* ~9 T, i' [' T& v% A3 S
  1. /**
    7 n8 c) }0 G7 T1 w' |
  2. ********************************  STM32F10x  *********************************
    % W( z7 E+ D( r4 [, a
  3. * @文件名称: stmflash.c! F& k: w$ o' D# C4 h2 G- ]
  4. * @作者名称: -----1 R: Q  E/ v* m( L1 b
  5. * @库版本号: V3.5.0  C( l3 t' x+ m. J
  6. * @工程版本: V1.0.0
    * H* l8 N! d' Y) w
  7. * @开发日期: 2020年9月15日. F0 L7 y6 ^: v
  8. * @摘要简述: stmflash源文件
    3 R, Q' c# ~3 ^' P- w4 k  Y0 [" m
  9. ******************************************************************************/
      {4 a( {( s1 p% Y
  10. /*-----------------------------------------------------------------------------, q( _; _, I% l4 x1 t# o# [) ?
  11. * @更新日志:
    - q/ |5 j! n- h/ k6 _5 S
  12. * @无
    - d0 n, b( \( z  s/ p- d
  13. * ---------------------------------------------------------------------------*/
    0 `% h! x: l; B# t& B4 h
  14. /* 包含的头文件 ---------------------------------------------------------------*/9 ~- I  b7 Z7 b1 ]1 `5 q$ ~2 }, |
  15. #include "stmflash.h"+ n+ h. s8 K! G2 U. N
  16. #include "delay.h"% t4 Y  {8 |9 V: |5 h
  17. #include "usart.h"
    2 J% v% v7 W. d7 E3 M8 J! o
  18. /************************************************1 z4 O. }: ?0 A/ `
  19. 函数名称:u16 STMFLASH_ReadHalfWord(u32 faddr)
    ! v' Q/ ?7 {1 {. o  J; C
  20. 函数功能:读取指定地址的半字(16位数据)
    4 M& `$ q/ V. A! Z# p' y; }
  21. 入口参数:u32 faddr:读地址(此地址必须为2的倍数!!)
    % r; A: g1 {; F+ n- l
  22. 返回参数:对应数据
    7 d* ^0 B" v- V2 n3 s! i
  23. 开发作者:------ {4 l% H  w  j+ z; x9 x, P; M- X
  24. *************************************************/+ O' j/ z$ s" f& w
  25. u16 STMFLASH_ReadHalfWord(u32 faddr)
    7 q% M, x7 ]! r1 J% I4 x8 u" o
  26. {
    - D9 i  j, q2 e7 y3 e
  27. return *(vu16*)faddr;
    : T4 C3 ?" [( |* s
  28. }0 t3 N5 N* ?/ k; P7 |; G, e% K6 S
  29. #if STM32_FLASH_WREN    /* 如何使能写入,默认使能 */   1 G# |! n8 t$ [) H- l
  30. /***************************************************************************
    # T( R% O! H+ S; i3 X; z
  31. 函数名称:STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)  4 |/ M9 m! Z9 m% d! L
  32. 函数功能:不检查的写入) P9 ~6 K/ X' }' K! O1 a8 q8 d
  33. 入口参数:WriteAddr:起始地址;pBuffer:数据指针;NumToWrite:半字(16位)数
    2 s/ m* U/ L' T% E4 N( ]+ e  g
  34. 返回参数:无
    0 _4 J. E; o7 O( F" T  [  _1 `4 @
  35. 开发作者:-----
    9 ?. ~1 w  [- f# d7 Z
  36. ****************************************************************************/* ?1 X* u, t) ~3 N& {
  37. void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)   
    ! @2 b- }: \5 B% K7 I- g
  38. {        0 W7 s$ v+ t3 {0 J1 B& F; ?
  39. u16 i;: E& Z4 E5 ^8 @2 G; x
  40. for(i=0;i<NumToWrite;i++)
    % e: q8 v+ ]1 }. |3 S& g9 R$ Y
  41. {0 D! E  U! D/ m- w- d3 t
  42.   FLASH_ProgramHalfWord(WriteAddr,pBuffer<i>);% u6 q2 X- |  o3 N2 h6 f" |
  43.   WriteAddr+=2;                                  /* 地址增加2 */
    6 e" ?' s0 E& H+ T+ h0 Q, d
  44. }  
    + G$ }* K  p5 e6 {
  45. } 5 g% d  S. \3 O6 a) _
  46. /***************************************************************************6 j/ Z  M4 v8 h  z: G/ }( i9 Y
  47. 函数名称:STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)  
    3 m6 h6 h7 R6 _9 a4 m3 e
  48. 函数功能:从指定地址开始写入指定长度的数据6 x; R7 m( k6 D" c* P$ V+ a
  49. 入口参数:WriteAddr:起始地址(此地址必须为2的倍数!!);! `2 _% `5 S$ V: S
  50.          pBuffer:数据指针;
    $ }7 D0 \6 T+ ^5 q& J
  51.          NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)/ m  ^' h, A( S, ^* r3 i8 D: U
  52. 返回参数:无
    * d5 H  S& `7 u7 O9 [5 l
  53. 开发作者:-----
      Q: D6 U& D. M6 e
  54. ****************************************************************************/
    9 F9 v! I- a7 |  X8 C6 M
  55. #if STM32_FLASH_SIZE<256
    ! K$ a) ^  V% \7 h2 ?. `8 m4 l( a1 L+ ?$ m
  56. #define STM_SECTOR_SIZE 1024                          /* 字节 */1 o- \- r  J1 d) r( B; k; s3 v* D
  57. #else
    9 G& |! s) r7 V$ E. w; {
  58. #define STM_SECTOR_SIZE 20484 l& L  X$ c. v
  59. #endif   
    % p; y2 }" G+ `2 W$ v8 Z
  60. u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];      /* 最多是2K字节 */
    6 o9 h# {7 {& e- B) l3 ^
  61. void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) 3 w! W) c( v% B
  62. {3 _! ?+ o' N* Y: @. [
  63. u32 secpos;    /* 扇区地址 */& [9 Y0 }1 n. B% `5 Q+ T  m
  64. u16 secoff;    /* 扇区内偏移地址(16位字计算) *// {& G- S9 S+ E* y+ o% v, h
  65. u16 secremain; /* 扇区内剩余地址(16位字计算) */    8 r/ x) G  v- x2 E; i. r9 Y
  66. u16 i;    # V$ S0 j' Q0 R& g/ H# ]
  67. u32 offaddr;   /* 去掉0X08000000后的地址 */
    ) H; M$ H+ ~1 p' h0 }
  68. if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;  /* 非法地址 */, u+ D7 D! y% s8 k7 y
  69. FLASH_Unlock();                                               /* 解锁 */7 y* }; C! X$ _2 A  `; T
  70. offaddr=WriteAddr-STM32_FLASH_BASE;                     /* 实际偏移地址 */
    4 w6 R3 |# a9 f0 [! F& W2 N; C
  71. secpos=offaddr/STM_SECTOR_SIZE;                        /* 扇区地址  0~127 for STM32F103RBT6 */9 k2 L: Q* O: `0 a
  72. secoff=(offaddr%STM_SECTOR_SIZE)/2;                        /* 在扇区内的偏移(2个字节为基本单位.) */2 ?* q. t, @1 S7 u) m3 E' z
  73. secremain=STM_SECTOR_SIZE/2-secoff;                        /* 扇区剩余空间大小 */   
    ; x3 }/ [3 s; h* Y  U/ R: t% l7 @
  74. if(NumToWrite<=secremain)secremain=NumToWrite;          /* 不大于该扇区范围 */- D0 K4 n7 d3 \$ t( B. F& l
  75. while(1)
    6 F( G( ?; U4 V% e( E
  76. { - Y4 u2 R, |8 ~" h  C6 o
  77.   STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);  /* 读出整个扇区的内容 */7 j! Y1 F: z- Y6 x
  78.   for(i=0;i<secremain;i++)                                                                /* 校验数据 */
    " n1 I4 U+ y1 c, w
  79.   {
      A) i9 L: v2 Q5 r$ f0 F$ ]2 s
  80.    if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;                                                                                   /* 需要擦除 */     & M+ p3 d% t! T2 Y
  81.   }
    ' ]* N% m9 ], ~: Q# [6 ^3 F
  82.   if(i<secremain)                                                                         /* 需要擦除 */3 K* t7 P) C. f1 M( z! ]  T
  83.   {
    ( Y$ _- ]1 R( \, u. J
  84.    FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);                                         /* 擦除这个扇区 */3 T% Q( S4 o3 X! H6 e& C  K
  85.    for(i=0;i<secremain;i++)                                                                                                      /* 复制 */# `  y2 B' }% b$ }" z6 ~1 R# \
  86.    {
    ' F9 Y1 L# H* Q: r# c5 a; \5 h
  87.     STMFLASH_BUF[i+secoff]=pBuffer<i>;   
      v; I* v' a; P+ l9 j' v7 T: ~- {- ?
  88.    }7 g+ ]3 t2 v( {( ~
  89.    STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);  /* 写入整个扇区 */  
    8 t8 T4 ?6 E" d* {! I$ S) q3 m
  90.   }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);                   /* 写已经擦除了的,直接写入扇区剩余区间 */     2 f  {3 A* S2 B- b3 c' Z2 w. e4 M1 D& M
  91.   if(NumToWrite==secremain)break;                                                                           /* 写入结束了 */# b' s( P, p2 _$ F3 F/ \2 b; ]
  92.   else                                                                                                                         /* 写入未结束 */2 Y2 ]7 g" Y' l! x, ^
  93.   {& `# z! R% z* o" a
  94.    secpos++;                                                                                             /* 扇区地址增1 */2 R8 D& m* P: H! f! p0 R0 _/ q
  95.    secoff=0;                                                                                                             /* 偏移位置为0 */   . o0 [5 u7 j+ P# Q
  96.    pBuffer+=secremain;                                                        /* 指针偏移 */
    8 {* }6 e6 n1 f$ B( W0 C% Q- B
  97.    WriteAddr+=secremain;                                                      /* 写地址偏移 */   
    ( q% S& r& p, Z/ t5 V
  98.    NumToWrite-=secremain;                                                                                         /* 字节(16位)数递减 */  T6 p2 M6 {* z- c0 h/ Y* P) ]
  99.    if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;             /* 下一个扇区还是写不完 */' x6 Y6 @* G5 l) d% t0 l2 z
  100.    else secremain=NumToWrite;                                                                                  /* 下一个扇区可以写完了 */
    , Q9 s, `7 W% F
  101.   }  
    9 ~; f3 A1 u2 N# i0 o) Z- ?% t! p
  102. }; ! `1 N5 X6 [, m( }
  103. FLASH_Lock();                                                                                                             /* 上锁 */
    4 ]5 w# k; {& ^& M  r
  104. }( \. F  M( Z; M9 ]  ?
  105. #endif# M% U/ |) ~. @% n6 c& h
  106. /******************************************************************************  D/ ^) F7 G2 Y" z" m) O
  107. 函数名称:STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)   
    2 ~  S- f) E8 ^  N2 \
  108. 函数功能:从指定地址开始读出指定长度的数据1 d( H$ P8 ?8 g7 L
  109. 入口参数:ReadAddr:起始地址;pBuffer:数据指针;NumToWrite:半字(16位)数
    * J- Z$ w# a) Q
  110. 返回参数:无" U" M0 l6 t3 v3 A8 e* z
  111. 开发作者:-----
    4 g0 J2 }6 a0 |7 e( ]
  112. *******************************************************************************/
    ( A/ k6 _1 @: i2 D0 D$ {
  113. void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)    . S6 S. g% `1 ~
  114. {  q6 d! \* {/ N  Z% a
  115. u16 i;$ @$ H9 Y5 x4 `" v# I; ^* H- ^
  116. for(i=0;i<NumToRead;i++)( |1 E' |5 J  d# R" Q0 g+ b8 n* g
  117. {6 |+ L6 F5 Z& w! Z; }
  118.   pBuffer<i>=STMFLASH_ReadHalfWord(ReadAddr);         /* 读取2个字节 */
    , r; ?$ ]8 h7 t* y1 |2 _2 h
  119.   ReadAddr+=2;                                                                /* 偏移2个字节 */+ ?5 \# [* ?! }5 a9 ^; k
  120. }# Z) c0 V; {, F: H4 _' W
  121. }
    " ?: i* r& p# @8 E) K! q1 Z2 ]
  122. /****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/' S4 C" {* o  Y6 x  O, o
  123. </i></i></i>
复制代码
6 K  E, }& z' g6 \, R& W: }$ t
2.3 main.c文件代码解读* e) n3 \" D0 e/ T. B/ e# F) k

1 U8 J* S0 o7 {( J/ A/ s
  1. /**
    : Z( u7 X7 J" |/ t1 Y3 |
  2. ********************************  STM32F10x  *********************************% z5 ?4 E, J: ^4 u( x
  3. * @文件名称: main.c
    4 @0 \8 t" L* `- G
  4. * @作者名称: -----/ y, L/ I! q: G! j
  5. * @库版本号: V3.5.0$ F( Y9 P0 k0 E& h
  6. * @工程版本: V1.0.0
      a: z! h$ H; g' \1 g) _
  7. * @开发日期: 2020年9月15日% m1 z# P- X6 J6 l- G# Y+ J
  8. * @摘要简述: main源文件  `6 z% y3 I) k) K) m3 `- n
  9. ******************************************************************************/2 T3 l6 w% }4 Z* z. Y
  10. /*-----------------------------------------------------------------------------
    9 q! F8 L, a' z( @
  11. * @更新日志:3 ~" O, j0 J7 `& Z7 a8 n5 r# p2 ?
  12. * @无
    : E' }0 f: q& U+ u* c3 l8 s2 f* Z
  13. * ---------------------------------------------------------------------------*/3 f7 y9 y6 p' T+ h) v& o) Q
  14. /* 包含的头文件 ---------------------------------------------------------------*/
    " r( y1 L- _# g; D  i* i& k  K
  15. #include "led.h"4 Y: e7 v" `  a$ c8 O. ^' o0 C7 B* {
  16. #include "key.h"- m% ~0 W( J2 ?7 L, O2 b2 B2 ?
  17. #include "delay.h"
    , A. E7 n6 B% Z* I; o( I; a
  18. #include "usart.h"  0 M' u* m, _, }: ^6 b. X
  19. #include "stmflash.h"6 @  g- _6 I& s7 w9 E
  20. /* 包含的头文件 ---------------------------------------------------------------*/0 X/ W4 G! t0 X6 c
  21. const u8 TEXT_Buffer[]={"STM32F103 FLASH TEST"};   /* 要写入到STM32 FLASH的字符串数组 */3 O$ M$ a& z2 O: k* v
  22. #define SIZE sizeof(TEXT_Buffer)                             /* 数组长度 */
    % @5 Z2 H& e' W4 m' |
  23. #define FLASH_SAVE_ADDR  0X08070000                    /* 设置FLASH保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000) */
    4 ]3 U" @/ M0 a- S% i
  24. /****************************************************************
    % f* ]+ K0 U4 V& X. z2 x+ a; S
  25. 函数名称:int main()5 N; L8 }& F" d+ f. _0 s
  26. 函数功能:主函数5 s" l  ]. B2 R
  27. 入口参数:无
    + ?4 o3 `! c( |( z' q
  28. 返回参数:int# ^& y! `" }" u. _8 M/ J' Y
  29. 开发作者:-----4 i$ l. Q0 ?$ W  \* O2 P6 h: w
  30. *****************************************************************/
    ( F" v. r( O- q
  31. int main(void)6 b. w" P" n, C8 K
  32. {  
    , X8 b* O& x$ F/ v  J+ |. c- y( V
  33. u8 key;: N7 j( d+ l+ z  u
  34. u16 i=0;
    7 C; o- T( e& p1 L4 h7 U
  35. u8 datatemp[SIZE];: ^+ k1 ^$ @+ p6 p7 @+ q% Z
  36. delay_init();                                                               /* 延时函数初始化 */   6 V# K+ p' `6 m) g) q* {/ Y  v
  37. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);   /* 设置中断优先级分组为组2:2位抢占优先级,2位响应优先级 */0 U4 q0 H) S- |8 v7 p1 w
  38. uart_init(115200);                                                  /* 串口初始化为115200 */
    , M+ R- D1 l9 T  H' T/ x
  39. LED_Init();                                                           /* 初始化LED管脚接口 */
    % U8 f2 x' k( Z8 d' m& m
  40. KEY_Init();                                                                  /* 初始化KEY管脚接口 */
    7 k; N, f- K; U$ g: u
  41. printf("KEY1:Write  KEY0:Read");  2 c' Q# |  [# {1 m! P
  42. while(1)* q0 g0 l) {5 ?9 V8 {
  43. {
    1 D8 c2 d, a" j2 `" d3 F
  44.   key=KEY_Scan(0);                                 /* 0表示不支持连按 */
    ' y6 [9 L2 e2 t/ i( P: s' S" k
  45.   if(key==KEY1_PRES)                               /* KEY1按下,写入STM32 FLASH */' m6 A, T( ]) V* _! e- M4 {
  46.   {) y% X0 Y% I: y. S' U6 T
  47.       printf("Start Write FLASH....\r\n");
    - G3 I2 M+ M: p4 g4 m1 i6 k
  48.    STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer,SIZE);4 M% g8 k8 a( @
  49.    printf("FLASH Write Finished!\r\n");            /* 提示传送完成 */. A: e: }, x8 |: W
  50.   }0 X) n( u9 N7 N- z
  51.   if(key==KEY0_PRES)                                               /* KEY0按下,读取字符串并显示 */
    4 v  |" x6 S5 G& o7 K2 s6 c
  52.   {
    ( |  g! j/ C" F" K3 P
  53.    printf("Start Read FLASH....\r\n");
    8 p$ |# b+ D. [8 |
  54.    STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE);& Y% a( C- C& G4 v! z; i! x$ F
  55.    printf("The Data Readed Is:%s\r\n",datatemp);  /* 提示传送完成 */
    . y/ }% L/ v; K1 }
  56.   }, V" n8 u. ~* t( [# x; K+ B3 Q
  57.   i++;0 V' H9 R4 D, b$ A4 _
  58.   delay_ms(10);  
    " D. Z: M* F% H
  59.   if(i==20)
    ; K3 n6 m4 v# \4 Q4 t) P
  60.   {
      D, ?  u) ^5 K. {, ]* `
  61.    LED0=!LED0;                                                            /* 提示系统正在运行 */$ E$ X  g8 a. I  K
  62.    i=0;0 H" `5 t1 P1 W( m1 o
  63.   }     + ]4 P  p0 U; n; _. i
  64. }
    4 U4 z$ w" ~. E* ?1 T
  65. }
    2 S0 Y0 o$ u: Y! ]4 g
  66. /****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/
    ) o& g$ {$ z7 e5 m
复制代码

# v2 h8 m0 X6 S, b2 C- q3 r3. 实验结果: `" D7 D- x3 |

. a& T. ?- |* l: v" N 20200915140953835.jpg
! B5 Y6 p! l5 v; x. V' E
: B# c3 z. d/ x) L————————————————
8 t/ e6 o5 d/ H6 X# S版权声明:天亮继续睡! T! K, M" t) @' i& e* K# J& ^

/ w1 H& m0 ~; n2 v* n! Y! ^
9 W1 ]2 Z2 @9 p- f( e0 S+ {( i( g
赞 收藏 评论0 发布时间:2022-11-22 14:15

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版