打印
[应用相关]

STM32内部Flash读写

[复制链接]
146|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2024-12-10 09:12 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
内部Flash构成了解:
对于stm32f103zet6来说,由三部分组成:主存储器,信息块,闪存存储器接口寄存器。



主存储器:用来存储代码和数据常数
其地址范围为0x0800 0000~0x0807 ffff,分为256页,每页2KB(小中容量每页为1KB),BOOT0接地,系统将从0x0800 0000地址处开始读取代码(从主存储器启动)。存储器映像如图



信息块:系统存储(启动程序代码)和选项字节(用户选择字节)
系统存储大小为2KB,用来存储ST自带的启动程序,用来串口下载代码,选项字节为16B,一般用于设置内存的读保护和写保护。当BOOT0接VCC,BOOT1接GND(串口下载程序),系统运行的就是这部分

闪存存储器接口寄存器:
用于控制闪存读写等,是闪存模块的控制结构

FPEC 键寄存器(FLASH_KEYR):



用来解锁 FPEC,必须在该寄存器写入特定的序列(KEY1 和 KEY2)解锁后,才能对 FLASH_CR 寄存器进行写操作。

闪存控制寄存器(FLASH_CR):



LOCK位:用于查看 FLASH_CR 寄存器是否被锁住,该位在检测到正确的解锁序列

后,硬件将其清零。

STRT 位:用于开始一次擦除操作,在该位写入 1,将执行一次擦除操作。

PER 位:用于选择页擦除操作,页擦除时将该位置 1

PG 位:用于选择编程操作,往 FLASH 写数据时该位置 1

闪存状态寄存器(FLASH_SR):



用来指示当前 FPEC 的操作编程状态,常看的是BSY位

闪存地址寄存器(FLASH_AR):



FLASH读写过程:
涉及读操作,写操作,擦除操作。FLASH物理特性:只能写0,不能写1,写1靠擦除

闪存的读取:
直接通过通用地址空间直接寻址即可。CPU通过ICode指令总线访问FLASH指令,通过DCode数据总线访问FLASH数据,但因为CPU运行速度比Flash快的多,FLASH<=24M,所以需加入等待时间。正确设置好等待周期后,利用指针读取数据。将addr强制转换为uintx_t指针,然后取该指针所指向地址的值,即可获得addr地址的数据。但需注意在进行写或擦除操作时,不能进行代码或数据的读取操作。

闪存的写入:
闪存编程由FPEC(闪存编程和擦除控制器)模块处理。

写操作有四步:解锁-擦除-写数据-上锁

解锁:将两个特定的解锁序列号key1和key2依次写入FLASH_KEYR键值寄存器中

擦除:分页擦除和批量擦除

闪存的擦除:读FLASH_CR的LOCK位,为1就解锁,设置FLASH_CR的PER位为1配置页擦除,设置FLASH_AR指定擦除地址,设置FLASH_CR的STRT位为1开始擦除,通过SR的BSY位去查询是否擦除完毕

写数据:擦除完成,可以向FALSH写数据,每次只能以16位方式写入数据(半字写入)

上锁:写入数据完成,设置FLASH_CR[LOCK]位1,重新上锁,以防数据不小心被修改。

FLASH的HAL库驱动:
1. FLASH解锁

HAL_StatusTypeDef HAL_FLASH_Unlock(void);

2.FLASH上锁

HAL_StatusTypeDef HAL_FLASH_Lock (void);

3FLASH写入

HAL_StatusTypeDef HAL_FLASHEx_Program(uint32_t TypeProgram, uint32_t Address,

uint64_t Data);

4FLASH擦除

HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit,

uint32_t *SectorError);

5等待FALSH操作完成(读SR)

HAL_StatusTypeDef FLASH_WaitForLastOperation(uint32_t Timeout);

程序设计:
头文件:
//FLASH容量
#define STM32_FLASH_SIZE         512//单位为K                
//FLASH起始地址
#define STM32_FLASH_BASE 0x08000000
//页大小
#define STM_SECTOR_SIZE        2048
C文件:
FLASH读:

uint16_t STMFLASH_BUF[STM_SECTOR_SIZE/2];//FLASH读写操作页缓存空间,2K

//FLASH读一个半字数据 字是32位,半字是16位
uint16_t STMFLASH_ReadHalfWord(uint32_t faddr)
{
    return *(uint16_t*)faddr;//利用指针直接读取返回
}
//FLASH读(多个半字数据),
void STMFLASH_Read(uint32_t ReadAddr,uint16_t *PDataBuf,uint16_t Num)//参数,读取起始地址,数据指针,读取半字数
{
    uint16_t i;
    for(i=0;i<Num;i++)
    {   
      PDataBuf = STMFLASH_ReadHalfWord(ReadAddr);
      ReadAddr+=2;  

    }   
}

FLASH写:

//FLASH写,不检查写入
void STMFLASH_Write_NoCheck(uint32_t WriteAddr,uint16_t *PDataBuf,uint16_t Num)
{
    uint16_t i;
    for(i=0;i<Num;i++)
    {  
        HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,WriteAddr,PDataBuf);
      WriteAddr+=2;      
    }      
}
//FLASH写
void STMFLASH_Write(uint32_t WriteAddr,uint16_t *PDataBuf,uint16_t Num)
{
    uint32_t offset;//实际偏移地址
    uint8_t sectornum;//扇区编号
    uint16_t secoff;//扇区内偏移地址(16位字计算)
    uint16_t secremain;//扇区剩余空间(16位字计算)
    uint16_t i;
    //地址校验
    if((WriteAddr<STM32_FLASH_BASE)||(WriteAddr>=(STM32_FLASH_BASE+STM32_FLASH_SIZE*1024))) return ;//非法地址
    //解锁
    HAL_FLASH_Unlock();
    offset=WriteAddr-STM32_FLASH_BASE;
    sectornum=WriteAddr/STM_SECTOR_SIZE;
    secoff=(WriteAddr%STM_SECTOR_SIZE)/2;//(2个字节为基本单位)
    secremain=STM_SECTOR_SIZE/2-secoff;//(2个字节为基本单位)
    if(Num<=secremain)secremain=Num;//不大于该扇区范围
    while(1)
        {       
        //读取该扇区内容,检验数据看是否需要擦除
        STMFLASH_Read(STM32_FLASH_BASE+sectornum*STM_SECTOR_SIZE,STMFLASH_BUF,STM_SECTOR_SIZE/2);
        for(i=0;i<secremain;i++)
        {
            if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;

        }
        if(i<secremain)//需要擦除
        {
            FLASH_PageErase(STM32_FLASH_BASE+sectornum*STM_SECTOR_SIZE);
            FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
            for(i=0;i<secremain;i++)
            {
                STMFLASH_BUF[secoff+i]=PDataBuf;         
            }
            STMFLASH_Write_NoCheck(STM32_FLASH_BASE+sectornum*STM_SECTOR_SIZE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区
        }
        else
        {

            FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
            STMFLASH_Write_NoCheck(WriteAddr,PDataBuf,secremain);//写入扇区剩余区间
        }
        if(secremain==Num) break;//写入结束
        else//写入未结束
           {
            sectornum++;
            secoff=0;  
            WriteAddr+=secremain*2;
            PDataBuf+=secremain;
            Num-=secremain;
            if(Num>STM_SECTOR_SIZE/2)
                secremain=STM_SECTOR_SIZE/2;
            else   
                secremain=Num;   
            }      
    };      
         //上锁
    HAL_FLASH_Lock();
}

注意思考一下Buf作用和写入结束条件设置,以及跨扇区后的处理即可呢,还有地址分析。

主函数测试:

void SystemClock_Config(void);
const uint8_t TEXT_Buffer[]={"STM32F103 FLASH TEST"};
#define SIZE sizeof(TEXT_Buffer)                //数组长度
#define FLASH_SAVE_ADDR  0X08070000                //设置FLASH 保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000)
int main(void)
{
  uint8_t i ;
  uint8_t datatemp[SIZE];
  HAL_Init();
  SystemClock_Config();
    Delay_init();
    Led_Init();   
    UART_Init();
    Lcd_Init();
    LOG_INFO(Terminal0,"Chipid:%x", lcd_paramter.id);
    STMFLASH_Write(FLASH_SAVE_ADDR,(uint16_t*)TEXT_Buffer,SIZE);      
    STMFLASH_Read(FLASH_SAVE_ADDR,(uint16_t*)datatemp,SIZE);
    LCD_ShowString(30,190,datatemp,16);//显示读到的字符串
  while (1)
  {

      HAL_GPIO_TogglePin(Led_DS1_GPIO_Port,Led_DS1_Pin);
      delay_ms(500);


  }
}

测试成功,屏幕正确显示了写入的字符,就不放图了。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/qq_52117635/article/details/144240656

使用特权

评论回复
沙发
AloneKaven| | 2024-12-10 11:41 | 只看该作者
内部读写的地址怎么判断不跟程序冲突啊

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2011

主题

15846

帖子

13

粉丝