当前位置:   article > 正文

STM32F10X、HK32F030单片机的Flash学习_单片机的内部flash 只能整页擦除?

单片机的内部flash 只能整页擦除?

STM32 | 使用STM32内部Flash额外的空间来存储数据(Flash模仿EEPROM)

单片机内部是NORflash,擦除只能整页擦除,当然写可以按“字”写。这款芯片flash一共32k,分成32页,每页1k的大小;程序编译完一共要占用11k的空间,还有后面的21kflash是空闲的。

STM32 芯片内部的 FLASH 存储器,主要用于存储我们代码。如果内部FLASH存储完我们的代码还有剩余的空间,那么这些剩余的空间我们就可以利用起来,存储一些需要掉电保存的数据。

本文以STM32103ZET6为例。STM32103ZET6属于大容量产品,其闪存模块组织如下:
在这里插入图片描述

其主存储器大小为512KB,分为256页,每页大小都为2KB。我们的程序一般默认烧写到第0页的起始地址(0x08000000)处。当BOOT0引脚和``BOOT1引脚都接GND时,就是从这个地址开始运行代码的。这个地址在keil中可以看到:
在这里插入图片描述

假如我们要下载的程序大小为4.05KB,则第0、1、2页用于保存我们的程序,我们需要掉电保存的数据只能保存在第3页至第255页这一部分空间内。我们最终要下载的程序大小可在工程对应的.map文件中看到。.map文件可以双击工程的Target的名字快速打开,如:
在这里插入图片描述

下面对STM32的内部FLASH进行简单的读写测试:

STM32的内部FLASH读写测试
过程图如下(省略异常情况,只考虑成功的情况):
在这里插入图片描述

示例代码:

本例的关键代码如下:

#define MAIN_CONFIG
#include "config.h"

/* STM32F103ZET6有256页,每一页的大小都为2KB */
#define ADDR_FLASH_PAGE_255     ((uint32_t)0x0807F800) /* Page255 2KB */

/* FLASH读写测试结果 */
#define  TEST_ERROR	   -1	/* 错误(擦除、写入错误) */
#define  TEST_SUCCESS  0	/* 成功 */
#define  TEST_FAILED   1	/* 失败 */

/* Flash读写测试buf */
#define BufferSize 6
uint16_t usFlashWriteBuf[BufferSize] = {0x0101,0x0202,0x0303,0x0404,0x0505,0x0606};
uint16_t usFlashReadBuf[BufferSize] = {0};

/* 供本文件调用的函数声明 */
static int FlashReadWriteTest(void);					   

/*******************************************************************************************************
** 函数: main
**------------------------------------------------------------------------------------------------------
** 参数: void
** 返回: 无
** 说明: 主函数
********************************************************************************************************/
int main(void)
{    
	/* 上电初始化 */
	SysInit();
	
	/* 内部Flash读写测试 */
	if (TEST_SUCCESS == FlashReadWriteTest())
	{
		printf("Flash test success!\n");
	}
	else
	{
		printf("Flash test failed!\n");
	}

	while (1)
	{}
}

/*******************************************************************************************************
** 函数: FlashReadWriteTest, 内部Flash读写测试函数
**------------------------------------------------------------------------------------------------------
** 参数: void
** 返回: TEST_ERROR:错误(擦除、写入错误)  TEST_SUCCESS:成功   TEST_FAILED:失败
** 说明: 无
********************************************************************************************************/
static int FlashReadWriteTest(void)
{
	uint32_t ucStartAddr;
	
	/* 解锁 */
	FLASH_Unlock();	
	
	/* 擦除操作 */
	ucStartAddr = ADDR_FLASH_PAGE_255;
	if (FLASH_COMPLETE != FLASH_ErasePage(ucStartAddr))
	{
		printf("Erase Error!\n");
		return TEST_ERROR;
	}
	else
	{
		ucStartAddr = ADDR_FLASH_PAGE_255;
		printf("擦除成功,此时FLASH中值为:\n");
		for (int i = 0; i < BufferSize; i++)
		{
			usFlashReadBuf[i] = *(uint32_t*)ucStartAddr;
			printf("ucFlashReadBuf[%d] = 0x%.4x\n", i, usFlashReadBuf[i]);
			ucStartAddr += 2;
		}
	}
	
	/* 写入操作 */
	ucStartAddr = ADDR_FLASH_PAGE_255;
	printf("\n往FLASH中写入的数据为:\n");
	for (int i = 0; i < BufferSize; i++)
	{
		if (FLASH_COMPLETE != FLASH_ProgramHalfWord(ucStartAddr, usFlashWriteBuf[i]))
		{
			printf("Write Error!\n");
			return TEST_ERROR;
		}
		printf("ucFlashWriteBuf[%d] = 0x%.4x\n", i, usFlashWriteBuf[i]);
		ucStartAddr += 2;
	}
	
	/* 上锁 */
	FLASH_Lock();
	
	/* 读取操作 */
	ucStartAddr = ADDR_FLASH_PAGE_255;
	printf("\n从FLASH中读出的数据为:\n");
	for (int i = 0; i < BufferSize; i++)
	{
		usFlashReadBuf[i] = *(__IO uint16_t*)ucStartAddr;
		printf("ucFlashReadBuf[%d] = 0x%.4x\n", i, usFlashReadBuf[i]);
		ucStartAddr += 2;
	}
	
	/* 读出的数据与写入的数据做比较 */
	for (int i = 0; i < BufferSize; i++)
	{
		if (usFlashReadBuf[i] != usFlashWriteBuf[i])
		{
			return TEST_FAILED;
		}
	}
	
	return TEST_SUCCESS;
}


/*********************************************************************************************************
**                            End Of File
********************************************************************************************************/
  • 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

(1)进行解锁操作

STM32 的闪存编程是由内嵌的闪存编程/擦除控制器(FPEC)管理 ,这个模块包含的寄存器如下:
在这里插入图片描述
STM32 复位后, FPEC 模块是被保护的, 不能写入 FLASH_CR 寄存器; 通过写入特定的序列到 FLASH_KEYR 寄存器可以打开 FPEC 模块(即写入 KEY1 和KEY2) , 只有在写保护被解除后, 我们才能操作相关寄存器。 固件库中的函数为:

void FLASH_Unlock(void);

(2)擦除将要写的页
STM32 的 FLASH 在编程的时候,也必须要求其写入地址的 FLASH 是被擦除了的(也就是其值必须是 0XFFFF),否则无法写入,在 FLASH_SR 寄存器的 PGERR 位将得到一个警告。 STM32 的闪存擦除分为两种:页擦除和整片擦除。 也就是其最小擦除单位为1页,尽管你只需往某页里写10个字节数据或者更少的数据,你也必须先擦除该页(2*1024个字节)。我们这里使用按页擦除,固件库中按页擦除的函数为:

FLASH_Status FLASH_ErasePage(uint32_t Page_Address);
  • 1

其返回值为枚举:

typedef enum
{ 
  FLASH_BUSY = 1,	/* 忙 */
  FLASH_ERROR_PG,   /* 编程错误 */
  FLASH_ERROR_WRP,  /* 写保护错误 */
  FLASH_COMPLETE,   /* 操作完成 */
  FLASH_TIMEOUT     /* 操作超时 */
}FLASH_Status;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

(3)往上一步擦写成功的页写入数据
STM32 闪存的编程每次必须写入16 位。虽然固件库中有如下三个写操作的函数:

FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);
  • 1
  • 2
  • 3

分别为按字(32bit)写入、按半字(16bit)写入、按字节(8bit)写入函数。32 位字节写入实际上是写入的两次 16 位数据,写完第一次后地址+2,这与我们前面讲解的 STM32 闪存的编程每次必须写入 16 位并不矛盾。 写入 8
位实际也是占用的两个地址了,跟写入 16 位基本上没啥区别。

(4)写入操作完成后进行上锁操作

对FLASH进行写操作完成后要进行上锁操作,对应的固件库中函数为:

void FLASH_Lock(void);
  • 1

(5)读出数据

固件库中并没有与读操作的函数。读操作其实就是读取FLASH某个地址的数据。

(6)对比写入的数据与读出的数据是否相等

最后对比我们写入的数据与读出的数据是否完全一致,若一致则表明读写测试成功,否则失败。

程序执行结果:
在这里插入图片描述

FLASH写入

FLASH的写入地址必须是偶数(FLASH机制决定的FLASH写入的时候只能是偶数地址写入,必须写入半字或字,也就是2个字节或是4字节的内容)
在这里插入图片描述
在这里插入图片描述

两个重要的例程代码

 STMFLASH_Write(0X0800f000,(uint16_t*)TEXT_Buffer,20);//写入数据	  应该加一个延时,因为写数据需要时间	
STMFLASH_Read(0X0800f000,(uint16_t*)datatemp,20);  //读取数据,读取的是 datatemp   0x0800f000 ,0x0800000起始地址  
  • 1
  • 2

可见,读出的数据与写入的数据一致,表明读写测试成功。

参考链接: https://blog.csdn.net/zhengnianli/article/details/102555964?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-8.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-8.control.

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/481376
推荐阅读
相关标签
  

闽ICP备14008679号