前言
在 STM32F103C8T6 微控制器中,Flash 地址空间是一个重要的概念,它用于存储程序代码、常量数据等。
一、FLASH介绍
(1)硬件层面
在STM32芯片内部有一个 FLASH 存储器,它主要用于存储代码,我们在电脑上编写好应用程序后,使用下载器把编译后的代码文件烧录到该内部 FLASH 中,由于 FLASH 存储器的内容在掉电后不会丢失,芯片重新上电复位后,内核可从内部 FLASH 中加载代码并运行。
STM32 的内部 FLASH 包含主存储器、系统存储器以及选项字节区域,它们的地址分布及大小见下表
(2)软件层面
指针操作:
在 C 语言编程中,通过指针来访问 Flash 地址空间的数据。例如,要读取 Flash 地址0x08000000处的数据,可以使用以下代码:
uint32_t data = *(volatile uint32_t*)0x08000000;
这里将0x08000000强制转换为uint32_t*类型的指针,然后解引用该指针来获取存储在该地址的 32 位数据。volatile关键字的作用是告诉编译器,该变量的值可能会在程序执行过程中被意外修改(例如,硬件直接修改了 Flash 中的数据),因此编译器不应优化对该变量的访问,每次都要从实际的内存地址读取数据。
(3)缓存与预取
缓存机制:
为了提高 Flash 数据的读取速度,STM32F103C8T6 可能会采用缓存机制。缓存是一种高速的存储器,它可以存储最近访问过的 Flash 数据。当 CPU 再次请求相同的数据时,首先会检查缓存中是否已经存在该数据。如果存在,则直接从缓存中读取,大大加快了数据的获取速度。
预取机制:
除了缓存,STM32F103C8T6 还可能采用预取机制。预取机制会在 CPU 实际需要数据之前,提前将可能需要的数据从 Flash 中读取到缓存中。这样,当 CPU 需要数据时,数据已经在缓存中,减少了等待时间。
(4)读取流程
CPU 将目标 Flash 地址发送到地址总线上。
Flash 控制器通过地址译码确定要读取的存储单元。
Flash 控制器从存储单元中读取数据,并通过数据总线将其传输回 CPU。
在软件层面,通过指针操作和指令执行,将读取到的数据存储在变量中供程序使用。
缓存和预取机制可以优化读取过程,提高数据读取的速度和效率。
二、利用Cube MX创建工程项目
(1)芯片选型
(2)RCC配置
(3)SYS配置
(4)GPIO口定义
(5)时钟树配置
(6)调整堆栈大小
以上就是利用Cube创建本次工程的步骤。
三、工程代码修改
程序下载地址:
flash: STM32读取内部flash操作
https://gitee.com/wocbsuds/flash
(1)添加flash.c和flash.h文件
将flash.c添加到src文件中
将flash.h添加到inc文件中
最后别忘了在文件中添加该路径。
(2)main.c函数修改
这里我直接给完整的main函数代码
#include "main.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "flash.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t FlashWBuff [255];
uint8_t FlashRBuff [255];
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
uint8_t i;
uint8_t FlashTest[] = "Hello This is senlinzhizi Flash Test DEMO";
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
FlashWriteBuff( DEVICE_INFO_ADDRESS, FlashTest,sizeof(FlashTest) ); // дÈëÊý¾Ýµ½Flash
for(i=0;i<255;i++)
FlashWBuff = i;
FlashWriteBuff( DEVICE_INFO_ADDRESS + sizeof(FlashTest), FlashWBuff,255 ); // дÈëÊý¾Ýµ½Flash
FlashReadBuff( DEVICE_INFO_ADDRESS + sizeof(FlashTest),FlashRBuff,255 ); // ´ÓFlashÖжÁÈ¡Êý
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
(3)编译
点击debug进行程序测试(记得把程序烧到板子上)
程序运行到下图:
打开观察窗口
输入0x0800C000地址按下回车键,此时你可以看到你自己的板子上的地址内容。
下一步就开始添加自己的内容
首先添加变量观察窗口
然后再开启开启变量自动更新:
此时可以再次观察watch1变量窗口
最后将我们自己的内容添加进去
显示内容
(4)成果展示
四、总结
在此次利用STM32读取flash内部操作实验中,我收获了许多宝贵的知识与技能。从原理机制上,我深入理解到 STM32 Flash 在硬件层面基于页和扇区的存储结构,以及软件层面通过寄存器配置和函数调用来实现各种操作的方式,明白了指针操作如何将抽象地址转化为实际数据。关键操作流程方面,我熟练掌握了读取、擦除和添加这三个核心操作。读取操作相对简单直接,而擦除和添加操作则需要严格遵循解锁、操作、锁定等步骤,并且要格外留意地址对齐和扇区状态等细节。同时,实验过程中遇到的种种问题极大地锻炼了我的问题解决能力,从擦除不完全到数据读取错误,每一次排查和解决问题的过程,都让我对细节的把控更加敏锐。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/gqiaos524/article/details/144760984
|