内部FLASH模拟EPPROM

news/2024/10/17 4:18:46/

本例程基于STM32F103ZET6
FLASH大小为512K。

介绍FLASH

不同型号的 STM32,其 FLASH 容量也有所不同,最小的只有 16K 字节,最大的则达到了
1024K 字节。我们的精英 STM32 开发板选择的是 STM32F103ZET6 的 FLASH 容量为 512K 字节,属于大容量产品,大容量产品的闪存模块组织如表 43.1.1 所示:
在这里插入图片描述

主存储器,该部分用来存放代码和数据常数(如 const 类型的数据)。
平时,我们烧写的代码就存放在主存储器部分,

闪存的写入步骤(写入用户数据数据)

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

解锁:将两个特定的解锁序列号(KEY1:0x45670123 KEY2:0xCDEF89AB)依次写入FLASH_KEYR寄存器

擦除:FLASH物理特性(只能写0,不能写1),所以写FLASH之前需要擦除,将要写入的区域变为0xFFFF.擦除操作分为:页擦除和批量擦除

写数据:擦除完成,可以向FLASH写数据,每次只能以16位方式写入。

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

代码分析

写入数据

可以看到当数据写入时,会存在两种情况。
情况1:要写入的地址范围都在一个扇区(页),不跨扇区
情况2:要写入的地址范围不在一个扇区(页),跨扇区

在这里插入图片描述
在这里插入图片描述

编写代码的核心重点:FLASH物理特性(只能写0,不能写1),如果待写入地址的数据不是0xFFFF(16字节写入),那么就要把它擦除为0xFFFF,且擦除的时候要按扇区为单位来擦。
这里擦除数据,按一个一个扇区的擦除。那么我们要先获取要被写入数据的当前扇区的全部数据(不写入的部分也要保存,以免数据被误改),然后其中要写入的范围里的数据进行判断,是否都为0xFFFF,如果不是,那么进行擦除整个扇区,再重新写入整个扇区数据。如果都为0xFFFF,那么直接写入待写入的数据即可。

对应代码如下

stmflash_write_nocheck函数在stmflash_write函数里调用

*** @brief       不检查的写入这个函数的假设已经把原来的扇区擦除过再写入* @param       waddr   : 起始地址 (此地址必须为2的倍数!!,否则写入出错!)* @param       pbuf    : 数据指针* @param       length  : 要写入的 半字(16)* @retval      无*/
void stmflash_write_nocheck(uint32_t waddr, uint16_t *pbuf, uint16_t length)
{uint16_t i;for (i = 0; i < length; i++){HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, waddr, pbuf[i]);waddr += 2; /* 指向下一个半字 */}
}
/*** @brief       在FLASH 指定位置, 写入指定长度的数据(自动擦除)*   @note      该函数往 STM32 内部 FLASH 指定位置写入指定长度的数据*              该函数会先检测要写入的扇区是否是空(全0XFFFF)的?, 如果*              不是, 则先擦除, 如果是, 则直接往扇区里面写入数据.*              数据长度不足扇区时,自动被回擦除前的数据* @param       waddr   : 起始地址 (此地址必须为2的倍数!!,否则写入出错!)* @param       pbuf    : 数据指针* @param       length  : 要写入的 半字(16位)数* @retval      无*/
uint16_t g_flashbuf[STM32_SECTOR_SIZE / 2]; /* 最多是2K字节 */
void stmflash_write(uint32_t waddr, uint16_t *pbuf, uint16_t length)
{uint32_t secpos;        /* 扇区地址 */uint16_t secoff;        /* 扇区内偏移地址(16位字计算) */uint16_t secremain;     /* 扇区内剩余地址(16位字计算) */uint16_t i;uint32_t offaddr;       /* 去掉0X08000000后的地址 */FLASH_EraseInitTypeDef flash_eraseop;uint32_t erase_addr;    /* 擦除错误,这个值为发生错误的扇区地址 */if (waddr < STM32_FLASH_BASE || (waddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE))){return;     /* 非法地址 */}HAL_FLASH_Unlock();                         /* FLASH解锁 */offaddr = waddr - STM32_FLASH_BASE;         /* 实际偏移地址. */secpos = offaddr / STM32_SECTOR_SIZE;       /* 扇区地址  0~255 for STM32F103ZET6 */secoff = (offaddr % STM32_SECTOR_SIZE) / 2; /* 在扇区内的偏移(2个字节为基本单位.) */secremain = STM32_SECTOR_SIZE / 2 - secoff; /* 扇区剩余空间大小 */if (length <= secremain){secremain = length; /* 不大于该扇区范围 */}while (1){stmflash_read(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE, g_flashbuf, STM32_SECTOR_SIZE / 2); /* 读出整个扇区的内容 */for (i = 0; i < secremain; i++)     /* 校验数据 */{if (g_flashbuf[secoff + i] != 0XFFFF){break;      /* 需要擦除 */}}if (i < secremain)  /* 需要擦除 */{ flash_eraseop.TypeErase = FLASH_TYPEERASE_PAGES;        /* 选择页擦除 */flash_eraseop.Banks = FLASH_BANK_1;flash_eraseop.NbPages = 1;flash_eraseop.PageAddress = secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE;  /* 要擦除的扇区 */HAL_FLASHEx_Erase( &flash_eraseop, &erase_addr);for (i = 0; i < secremain; i++)                         /* 复制 */{g_flashbuf[i + secoff] = pbuf[i];}stmflash_write_nocheck(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE, g_flashbuf, STM32_SECTOR_SIZE / 2); /* 写入整个扇区 */}else{stmflash_write_nocheck(waddr, pbuf, secremain);         /* 写已经擦除了的,直接写入扇区剩余区间. */}if (length == secremain){break; /* 写入结束了 */}else       /* 写入未结束 */{secpos++;               /* 扇区地址增1 */secoff = 0;             /* 偏移位置为0 */pbuf += secremain;      /* 指针偏移 */waddr += secremain * 2; /* 写地址偏移(16位数据地址,需要*2) */length -= secremain;    /* 字节(16位)数递减 */if (length > (STM32_SECTOR_SIZE / 2)){secremain = STM32_SECTOR_SIZE / 2; /* 下一个扇区还是写不完 */}else{secremain = length; /* 下一个扇区可以写完了 */}}}HAL_FLASH_Lock();   /* 上锁 */
}

注意事项

!!! 注意,如果要在这部分存储用户数据的话,不要把用户数据地址和存放代码和数据常数的地址重合
占用flash大小 = Code段+RO-data+RW-data.
占用SRAM大小 = RW-data + ZI-data

在这里插入图片描述
这里计算出占用FLASH大小为40588,16进制为9E8C,实际上可以.map文件,实际生成的占用FLASH大小会小于计算出来大小,这里为40332,16进制为9D8C,这是因为,未使用变量被优化掉了。

在这里插入图片描述

因为起始地址为0x8000 0000,那么用户存储数据的地址只能在0x8000 9D8C之后。

这里就定义为0X0807 0000。
在这里插入图片描述


http://www.ppmy.cn/news/1279143.html

相关文章

计算机网络 网络安全技术

网络安全基本要素 机密性 不泄密完整性 信息不会被破坏可用性 授权用户 正常有效使用可控性 被控制可审查性 网络安全的结构层次 物理安全 物理介质安全控制 计算机操作系统安全服务 应用层次 被动攻击 :截获信息 主动攻击 : 中断信息,篡改,伪造 篡改 …

conda环境下执行conda命令提示无法识别解决方案

1 问题描述 win10环境命令行执行conda命令&#xff0c;报命令无法识别&#xff0c;错误信息如下&#xff1a; PS D:\code\cv> conda activate pt conda : 无法将“conda”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写&#xff0c;如果包括路径&a…

2023年12月GESP Python五级编程题真题解析

【五级编程题1】 【试题名称】&#xff1a;小杨的幸运数 【问题描述】 小杨认为&#xff0c;所有大于等于a的完全平方数都是他的超级幸运数。 小杨还认为&#xff0c;所有超级幸运数的倍数都是他的幸运数。自然地&#xff0c;小杨的所有超级幸运数也都是幸运数。 对于一个…

Jenkins 构建触发器指南

目录 触发远程构建 (例如&#xff0c;使用脚本) 描述 配置步骤 安全令牌 在其他项目构建完成后触发构建 描述 配置步骤 定时触发构建 描述 配置步骤 GitHub钩子触发GITScm轮询 描述 配置步骤 Poll SCM - 轮询版本控制系统 描述 触发远程构建 (例如&#xff0c;使…

数据分析基础之《numpy(5)—合并与分割》

了解即可&#xff0c;用panads 一、作用 实现数据的切分和合并&#xff0c;将数据进行切分合并处理 二、合并 1、numpy.hstack 水平拼接 # hstack 水平拼接 a np.array((1,2,3)) b np.array((2,3,4)) np.hstack((a, b))a np.array([[1], [2], [3]]) b np.array([[2], […

电机控制 相关基础概念

基本概念: 定子或者转子上有铁心或者绕铜线的地方,绕铜线的地方叫槽,而将槽分开的叫齿,将所有的齿连起来的部位较轭部。 磁感应强度与磁场强度之间的关系可以通过以下公式表示: B=μH 其中,B 是磁感应强度,H 是磁场强度,μ 是磁导率。这个关系表明,在给定磁场强度下…

分而治之:Fork/Join框架(构造一个1到200 000求和的任务)

Fork一词的原始含义是吃饭用的叉子&#xff0c;也有分叉的意思。在linux平台中&#xff0c; 方法fork用来创建子进程。使得系统进程可以多一个执行分组。 而join方法这里表示等待&#xff0c;也就是使用fork方法后系统多了一个执行分支(线程) 所以需要等待这个执行分支执行完毕…

VideoPoet: Google的一种用于零样本视频生成的大型语言模型

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…