淺析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(內存控制寄存器)的各個位的作用,各位請參考一下。





 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM