在哪里建网站,宁波网站设计价格,wordpress 分享封面图片尺寸,wordpress 发文章api本例程基于STM32F103ZET6 FLASH大小为512K。
介绍FLASH
不同型号的 STM32#xff0c;其 FLASH 容量也有所不同#xff0c;最小的只有 16K 字节#xff0c;最大的则达到了 1024K 字节。我们的精英 STM32 开发板选择的是 STM32F103ZET6 的 FLASH 容量为 512K 字节#xff0…本例程基于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-dataRW-data. 占用SRAM大小 RW-data ZI-data 这里计算出占用FLASH大小为4058816进制为9E8C实际上可以.map文件实际生成的占用FLASH大小会小于计算出来大小这里为4033216进制为9D8C这是因为未使用变量被优化掉了。 因为起始地址为0x8000 0000,那么用户存储数据的地址只能在0x8000 9D8C之后。
这里就定义为0X0807 0000。