STM32 使用HAL库操作FLASH

本文参考STM32CubeMX学习笔记(51)——读写内部Flash - 简书

STM32F103C8T6

stmhalflash.h

#ifndef __STMHALFLASH_H__
#define __STMHALFLASH_H__

/* FLASH大小:STM32F103C8T6:64K */
#define STM32FLASH_SIZE             0x00010000UL
/* FLASH起始地址 */
#define STM32FLASH_BASE             0x08000000UL
/* FLASH结束地址 */
#define STM32FLASH_END              (STM32FLASH_BASE|STM32FLASH_SIZE)
/* FLASH页大小:1K */
#define STM32FLASH_PAGE_SIZE        0x400
/* FLASH总页数 */
#define STM32FLASH_PAGE_NUM         (STM32FLASH_SIZE/STM32FLASH_PAGE_SIZE)


#define WRITE_START_ADDR            ((unsigned int)0x08008000)
#define WRITE_END_ADDR              ((unsigned int)0x0800C000)


int Internal_ReadFlash(unsigned int addrStart,void *pData,unsigned int dataLen);

int Internal_WriteFlash(unsigned int addrStart,const unsigned short *pData,unsigned int dataLen);


#endif

stmhalflash.c

#include "stmhalflash.h"
#include "main.h"


// FLASH一个扇区数据的缓存
unsigned short FlashBuffer[STM32FLASH_PAGE_SIZE/2] = {0};

/**
 * @brief 内部FLASH读取
 * 
 * @param addrStart [IN]读取的地址
 * @param pData     [OUT]指向需要操作的数据
 * @param dataLen   [IN]数据字节长度
 * @return int 读出成功的字节数
 */
int Internal_ReadFlash(unsigned int addrStart,void *pData,unsigned int dataLen)
{
    unsigned int nread = dataLen;
    unsigned char *pBuffer = (unsigned char *)pData;
    const unsigned char *pAddr = (const unsigned char *)addrStart;

    /* 检测地址和数据合法性 */
    if( (!pData) || (addrStart < STM32FLASH_BASE) || (addrStart > STM32FLASH_END) )
    {
        return 0;
    }

    /* 读取数据不少于4字节时,每次读4字节 */
    while((nread >= sizeof(unsigned int)) && ( ((unsigned int)pAddr) <= (STM32FLASH_END - 4) ) )
    {
        *(unsigned int *)pBuffer = *(unsigned int *)pAddr;
        pBuffer += sizeof(unsigned int);
        pAddr += sizeof(unsigned int);
        nread -= sizeof(unsigned int);
    }

    /* 读取剩余不足4字节的数据 */
    while(nread && ( ((unsigned int)pAddr)<STM32FLASH_END ))
    {
        *pBuffer++ = *pAddr++;
        nread--;
    }

    return dataLen - nread;
}




/**
 * @brief 内部Flash页擦除
 * 
 * @param pageAddress   [IN]擦除起始地址
 * @param nbPages       [IN]擦除页数
 * @return int 0-OK !0-Err
 */
static int Internal_ErasePage(unsigned int pageAddress,unsigned int nbPages)
{
    unsigned int pageError = 0;
    
    FLASH_EraseInitTypeDef eraseInit;
    eraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
    eraseInit.PageAddress = pageAddress;
    eraseInit.Banks = FLASH_BANK_1;
    eraseInit.NbPages = nbPages;
    
    if(HAL_FLASHEx_Erase(&eraseInit,&pageError) != HAL_OK)
    {
        return -1;
    }
    return 0;
}


/**
 * @brief 内部Flash无检查写入(半字写入)
 * 
 * @param addrStart [IN]写入的地址
 * @param pData     [IN]执行需要操作的数据
 * @param dataLen   [IN]写入数据的半字数
 * @return unsigned int 实际写入的字节数 
 */
static unsigned int Internal_WriteFlashNoCHeck(unsigned int addrStart,const unsigned short *pData,unsigned int dataLen)
{
    unsigned int nwrite = dataLen;              //记录剩余要写入的数据量
    unsigned int addrmax = STM32FLASH_END - 2;  //记录写入的最大FLASH地址
    
    while(nwrite)
    {
        /* 地址非法检查 */
        if(addrStart > addrmax)
        {
            break;
        }
        
        HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,addrStart,*pData);
        if( (*(volatile unsigned short*)addrStart) != *pData )
        {
            break;
        }
        
        nwrite--;
        pData++;
        addrStart += 2;
        
    }
    
    return (dataLen - nwrite);
}


/**
 * @brief 内部Flash写入
 * 
 * @param addrStart     [IN]写入的地址
 * @param pData         [IN]指向需要操作的数据
 * @param dataLen       [IN]写入数据半字数
 * @return unsigned int 实际写入的字节数
 */
int Internal_WriteFlash(unsigned int addrStart,const unsigned short *pData,unsigned int dataLen)
{
    unsigned int i = 0;
    unsigned int pagepos = 0;       //页位置
    unsigned int pageoff = 0;       //页内偏移地址
    unsigned int pagefre = 0;       //页内空余空间
    unsigned int offset  = 0;       //Address在Flash中的偏移
    unsigned int nwrite = dataLen;  //记录剩余要写入的数据量
    const unsigned short *pBuffer = (const unsigned short *)pData;
    
    /* 检测非法地址 */
    if( (addrStart < STM32FLASH_BASE) || (addrStart > (STM32FLASH_END - 2)) || (dataLen == 0) || (pData == NULL) )
    {
        return 0;
    }
    
    /* 解锁FLASH */
    HAL_FLASH_Unlock();
    
    
    //计算写入地址在FLASH中的偏移地址
    offset = addrStart - STM32FLASH_BASE;
    //计算当前页位置
    pagepos = offset/STM32FLASH_PAGE_SIZE;
    //计算要写入数据的起始地址在当前页内的偏移地址(半字写入)
    pageoff = ((offset%STM32FLASH_PAGE_SIZE)>>1);
    //计算当前页内空余空间(半字写入)
    pagefre = ((STM32FLASH_PAGE_SIZE>>1) - pageoff);
    //要写入的数量低于当前页空余量
    if(nwrite <= pagefre)
    {
        pagefre = nwrite;
    }
    
    while(nwrite !=0)
    {
        /* 检查是否超页 */
        if(pagepos >= STM32FLASH_PAGE_NUM)
        {
            //写入地址超出FLASH最大位置
            break;
        }
        
        /* 读取一页 */
        Internal_ReadFlash(STM32FLASH_BASE + pagepos*STM32FLASH_PAGE_SIZE,FlashBuffer,STM32FLASH_PAGE_SIZE);
        
        /* 检查是否需要擦除 */
        for(i=0;i<pagefre;i++)
        {
            if( *(FlashBuffer + pageoff + i) != 0xFFFF ) // FLASH擦除后默认内容全为0xFF
            {
                break;
            }
        }
        
        if(i < pagefre)
        {
            /* 需要擦除 */
            unsigned int count = 0;
            unsigned int index = 0;
            unsigned int PageError = 0;
            
            FLASH_EraseInitTypeDef pEraseInit;
            
            /* 擦除一页 */
            pEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
            pEraseInit.PageAddress = STM32FLASH_BASE+pagepos*STM32FLASH_PAGE_SIZE;
            pEraseInit.Banks = FLASH_BANK_1;
            pEraseInit.NbPages = 1;
            if(HAL_FLASHEx_Erase(&pEraseInit,&PageError) != HAL_OK)
            {
                break;
            }
            
            /* 复制缓存 */
            for(index = 0;index<pagefre;index++)
            {
                *(FlashBuffer+pageoff+index) = *(pBuffer+index);
            }
            
            /* 写回FLASH */
            count = Internal_WriteFlashNoCHeck(STM32FLASH_BASE+pagepos*STM32FLASH_PAGE_SIZE,FlashBuffer,(STM32FLASH_PAGE_SIZE>>1));
            if(count != (STM32FLASH_PAGE_SIZE>>1))
            {
                nwrite -= count;
                break;
            }
            
        }else{
            /* 无需擦除,直接写 */
            unsigned int count = Internal_WriteFlashNoCHeck(addrStart,pBuffer,pagefre);
            if(count != pagefre)
            {
                nwrite -= count;
                break;
            }
        }
        
        
        pBuffer += pagefre;             //读取地址递增
        addrStart += (pagefre<<1);      //写入地址递增
        nwrite -= pagefre;              //更新剩余未写入数据量
        
        pagepos++;                      //下一页
        pageoff = 0;                    //页内偏移地址置零
        
        /* 根据剩余量计算下次写入数据量 */
        pagefre = nwrite >= (STM32FLASH_PAGE_SIZE>>1) ? (STM32FLASH_PAGE_SIZE>>1) : nwrite;
    }
    
    /* 解锁FLASH */
    HAL_FLASH_Lock();
    
    return ((dataLen - nwrite)<<1);
}

使用示例

unsigned char t_arr[40]={0};
//写数据
unsigned int writedata_addr = WRITE_START_ADDR;
i=0;
if(i<100)
{
    //写入数据
    Internal_WriteFlash(writedata_addr,(unsigned short*)&t_arr[0],sizeof(t_arr)/2);
    writedata_addr+= 40;
    i++;
}

//读数据
calidata_addr = WRITE_START_ADDR;
i=0;
if(i<100)
{
    //读取数据
    Internal_ReadFlash(calidata_addr,t_arr,sizeof(t_arr));
	calidata_addr+=40;
    i++;
}

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
你可以使用STM32HAL库来编写Flash操作。下面是一个简单的示例代码,演示了如何使用HAL库来进行Flash读写操作: ```c #include "stm32f4xx_hal.h" #define FLASH_USER_START_ADDR ADDR_FLASH_SECTOR_5 // Flash起始地址 #define FLASH_USER_END_ADDR ADDR_FLASH_SECTOR_6 // Flash结束地址 #define FLASH_PAGE_SIZE 0x400 // Flash页大小 void write_flash(uint32_t addr, uint32_t* data, uint32_t size) { FLASH_EraseInitTypeDef erase_init; erase_init.TypeErase = FLASH_TYPEERASE_SECTORS; erase_init.Sector = FLASH_SECTOR_5; erase_init.NbSectors = 1; erase_init.VoltageRange = FLASH_VOLTAGE_RANGE_3; uint32_t page_error = 0; HAL_FLASH_Unlock(); // 解锁Flash // 擦除指定扇区 HAL_FLASHEx_Erase(&erase_init, &page_error); // 写入数据 for (uint32_t i = 0; i < size; i++) { HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + i * 4, data[i]); } HAL_FLASH_Lock(); // 锁定Flash } void read_flash(uint32_t addr, uint32_t* data, uint32_t size) { for (uint32_t i = 0; i < size; i++) { data[i] = *(__IO uint32_t*)(addr + i * 4); } } int main(void) { HAL_Init(); // 初始化HAL库 // 假设要写入的数据为32位整数数组 uint32_t data_to_write[] = {0x12345678, 0x87654321, 0xABCDEF12}; uint32_t data_to_read[3]; // 写入Flash write_flash(FLASH_USER_START_ADDR, data_to_write, sizeof(data_to_write) / sizeof(uint32_t)); // 从Flash中读取数据 read_flash(FLASH_USER_START_ADDR, data_to_read, sizeof(data_to_read) / sizeof(uint32_t)); while (1) { // 执行其他操作 } } ``` 在上面的示例代码中,`write_flash`函数用于擦除指定Flash扇区并写入数据,而`read_flash`函数用于从Flash中读取数据。你可以根据自己的需求修改Flash起始地址、结束地址以及数据大小等参数。 需要注意的是,使用HAL库进行Flash操作时,需要先解锁Flash(`HAL_FLASH_Unlock`),操作完成后再锁定Flash(`HAL_FLASH_Lock`)。同时,还需要提供正确的Flash扇区和页大小信息,并确保所操作的地址和数据类型正确。 请注意,以上示例代码仅供参考,实际应用中可能需要根据具体情况进行适当的修改。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值