我是靠谱客的博主 无限水池,这篇文章主要介绍基于STM32的FLASH读写实验含代码(HAL库) 一、FLASH简介二、CubexMX配置 三、代码四、Flash读写查看,现在分享给大家,希望可以做个参考。

        前言:本文基于STM32的内置FLASH的读写实验,FLASH作为MCU必备模块是一定需要深入掌握了解的,这对后续学习Linux开发板都具有重要意义。在某些情况下,我们想要实现单片机中某些数据实现断电保存,这个时候我们可以添加外部存储器(EEPROM,但是这样就给硬件方面添加了工作量。在不得已的情况下(如果单片机内部Flash空间余量可观,因为运行程序也存储在flash中),我们就可以使用FLASH作为存储设备保存我们需要实现断电保存的操作

        实验硬件:STM32F103C8T6;

        硬件实物图:

 一、FLASH简介

1.1 Flash的基本概念

        Flash 是存储芯片的一种,通过特定的程序可以修改里面的数据。Flash 存储器又称闪存,它结合了ROMRAM 的长处,不仅具备电子可擦除可编程EEPROM)的性能——不会断电丢失数据,同时可以快速读取数据(NVRAM 的优势)。

Flash的巨大特点不会断电丢失数据、快速读取数据

Flash的巨大缺点

        (1)根据手册和网友的帖子得知,STM32的flash的读写次数是1万次,也就意味着超过1万次的读写之后,flash很有可能会损坏。所以不建议大家真的经常使用flash去读写数据。(有专门常用的EEPROM,比如:ATC24XX

        (2)Flash内存做大之后造价较高

        EEPROM (Electrically Erasable Programmable read only memory)是指带电可擦可编程只读存储器。是一种掉电后数据不丢失的存储芯片。换句话就是说,如果你这次使用断电后,下次使用时,上一次储存的代码依然存在

        在开发项目的时候,总会有需要掉电存储一些配置信息的时候。不过,ST公司为了节约成本,没有加入 EEPROM,这个时候可能需要我们使用自带的flash来进行数据存储!(再次强调:尽量不要使用flash去读写保存数据!!!特别是正式项目的时候,几年的运行时间之后,芯片flash批量出问题,非常可怕

1.2 STM32的Flash补充说明

        (1) STM32根据闪存(Flash)容量的大小,将Flash分为每页1K字节每页2K字节超过256K容量的每页为2K字节。对于本次使用的SMT32F103C8T6,其容量为64K(可选配置128k,官方不推荐可能存在隐患),则内部分为每页1K字节。

        (2) SMT32的Flash起始地址为0X0800 0000 。本次使用的STM32F103C8T6的FLASH范围是0X08000000-0X0800FFFF。示意图如下:

        (3) STM32运行代码从地址0X0800 0000开始,所以我们使用的Flash空间开始地址应该往后偏移,不然就会将程序部分覆盖掉。

        (4) Flash的写操作,需要擦除一整页后再重新写入,不能对特定处进行修改,写的时候可以分多次写入

        (5) 擦写次数较多数据的不建议使用内部Flash进行存储,手册中给的数据是擦写1W次

二、CubexMX配置

1、RCC配置外部高速晶振(精度更高)——HSE;

2、SYS配置:Debug设置成Serial Wire否则可能导致芯片自锁);

 3、时钟树配置:

 4、工程配置

 三、代码

flash读写流程图:

        注意:要明白Flash的编程原理都是只能将1写为0,而不能将0写为1,所以在进行Flash编程前,必须将对应的块擦除,即将该块的每一位都变为1,块内所有字节变为0xFF。

flash.h函数:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#ifndef __FLASH_H__ #define __FLASH_H__ #include "main.h" //=========================数据类型宏定义 #define u8 uint8_t #define u16 uint16_t #define u32 uint32_t #define __IO volatile typedef __IO uint16_t vu16; //=========================用户根据自己的需要设置 #define STM32_FLASH_SIZE 64 //所选STM32的FLASH容量大小(单位为K) #if STM32_FLASH_SIZE < 256 //设置扇区大小 #define STM_SECTOR_SIZE 1024 //1K字节 #else #define STM_SECTOR_SIZE 2048 //2K字节 #endif #define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址 #define FLASH_SAVE_ADDR STM32_FLASH_BASE+STM_SECTOR_SIZE*62 //写Flash的地址,这里从倒数第二页开始 #define STM32_FLASH_WREN 1 //使能FLASH写入(0,不是能;1,使能) #define FLASH_WAITETIME 50000 //FLASH等待超时时间 u8 STMFLASH_GetStatus(void); //获得状态 u8 STMFLASH_WaitDone(u16 time); //等待操作结束 u8 STMFLASH_ErasePage(u32 paddr); //擦除页 u8 STMFLASH_WriteHalfWord(u32 faddr, u16 dat);//写入半字 u16 STMFLASH_ReadHalfWord(u32 faddr); //读出半字 void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len); //指定地址开始写入指定长度的数据 u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len); //指定地址开始读取指定长度数据 void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite); //从指定地址开始写入指定长度的数据 void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead); //从指定地址开始读出指定长度的数据 void Flash_PageErase(uint32_t PageAddress); //扇区擦除 #endif

flash.c函数:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include "flash.h" FLASH_ProcessTypeDef p_Flash; u16 STMFLASH_BUF[STM_SECTOR_SIZE/2]; //缓存数组 /********************************************************************************** * 函数功能: 读取指定地址的半字(16位数据) * 输入参数: faddr:读地址 * 返 回 值: 对应数据 * 说 明: */ u16 STMFLASH_ReadHalfWord(u32 faddr) { return *(vu16*)faddr; } #if STM32_FLASH_WREN //如果使能了写 /********************************************************************************** * 函数功能:不检查的写入 * 输入参数: WriteAddr:起始地址、pBuffer:数据指针、NumToWrite:半字(16位)数 * 返 回 值: 无 * 说 明: */ void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) { u16 i; for(i=0;i<NumToWrite;i++) { HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,WriteAddr,pBuffer[i]); WriteAddr+=2;//地址增加2. } } /********************************************************************************** * 函数功能:从指定地址开始写入指定长度的数据 * 输入参数:WriteAddr:起始地址(此地址必须为2的倍数!!)、pBuffer:数据指针、NumToWrite:半字(16位)数(就是要写入的16位数据的个数.) * 返 回 值: 无 * 说 明: */ void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) { u32 secpos; //扇区地址 u16 secoff; //扇区内偏移地址(16位字计算) u16 secremain; //扇区内剩余地址(16位字计算) u16 i; u32 offaddr; //去掉0X08000000后的地址 if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址 HAL_FLASH_Unlock(); //解锁 offaddr=WriteAddr-STM32_FLASH_BASE; //实际偏移地址. secpos=offaddr/STM_SECTOR_SIZE; //扇区地址 0~64 for STM32F103C8T6 secoff=(offaddr%STM_SECTOR_SIZE)/2; //在扇区内的偏移(2个字节为基本单位.) secremain=STM_SECTOR_SIZE/2-secoff; //扇区剩余空间大小 if(NumToWrite<=secremain)secremain=NumToWrite;//不大于该扇区范围 while(1) { STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容 for(i=0;i<secremain;i++) //校验数据 { if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除 } if(i<secremain) //需要擦除 { Flash_PageErase(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE); //擦除这个扇区 FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成 CLEAR_BIT(FLASH->CR, FLASH_CR_PER); //清除CR寄存器的PER位,此操作应该在FLASH_PageErase()中完成! //但是HAL库里面并没有做,应该是HAL库bug! for(i=0;i<secremain;i++)//复制 { STMFLASH_BUF[i+secoff]=pBuffer[i]; } STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区 }else { FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成 STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间. } if(NumToWrite==secremain)break;//写入结束了 else//写入未结束 { secpos++; //扇区地址增1 secoff=0; //偏移位置为0 pBuffer+=secremain; //指针偏移 WriteAddr+=secremain*2; //写地址偏移(16位数据地址,需要*2) NumToWrite-=secremain; //字节(16位)数递减 if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完 else secremain=NumToWrite;//下一个扇区可以写完了 } }; HAL_FLASH_Lock(); //上锁 } #endif /********************************************************************************** * 函数功能:从指定地址开始读出指定长度的数据 * 输入参数:ReadAddr:起始地址、pBuffer:数据指针、NumToWrite:半字(16位)数 * 返 回 值: 无 * 说 明: */ void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead) { u16 i; for(i=0;i<NumToRead;i++) { pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节. ReadAddr+=2;//偏移2个字节. } } /********************************************************************************** * 函数功能:擦除扇区 * 输入参数:PageAddress:擦除扇区地址 * 返 回 值: 无 * 说 明: */ void Flash_PageErase(uint32_t PageAddress) { /* Clean the error context */ p_Flash.ErrorCode = HAL_FLASH_ERROR_NONE; #if defined(FLASH_BANK2_END) if(PageAddress > FLASH_BANK1_END) { /* Proceed to erase the page */ SET_BIT(FLASH->CR2, FLASH_CR2_PER); WRITE_REG(FLASH->AR2, PageAddress); SET_BIT(FLASH->CR2, FLASH_CR2_STRT); } else { #endif /* FLASH_BANK2_END */ /* Proceed to erase the page */ SET_BIT(FLASH->CR, FLASH_CR_PER); WRITE_REG(FLASH->AR, PageAddress); SET_BIT(FLASH->CR, FLASH_CR_STRT); #if defined(FLASH_BANK2_END) } #endif /* FLASH_BANK2_END */ }

main函数:

复制代码
1
2
3
4
5
6
7
8
while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ STMFLASH_Write(0x080010CC,(uint16_t*)Flash_WData,8); //第一个参数写入地址,需要flash地址正确 }

四、Flash读写查看

        当将程序成功编译,并且下载之后,按照点击下图中红框标志进入仿真调试界面。

         随后点击下图中的信息窗图标——Memory 1

         随后可以在右下角得到如下的Memory 1的窗口界面,可以查看各地址下的数据信息:

         其中,Address可以写入需要查询的地址:

         特别注意:由于不同的MCU其flash大小都可能不同,同时,flash中需要保存运行程序和部分已经初始化的数据段,所以写入的数据的地址最后偏后一点,避开程序段。

        具体查看可以使用notepad++打开该工程下的.map文件去查一下工程的flash存储情况,还是不理解的读者朋友可以评论区留言,笔者看到尽可能回答。

        根据程序所写的意义,可得如下结果:

        特别注意:不同的MCU其写入的数据长度也是不一样的。

        笔者这里举个例子:

        STM32F103C8T6如上图,可以看出其实是以16位数据进行存储的(uint16),而STM32H743则是以32位数据进行写入存储的,大家一定要注意!!!

代码开源: 链接:https://pan.baidu.com/s/1XW8EYOCMLMZynLw2d9LPjg 提取码:wpj3

最后

以上就是无限水池最近收集整理的关于基于STM32的FLASH读写实验含代码(HAL库) 一、FLASH简介二、CubexMX配置 三、代码四、Flash读写查看的全部内容,更多相关基于STM32内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(54)

评论列表共有 0 条评论

立即
投稿
返回
顶部