我是靠谱客的博主 单薄雪碧,最近开发中收集的这篇文章主要介绍[转]STM32 --- 使用内部FLASH存储数据一、内部FLASH要点二、读取数据三、存储数据四、应用示例,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
本文记录了对一些知识点的理解、操作方法,如有错误,请务必批评指正!!
最终的测试截图:
目录
一、内部FLASH要点
关于地址:
关于解锁:
关于擦除:
关于写入:
二、读取数据
三、存储数据
四、应用示例
一、内部FLASH要点
关于地址:
- 内部FLASH地址开始地址:0x0800 0000;
- 结束地址:0x08000000+FLASH大小
- FLASH的大小,可根据芯片型号得知,如F103x8=64K, F103xC=256K, F103xE=512K
- FLASH的大小,也可读*(uint16_t*)0x1FFFF7E0直接获得;
关于解锁:
- 对FLASH寄存器的操作, 需要在操作之前解锁;
- 如果解锁过程发生错误,FLASH寄存器操作将死锁至掉电;
- 解锁步骤(1): FLASH->KEYR= (uint32_t)0x45670123;
- 解锁步骤(2): FLASH->KEYR= (uint32_t)0xCDEF89AB;
关于擦除:
- 内部FLASH空间,按页划分成连续的数据块;
- 每次擦除的最小单位是:页;
- F103中,中小容量FLASH的页(扇区)大小为1K, 而大容量的页大小为2K
- 每页擦除时长:中小容量约30ms, 大容量约50ms
- 页寿命:中小容量约可擦: 1K次, 大容量约可擦: 10K次
- 擦除步骤(1):FLASH->CR|= 1<<1; // 选择以页进行擦除
- 擦除步骤(2):FLASH->AR = 0x0800xxxx; // 要擦除的页地址
- 擦除步骤(3):FLASH->CR|= 0x40; // 开始擦除
- 擦除步骤(4):while((FLASH->SR & 0x01); // 等待空闲,即BSY=0
关于写入:
- 写入的地址,必须是偶数;
- 每一次写入,必须以半字进行(uint16_t)!单字节或全字将发生错误;
- 写入步骤(1): FLASH->CR |= 0x01<<0; // 使能可编程
- 写入步骤(2): *(uint16_t*)addr = (uint16_t)value; // 写入数据
- 写入步骤(3): while((FLASH->SR & 0x01); // 等待空闲,即BSY=0
- 写入步骤(4): FLASH->CR &= ((uint32_t)0x00001FFE); // 关闭编程
二、读取数据
- 可以按byte读取,也可以按半字、全字方式读取;
- 读取的方式很简单,如:short value = *(uint16_t*)readAddr;
下面的读取函数,经多次完善,按字节读取,方便匹配奇数数量的数据读取;
/****************************************************************************** * 函 数: System_ReadInteriorFlash * 功 能: 在芯片的内部FLASH里,读取指定长度数据 * 参 数: uint32_t readAddr 数据地址 * uint8_t *pBuffer 读出后存放位置 * uint16_t numToRead 读取的字节数量 * 返回值: 0_成功 1_失败,地址小于FLASH基址 2_失败,地址大于FLASH最大值 ******************************************************************************/ uint8_t System_ReadInteriorFlash (uint32_t readAddr, uint8_t *pBuffer, uint16_t numToRead) { // 获取芯片FLASH大小 uint16_t flashSize = *(uint16_t*)(0x1FFFF7E0); // 读取芯片FLASH大小;本寄存器值为芯片出厂前写入的FLASH大小,只读单位:KByte // 判断地址有效性 if(readAddr < STM32_FLASH_ADDR_BASE) return 1; // 如果读的地址,小于FLASH的最小地址,则退出 if(readAddr > (STM32_FLASH_ADDR_BASE+(flashSize*1024))) return 2; // 如果读的地址,超出FLASH的最大地址,则退出 // 开始复制 while(numToRead--){ *pBuffer = *(__IO uint8_t*)readAddr; pBuffer++; // 指针后移一个数据长度 readAddr++; // 偏移一个数据位 } return 0; // 成功,返回0; }
三、存储数据
1:在存储函数前,需要三个数据的定义:
// 下面这三个定义,用于存取内部FLASH数据,F103系列都不用修改#ifdef STM32F10X_HD#define STM32_FLASH_SECTOR_SIZE 2048 // 内部FLASH页大小, 单位:bytes (注:STM32F10xx系列下,小中容量存储器扇区为1K, 大容量存储器扇区为2K)#else#define STM32_FLASH_SECTOR_SIZE 1024 // 内部FLASH页大小, 单位:bytes (注:STM32F10xx系列下,小中容量存储器扇区为1K, 大容量存储器扇区为2K)#endif #define STM32_FLASH_ADDR_BASE 0x08000000 // 芯片内部FLASH基址(这个基本不用修改)static uint8_t sectorbufferTemp[STM32_FLASH_SECTOR_SIZE]; // 开辟一段内存空间,作用:在内部FLASH写入时作数据缓冲
2:等待空闲的函数:
// 作用:内部FLASH写入时,等待空闲,BSY位标志:0闲1忙static uint8_t waitForFlashBSY(uint32_t timeOut){ while((FLASH->SR & 0x01) && (timeOut-- != 0x00)) ; // 等待BSY标志空闲 if(timeOut ==0) return 1; // 失败,返回1, 等待超时; return 0; // 正常,返回0 }
3:完整可用的存储函数:
/****************************************************************************** * 函 数: System_WriteInteriorFlash * 功 能: 在芯片的内部FLASH里,写入指定长度数据 * 参 数: uint32_t writeAddr 写入地址,重要:写入地址必须是偶数!!! * uint8_t *writeToBuffer * uint16_t numToWrite * * 返回值: 0_成功, * 1_失败,地址范围不正确 * 2_失败,FLASH->SR:BSY忙超时 * 3_失败,擦除超时 ******************************************************************************/ uint8_t System_WriteInteriorFlash(uint32_t writeAddr, uint8_t *writeToBuffer, uint16_t numToWrite) { uint16_t flashSize = *(uint16_t*)(0x1FFFF7E0); // 读取芯片FLASH大小;本寄存器值为芯片出厂前写入的FLASH大小,只读,单位:KByte uint32_t addrOff = writeAddr - STM32_FLASH_ADDR_BASE; // 去掉0x08000000后的实际偏移地址 uint32_t secPos = addrOff / STM32_FLASH_SECTOR_SIZE;; // 扇区地址,即起始地址在第几个扇区 uint16_t secOff = addrOff%STM32_FLASH_SECTOR_SIZE ; // 开始地始偏移字节数: 数据在扇区的第几字节存放 uint16_t secRemain = STM32_FLASH_SECTOR_SIZE - secOff; // 本扇区需要写入的字节数 ,用于判断够不够存放余下的数据 // 判断地址有效性 if(writeAddr < STM32_FLASH_ADDR_BASE) return 1; // 如果读的地址,小于FLASH的最小地址,则退出,返回1_地址失败 if(writeAddr > (STM32_FLASH_ADDR_BASE+(flashSize*1024))) return 1; // 如果读的地址,超出FLASH的最大地址,则退出, 返回1_地址失败 // 0_解锁FLASH FLASH->KEYR = ((uint32_t)0x45670123); FLASH->KEYR = ((uint32_t)0xCDEF89AB); if(numToWrite <= secRemain) secRemain=numToWrite; while(1){ // 1_读取当前页的数据 if(waitForFlashBSY(0x00888888)) return 2; // 失败,返回:2, 失败原因:FLASH->SR:BSY忙超时 System_ReadInteriorFlash ( secPos*STM32_FLASH_SECTOR_SIZE+STM32_FLASH_ADDR_BASE , sectorbufferTemp, STM32_FLASH_SECTOR_SIZE ); // 读取扇区内容到缓存 // 2_擦险指定页(扇区) if(waitForFlashBSY(0x00888888)) return 2; // 失败,返回:2, 失败原因:FLASH->SR:BSY忙超时 FLASH->CR|= 1<<1; // PER:选择页擦除;位2MER为全擦除 FLASH->AR = STM32_FLASH_ADDR_BASE + secPos*STM32_FLASH_SECTOR_SIZE; // 填写要擦除的页地址 FLASH->CR|= 0x40; // STRT:写1时触发一次擦除运作 if(waitForFlashBSY(0x00888888)) return 2; // 失败,返回:3, 失败原因:擦除超时 FLASH->CR &= ((uint32_t)0x00001FFD); // 关闭页擦除功能 for(uint16_t i=0; iSR:BSY忙超时 FLASH->CR |= 0x01<<0; // PG: 编程 *(uint16_t*)(STM32_FLASH_ADDR_BASE + secPos*STM32_FLASH_SECTOR_SIZE +i*2) = (sectorbufferTemp[i*2+1]<<8) | sectorbufferTemp[i*2] ; // 缓存数据写入设备 if(waitForFlashBSY(0x00888888)) return 2; // 失败,返回:2, 失败原因:FLASH->SR:BSY忙超时 FLASH->CR &= ((uint32_t)0x00001FFE) ; // 关闭编程 } if(secRemain == numToWrite){ break; // 已全部写入 } else{ // 未写完 writeToBuffer += secRemain ; // 原始数据指针偏移 secPos ++; // 新扇区 secOff =0; // 新偏移位,扇区内数据起始地址 numToWrite -= secRemain ; // 剩余未写字节数 secRemain = (numToWrite>STM32_FLASH_SECTOR_SIZE)?(STM32_FLASH_SECTOR_SIZE):numToWrite; // 计算新扇区写入字节数 } } FLASH->CR |= 1<<7 ; // LOCK:重新上锁 return 0; }
四、应用示例
下面代码中,分别测试了四种数据类型的存储、读取、转换。
注意:string不是独立的数据类型;
printf("rn>>>>开始测试内部FLASH读写,及数据转换:rn"); // 测试1:字符 uint32_t addrChar = 0x08000000+38*1024; // 注意地址必须是偶数,且地址段尽量安排在FLASH尾部,避免覆盖了代码段数据 char writeChar='F'; System_WriteInteriorFlash(addrChar, (uint8_t*)&writeChar, 1); // 存入数据,函数内自动解锁,擦除,写入 char readChar=0; System_ReadInteriorFlash(addrChar, (uint8_t*)&readChar, 1); // 读取数据,函数内自动按半字读取 printf("测试char 地址:0x%X 写入:%c 读取:%c rn", addrChar, writeChar, readChar); // 测试2:带符号的16位 uint32_t addrShort = 0x08000000+155*1024-2; // 注意地址必须是偶数,且地址段尽量安排在FLASH尾部,避免覆盖了代码段数据 short writeShort=-888; System_WriteInteriorFlash(addrShort, (uint8_t*)&writeShort, sizeof(writeShort)); // 存入数据,函数内自动解锁,擦除,写入 short readShort=0; System_ReadInteriorFlash(addrShort, (uint8_t*)&readShort, sizeof(writeShort)); // 读取数据,函数内自动按半字读取 printf("测试short 地址:0x%X 写入:%d 读取:%d rn", addrShort, writeShort, readShort); // 测试3:无符号的32位 uint32_t addrUint = 0x08000000+254*1024; // 注意地址必须是偶数,且地址段尽量安排在FLASH尾部,避免覆盖了代码段数据 uint32_t writeUint= 0x12345678; System_WriteInteriorFlash(addrUint, (uint8_t*)&writeUint, 4); // 存入数据,函数内自动解锁,擦除,写入 uint32_t readUint = 0; System_ReadInteriorFlash(addrUint, (uint8_t*)&readUint, 4); // 读取数据,函数内自动按半字读取 printf("测试uint 地址:0x%X 写入:0x%X 读取:0x%X rn", addrUint, writeUint, readUint); // 测试4:字符串 uint32_t addrString = 0x08000000+166*1024-6; // 注意地址必须是偶数,且地址段尽量安排在FLASH尾部,避免覆盖了代码段数据 char writeString[100]="早吖! 去哪呢?"; System_WriteInteriorFlash(addrString, (uint8_t*)writeString, sizeof(writeString)); // 存入数据,函数内自动解锁,擦除,写入 char readString[100]; System_ReadInteriorFlash(addrString, (uint8_t*)readString, sizeof(readString)); // 读取数据,函数内自动按半字读取 printf("测试string 地址:0x%X 写入:%s 读取:%s rn", addrString, writeString, readString);
之前建了个Q群,玩STM32的朋友,可以一起吹吹水:262901124.
---------------------
作者:_老周_
来源:CSDN
原文:https://blog.csdn.net/zhouml_msn/article/details/120178349
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件
最后
以上就是单薄雪碧为你收集整理的[转]STM32 --- 使用内部FLASH存储数据一、内部FLASH要点二、读取数据三、存储数据四、应用示例的全部内容,希望文章能够帮你解决[转]STM32 --- 使用内部FLASH存储数据一、内部FLASH要点二、读取数据三、存储数据四、应用示例所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复