赞
踩
参考博主-STM32系列(HAL库)——内部FLASH读写实验
软件:CubeMX、SSCOM(串口调试助手)
硬件:SMT32F103C8T6
(1)STM32根据闪存(Flash)容量的大小,将Flash分为每页1K字节 或 每页2K字节。超过256K容量的每页为2K字节。对于本次使用的SMT32F103C8T6,其容量为64K,则内部分为每页1K字节
(2)SMT32的Flash起始地址为0X0800 0000 。本次使用的STM32F103C8T6的FLASH范围是0X08000000-0X0800FFFF。示意图如下
(3)STM32运行代码从地址0X0800 0000开始,所以我们使用的Flash空间开始地址应该往后偏移,不然就会将程序部分覆盖掉。
(4)Flash的写操作,需要擦除一整页后再重新写入,不能对特定处进行修改,写的时候可以分多次写入
(5)擦写次数较多数据的不建议使用内部Flash进行存储,手册中给的数据是擦写1W次
3.STM32配置及主要代码
CubeMX简单配置一个串口1即可,usart.c添加打印函数,即
#include "stdio.h"
/*********************************************************
*
*重定义 fputc 函数
*
*********************************************************/
int fputc(int ch,FILE *f)
{
HAL_UART_Transmit (&huart1 ,(uint8_t *)&ch,1,HAL_MAX_DELAY );
return ch;
}
main.c
创建一个结构体FuyuState,结构体下的变量随便赋值后经过FLASH写入后,读出出该地址的值赋值给另外一个数值datatemp,再通过打印输出查看是否正确
/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * <h2><center>© Copyright (c) 2022 STMicroelectronics. * All rights reserved.</center></h2> * * This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "stmflash.h" #include "stdio.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ const u8 TEXT_Buffer_1[]={"STM32 FLASH TEST_1"};//要写入到STM32 FLASH的字符串数组 const u8 TEXT_Buffer_2[]={"STM32 FLASH TEST_2"};//要写入到STM32 FLASH的字符串数组 #define SIZE sizeof(TEXT_Buffer_1) //数组长度 /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ typedef __packed struct { u16 Set_Temperature; //设置温度 = 保持温度 u16 SetTime; //设置时间 u16 CurrentTemp[3]; //当前温度 u16 ProtectTemp[3]; //保护温度 u16 Speed; u32 RemainTime; u16 Current; u16 PWM_Time; u8 ActiveState; } FuyuState,*pFuyuState; FuyuState g_FuyuState= {0}; /* 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 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ u16 datatemp[20]; //Flash读取缓存数组 u16 *p=datatemp; //数组指针 /* USER CODE END 1 */ u8 i; /* 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 */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ printf("串口打印正常!\r\n"); //结构体变量赋值 g_FuyuState.Set_Temperature = 50; //设置温度 = 保持温度 g_FuyuState.SetTime = 2 ; g_FuyuState.CurrentTemp[0] = 3 ; g_FuyuState.CurrentTemp[1] = 4 ; g_FuyuState.CurrentTemp[2] = 5 ; g_FuyuState.ProtectTemp[0] = 6 ; g_FuyuState.ProtectTemp[1] = 7 ; g_FuyuState.ProtectTemp[2] = 8 ; g_FuyuState.Speed = 5; g_FuyuState.RemainTime = 6; g_FuyuState.Current = 7; g_FuyuState.PWM_Time = 8; g_FuyuState.ActiveState = 9; STMFLASH_Write(FLASH_SAVE_ADDR,(uint16_t*)&g_FuyuState,sizeof(g_FuyuState));//第一次写 HAL_Delay(50); STMFLASH_Read(FLASH_SAVE_ADDR,(uint16_t*)datatemp,sizeof(g_FuyuState)); for(i=0;i<sizeof(g_FuyuState);i++) { printf("datatemp%d:%d\r\n",i,datatemp[i]); } //STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer_1,SIZE);//第一次写读 //STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE); //printf("%s\r\n",p); //STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer_2,SIZE);//第二次写读 //STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE); //printf("%s\r\n",p); /* 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 RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ 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 buses 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 */ __disable_irq(); while (1) { } /* 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, ex: 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****/
stmflash.h
#ifndef __STMFLASH_H__ #define __STMFLASH_H__ #include "main.h" //=========================数据类型宏定义 #define u8 uint8_t #define u16 uint16_t #define u32 uint32_t #define __IO volatile typedef __IO uint16_t vu16; //=========================用户根据自己的需要设置 #define STM32_FLASH_SIZE 64 //所选STM32的FLASH容量大小(单位为K) #if STM32_FLASH_SIZE < 256 //设置扇区大小 #define STM_SECTOR_SIZE 1024 //1K字节 #else #define STM_SECTOR_SIZE 2048 //2K字节 #endif #define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址 #define FLASH_SAVE_ADDR STM32_FLASH_BASE+STM_SECTOR_SIZE*62 //写Flash的地址,这里从倒数第二页开始 #define STM32_FLASH_WREN 1 //使能FLASH写入(0,不是能;1,使能) #define FLASH_WAITETIME 50000 //FLASH等待超时时间 u8 STMFLASH_GetStatus(void); //获得状态 u8 STMFLASH_WaitDone(u16 time); //等待操作结束 u8 STMFLASH_ErasePage(u32 paddr); //擦除页 u8 STMFLASH_WriteHalfWord(u32 faddr, u16 dat);//写入半字 u16 STMFLASH_ReadHalfWord(u32 faddr); //读出半字 void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len); //指定地址开始写入指定长度的数据 u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len); //指定地址开始读取指定长度数据 void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite); //从指定地址开始写入指定长度的数据 void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead); //从指定地址开始读出指定长度的数据 void Flash_PageErase(uint32_t PageAddress); //扇区擦除 #endif
stmflash.c
#include "stmflash.h" FLASH_ProcessTypeDef p_Flash; u16 STMFLASH_BUF[STM_SECTOR_SIZE/2]; //缓存数组 /********************************************************************************** * 函数功能: 读取指定地址的半字(16位数据) * 输入参数: faddr:读地址 * 返 回 值: 对应数据 * 说 明: */ u16 STMFLASH_ReadHalfWord(u32 faddr) { return *(vu16*)faddr; } #if STM32_FLASH_WREN //如果使能了写 /********************************************************************************** * 函数功能:不检查的写入 * 输入参数: WriteAddr:起始地址、pBuffer:数据指针、NumToWrite:半字(16位)数 * 返 回 值: 无 * 说 明: */ void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) { u16 i; for(i=0;i<NumToWrite;i++) { HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,WriteAddr,pBuffer[i]); WriteAddr+=2;//地址增加2. } } /********************************************************************************** * 函数功能:从指定地址开始写入指定长度的数据 * 输入参数:WriteAddr:起始地址(此地址必须为2的倍数!!)、pBuffer:数据指针、NumToWrite:半字(16位)数(就是要写入的16位数据的个数.) * 返 回 值: 无 * 说 明: */ void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) { u32 secpos; //扇区地址 u16 secoff; //扇区内偏移地址(16位字计算) u16 secremain; //扇区内剩余地址(16位字计算) u16 i; u32 offaddr; //去掉0X08000000后的地址 if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址 HAL_FLASH_Unlock(); //解锁 offaddr=WriteAddr-STM32_FLASH_BASE; //实际偏移地址. secpos=offaddr/STM_SECTOR_SIZE; //扇区地址 0~64 for STM32F103C8T6 secoff=(offaddr%STM_SECTOR_SIZE)/2; //在扇区内的偏移(2个字节为基本单位.) secremain=STM_SECTOR_SIZE/2-secoff; //扇区剩余空间大小 if(NumToWrite<=secremain)secremain=NumToWrite;//不大于该扇区范围 while(1) { STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容 for(i=0;i<secremain;i++) //校验数据 { if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除 } if(i<secremain) //需要擦除 { Flash_PageErase(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE); //擦除这个扇区 FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成 CLEAR_BIT(FLASH->CR, FLASH_CR_PER); //清除CR寄存器的PER位,此操作应该在FLASH_PageErase()中完成! //但是HAL库里面并没有做,应该是HAL库bug! for(i=0;i<secremain;i++)//复制 { STMFLASH_BUF[i+secoff]=pBuffer[i]; } STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区 }else { FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成 STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间. } if(NumToWrite==secremain)break;//写入结束了 else//写入未结束 { secpos++; //扇区地址增1 secoff=0; //偏移位置为0 pBuffer+=secremain; //指针偏移 WriteAddr+=secremain*2; //写地址偏移(16位数据地址,需要*2) NumToWrite-=secremain; //字节(16位)数递减 if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完 else secremain=NumToWrite;//下一个扇区可以写完了 } }; HAL_FLASH_Lock(); //上锁 } #endif /********************************************************************************** * 函数功能:从指定地址开始读出指定长度的数据 * 输入参数:ReadAddr:起始地址、pBuffer:数据指针、NumToWrite:半字(16位)数 * 返 回 值: 无 * 说 明: */ void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead) { u16 i; for(i=0;i<NumToRead;i++) { pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节. ReadAddr+=2;//偏移2个字节. } } /********************************************************************************** * 函数功能:擦除扇区 * 输入参数:PageAddress:擦除扇区地址 * 返 回 值: 无 * 说 明: */ void Flash_PageErase(uint32_t PageAddress) { /* Clean the error context */ p_Flash.ErrorCode = HAL_FLASH_ERROR_NONE; #if defined(FLASH_BANK2_END) if(PageAddress > FLASH_BANK1_END) { /* Proceed to erase the page */ SET_BIT(FLASH->CR2, FLASH_CR2_PER); WRITE_REG(FLASH->AR2, PageAddress); SET_BIT(FLASH->CR2, FLASH_CR2_STRT); } else { #endif /* FLASH_BANK2_END */ /* Proceed to erase the page */ SET_BIT(FLASH->CR, FLASH_CR_PER); WRITE_REG(FLASH->AR, PageAddress); SET_BIT(FLASH->CR, FLASH_CR_STRT); #if defined(FLASH_BANK2_END) } #endif /* FLASH_BANK2_END */ }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。