STM32CubeMX系列教程9:FLASH读写及HardFault_Handler报错

摘要

  这章记录STM32F103C8T6的Flash进行程序内读写操作。程序源码基于STM32CubeMX系列教程8:配置工程模板(串口+不定长数据收发+DMA+IDLE中断+软中断)工程,在工程中添加Flash读写的驱动文件。通过调用调试好的读写API完成Flash的读写操作。这里仅作基础操作的实现,具体的细节需要根据实际情况增加。
  Flash的操作流程一般为 解锁->擦除->写入->上锁 四个步骤。如果出现数据无法写入,可检查在写入之前是否进行了擦除操作,注意:这里的擦除最小单位为页擦除,所以会将数据整页擦除,如果需要对数据进行增量式写入,可将每页数据先读取进内存,擦除整页后,在进行写入。

查阅相关数据

  通过查阅数据手册,可获取单片机的Flash空间的分布情况,每种型号的单片机Flash空间不同,分布也不同,因此各种单片机的Flash读写操作的代码不通用,所以需要根据单片机型号修改相关的函数功能,但大致流畅相同,因此修改起来也不麻烦。
在这里插入图片描述
  STM32F103C8T6单片机的flash空间通过页进行操作,Flash空间有64Kb,属于小容量单片机,因此Flash空间的每页大小为1K字节,中容量与大容量的单片机每页大小为2K字节,各型号单片机需要查阅相关手册。

数据读取

  这里先完成相对简单的Flash空间的读取操作。
  创建FlashRW.h头文件与FlashRW.c文件,在头文件中定义一些宏。

//第一个页的基地址
#define ADDR_PAGE_0 ((uint32_t)0x08000000)

//FLASH_PAGE_SIZE Flash空间的页大小,已经在stm32f1xx_hal_flash_ex.h中定义
//小容量与中容量每页有1K(0x400)字节,大容量每页2K(0x800)字节。
//ADDR_PAGE(n) 计算每个页的地址
#define ADDR_PAGE(n) (ADDR_PAGE_0 + (uint32_t)(n) * FLASH_PAGE_SIZE)

//允许作为EEPROM的Flash空间的基地址
#define STM32_EEPRAM_FLASH_BASE  ADDR_PAGE(32)

//FLASH等待超时时间
#define FLASH_WAITETIME  50000       

  在FlashRW.c文件中创建数据读取函数。

void STMFLASH_Read(uint32_t ReadAddr,uint32_t *pBuffer,uint32_t NumToRead)   	
{
	uint32_t i;
	for(i=0;i<NumToRead;i++)
	{
		pBuffer[i]= *(__IO uint32_t*)ReadAddr; //读取4个字节.
		ReadAddr+=4;//偏移4个字节.	
	}
}

  在主函数中,调用这个函数,输入地址参数、保存数据的指针、读取的数据的个数。这里注意,地址必须要四字节对齐。
在这里插入图片描述

数据擦除与解锁

  Flash的操作流程一般为 解锁->擦除->写入->上锁 四个步骤。
  在FlashRW.c文件中创建Flash擦除函数。

HAL_StatusTypeDef STMFLASH_Erase(uint32_t PageAddr,uint32_t Num)	
{
		HAL_StatusTypeDef status;
		FLASH_EraseInitTypeDef FlashEraseInit;
		uint32_t PageError=0;
	
		HAL_FLASH_Unlock();             //解锁	
	
		FlashEraseInit.TypeErase=FLASH_TYPEERASE_PAGES;
		FlashEraseInit.PageAddress=PageAddr;
		FlashEraseInit.NbPages=Num;
		
		status = HAL_FLASHEx_Erase(&FlashEraseInit,&PageError);
		if(status !=HAL_OK)
		{
			HAL_FLASH_Lock();           //上锁
			return status;//发生错误了	
		}
		FLASH_WaitForLastOperation(FLASH_WAITETIME);                //等待上次操作完成
		HAL_FLASH_Lock();           //上锁
		return status;
}

这里使用了一个FLASH_EraseInitTypeDef结构体,这个结构体是Hal库提供的Flash擦除操作的一个参数类型,主要有四个参数,分别为

参数说明
TypeErase擦除类型 这里选择页擦除模式。
Banks因为擦除类型选择的是页擦除模式,因此这个参数不需要设置。
PageAddress页地址,需要擦除的页的地址。
NbPages需要擦除的页的数量,大于0,小于最大页数。

HAL_FLASH_Unlock()HAL_FLASH_Lock()函数为Hal库提供的Flash的解锁与上锁API,在擦除与写入Flash时,必须要先解锁Flash,擦除与写入完毕后,需要再次上锁。

数据写入

  在FlashRW.c文件中创建Flash写入函数。函数中对写入地址进行了简单校验,如果地址非法,则函数跳出。

void STMFLASH_Write(uint32_t WriteAddr,uint32_t *pBuffer,uint32_t NumToWrite)	
{
	uint32_t endaddr=0;	
	
	if(WriteAddr<STM32_EEPRAM_FLASH_BASE||WriteAddr%4) return;	//非法地址
    
	HAL_FLASH_Unlock();             //解锁	
	
	endaddr=WriteAddr+NumToWrite*4;	//写入的结束地址
    
	 while(WriteAddr<endaddr)//写数据
	 {
		if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,WriteAddr,*pBuffer)!=HAL_OK)//写入数据
		{
			break;	//写入异常
		}
		WriteAddr+=4;
		pBuffer++;
	}  
	HAL_FLASH_Lock();           //上锁
}

测试程序

  通过对一页flash进行读取,擦除,写入,读取,擦除,在写入,可看出,上面写的三个函数能够完成对单片机flash的读写操作。
  完整代码前往Gitee仓库获取 STM32F103C8T6_FlashRW

	STMFLASH_Read(ADDR_PAGE(32),r_buf,256);	//从第32页读取1KB数据,这里数据类型为32位数据,因此数量为256个。
	printf("\r\n****\r\n");
	for(i=0; i<16; i++)
	{
		for(j=0; j<16; j++)
			printf("0x%04x ",r_buf[i*16+j]);
		printf("\r\n");
	}
	printf("\r\n****\r\n");
	

	if(STMFLASH_Erase(ADDR_PAGE(32),1) != HAL_OK)	//擦除第32页的数据
	{
		printf("ERASE ERR\r\n");
	}
	
	for(i=0;i<256;i++)
	{
		w_buf[i] = i;
	}
	
	STMFLASH_Write(ADDR_PAGE(32),w_buf,256);		//写入1KB字节新的数据 这里数据类型为32位数据,因此数量为256个。
	STMFLASH_Read(ADDR_PAGE(32),r_buf,256);			//从第32页读取1KB数据 这里数据类型为32位数据,因此数量为256个。
	printf("\r\n****\r\n");
	for(i=0; i<16; i++)
	{
		for(j=0; j<16; j++)
			printf("0x%04x ",r_buf[i*16+j]);
		printf("\r\n");
	}
	printf("\r\n****\r\n");

	if(STMFLASH_Erase(ADDR_PAGE(32),1) != HAL_OK){	//擦除第32页的数据
		printf("ERASE ERR\r\n");
	}
	
	for(i=0;i<256;i++)
	{
		w_buf[i] = i+0xff00;
	}
	
	STMFLASH_Write(ADDR_PAGE(32),w_buf,256);			//写入1KB字节新的数据 这里数据类型为32位数据,因此数量为256个。
	STMFLASH_Read(ADDR_PAGE(32),r_buf,256);				//从第32页读取1KB数据 这里数据类型为32位数据,因此数量为256个。
	printf("\r\n****\r\n");
	for(i=0; i<16; i++)
	{
		for(j=0; j<16; j++)
			printf("0x%04x ",r_buf[i*16+j]);
		printf("\r\n");
	}
	printf("\r\n****\r\n");

	if(STMFLASH_Erase(ADDR_PAGE(32),1) != HAL_OK)	//擦除第32页的数据
	{			
		printf("ERASE ERR\r\n");
	}

在这里插入图片描述

关于HardFault_Handler报错

  程序调试过程中,发现在读取flash时
总会死机,排查后发现是进入了HardFault_Handler中断的wiile中,经过排查后发现是由于ADDR_PAGE_0的基地址多写了一个0导致。由于地址错误,指针指向了一个非Flash空间的地址,因此进入HardFault_Handler中断。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萌新程序猿~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值