一.Flash地址空间的数据读取
1、Flash地址空间的基本原理
不同型号的 STM32,其 FLASH 容量也有所不同,最小的只有 16K 字节,最大的则达到了1024K 字节。市面上 STM32F1 开发板使用的芯片是 STM32F103系列,其 FLASH 容量一般为 512K 字节,属于大容量芯片; Flash的编程原理都是只能将1写为0,而不能将0写为1,所以在进行Flash编程前,必须将对应的块擦除,即将该块的每一位都变为1,块内所有字节变为0xFF。
STM32F1 的闪存(Flash)模块:主存储器、信息块、闪存存储器接口寄存器,在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。
2、使用工具
STM32F103C8T6、STlink、Keil、STM32CubeMX
3.工程配置
3.1具体要求
lash地址空间的数据读取。stm32f103c8t6只有20KB 内存(RAM)供程序代码和数组变量存放,因此,针对内部Flash的总计64KB存储空间(地址从0x08000000开始),运行一次写入8KB数据,总计复位运行代码8次,将64KB数据写入Flash,并验证写入数据的正确性和读写速率。此外,继续往后续地址写入数据,检验stm32f103c8t6 实际FlashROM是否超过64KB。
3.2CubeMx建立工程
首先打开CubeMx,点击左上角的File文件选择“New Project”,创建工程,在弹出的界面中,下拉列表输入芯片名称“STM32F103C8T6”,右下角选中后,点击“Start Project,
然后配置“.sys”,下拉debug模式 ,选中"Serial Wire"Timebase Source选择“SysTick”,
接着配置“.RCC”,HSE选中"Crystal/Ceramic Resonator"项,LSE选择Disable,
系统时钟树配置,
配置GPIO口,对应外设c8t6本身设计好的PC13 LED灯,
中断配置(保持默认),
设置堆栈大小为4K或2K,
接着设置Project Manager,
最后设置“Code Generator”,勾选图示内容,再点击右上角"GENERATE CODE",生成文件,再用keil打开工程即可。
3.3添加相关代码
先在工程文件中创建lash.c和flash.h,然后在keil中同样创立两个新文件,flash.c和flash.h,
接着添加flash.c和flash.h的相关代码
flash.c:
/*
* flash.c
*
* Created: 2018-01-29
* Author: zhanglifu
*/
/*********************************************************************/
// Í·Îļþ
/*********************************************************************/
#include "flash.h"
// ²»¼ì²éµÄдÈë
// WriteAddr:ÆðʼµØÖ·
// pBuffer: Êý¾ÝÖ¸Õë
// NumToWrite:×Ö½ÚÊýÊý
void FlashWriteNoCheck( uint32_t WriteAddr,uint8_t *pBuffer,uint16_t NumToWrite )
{
uint16_t i;
for( i=0; i<NumToWrite; i+=4 )
{
while( HAL_OK != HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, WriteAddr+i,*(uint32_t *)(pBuffer+i) ) );
}
}
extern void FLASH_PageErase(uint32_t PageAddress);
void FlashWriteBuff( const uint32_t destination_address, uint8_t *const buffer,uint32_t length )
{
uint16_t i;
uint8_t FlashBuff[FMC_SECTOR_SIZE];
uint32_t StartAddress = destination_address - destination_address%FMC_SECTOR_SIZE;
uint16_t Offset = destination_address - StartAddress;
uint8_t *pBuf = buffer;
uint32_t remaindNum = length;
HAL_StatusTypeDef status = HAL_ERROR;
// µØÖ·¼ì²é
if( (destination_address < FMC_FLASH_BASE) || ( destination_address + length >= FMC_FLASH_END) || (length <= 0) )
return;
HAL_FLASH_Unlock(); // ½âËø
do
{
// ¶Á³öÒ»Ò³Êý¾Ý
for(i=0; i < FMC_SECTOR_SIZE; i += 4 )
*(uint32_t *)(FlashBuff+i) = *(uint32_t *)(StartAddress+i);
// ÐÞ¸ÄÒª¸ÄÈëµÄÊý¾Ý
for ( i=0; (i+Offset)<FMC_SECTOR_SIZE && i< remaindNum; i++ )
*(FlashBuff+Offset+i) = *(pBuf+i);
// ²Á³ýÒ»ROWÊý¾Ý
FLASH_PageErase( StartAddress );
// HAL¿â FLASH_PageEraseÓÐBUFF,Òª¼ÓÉÏÏÂÃæÈýÐдúÂë
while( status != HAL_OK )
status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
CLEAR_BIT(FLASH->CR, FLASH_CR_PER);
// дÈëÊý¾Ý
FlashWriteNoCheck( StartAddress,FlashBuff,FMC_SECTOR_SIZE );
// ΪÏÂÒ»Ò³×ö×¼±¸
StartAddress += FMC_SECTOR_SIZE;
remaindNum -= i;
pBuf += i;
Offset = 0;
} while( remaindNum > 0 );
HAL_FLASH_Lock(); // ÉÏËø
}
// ´ÓFLASHÖжÁÖ¸¶¨³¤¶ÈÊý¾Ý
void FlashReadBuff(const uint32_t source_address,uint8_t *const buffer,uint16_t length)
{
uint16_t i;
uint8_t Offset = 0;
uint32_t StartAddress = source_address;
uint16_t data;
// µØÖ·¼ì²â
if( source_address + length > FMC_FLASH_END ) return;
// Èç¹ûûÓжÔ16Æë
if( source_address & 1 )
{
Offset = 1;
StartAddress = source_address-1;
}
// flashµÄ²Ù×÷ÒªÇó16¶ÔÆë ×îС¶Áд²Ù×÷16¸ö±ÈÌØ
if ( Offset )
{
data = *(uint16_t *)(StartAddress);
buffer[0] = data >> 8;
StartAddress += 2;
}
for ( i = 0; i < (length-Offset); i += 2)
{
data = *(uint16_t *)(StartAddress+i);
buffer[i+Offset] = (data & 0xFF);
if ( (i+Offset) < (length - 1) )
buffer[i + Offset + 1] = (data >> 8);
}
}
flash.h:
/*
* flash.h
*
* Created: 2016-04-22
* Author: zhanglifu
*/
#ifndef _flash_H_
#define _flash_H_
#include "stm32f1xx_hal.h"
/*********************************************************************/
// ±äÁ¿¶¨Òå
/*********************************************************************/
//-- ÓÃ;»®·Ö
// 0x0800FC00-0x0800FFFF -- ʹÓÃ×îºó4k ¸ö×Ö½ÚÓÃÀ´´æ·Åµç»úÐÅÏ¢
#define FMC_FLASH_BASE 0x08000000 // FLASHµÄÆðʼµØÖ·
#define APP_MAX_SIZE 0x00010000 //
#define FMC_FLASH_END 0x08010000 // FLASHµÄ½áÊøµØÖ· 256
#define DEVICE_INFO_ADDRESS 0x0800C000 //£¨STM32_FLASH_END - DEVICE_INFO_SIZE£© // É豸ÐÅÏ¢ÆðʼµØÖ·
#define DEVICE_LOG_ADDRESS 0x0800E000 //£¨STM32_FLASH_END - 2*DEVICE_INFO_SIZE£© // É豸ÈÕÖ¾ÆðʼµØÖ·
#define FMC_FLASH_SIZE 64 // ¶¨ÒåFlash´óС£¬µ¥Î»KB
#if FMC_FLASH_SIZE < 256
#define FMC_SECTOR_SIZE 1024 // ×Ö½Ú
#define MOD_SECTOR_SIZE 0X3FF
#define PAGE_COUNT_BY16 512
#else
#define FMC_SECTOR_SIZE 2048
#define MOD_SECTOR_SIZE 0X7FF
#define PAGE_COUNT_BY16 1024
#endif
/*********************************************************************/
// º¯ÊýÉùÃ÷
/*********************************************************************/
void FlashWriteBuff( const uint32_t destination_address, uint8_t *const buffer,uint32_t length );
void FlashReadBuff(const uint32_t source_address,uint8_t *const buffer,uint16_t length);
#endif
在main.c文件中添加部分代码,
编译无误,使用STlink下载即可。
dd2f34393d84b4.png)
4.程序调试
点击View->memory windows->memory 1打开内存观察窗口,并在地址栏中输入:0x800c000,观察将要修改的flash区间区容。
View->Watch windows->Watch 1打开一个变量观察窗口,将变量FlashWBuff 和 FlashRBuff加入到 Watch 1 观察窗口,View->Periodic Windows Update开启变量自动更新点击全速运行,同时在Memory 1窗口中可以看到在FLASH地址0x0800C000区成功写入对应内容,
二.总结
本次实验聚焦于 stm32f103c8t6 芯片的 Flash 地址空间数据读取相关操作。鉴于该芯片仅有 20KB 的 RAM 用于存放程序代码与数组变量,于是把目光投向了内部 Flash 的 64KB 存储空间(起始地址为 0x08000000)。实验过程采取分批次写入的策略,每次写入 8KB 数据,历经 8 次复位运行代码,达成 64KB 数据完整写入 Flash 的目标。在此之后,着重验证写入数据的准确性以及读写速率,以此衡量数据存储的可靠性与读写性能。不仅如此,实验还进一步尝试往后续地址写入数据,意图探究该型号芯片实际 FlashROM 容量是否突破标称的 64KB ,全面挖掘芯片的存储潜力。
心得
整个实验下来,感受颇为深刻。一开始面对有限的 RAM 和 Flash 存储空间规划,着实费了一番脑筋,这使我对芯片的存储架构有了更前置性的思考,意识到资源受限下合理分配的重要性。在执行 8KB 数据多次写入 Flash 的操作时,每一次复位、写入的循环,都像是在和芯片的底层逻辑对话,我开始理解复杂的写入流程背后是为了确保数据稳定存储。
验证数据正确性环节,看着读出的数据与写入的完美匹配,满心都是成就感,也更清楚严谨测试对于保障数据完整性的意义。而探究读写速率,让我察觉到性能优化的空间,思考如何在现有硬件条件下减少等待时间、提升效率。尝试向后续地址写入数据充满未知与期待,虽然最终结果可能未超出 64KB,但这个探索过程加深了我对芯片物理极限的认知,激发了钻研硬件底层的热情,往后面对类似存储相关项目,相信这次积累的经验能让我更加游刃有余。