• 浅析STM32内部FLASH读写


    http://www.openedv.com/docs/index.html

    这个是正点原子开发板的下载资料,您可以到这个网站下载STM32F103精英板资料,拿到具体例程(实验32 FLASH模拟EEPROM实验)。

    此例程是基于STM32F103精英板(标准库)进行开发,对STM32内部的FLASH进行读写操作。

    通过main函数,我们来解析这个函数的目的

    #include "led.h"
    #include "delay.h"
    #include "key.h"
    #include "sys.h"
    #include "lcd.h"
    #include "usart.h"     
    #include "stmflash.h"
     
     
    /************************************************
     ALIENTEK精英STM32开发板实验32
     FLASH模拟EEPROM 实验   
     技术支持:www.openedv.com
     淘宝店铺:http://eboard.taobao.com 
     关注微信公众平台微信号:"正点原子",免费获取STM32资料。
     广州市星翼电子科技有限公司  
     作者:正点原子 @ALIENTEK
    ************************************************/
    
    
    //要写入到STM32 FLASH的字符串数组
    const u8 TEXT_Buffer[]={"22STM32FLASH_TEXT22"};
    #define SIZE sizeof(TEXT_Buffer)        //数组长度
    #define FLASH_SAVE_ADDR  0X08070000        //设置FLASH 保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000)
    
     int main(void)
     {     
        u8 key;
        u16 i=0;
        u8 datatemp[SIZE];
    
        delay_init();             //延时函数初始化      
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
        uart_init(115200);         //串口初始化为115200
         LED_Init();                      //初始化与LED连接的硬件接口
        KEY_Init();                    //初始化按键
        LCD_Init();                       //初始化LCD  
         POINT_COLOR=RED;            //设置字体为红色 
        LCD_ShowString(30,50,200,16,16,"ELITE STM32");    
        LCD_ShowString(30,70,200,16,16,"FLASH EEPROM TEST");    
        LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
        LCD_ShowString(30,110,200,16,16,"2015/1/18"); 
        LCD_ShowString(30,130,200,16,16,"KEY1:Write  KEY0:Read");
        while(1)
        {
            key=KEY_Scan(0);
            if(key==KEY0_PRES)
            {
                LCD_Fill(0,170,239,400,WHITE);
                LCD_ShowString(30,170,200,16,16,"The Data Readed Is:");
                STMFLASH_Read(FLASH_SAVE_ADDR,(u16 *)datatemp,SIZE);
                LCD_ShowString(30,190,200,16,16,datatemp);
                
            }
            else if(key==KEY1_PRES)
            {
                LCD_Fill(0,170,239,400,WHITE);
                LCD_ShowString(30,170,200,16,16,"FLASH Write Finished!");
                STMFLASH_Write(FLASH_SAVE_ADDR,(u16 *)TEXT_Buffer,SIZE);
            }
            i++;
            delay_ms(10);  
            if(i==20)
            {
                LED0=!LED0;//提示系统正在运行    
                i=0;
            }           
        } 
    }

    mian函数大体是对各种外设进行初始化:

      uart_init(115200);         //串口初始化为115200               =========〉通过串口1把数据传输到LCD,LCD才能正常显示
         LED_Init();                      //初始化与LED连接的硬件接口     =========〉对流水灯轮流亮灭,确认工程是否在正常工作
        KEY_Init();                    //初始化按键              ==========>按键初始化,当某一按键按下时,执行对应操作
        LCD_Init();                       //初始化LCD             ==========>对LCD屏幕初始化,可以人机界面交流
    如mian函数中红色部分,按键0(KEY0)按下对FLASH进行读取操作,并通过LCD屏幕显示出来“The Data Readed Is: 11STM32FLASH_TEXT11”
               按键1(KEY1)按下时对FLASH进行写入操作,并通过LCD显示“FLASH Write Finished!”



    接下来就要来到今天的重头戏了,FLASH如何实现读写?
    其中最重要的是STMFLASH_Read和STMFLASH_Write这两个函数。
    提到FLASH的读写函数,我把读写函数的流程通过一个图说明一下


    我们通过具体的代码来分析,其中注释基本已经清晰明了地说明了
    #include "stmflash.h"
    
    /*
    函数名:STMFLASH_ReadHalfWord
    功能:    读取半字底层函数
    输入:  (u32*)fladdr(地址)
    返回:  (u16)    (数据)
    */
    u16 STMFLASH_ReadHalfWord(u32 fladdr)
    {
        return *(u32*)fladdr;
    }
    
    
    /*
    函数名:STMFLASH_WriteNoCheck
    功能:    利用库函数(写半字进FLASH的底层函数)对多个数据进行写入
    输入: (u32*)fladdr(写入地址) (u16*)WriteBuf(要写入的数据)   (u16)NumToWrite(写入数据的个数)
    返回:  NULL
    */
    //写入多个数据进入Flash
    //这个FLAS有512K字节,但是这里把一次最多读取限制为(u16 0xFFFFFFFF=65536字节=64K字节)
    void STMFLASH_WriteNoCheck(u32 fladdr,u16* Write_Buf,u16 NumToWrite)
    {
        u16 i;
        for(i=0;i<NumToWrite;i++)
        {
            FLASH_ProgramHalfWord(fladdr,Write_Buf[i]);
            fladdr+=2;
        }
    }
    
    
    
    /*
    函数名:STMFLASH_Read
    功能:    对多个数据进行读取
    输入: (u32*)fladdr(读取地址) (u16*)WriteBuf(要读取的数据)   (u16)NumToWrite(读取数据的个数)
    返回:  NULL
    */
    void STMFLASH_Read(u32 fladdr,u16* Read_Buf,u16 NumToRead)
    {
        u16 i;
        for(i=0;i<NumToRead;i++)
        {
            Read_Buf[i]=STMFLASH_ReadHalfWord(fladdr);
            fladdr+=2;
        }
    }
    
    
    
    /*
    函数名:STMFLASH_Write
    功能:    分扇区对Flash进行写入操作
    输入: (u32*)fladdr(写入地址) (u16*)WriteBuf(要写入的数据)   (u16)NumToWrite(写入数据的个数)
    返回:  NULL
    注意:  每次进行对Flash操作之间要进行解锁操作,之后再上锁,不然会造成数据写入失败
    */
    
    #define STMFLASH_Sector_Size 2048
    u16 FLASH_BUF[STMFLASH_Sector_Size/2];
    void STMFLASH_Write(u32 fladdr,u16* Write_Buf,u16 NumToWrite)
    {
        u16 i;
        u32 offaddr;
        u16 secpos,secoff,secremain;
        offaddr=fladdr-STM32_FLASH_BASE;                //去掉基地址之后,得出偏移量
        secpos=offaddr/STMFLASH_Sector_Size;                //偏移量/扇区大小=当前所在扇区块
        secoff=(fladdr%STMFLASH_Sector_Size)/2;                //(当前扇区区域)已经写入的次数
        secremain=STMFLASH_Sector_Size/2-secoff;            //(扇区剩余区域)可写的次数
        if(secremain>=NumToWrite) secremain=NumToWrite;
        FLASH_Unlock();                        //解锁
        while(1)
        {
            STMFLASH_WriteNoCheck(secpos*STMFLASH_Sector_Size+STM32_FLASH_BASE,FLASH_BUF,STMFLASH_Sector_Size/2);//读取要写入的扇区的数据
            
            for(i=0;i<secremain;i++)                    //判断该扇区的空余区域是否都是空的
            {
                if(FLASH_BUF[i+secoff]!=0xFFFF)
                    break;
                
            }
            
            if(i<secremain)                            //非空情况,要先擦除,在进行写入
            {
                FLASH_ErasePage(secpos*STMFLASH_Sector_Size+STM32_FLASH_BASE);
                for(i=0;i<secremain;i++)
                {
                    FLASH_BUF[secoff+i]=Write_Buf[i];
                }
                STMFLASH_WriteNoCheck(secpos*STMFLASH_Sector_Size+STM32_FLASH_BASE,FLASH_BUF,secremain);
            
            }
            else
            {
                STMFLASH_WriteNoCheck(fladdr,Write_Buf,NumToWrite);
            }
            
            if(secremain==NumToWrite) break;
            else                  //假如一个扇区还写不完,则进入下一个扇区
            {
                secpos++;          //扇区加一,可写入的次数清零
                secoff=0;
                
                fladdr+=(secremain*2);   //写入地址要加上已经写过的地址偏移量
                Write_Buf+=secremain;    //数组的起始地址要加上已经写入的次数
                NumToWrite-=secremain;   //写入的个数要减去已经写入的次数
                if(NumToWrite>=1024) secremain=1024;  //本个扇区要写入的次数
                else secremain=NumToWrite;
                
            }
            
        }
        FLASH_Lock();//上锁
        
    }

      这上面毕竟值得注意的是在对内部FLASH进行写入操作时要记得解锁FLASH_Unlock();,写完之后要记得上锁FLASH_Lock();

      接着必须说明一下库函数FLASH_ErasePage

    if(status == FLASH_COMPLETE)
        { 
          /* if the previous operation is completed, proceed to erase the page */
          FLASH->CR|= CR_PER_Set;                //PER:页擦除选择擦除页。
          FLASH->AR = Page_Address;         //当进行页擦除时,通过AR设置要擦除的页地址
          FLASH->CR|= CR_STRT_Set;            //STRT:开始当该位为’1’时将触发一次擦除操作。该位只可由软件置为’1’并在BSY变为’1’时清为’0’。
        
          /* Wait for last operation to be completed */
          status = FLASH_WaitForLastBank1Operation(EraseTimeout);        //延时等待擦除操作完成
    
          /* Disable the PER Bit */
          FLASH->CR &= CR_PER_Reset;
        }

      

      其中值得一提的是FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)函数

     该函数也是官方给出的,我们只需要用就好了。但要注意,这个是个半字的写操作,即uint16_t 的数据算半字呢,
    因为单片机是32的,对于32位单片机系统来说,一个字是4个字节的,8位的比如51单片机系统一个字就是2位的,
    64位单片机系统一个字就是8个字节,脱离单片机系统说字是多少个字节是没意义的。
    所以这里写入/读出半字也就是一次写入2个字节,写完/读出一次地址会加2。
    if(Address < FLASH_BANK1_END_ADDRESS)
      {
        if(status == FLASH_COMPLETE)
        {
          /* if the previous operation is completed, proceed to program the new data */
          FLASH->CR |= CR_PG_Set;                //PG:编程选择编程操作。        
      
          *(__IO uint16_t*)Address = Data;//对写入地址进行赋值
          /* Wait for last operation to be completed */
          status = FLASH_WaitForLastBank1Operation(ProgramTimeout);        //延时等待操作完成
    
          /* Disable the PG Bit */
          FLASH->CR &= CR_PG_Reset;                        //把PG位清零,即停止编写操作
        }
      }

     在下面将插入一张FLASH->CR(内存控制寄存器)的各个位的作用,各位请参考一下。





  • 相关阅读:
    Lombok 安装、入门
    详细解析@Resource和@Autowired的区别 , 以及@Qualifier的作用
    Spring中@Resource与@Autowired、@Qualifier的用法与区别
    springMVC整合swagger
    jetty maven插件
    【原创】Sagger使用
    Eclipse详细设置护眼背景色和字体颜色
    eclipse中相同代码的高亮显示
    Mybatis分页插件
    mybatis
  • 原文地址:https://www.cnblogs.com/18689400042qaz/p/13340203.html
Copyright © 2020-2023  润新知