国产精品久久久久影院,成人午夜福利视频,国产精品久久久久高潮,国产精品 欧美 亚洲 制服,国产精品白浆无码流出

STM32速成筆記(12)—Flash閃存

發(fā)布者:HappyHearted最新更新時間:2024-04-03 來源: elecfans關(guān)鍵字:STM32  Flash閃存  快閃存儲器 手機看文章 掃描二維碼
隨時隨地手機看文章

一、Flash簡介

快閃存儲器(flash memory),是一種電子式可清除程序只讀存儲器的形式,允許在操作中被多次擦或?qū)懙拇鎯ζ?。它是一種非易失性存儲器,即斷電數(shù)據(jù)也不會丟失。

二、STM32F1的Flash

STM32F103ZET6的Flash大小為512KB,屬于大容量產(chǎn)品。在中文參考手冊中給出了大容量產(chǎn)品的Flash模塊組織結(jié)構(gòu)圖

圖片

大容量產(chǎn)品Flsh模塊組織結(jié)構(gòu)圖

  • ? 主存儲器 主存儲器用來存儲我們的代碼和定義的一些常量數(shù)據(jù)。當(dāng)Boot0和Boot1都接GND時,芯片從主存儲器的起始地址0x0800 0000開始運行代碼。

  • ? 信息塊

系統(tǒng)存儲器中存儲的是啟動程序代碼。啟動程序就是串口下載的代碼。當(dāng)Boot0接VCC,Boot1接GND時,運行的就是系統(tǒng)存儲器中的代碼。系統(tǒng)存儲器中存儲的啟動代碼,是ST公司在芯片出廠時就已經(jīng)下載好的,用戶無法修改。選擇字節(jié)是用來配置寫保護和杜保護功能。

  • ? 閃存存儲器接口寄存器 閃存存儲器接口寄存器,是整個閃存的控制機構(gòu),里面包含了很多的閃存的控制寄存器和狀態(tài)寄存器。

在執(zhí)行閃存寫操作時,任何對閃存的讀操作都會被鎖住。只有對閃存的寫操作結(jié)束后,讀操作才能夠正常執(zhí)行。也就是說,在對閃存進行寫操作或者擦除操作時,無法對閃存進行讀操作。

三、Flash操作步驟

  • ? 解鎖和鎖定

  • ? 寫/擦除操作

  • ? 獲取Flash狀態(tài)

  • ? 等待操作完成

  • ? 讀取Flash指定地址數(shù)據(jù)

    四、程序設(shè)計

    操作內(nèi)部Flash時,最小單位是半字(16位)。

    44.1 讀取數(shù)據(jù)

    讀取數(shù)據(jù)用的是指針的方式,在之前博主的文章中有關(guān)于如何利用指針在指定地址讀寫數(shù)據(jù)的操作。 ```c /*

*============================================================================== *函數(shù)名稱:Med_Flash_ReadHalfWord *函數(shù)功能:讀取指定地址的半字(16位數(shù)據(jù)) *輸入?yún)?shù):faddr:讀取地址 *返回值:對應(yīng)讀取地址數(shù)據(jù) *備 注:對內(nèi)部Flash的操作是以半字為單位,所以讀寫地址必須是2的倍數(shù) *============================================================================== */ vu16 Med_Flash_ReadHalfWord (u32 faddr) { return (vu16)faddr; }


```c

/*

 *==============================================================================

 *函數(shù)名稱:Med_Flash_Read

 *函數(shù)功能:從指定地址開始讀出指定長度的數(shù)據(jù)

 *輸入?yún)?shù):ReadAddr:讀取起始地址;pBuffer:數(shù)據(jù)指針;

                        NumToRead:讀?。ò胱郑?shù)

 *返回值:無

 *備  注:對內(nèi)部Flash的操作是以半字為單位,所以讀寫地址必須是2的倍數(shù)

 *==============================================================================

 */

void Med_Flash_Read (u32 ReadAddr,u16 *pBuffer,u16 NumToRead)

{

    u16 i;

    for(i = 0;i < NumToRead;i ++)

    {

        pBuffer[i] = Med_Flash_ReadHalfWord(ReadAddr);   // 讀取2個字節(jié).

        ReadAddr += 2;   // 偏移2個字節(jié). 

    }

}

4.2 寫入數(shù)據(jù)(不檢查)

這里的不檢查,是指在寫入之前,不檢查寫入地址是否可寫。


/*

 *==============================================================================

 *函數(shù)名稱:Med_Flash_Write_NoCheck

 *函數(shù)功能:不檢查的寫入

 *輸入?yún)?shù):WriteAddr:寫入起始地址;pBuffer:數(shù)據(jù)指針;

                        NumToWrite:寫入(半字)數(shù)

 *返回值:無

 *備  注:對內(nèi)部Flash的操作是以半字為單位,所以讀寫地址必須是2的倍數(shù)

 *==============================================================================

 */

void Med_Flash_Write_NoCheck (u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)

{      

    u16 i;

    for(i = 0;i < NumToWrite;i ++)

    {

        FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);

        WriteAddr += 2;   // 地址增加2.

    }  

}

4.3 寫入數(shù)據(jù)(檢查)

/*

 *==============================================================================

 *函數(shù)名稱:Med_Flash_Read

 *函數(shù)功能:從指定地址開始寫入指定長度的數(shù)據(jù)

 *輸入?yún)?shù):WriteAddr:寫入起始地址;pBuffer:數(shù)據(jù)指針;

                        NumToRead:寫入(半字)數(shù)

 *返回值:無

 *備  注:對內(nèi)部Flash的操作是以半字為單位,所以讀寫地址必須是2的倍數(shù)

 *==============================================================================

 */


// 根據(jù)中文參考手冊,大容量產(chǎn)品的每一頁是2K字節(jié)

#if STM32_FLASH_SIZE < 256

    #define STM32_SECTOR_SIZE   1024   // 字節(jié)

#else 

    #define STM32_SECTOR_SIZE   2048

#endif


// 一個扇區(qū)的內(nèi)存

u16 STM32_FLASH_BUF[STM32_SECTOR_SIZE / 2];


void Med_Flash_Write (u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)

{

    u32 secpos;   // 扇區(qū)地址

    u16 secoff;   // 扇區(qū)內(nèi)偏移地址(16位字計算)

    u16 secremain;   // 扇區(qū)內(nèi)剩余地址(16位計算)    

     u16 i;    

    u32 offaddr;   // 去掉0X08000000后的地址

    

    // 判斷寫入地址是否在合法范圍內(nèi)

    if (WriteAddr < STM32_FLASH_BASE || (WriteAddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE)))

    {

        return;   // 非法地址

    }

    

    FLASH_Unlock();   // 解鎖

    offaddr = WriteAddr - STM32_FLASH_BASE;   // 實際偏移地址

    secpos = offaddr / STM32_SECTOR_SIZE;   // 扇區(qū)地址

    secoff = (offaddr % STM32_SECTOR_SIZE) / 2;   // 在扇區(qū)內(nèi)的偏移(2個字節(jié)為基本單位)

    secremain = STM32_SECTOR_SIZE / 2 - secoff;   // 扇區(qū)剩余空間大小

    

    if (NumToWrite <= secremain)

    {

        secremain = NumToWrite;   // 不大于該扇區(qū)范圍

    }

    while (1) 

    {

        // 讀出整個扇區(qū)的內(nèi)容

        Med_Flash_Read(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);

        

        // 校驗數(shù)據(jù)

        for (i = 0;i < secremain;i ++)

        {

            // 需要擦除 

            if (STM32_FLASH_BUF[secoff + i] != 0XFFFF)

            {

                break; 

            }    

        }

        // 需要擦除

        if (i < secremain)

        {

            FLASH_ErasePage(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE);   // 擦除這個扇區(qū)

            

            // 復(fù)制

            for (i = 0;i < secremain;i ++)

            {

                STM32_FLASH_BUF[i + secoff] = pBuffer[i];   

            }

            // 寫入整個扇區(qū)

            Med_Flash_Write_NoCheck(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);

        }

        else

        {

            // 寫已經(jīng)擦除了的,直接寫入扇區(qū)剩余區(qū)間

            Med_Flash_Write_NoCheck(WriteAddr,pBuffer,secremain);

        }

        

        if (NumToWrite == secremain)

        {

            break;   // 寫入結(jié)束了

        }

        // 寫入未結(jié)束

        else

        {

            secpos ++;   // 扇區(qū)地址增1

            secoff=0;   // 偏移位置為0   

            pBuffer+=secremain;   // 指針偏移

            WriteAddr+=secremain;   // 寫地址偏移    

            NumToWrite-=secremain;   // 字節(jié)(16位)數(shù)遞減

            if (NumToWrite >(STM32_SECTOR_SIZE/2))

            {

                secremain=STM32_SECTOR_SIZE/2;   // 下一個扇區(qū)還是寫不完

            }

            else

            {

                secremain=NumToWrite;   // 下一個扇區(qū)可以寫完了

            }

        }  

    } 

    FLASH_Lock();   // 上鎖

}

宏定義如下


// STM32的Flash容量,單位為KB

#define STM32_FLASH_SIZE   512


// FLASH主存儲塊起始地址

#define STM32_FLASH_BASE   0x08000000

上面的讀取數(shù)據(jù)和不檢查的寫入都比較簡單,因此并沒有再做分析。這里分析一下帶檢查的寫入的程序設(shè)計思路。


? 首先用一小段條件編譯來區(qū)分一下大容量產(chǎn)品和其他產(chǎn)品。因為大容量產(chǎn)品的一頁(一個扇區(qū))是2K字節(jié),中小容量產(chǎn)品的一頁是1K字節(jié)。定一個了一個數(shù)組,數(shù)組大小是一個扇區(qū)的大小。

// 根據(jù)中文參考手冊,大容量產(chǎn)品的每一頁是2K字節(jié)

#if STM32_FLASH_SIZE < 256

    #define STM32_SECTOR_SIZE   1024   // 字節(jié)

#else 

    #define STM32_SECTOR_SIZE   2048

#endif


// 一個扇區(qū)的內(nèi)存

u16 STM32_FLASH_BUF[STM32_SECTOR_SIZE / 2];

大容量產(chǎn)品,一個扇區(qū)2K字節(jié),除以2是因為在對內(nèi)部Flash操作時,最小單位是半字。


? 接下來,判斷要寫入的地址是否合法,也就是是否在主存儲塊地址范圍內(nèi)。

// 判斷寫入地址是否在合法范圍內(nèi)

    if (WriteAddr < STM32_FLASH_BASE || (WriteAddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE)))

    {

        return;   // 非法地址

    }

? 如果要寫入的地址合法,那么解鎖后計算一些參數(shù)值。

offaddr = WriteAddr - STM32_FLASH_BASE;   // 實際偏移地址

實際偏移地址 ,指的是要寫入的地址與主存儲塊基地址(0x0800 0000)的差值。


secpos = offaddr / STM32_SECTOR_SIZE;   // 扇區(qū)地址

扇區(qū)地址指的是要寫入的地址所在扇區(qū)前面的扇區(qū)數(shù)。由于所有的參數(shù)都不是浮點型,因此在做除法時,小數(shù)位都是0。最終除出來的結(jié)果就是當(dāng)前扇區(qū)前面的扇區(qū)數(shù)。


secoff = (offaddr % STM32_SECTOR_SIZE) / 2;   // 在扇區(qū)內(nèi)的偏移(2個字節(jié)為基本單位)

在扇區(qū)內(nèi)的偏移指的是要寫入的地址與其所在扇區(qū)首地址的差值。用要寫入的地址取余每一個扇區(qū)的字節(jié)數(shù),余數(shù)就是偏移地址。但是由于操作內(nèi)部Flash時的最小單位是半字,因此要除以2。


secremain = STM32_SECTOR_SIZE / 2 - secoff;   // 扇區(qū)剩余空間大小


扇區(qū)內(nèi)剩余空間大小只需要用扇區(qū)總的空間大小減去偏移地址即可得到。但是需要注意的是,單位都是半字。這里的剩余空間大小,并不是真正的剩余空間大小。而是指寫入地址后面的扇區(qū)大小。這里不太好理解,畫一個圖表示一下。

圖片

扇區(qū)內(nèi)剩余空間大小示意圖


正是因為這里的扇區(qū)剩余空間大小并不是指真正的剩余空間大小。在剩余空間內(nèi),也可能存在已經(jīng)寫入數(shù)據(jù)的地址。所以后面需要進行判斷,來確定是否需要擦除。


? 判斷在寫入地址所在扇區(qū)能否將寫入內(nèi)容全部寫入完成

if (NumToWrite <= secremain)

    {

        secremain = NumToWrite;   // 不大于該扇區(qū)范圍

    }

如果可以,直接將要寫入的半字數(shù)賦值給當(dāng)前扇區(qū)剩余空間大小。如果當(dāng)前扇區(qū)剩余空間大小可以容納要寫入的半字數(shù),那么只需要寫入一次即可,在后續(xù)判斷是否寫完時,直接通過,while循環(huán)只執(zhí)行一次。


? 讀出整個扇區(qū)內(nèi)容,判斷是否需要擦除

// 讀出整個扇區(qū)的內(nèi)容

        Med_Flash_Read(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);

        

        // 校驗數(shù)據(jù)

        for (i = 0;i < secremain;i ++)

        {

            // 需要擦除 

            if (STM32_FLASH_BUF[secoff + i] != 0XFFFF)

            {

                break; 

            }    

[1] [2]
關(guān)鍵字:STM32  Flash閃存  快閃存儲器 引用地址:STM32速成筆記(12)—Flash閃存

上一篇:STM32基礎(chǔ)知識:串口通信-輪詢方式
下一篇:STM32CubeMx計數(shù)器基本使用方法

推薦閱讀最新更新時間:2025-06-24 06:10

STM32 HAL庫 IIC 協(xié)議庫函數(shù)
/* 第1個參數(shù)為I2C操作句柄 第2個參數(shù)為從機設(shè)備地址 第3個參數(shù)為從機寄存器地址 第4個參數(shù)為從機寄存器地址長度 第5個參數(shù)為發(fā)送的數(shù)據(jù)的起始地址 第6個參數(shù)為傳輸數(shù)據(jù)的大小 第7個參數(shù)為操作超時時間 */ HAL_I2C_Mem_Write(&hi2c2,salve_add,0,0,PA_BUFF,sizeof(PA_BUFF),0x10); HAL_I2C_Mem_Write_IT(); HAL_I2C_Mem_Read(); HAL_I2C_Mem_Read_IT(); HAL_I2C_Mem_Read_DMA(); HAL_I2C_Mem_Write_DMA(); HAL_I2C_Mas
[單片機]
stm32系統(tǒng)架構(gòu)及其時鐘系統(tǒng)
1 系統(tǒng)組成 八個單元----4主動,4被動,主動或被動單元與總線矩陣有關(guān)。 2 時鐘信號處理模式 3 時鐘設(shè)計 3.1 時鐘源分類 3.1.1RC時鐘 RC時鐘其特點是精度低,但體積小,因此經(jīng)常被單片機作為內(nèi)置時鐘 3.1.2 OSC時鐘 osc時鐘與RC正好相反,即更高的精度與更大的體積,在需要高精度時鐘信號的場景中作為外接時鐘接入 4 選擇器開關(guān) 4.1 作用 他的作用為選擇時鐘信號由它來絕定何種時鐘信號可以通過,下圖中用綠線標(biāo)出 4.2分頻器 4.2.1預(yù)分頻器 預(yù)分頻器對時鐘信號進行預(yù)分頻 4.2.2倍頻器 5 控制器(與門)
[單片機]
STM32讀取HMC5883L的偏航角數(shù)據(jù)
HMC5883L是一種IIC通信的電子羅盤芯片,輸出與北的角度偏差,數(shù)值是0-360度,靠西方向增長,可以理解為逆時針為正。配合GPS模塊簡直就是四軸飛行器的鐵桿標(biāo)配。 1. 5883的初始化 HMC5883L也是一個IIC器件,具體的操作方式和前面介紹的MPU6050類似,這里就不在贅述了。主要區(qū)別在于5883作為IIC從機時,其地址為0x3C。 1.1 初始化HMC5883 初始化HMC5883的方法和初始化MPU6050的方法差不太多,主要是模擬出IIC信號,對對應(yīng)的寄存器操作,具體的可查看HMC5883的說明手冊。 void HMC5883L_Init() { MPU_IIC_Init();//初始化IIC MP
[單片機]
單片機stm32直流電機驅(qū)動與測速學(xué)習(xí)筆記
  通過實驗發(fā)現(xiàn),定時器的一個通道控制一個pwm信號。   在正式開始之前也可以參考這個視頻學(xué)習(xí)資料   (stm32直流電機驅(qū)動)   http://www.makeru.com.cn/live/1392_1218.html?s=45051   超聲波雷達測距儀   http://www.makeru.com.cn/live/15971_2626.html?s=45051   PWM驅(qū)動電機不需要中斷。   ① timer.h:   #ifndef __TIMER_H   #define __TIMER_H   #include “sys.h”   void TIM3_PWM_Init(u16 arr,u16 psc);   #
[單片機]
stm32 調(diào)試
keil armcc這編譯,elf運行不正常(突然崩潰、停在HardFault_Handler (void)),照著VSM Studio改了改編譯選項似乎還是不行 還是用gcc編譯出elf正常 --c99 -c --cpu Cortex-M3 -D__EVAL -g -O0 --apcs=interwork --split_sections -I ...... -D__UVISION_VERSION= 518 -D_RTE_ -DUSE_HAL_DRIVER -DSTM32F103x6 -o jiaotongdeng*.o --omf_browse jiaotongdeng*.crf --depend jiaotongdeng*.
[單片機]
STM32 LL庫編程】端口復(fù)用與重映射
0x01 什么是端口復(fù)用 STM32有很多的內(nèi)置外設(shè),這些外設(shè)的外部引腳都是與GPIO復(fù)用的。 一個GPIO如果可以復(fù)用為內(nèi)置外設(shè)的功能引腳,那么當(dāng)這個GPIO作為內(nèi)置外設(shè)使用的時候,就叫做復(fù)用。 端口復(fù)用 通過上圖我們可以將 PA9、PA10 復(fù)用為 USART1_TX UASRT1_RX 或 定時器 TIM1的通道2和通道3 0x02 端口復(fù)用 端口復(fù)用步驟包括 使能端口時鐘 使能復(fù)用的端口時鐘 對應(yīng)的端口模式設(shè)置 在配置復(fù)用端口時如何選擇端口模式 ? 在 IO 復(fù)用位內(nèi)置 外設(shè)功能引腳的時候,必須設(shè)置GPIO 端口的模式,至于在復(fù)用功能下 GPIO 的模式是怎么對應(yīng),這個可以查看手冊《 STM32 STM
[單片機]
gentoo stm32 交叉編譯環(huán)境
crossdev -t s4 --gcc =5.4.0 -v arm-none-eabi gcc版本為當(dāng)前系統(tǒng)安裝的版本,通過gcc -v 可以獲得 編譯完成后: arm-none-eabi-addr2line arm-none-eabi-elfedit arm-none-eabi-gcc-ranlib arm-none-eabi-ld.gold arm-none-eabi-size arm-none-eabi-ar arm-none-eabi-emerge arm-none-eabi-gcov arm-none-eabi-nm arm-none-eabi-strings arm-none-ea
[單片機]
STM32自學(xué)筆記 零死角玩轉(zhuǎn)STM32
工程師經(jīng)驗手記例說STM32 將由淺入深,帶領(lǐng)大家進入STM32的世界。本書總共分為3篇:第1篇為硬件篇,主要介紹實驗平臺;第2篇為軟件篇,主要介紹STM32開發(fā)軟件的使用以及一些下載調(diào)試的技巧,并詳細地介紹了幾個常用的系統(tǒng)文件(程序);第3篇為實戰(zhàn)篇,通過38個實例(絕大部分是直接操作寄存器完成的)帶領(lǐng)大家一步步深入STM32的學(xué)習(xí)。相較于第2版,本書在開發(fā)環(huán)境、源代碼、教程說明等方面進行了更新和完善。 STM32自學(xué)筆記 《STM32自學(xué)筆記》以新穎的思路、簡單的邏輯、簡潔的語言來闡述作者初遇STM32以來的種種認識,書中多處內(nèi)容都是由作者從STM32初學(xué)時的實踐中總結(jié)而來。 本書主要介紹ARM Cortex—M3系列
[單片機]
小廣播
設(shè)計資源 培訓(xùn) 開發(fā)板 精華推薦

最新單片機文章

 
EEWorld訂閱號

 
EEWorld服務(wù)號

 
汽車開發(fā)圈

 
機器人開發(fā)圈

電子工程世界版權(quán)所有 京ICP證060456號 京ICP備10001474號-1 電信業(yè)務(wù)審批[2006]字第258號函 京公網(wǎng)安備 11010802033920號 Copyright ? 2005-2025 EEWORLD.com.cn, Inc. All rights reserved