[转]STM32 --- 使用内部FLASH存储数据

本文记录了对一些知识点的理解、操作方法,如有错误,请务必批评指正!!

最终的测试截图:

目录

一、内部FLASH要点

关于地址:

关于解锁:

关于擦除:

关于写入:

二、读取数据

三、存储数据

四、应用示例


一、内部FLASH要点

关于地址:

  1. 内部FLASH地址开始地址:0x0800 0000;
  2. 结束地址:0x08000000+FLASH大小
  3. FLASH的大小,可根据芯片型号得知,如F103x8=64K,  F103xC=256K, F103xE=512K
  4. FLASH的大小,也可读*(uint16_t*)0x1FFFF7E0直接获得;

关于解锁:

  1. 对FLASH寄存器的操作, 需要在操作之前解锁;
  2. 如果解锁过程发生错误,FLASH寄存器操作将死锁至掉电;
  3. 解锁步骤(1): FLASH->KEYR= (uint32_t)0x45670123;
  4. 解锁步骤(2): FLASH->KEYR= (uint32_t)0xCDEF89AB;

关于擦除:

  1. 内部FLASH空间,按页划分成连续的数据块;
  2. 每次擦除的最小单位是:页;
  3. F103中,中小容量FLASH的页(扇区)大小为1K, 而大容量的页大小为2K
  4. 每页擦除时长:中小容量约30ms, 大容量约50ms
  5. 页寿命:中小容量约可擦: 1K次, 大容量约可擦: 10K次
  6. 擦除步骤(1):FLASH->CR|= 1<<1;             // 选择以页进行擦除
  7. 擦除步骤(2):FLASH->AR = 0x0800xxxx;  // 要擦除的页地址
  8. 擦除步骤(3):FLASH->CR|= 0x40;             // 开始擦除
  9. 擦除步骤(4):while((FLASH->SR & 0x01); // 等待空闲,即BSY=0

关于写入:

  1. 写入的地址,必须是偶数;
  2. 每一次写入,必须以半字进行(uint16_t)!单字节或全字将发生错误;
  3. 写入步骤(1):  FLASH->CR |= 0x01<<0;                // 使能可编程
  4. 写入步骤(2):  *(uint16_t*)addr = (uint16_t)value;  // 写入数据
  5. 写入步骤(3):  while((FLASH->SR & 0x01);           // 等待空闲,即BSY=0
  6. 写入步骤(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("\r\n>>>>开始测试内部FLASH读写,及数据转换:\r\n");                 // 测试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 \r\n", 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 \r\n", 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 \r\n", 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 \r\n", addrString, writeString, readString);

之前建了个Q群,玩STM32的朋友,可以一起吹吹水:262901124.

---------------------
作者:_老周_
来源:CSDN
原文:https://blog.csdn.net/zhouml_msn/article/details/120178349
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

  • 5
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用STM32F103C8T6内部FLASH存储数据的示例代码: ```c #include "stm32f10x.h" #define FLASH_USER_START_ADDR ((uint32_t)0x08008000) // FLASH起始地址 #define FLASH_USER_END_ADDR ((uint32_t)0x0800FFFF) // FLASH结束地址 uint32_t FLASH_WriteData(uint32_t address, uint32_t data) { uint32_t flashstatus = 0; if (address < FLASH_USER_START_ADDR || address > FLASH_USER_END_ADDR) return 1; // 地址非法 FLASH_Unlock(); flashstatus = FLASH_ErasePage(address); // 擦除页 if (flashstatus == FLASH_COMPLETE) { flashstatus = FLASH_ProgramWord(address, data); // 写入数据 if (flashstatus == FLASH_COMPLETE) return 0; // 写入成功 else return 2; // 写入失败 } else return 3; // 擦除失败 } uint32_t FLASH_ReadData(uint32_t address) { if (address < FLASH_USER_START_ADDR || address > FLASH_USER_END_ADDR) return 0xFFFFFFFF; // 地址非法 return (*(__IO uint32_t*)address); // 读取数据 } ``` 在这个示例代码中,`FLASH_USER_START_ADDR` 和 `FLASH_USER_END_ADDR` 定义了我们可以使用FLASH地址范围。`FLASH_WriteData` 函数用于向指定地址写入数据,`FLASH_ReadData` 函数用于从指定地址读取数据。需要注意的是,写入操作会先擦除整个写入页,因此写入速度会比较慢。 使用示例: ```c uint32_t data = 0x12345678; if (FLASH_WriteData(FLASH_USER_START_ADDR, data) == 0) printf("Write data to FLASH succeeded!\r\n"); uint32_t read_data = FLASH_ReadData(FLASH_USER_START_ADDR); if (read_data == data) printf("Read data from FLASH succeeded!\r\n"); ``` 这个示例将 `data` 写入到FLASH的起始地址,然后再从这个地址读取数据,如果读取到的数据与写入的数据相同,就说明读写操作成功。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值