リファレンス チュートリアル:[15-1] FLASH_bilibili_bilibili
1. STM32F1 シリーズのフラッシュには、プログラム メモリ、システム メモリ、オプション バイトの 3 つの部分が含まれています。プログラム メモリとオプション バイトには、フラッシュ メモリ インターフェイス (ペリフェラル)、消去とプログラムを実行します。
(1) FLASH の読み書きの目的:
①プログラムメモリの残りのスペースを使用して、停電後も失われないユーザーデータを保存します。
② プログラム(IAP)への書き込みにより、プログラムの自己更新を実現します。
(2) オンライン プログラミング (インサーキット プログラミング – ICP) は、プログラム メモリの内容全体を更新するために使用され、JTAG、SWD プロトコル、またはシステム ローダー (ブートローダー) を通じてプログラムをダウンロードします。
(3) アプリケーション内プログラミング – IAP) は、マイクロコントローラーがサポートする任意の通信インターフェイスを使用してプログラムをダウンロードできます。
2. フラッシュ メモリ モジュールの構成:
3. フラッシュの基本構造:
4.フラッシュのロック解除:
(1) FPEC には 3 つのキー値があります: RDPRT キー = 0x000000A5; KEY1 = 0x45670123; KEY2 = 0xCDEF89AB
(2) ロックを解除する:
①リセット後、FPEC はプロテクトされており、デフォルトでは FLASH_CR (制御レジスタ) に書き込むことができません。
②在FLASH_KEYR先写入KEY1,再写入KEY2,即可解锁。
③操作シーケンスを誤ると、次のリセット前に FPEC と FLASH_CR がロックされます。
(3) ロック:
FLASH_CR の LOCK ビットを設定して、FPEC と FLASH_CR をロックします。
5. ポインタを使用してメモリにアクセスします。
(1) ポインタを使用して、指定されたアドレスのメモリを読み取ります。
uint16_t Data = *((__IO uint16_t *)(0x08000000)); //读地址0x08000000下的2个字节(16位,半字)
(2) ポインタを使用して、指定されたアドレスにメモリを書き込みます。
*((__IO uint16_t *)(0x08000000)) = 0x1234; //写2个字节(16位,半字)的数据0x1234到地址0x08000000
で:
#define __IO volatile //volatile在逻辑上没有什么作用,只是为了防止编译器优化
6. プログラムメモリプログラミング:
(1) 最初に制御レジスタの LOCK ビットを読み取ります。LOCK=1 の場合は、操作を続行する前にロックを解除する必要があります (まず KEY1、次に KEY2 を KEYR レジスタに書き込みます)。LOCK=0 の場合、プログラミングを続行できます ( Enter) 操作を書き込みます。
(2) CR レジスタの PG ビットを 1 にセットして書き込み動作を開始しますが、STM32 フラッシュメモリはデータを書き込む前に、指定されたアドレスが消去されているかどうかを確認します。消去されていない場合は、 STM32 は書き込み操作を実行しません。 (さらに各書き込みは一度に半ワード、つまり 16 ビットを書き込む必要があります)
(3) 書き込み処理にはある程度の時間がかかりますが、この間、CR レジスタの BSY ビットが 0 にセットされるまで、つまり書き込み動作が完了するのを待ちます。
(4) 「全ページのデータを読み出して検証する」ステップはテストプログラムの一ステップであり、通常プログラムでは実行されません。
7. プログラム メモリ ページの消去: (消去された部分はすべて 1 に設定されます)
(1) 最初に制御レジスタの LOCK ビットを読み取ります。LOCK=1 の場合は、操作を続行する前にロックを解除する必要があります (KEYR レジスタに KEY1 を書き込み、次に KEY2 を書き込みます)。LOCK=0 の場合は続行できます。消去操作で。
(2) まず CR レジスタの PER ビットを 1 に設定し、消去するページを AR レジスタに書き込み、次に STRT ビットを 1 に設定します。STRT を 1 に設定することがトリガ条件となり、チップが動作を開始します。 、PER=1 チップがページ消去動作を実行する必要があると判断される。
(3) 消去処理にはある程度の時間がかかりますが、この間、プログラムは CR レジスタの BSY ビットが 0 にセットされるまで、つまり消去動作が完了するのを待ちます。
(4) 「全ページのデータを読み出して検証する」ステップはテストプログラムの一ステップであり、通常プログラムでは実行されません。
8. プログラムメモリの完全消去:
(1) 最初に制御レジスタの LOCK ビットを読み取ります。LOCK=1 の場合は、操作を続行する前にロックを解除する必要があります (KEYR レジスタに KEY1 を書き込み、次に KEY2 を書き込みます)。LOCK=0 の場合は続行できます。消去操作で。
(2) 最初に CR レジスタの MER ビットを 1 に設定し、STRT ビットを 1 に設定します。STRT を 1 に設定すると、チップが動作を開始するトリガ条件になります。MER=1 は、チップがフル動作を実行する必要があることを決定します。消去操作。
(3) 消去処理にはある程度の時間がかかりますが、この間、プログラムは CR レジスタの BSY ビットが 0 にセットされるまで、つまり消去動作が完了するのを待ちます。
(4) 「全ページのデータを読み出して検証する」ステップはテストプログラムの一ステップであり、通常プログラムでは実行されません。
9. オプションバイト:(nXXXはXXXの補数を表します)
(1) 情報ブロックの組織構造:
①RDP:RDPRTキー(0x000000A5)への書き込み後、読み取り保護を解除します。
②USER: ハードウェアウォッチドッグと、シャットダウン/スタンバイモードに入るときにリセットを生成するかどうかを設定します。
③Data0/1: ユーザーがカスタマイズできます。
④WRP0/1/2/3: 書き込み保護を設定します。各ビットは 4 つのメモリページ (中容量) を保護します。
(2) オプションバイトプログラミング:
① フラッシュメモリのロックを解除(LOCK ビットのロックを解除)し、FLASH_SR の BSY ビットをチェックして、他の書き込み動作が実行されていないことを確認します。
② FLASH_CR の OPTWRE ビットのロックを解除します。
③FLASH_CRのOPTPGビットを1に設定します。
④指定されたアドレスにプログラムしたいハーフワードを書き込みます。
⑤BSYビットが0になるのを待ちます。
⑥書き込まれたアドレスを読み出し、データを確認します。
(3) オプションバイト消去:
① フラッシュメモリのロックを解除(LOCK ビットのロックを解除)し、FLASH_SR の BSY ビットを確認し、他にフラッシュメモリの動作が行われていないことを確認します。
② FLASH_CR の OPTWRE ビットのロックを解除します。
③FLASH_CRのOPTERビットを1に設定します。
④FLASH_CRのSTRTビットを1に設定します。
⑤BSYビットが0になるのを待ちます。
⑥消去された選択バイトを読み出して確認します。
10. デバイス電子署名: 電子署名はフラッシュメモリモジュールのシステム記憶領域に保存されます. 含まれるチップ識別情報は工場で書き込まれ、変更できません. ポインタを使用して指定されたアドレスのメモリを読み取ります電子署名を取得します。
(1) フラッシュメモリ容量レジスタ: ベースアドレスは 0x1FFF F7E0、サイズは 16 ビットです。
(2) 製品固有 ID レジスタ: ベース アドレスは 0x1FFF F7E8、サイズは 96 ビットです。
11. 内部フラッシュの読み取りと書き込み:
(1) 下図のように回路を接続し、OLED ディスプレイのプロジェクトフォルダーをコピーしてテンプレートとして使用します。
(2) stm32f10x_flash.h ファイルにはフラッシュメモリモジュールに関する関数があります。
[1]FLASH_Unlock函数:解锁LOCK位。
[2]FLASH_Lock 関数: LOCK ビットをロックします。
[3]FLASH_ErasePage 関数: フラッシュ メモリ内のページを消去します。
[4]FLASH_EraseAllPages 関数: Flash はすべてのページを消去します。
[5]FLASH_EraseOptionBytes 関数: オプション バイトを消去します。
[6]FLASH_ProgramWord 関数: フラッシュ メモリの指定されたアドレスにワード (32 ビット) を書き込みます。
[7]FLASH_ProgramHalfWord 関数: フラッシュ メモリの指定されたアドレスにハーフ ワード (16 ビット) を書き込みます。
[8]FLASH_ITConfig 関数: 割り込みを有効にします。
[9]FLASH_GetFlagStatus関数:ステータスフラグビットを取得します。
[10]FLASH_ClearFlag関数:ステータスフラグビットをクリアします。
[11]FLASH_GetStatus関数:現在のステータスを取得します。
[12]FLASH_WaitForLastOperation 関数: 最後のオペレーションが完了するまで待ちます (BSY=0 を待ちます)。
(3) MyFLASH.h ファイルと MyFLASH.c ファイルをプロジェクトの System グループに追加し、フラッシュ メモリ モジュールのコードをカプセル化します。
①MyFLASH.hファイル:
#ifndef __MyFLASH_H
#define __MyFLASH_H
uint32_t MyFLASH_ReadWord(uint32_t Address);
uint16_t MyFLASH_ReadHalfWord(uint32_t Address);
uint8_t MyFLASH_ReadByte(uint32_t Address);
void MyFLASH_EraseAllPages(void);
void MyFLASH_ErasePages(uint32_t PageAddress);
void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data);
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
#endif
②MyFLASH.cファイル:
#include "stm32f10x.h" // Device header
uint32_t MyFLASH_ReadWord(uint32_t Address) //读取Address地址下的一个字
{
return *((__IO uint32_t *)(Address));
}
uint16_t MyFLASH_ReadHalfWord(uint32_t Address) //读取Address地址下的半个字
{
return *((__IO uint16_t *)(Address));
}
uint8_t MyFLASH_ReadByte(uint32_t Address) //读取Address地址下的一个字节
{
return *((__IO uint8_t *)(Address));
}
void MyFLASH_EraseAllPages(void) //擦除全部页
{
FLASH_Unlock(); //解锁
FLASH_EraseAllPages(); //擦除全部页
FLASH_Lock(); //加锁
}
void MyFLASH_ErasePages(uint32_t PageAddress) //擦除页起始地址PageAddress下的页
{
FLASH_Unlock(); //解锁
FLASH_ErasePage(PageAddress); //擦除指定页
FLASH_Lock(); //加锁
}
void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data) //往Address地址写一个字Data
{
FLASH_Unlock(); //解锁
FLASH_ProgramWord(Address, Data); //往指定地址写一个字
FLASH_Lock(); //加锁
}
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) //往Address地址写半个字Data
{
FLASH_Unlock(); //解锁
FLASH_ProgramHalfWord(Address, Data); //往指定地址写半个字
FLASH_Lock(); //加锁
}
(4) Store.h ファイルと Store.c ファイルをプロジェクトの System グループに追加して、ビジネス層のコードをカプセル化します。
①Store.hファイル:
#ifndef __Store_H
#define __Store_H
extern uint16_t Store_Data[];
void Store_Init(void);
void Store_Save(void);
void Store_Clear(void);
#endif
②Store.cファイル:
#include "stm32f10x.h" // Device header
#include "MyFLASH.h"
#define STORE_START_ADDRESS 0x800FC00 //使用闪存0x800FC00的一页进行调试(只要程序占用空间不大,该页一般空闲)
#define STORE_COUNT 512 //一页有512个半字
uint16_t Store_Data[STORE_COUNT]; //存储在SRAM中的数组,用于暂存FLASH中的数据
void Store_Init(void)
{
if(MyFLASH_ReadHalfWord(STORE_START_ADDRESS) != 0xA5A5) //闪存的0x800FC00页只初始化一次即可
{
MyFLASH_ErasePages(STORE_START_ADDRESS); //擦除0x800FC00页
MyFLASH_ProgramHalfWord(STORE_START_ADDRESS, 0xA5A5);
//0x800FC00页的第一个半字用来做标志,如果初始化过一次,数据掉电不丢失,重新上电后该半字仍为0xA5A5
for(uint16_t i = 1; i < STORE_COUNT; i++) //该页剩下的空间全部初始化为0
{
MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, 0x0000);
}
}
for(uint16_t i = 0; i < STORE_COUNT; i++) //将0x800FC00页的数据拷贝到SARM的数组中,便于程序修改
{
Store_Data[i] = MyFLASH_ReadHalfWord(STORE_START_ADDRESS + i * 2);
}
}
void Store_Save(void) //将SARM中的数据保存到闪存的0x800FC00页
{
MyFLASH_ErasePages(STORE_START_ADDRESS); //先擦除原来保存的数据
for(uint16_t i = 0; i < STORE_COUNT; i++) //把SRAM中数组的数据逐半字写入FLASH
{
MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, Store_Data[i]);
}
}
void Store_Clear(void) //清空0x800FC00页保存的数据(不是擦除)
{
for(uint16_t i = 1; i < STORE_COUNT; i++) //先把SARM中暂存的数据请空
{
Store_Data[i] = 0;
}
Store_Save(); //将SARM中的数据保存到闪存的0x800FC00页
}
(5) 次のコードを main.c ファイルに貼り付けてコンパイルし、デバッグ用にプログラムを開発ボードにダウンロードします。
#include "stm32f10x.h" // Device headerCmd
#include "OLED.h"
#include "Key.h"
#include "Store.h"
uint8_t KeyNum;
int main()
{
OLED_Init();
Key_Init();
Store_Init();
OLED_ShowString(1,1,"Flag:");
OLED_ShowString(2,1,"Data:");
while(1)
{
KeyNum = Key_GetNum();
if(KeyNum == 1) //按下按键1,修改闪存数据
{
Store_Data[1] += 1;
Store_Data[2] += 2;
Store_Data[3] += 3;
Store_Data[4] += 4;
Store_Save(); //保存数据
}
if(KeyNum == 2) //按下按键2,清空指定页STORE_START_ADDRESS的数据
{
Store_Clear(); //清空数据
}
OLED_ShowHexNum(1,6,Store_Data[0],4); //0xA5A5(标志)
OLED_ShowHexNum(3,1,Store_Data[1],4);
OLED_ShowHexNum(3,6,Store_Data[2],4);
OLED_ShowHexNum(4,1,Store_Data[3],4);
OLED_ShowHexNum(4,6,Store_Data[4],4);
}
}
(6) STM32 ST-LINK ユーティリティを使用して、フラッシュ メモリ内のデータ ストレージを表示します。プログラムを開発ボードにダウンロードした後、赤枠のボタンをクリックし、アドレスを 0x0800FC00 に変更して、フラッシュ メモリのストレージを表示します。ページ 0x0800FC00.コンテンツ。 (新しいプログラムをダウンロードしたい場合は、青いボックスのボタンをクリックする必要があります。そうしないと、ST-LINK が占有されてしまいます)
(7) プログラムの最大占有容量を設定でき、プログラムの占有容量が設定値を超える場合、プログラムはコンパイルされません。
12. チップ ID を読み取ります。
(1) 下図のように回路を接続し、OLED ディスプレイのプロジェクトフォルダーをコピーしてテンプレートとして使用します。
(2) 製品の固有識別レジスタのアドレス:
(3) 次のコードを main.c ファイルに貼り付けてコンパイルし、デバッグ用にプログラムを開発ボードにダウンロードします。
#include "stm32f10x.h" // Device header
#include "OLED.h"
int main(void)
{
OLED_Init(); //OLED初始化
OLED_ShowString(1, 1, "F_SIZE:"); //显示静态字符串
OLED_ShowHexNum(1, 8, *((__IO uint16_t *)(0x1FFFF7E0)), 4); //使用指针读取指定地址下的闪存容量寄存器
OLED_ShowString(2, 1, "U_ID:"); //显示静态字符串
OLED_ShowHexNum(2, 6, *((__IO uint16_t *)(0x1FFFF7E8)), 4); //使用指针读取指定地址下的产品唯一身份标识寄存器
OLED_ShowHexNum(2, 11, *((__IO uint16_t *)(0x1FFFF7E8 + 0x02)), 4);
OLED_ShowHexNum(3, 1, *((__IO uint32_t *)(0x1FFFF7E8 + 0x04)), 8);
OLED_ShowHexNum(4, 1, *((__IO uint32_t *)(0x1FFFF7E8 + 0x08)), 8);
while (1)
{
}
}