STM32H库内部FLASH读写操作及结构体数组数据的写入与读取技巧

stm32H库的内部FLASH读写操作与结构体数组数据写入与读取

  • 1.软硬件准备
  • 2.关于STM32的Flash的一些说明
  • 3.实验结果
  • 参考博主-STM32系列(HAL库)——内部FLASH读写实验

    1.软硬件准备

    软件:CubeMX、SSCOM(串口调试助手)

    硬件:SMT32F103C8T6

    2.关于STM32的Flash的一些说明

    (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>&copy; 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 */
      }
     
    

    3.实验结果

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32H库内部FLASH读写操作及结构体数组数据的写入与读取技巧

    发表评论