IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STM32 --- 在内部FLASH里存储数据 -> 正文阅读

[嵌入式]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; i<secRemain ; i++)                                // 原始数据写入缓存
            sectorbufferTemp[secOff+i] = writeToBuffer[i];
       
        for(uint16_t i=0; i<STM32_FLASH_SECTOR_SIZE/2 ; i++){               // 缓存数据写入芯片FLASH                      
            if(waitForFlashBSY(0x00888888))   return 2;                     // 失败,返回:2, 失败原因:FLASH->SR: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.

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-09-09 11:56:09  更:2021-09-09 11:56:59 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/29 8:37:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计