STM32 教程 – FLASH的读写操作
前言
在 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] = 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 在硬件层面基于页和扇区的存储结构,以及软件层面通过寄存器配置和函数调用来实现各种操作的方式,明白了指针操作如何将抽象地址转化为实际数据。关键操作流程方面,我熟练掌握了读取、擦除和添加这三个核心操作。读取操作相对简单直接,而擦除和添加操作则需要严格遵循解锁、操作、锁定等步骤,并且要格外留意地址对齐和扇区状态等细节。同时,实验过程中遇到的种种问题极大地锻炼了我的问题解决能力,从擦除不完全到数据读取错误,每一次排查和解决问题的过程,都让我对细节的把控更加敏锐。
作者:森林里的林