(HAL库学习11)内部flash的使用,同时教得到bin文件的方法(反应单片机烧录进去的程序大小)

首先这一期肯定是要做的,同时下一期要更的触摸功能也需要这个flash存几个关键的触摸参数

第一:先讲讲你不得不知道的flash使用:

不同型号的STM芯片,FLASH 大小不一样,最小只有16k字节,最大的1024k字节,它由一些信息快与自身操作相关的寄存器以及最重要的主存储器组成,而主存储器就是我们存放代码和常量(比如const类型)的地方,比如上一篇博文讲的4寸SPI_TFTLCD的屏幕例子,那张图片以及单片机内部的执行代码都是在flash里面存储的,而flash特点在于,他在掉电之后不会被擦除,仍然保留原来的数据,所以这里我们可以利用它存一些我们需要记录下来的数据。
flash以页为单位,以256k flash为分界线,flash大于等于256k的为大容量产品,2k字节一页 ,小于256k的为中小型容量产品,每页为1k字节
flash写入操作顺序:1.flash解锁 2.查看flash是否能被操作(如果有别的进程占用了flash显然就不行了) 3.擦除这一页 4.写入新的数据 5.flash上锁 只说了关键步骤,其实细分还有很多。

第二:讲讲你要把你的地址选在哪里!!!

每个人手上的单片机不一样,flash大小也不一样,如果自己选地址存放,肯定是要选flash靠后位置的区域来存储,因为自己后来私存的数据决不能将原来代码存储的部分擦除了,否则单片机程序会直接崩溃,但这里还要教大家如何查看自己烧录到单片机里面的代码到底占用了多大呢,这样大家用起来心里也更有底。

这里给大家另一篇博文,这里讲的很清楚了,如果不是很想了解原理的可以跳过https://www.cnblogs.com/zhangbing12304/p/11456844.html

最重要的:bin文件,很多人以为hex文件就是最后烧录到单片机内的程序占用的大小,并不是哈,因为hex文件里面还带有一些ascll码值等一些别的东西,而我说的bin文件是啥呢?Bin文件是最纯粹的二进制机器代码, 或者说是"顺序格式"。按照assembly code顺序翻译成binary machine code,内部没有地址标记。Bin是直接的内存映象表示,二进制文件大小即为文件所包含的数据的实际大小。 BIN文件就是直接的二进制文件,一般用编程器烧写时从00开始,而如果下载运行,则下载到编译时的地址即可。可以直接在裸机上运行。我们需要把.afx文件转换成bin文件,而bin文件就是最后烧录到单片机内flash占用的大小了,而如何转化呢?
在这里插入图片描述
以上就是bin文件得到的方法,我们要写数据就绝不能占用到这一部分

flash的地址规划:

flash起始地址是0x08000000,后面开始就是我们片内的东西了。比如给大家看看资料f103zet6的flash地址手册:
在这里插入图片描述
不要觉得非要看手册才会用哈,其实起始地址大家都一样,大容量就是2k一页,中小容量就是1k一页,地址往后面推,flash有多大就推多少。

给大家展示一下我用f411CEU6,512kflash 测试使用flash主程序里面的代码(先自己移植好我下面的.c .h文件,不会移植的看我之前的教程,然后在main.c中包含flash.h头文件就可以使用了)

uint16_t pBuffer_send[8] = {1314 ,520 ,1999 ,625 ,9527 ,7812 ,12121 ,15555};
uint16_t pBuffer[8];

STMFLASH_Write ( 0x08077800, &pBuffer_send[0], 8 ) ;  //从这个地址写入8个16位数据,也就是把我们上面这个数组存入,&pBuffer_send[0]就代表这个数组的起始地址

//以下是分别读出数据(每次读16位为单位的数据)存到我们准备好的数组里面
		STMFLASH_Read ( 0x08077800, &pBuffer[0], 1 );HAL_Delay(2);
		STMFLASH_Read ( 0x08077802, &pBuffer[1], 1 );HAL_Delay(2);
		STMFLASH_Read ( 0x08077804, &pBuffer[2], 1 );HAL_Delay(2);
		STMFLASH_Read ( 0x08077806, &pBuffer[3], 1 );HAL_Delay(2);
		STMFLASH_Read ( 0x08077808, &pBuffer[4], 1 );HAL_Delay(2);
		STMFLASH_Read ( 0x0807780A, &pBuffer[5], 1 );HAL_Delay(2);
		STMFLASH_Read ( 0x0807780C, &pBuffer[6], 1 );HAL_Delay(2);
		STMFLASH_Read ( 0x0807780E, &pBuffer[7], 1 );HAL_Delay(2);
		printf("data_read:%d\r\n", pBuffer[0]);
		printf("data_read:%d\r\n", pBuffer[1]);
		printf("data_read:%d\r\n", pBuffer[2]);
		printf("data_read:%d\r\n", pBuffer[3]);
		printf("data_read:%d\r\n", pBuffer[4]);
		printf("data_read:%d\r\n", pBuffer[5]);
		printf("data_read:%d\r\n", pBuffer[6]);
		printf("data_read:%d\r\n", pBuffer[7]);
 

flash驱动代码部分

代码是在网上找到的驱动代码,已经用过是可以用的,刚使用这个找的代码时flash.c里面有一个错误,他的扇区删除函数没猜错的话应该是封装过的,但是他根本没有提到这个函数,所以找不到这个函数,那就自己写了一个删除扇区的函数,应该是没问题的,我后面已经用过了

以下是flash.h的内容

#ifndef __FLASH_H__
#define __FLASH_H__

#include "main.h"
#define STM32_FLASH_SIZE        512  // 所选STM32的FLASH容量大小(单位为K)
#define STM32_FLASH_WREN        1    // stm32芯片内容FLASH 写入使能(0,禁用;1,使能)

uint16_t STMFLASH_ReadHalfWord(uint32_t faddr);                  //读出半字

void STMFLASH_WriteLenByte(uint32_t WriteAddr, uint32_t DataToWrite, uint16_t Len );              //指定地址开始写入指定长度的数据
uint32_t STMFLASH_ReadLenByte(uint32_t ReadAddr, uint16_t Len );                                                                    //指定地址开始读取指定长度数据
void STMFLASH_Write( uint32_t WriteAddr, uint16_t * pBuffer, uint16_t NumToWrite );                //从指定地址开始写入指定长度的数据
void STMFLASH_Read( uint32_t ReadAddr, uint16_t * pBuffer, uint16_t NumToRead );           //从指定地址开始读出指定长度的数据
void Test_Write( uint32_t WriteAddr, uint16_t WriteData );                        //测试写入                

#endif /* __STMFLASH_H__ */

以下是flash.c的内容

#include "flash.h"

#if STM32_FLASH_SIZE < 256
  #define STM_SECTOR_SIZE  1024 //字节
#else 
  #define STM_SECTOR_SIZE         2048
#endif

#if STM32_FLASH_WREN        //如果使能了写 
static uint16_t STMFLASH_BUF [ STM_SECTOR_SIZE / 2 ];//最多是2K字节
#endif

/**
  * 函数功能: 读取指定地址的半字(16位数据)
  * 输入参数: faddr:读地址(此地址必须为2的倍数!!)
  * 返 回 值: 返回值:对应数据.
  * 说    明:无
  */
uint16_t STMFLASH_ReadHalfWord ( uint32_t faddr )
{
        return *(__IO uint16_t*)faddr; 
}

#if STM32_FLASH_WREN        //如果使能了写   
/**
  * 函数功能: 不检查的写入
  * 输入参数: WriteAddr:起始地址
  *           pBuffer:数据指针
  *           NumToWrite:半字(16位)数
  * 返 回 值: 无
  * 说    明:无
  */
void STMFLASH_Write_NoCheck ( uint32_t WriteAddr, uint16_t * pBuffer, uint16_t NumToWrite )   
{                                           
        uint16_t i;        
        
        for(i=0;i<NumToWrite;i++)
        {
                HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,WriteAddr,pBuffer[i]);
          WriteAddr+=2;                                    //地址增加2.
        }  
} 

void FLASH_PageErase(unsigned int addr)
{
	//传入的就是写入的那个扇区的地址,这里的目的就是清除这个扇区
	uint32_t SectorError;

	FLASH_EraseInitTypeDef EraseInitStruct;
	
	EraseInitStruct.TypeErase = TYPEERASE_SECTORS;
	EraseInitStruct .VoltageRange =VOLTAGE_RANGE_3 ;  //提供的电压是3.3v的所以选VOLTAGE_RANGE_3
	//EraseInitStruct.Sector = FLASH_SECTOR_7 ;  //从FLASH_SECTOR_7也就是第七块开始删除,删除的数量是2个,不同的芯片块数量不一样,具体参照自己的芯片
	EraseInitStruct.NbSectors = addr;
	
	HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError);  //清除函数,如果出错则错误码存储到SectorError这个里面
	
}

/**
  * 函数功能: 从指定地址开始写入指定长度的数据
  * 输入参数: WriteAddr:起始地址(此地址必须为2的倍数!!)
  *           pBuffer:数据指针
  *           NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
  * 返 回 值: 无
  * 说    明:无
  */
void STMFLASH_Write ( uint32_t WriteAddr, uint16_t * pBuffer, uint16_t NumToWrite )        
{
        uint16_t secoff;           //扇区内偏移地址(16位字计算)
        uint16_t secremain; //扇区内剩余地址(16位字计算)           
         uint16_t i;    
        uint32_t secpos;           //扇区地址
        uint32_t offaddr;   //去掉0X08000000后的地址
        
        if(WriteAddr<FLASH_BASE||(WriteAddr>=(FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
        
        HAL_FLASH_Unlock();                                                //解锁
        
        offaddr=WriteAddr-FLASH_BASE;                //实际偏移地址.
        secpos=offaddr/STM_SECTOR_SIZE;                        //扇区地址  0~127 for STM32F103RBT6
        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+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+FLASH_BASE);//擦除这个扇区
									      FLASH_PageErase(secpos);//擦除这个扇区  
                        for(i=0;i<secremain;i++)//复制
                        {
                                STMFLASH_BUF[i+secoff]=pBuffer[i];          
                        }
                        STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区  
                }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    
                if(NumToWrite==secremain)break;//写入结束了
                else//写入未结束
                {
                        secpos++;                                //扇区地址增1
                        secoff=0;                                //偏移位置为0          
                           pBuffer+=secremain;          //指针偏移
                        WriteAddr+=secremain;        //写地址偏移           
                           NumToWrite-=secremain;        //字节(16位)数递减
                        if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
                        else secremain=NumToWrite;//下一个扇区可以写完了
                }         
        };        
        HAL_FLASH_Lock();//上锁
}
#endif

/**
  * 函数功能: 从指定地址开始读出指定长度的数据
  * 输入参数: ReadAddr:起始地址
  *           pBuffer:数据指针
  *           NumToRead:半字(16位)数
  * 返 回 值: 无
  * 说    明:无
  */
void STMFLASH_Read ( uint32_t ReadAddr, uint16_t *pBuffer, uint16_t NumToRead )           
{
        uint16_t i;
        
        for(i=0;i<NumToRead;i++)
        {
                pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节.
                ReadAddr+=2;//偏移2个字节.        
        }
}

/**
  * 函数功能: 向内部flash写入数据测试
  * 输入参数: WriteAddr:起始地址
  *           WriteData:要写入的数据
  * 返 回 值: 无
  * 说    明:无
  */
void Test_Write( uint32_t WriteAddr, uint16_t WriteData )           
{
        STMFLASH_Write(WriteAddr,&WriteData,1);//写入一个字 
}
/******************* (C) COPYRIGHT 2015-2020 硬石嵌入式开发团队 *****END OF FILE****/

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是使用HAL库在STM32L431微控制器上读写内部Flash的详细步骤和完整代码案例: 步骤1:打开STM32CubeMX并选择STM32L431微控制器。然后在“Pinout & Configuration”选项卡配置Flash的引脚和时钟。 步骤2:在“Project Manager”选项卡选择“Generate Code”按钮以生成代码。 步骤3:在生成的代码打开“main.c”文件。 步骤4:在“main.c”文件添加以下代码以初始化Flash: ```c HAL_FLASH_Unlock(); FLASH_Erase_Sector(FLASH_SECTOR_2, VOLTAGE_RANGE_3); HAL_FLASH_Lock(); ``` 这将解锁Flash并擦除扇区2。 步骤5:使用以下代码向Flash写入数据: ```c uint32_t Address = 0x08008000; uint32_t Data = 0x12345678; HAL_FLASH_Unlock(); if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK) { // Data has been successfully written to Flash } HAL_FLASH_Lock(); ``` 这将在Flash的地址0x08008000处写入数据0x12345678。 步骤6:使用以下代码从Flash读取数据: ```c uint32_t Address = 0x08008000; uint32_t Data; Data = *(uint32_t*)Address; ``` 这将从Flash的地址0x08008000处读取数据并将其存储在变量Data。 完整代码示例: ```c #include "main.h" #include "stm32l4xx_hal.h" void SystemClock_Config(void); static void MX_GPIO_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); uint32_t Address = 0x08008000; uint32_t Data = 0x12345678; HAL_FLASH_Unlock(); if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) == HAL_OK) { // Data has been successfully written to Flash } HAL_FLASH_Lock(); uint32_t ReadData = *(uint32_t*)Address; while (1) { } } 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_LSE|RCC_OSCILLATORTYPE_MSI; RCC_OscInitStruct.LSEState = RCC_LSE_ON; RCC_OscInitStruct.MSIState = RCC_MSI_ON; RCC_OscInitStruct.MSICalibrationValue = 0; RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; 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_MSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK) { Error_Handler(); } } static void MX_GPIO_Init(void) { /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); } ``` 请注意,此示例代码仅写入和读取了一个32位数据。您可以根据自己的需求修改代码以读写更多数据。此外,还应该添加错误处理程序以在出现错误时通知用户。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值