第6章
SRAM與Flash記憶體存取
6.1 STM32內建SRAM記憶體簡介
SIOC內的處理器(STM32F103C8T6)內含Embedded SRAM共20K Byte,SRAM最主要的目的是存放暫存變數的地方,如宣告一個整數變數int a;,系統就會在SRAM中配置一個區塊給變數a使用,而處理器是透過32bit的DBus(Data Bus)對SRAM進行存取的動作,如下圖一所示:
處理器傳送資料是透過Data Bus,在存取資料之前要先找到SRAM的位址是在哪裡,因此透過Memory Mapping的方式來找到SRAM,SRAM的起始位址在0x2000 0000,結束位址為0x2000 5000,如下圖二所示:
若是透過Keil軟體來撰寫程式的話,那麼在Keil環境設定如下圖三所示:
(圖三)
圖中的IRAM1的Start和Size欄位即為設定SRAM的起始位址和記憶體大小,
圖中的RAM1~RAM3是設定外部SRAM的地方,因為SIOC沒有外部SRAM,因此這個地方不用做任何設定。
6.2 加速SRAM資料存取的方法
SIOC可外接其他周邊,而大部分的周邊的通訊協定每次都只能傳8bit資料,如UART、I2C、…,而SIOC內含32bit處理器、32bit Data Bus、32bit Address Bus,因為每筆資料只有8bit,因此在進行傳輸的動作時Data bus有24bit將會沒有使用造成浪費(32 bit Data bus傳8bit資料,會將剩下24bit mask掉)。
為了提高Data bus的使用率,可以用下列程式寫作技巧來改善
u8 A[128],B[128];
u8 *temp1=&A[0],*temp2=&B[0];
u32 *temp3=(u32 *)A,*temp4=(u32 *)B;
int i;
printf("Matrix A= \r\n");
for(i=0;i<128;i++){
*(temp2+i)=1;
printf(“%2d”,*(temp2+i)); }
printf("\r\n");
printf("8bit \r\n");
for(i=0;i<512;i++){
*(temp2+i)=*(temp1+i);
State_flag = 1;
Stopwatch_counter();}
printf("\r\n"); |
(法一)
printf("32bit \r\n");
for(i=0;i<128;i++){
*(temp4+i)=*(temp3+i);
State_flag = 2;
Stopwatch_counter();}
printf("\r\n");
|
(法二)
假設我們要對同訊協定為I2C的CMOS Senser先取得影像後再將RGB格式的圖片轉成YUV格式的圖片,在上述程式中的A陣列為原始圖片(RGB格式),B陣列為轉換後圖片(YUV格式),兩個陣列每筆資料皆為8bit,一般作法會使用法一的方式,也就是不透過任何技巧,每次都是搬移8bit資料,共要搬512次,才能夠把A陣列的資料搬到B陣列中,為了提高Data Bus的使用率,可以透過法二的方式,先宣告兩個指標temp3和temp4,兩者型態皆為32bit,透過型態轉換後,在進行搬移資料的動作,從下列結果就可以看出,只要搬128次就可以結束。
(圖四)
圖四中的for 1代表法一搬移資料的次數共512次,而for 2就是代表法二搬移資料的次數共128次,由此可知採用法二可以提升Data Bus和處理器的使用率,提升整體速度。
6.3 STM32內建Flash記憶體簡介
SIOC內的處理器(STM32F103C8T6)內含Embedded Flash,大小為128K Byte,Flash主要是用來存放DFU程式、程式碼、資料,Flash最大的特性就是可以重複讀寫、電源關閉後資料不會消失和保護資料的功能。
SIOC的內部結構圖如下圖一所示:
(圖一)
處理器要存取FLASH之前,必須先透過圖中的flash interface來存取flash資料,而flash interface就具備保護的功能,當我們保護flash的某些page時,如外界要對這些page存取時,就會被flash interface擋下。
一開始有提到,程式碼是放在flash裡,當CPU要透過Ibus(Instruction Bus)存取指令時,所存取的位址如下圖二所示:
由圖二可得知Flash的起始位址為0x0800 0000結束位址為0x0801FFF,若將此部分更加細分來看,如圖三所示:
(圖三)
Flash每個page size為1Kbyte,共有128個page可以使用,Main是用來存放DFU程式、程式碼、資料的地方,而Information block是給flash interface使用。
SIOC也提供了一些Register給flash interface使用,如下圖四所示:
(圖四)
Register細部內容可以參考http://140.115.155.53/~miat/roger/FLASH.pdf
這份Data Sheet第23頁~第29頁。
ST提供了一些library function讓程式開發人員能夠更容易存取flash,如下圖五所示:
(圖五)
最常使用的function分別為:
FLASH_Unlock(打開controller清除的功能)
FLASH_Lock(關閉controller清除的功能)
Flash_ErasePage(指定特定Page進行清除的動作)
Flash_EraseOptionBytes(清除掉OptionByte內容)
FLAH_ProgramWorld(寫入一筆1world資料到FLASH的特定位址內)
FLASH_EnableWriteProtection(對某page進行寫入的保護)
FLASH_ReadOutProtection(對某page進行讀取的保護)
而FLASH_ProgramHalfWorld(寫入一筆16bit資料到FLASH的特定位址內)的流程圖如圖六所示:
(圖六)
Grafcet:
此流程圖一開始是檢查FLASH controller是否lock住,若有則離開,否則就繼續往下執行,接著將FLASH_CR_PG設定為1(要對某page進行存取的動作),然後就可以將半個world(16 bit資料)寫入到指定位址中,再寫的時候,為了避免還沒寫完就離開這種情形發生,所以透過FLASH_SR_BSY讓CPU知道現在是否已經將資料寫入完畢,若還沒則繼續等,否則就完成寫入動作。
其他Function的流程圖在http://140.115.155.53/~miat/roger/FLASH.pdf
這份data sheet裡面有,請讀者自行參考
6.4 立即演練
實驗1:flash memory資料存取
實驗說明:將資料0x1504197寫入到FLASH指定位址中。
1. Grafcet:
2. 程式說明:
#define StartAddr ((u32)0x08008000) [ 區塊一] #define EndAddr ((u32)0x0800C000) u32 EraseCounter = 0x00, Address = 0x00; u32 Data; vu32 NbrOfPage = 0x00; volatile FLASH_Status FLASHStatus; volatile TestStatus MemoryProgramStatus; void NVIC_Configuration(void); int main(void) { u32 *temp; Set_System(); /*pause*/ getchar(); printf("start \r\n"); FLASHStatus = FLASH_COMPLETE; MemoryProgramStatus = PASSED; Data = 0x15041925; [ 區塊二 ] /* NVIC configuration */ NVIC_Configuration(); /* Unlock the Flash Program Erase controller */ FLASH_Unlock(); /* Define the number of page to be erased */ NbrOfPage = (EndAddr - StartAddr) / FLASH_PAGE_SIZE; /* Clear All pending flags */ FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR); /* Erase the FLASH pages */ for(EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++) [ 區塊三 ] {FLASHStatus = FLASH_ErasePage(StartAddr + (FLASH_PAGE_SIZE * EraseCounter));} /* FLASH Word program of data 0x15041979 at addresses defined by StartAddr and EndAddr*/ Address = StartAddr; while((Address < EndAddr) && (FLASHStatus == FLASH_COMPLETE)) { FLASHStatus = FLASH_ProgramWord(Address, Data); temp=(u32 *)Address; [ 區塊四 ] printf("0x%x data is 0x%x \r\n",Address,*temp); Address = Address + 4; } |
區塊一的主要目的在於設定資料要寫在FLASH的哪裡到哪裡,而我們資料要寫入FLASH必須要注意不可以覆蓋到DFU程式和主程式的部分
0x0800 0000~0x0800 2FFF為DFU程式碼區段
0x0800 3000~0x0800 5C10(假設程式結束位址在0x0800 5C10)為主程式區段
而資料就必須放在0x0800 5C10資後,所以資料的StartAddr可設定為
0x0800 5C14(範例中是設定0x0800 8000,會浪費0x0800 5C14~0x0800 7FFF這段FLASH)。
程式結束位址算法如下:
(1) 撰寫程式然後進行compiler的動作
(2) Compiler之後會產生.hex的十六進制檔案(MDK-ARM\STM3210B-EVAL\STM3210B-EVAL.hex)
(3) 依照下列格式
其中:代表這行是十六進制資料
aaaa即為FLSH位址的後四碼
例如:
|
在STM3210B-EVAL.hex的倒數第三行即為程式碼的最後一行
對照上述格式,可以求出程式碼最後一行放在FLASH的0x0800 5C10
因此資料必須重0x0800 5C14開始放,才不會覆蓋到主程式碼.
區塊二的主要目的在於設定資料要寫的data值
區塊三的主要目的在於清除FLASHPAGE的內容
區塊四的主要目的在於在起始和結束地址間寫入內容
3. 執行結果:
實驗2:非揮發性資料寫入
實驗說明:檢查電源關閉後,FLASH內容是否存在。
1. Grafect:
2. 程序說明:
/* Erase the FLASH pages */ for(EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++) {
// FLASHStatus = FLASH_ErasePage(StartAddr + (FLASH_PAGE_SIZE * EraseCounter)); }
/* FLASH Word program of data 0x15041979 at addresses defined by StartAddr and EndAddr*/ Address = StartAddr; while((Address < EndAddr) && (FLASHStatus == FLASH_COMPLETE)) { // FLASHStatus = FLASH_ProgramWord(Address, Data); temp=(u32 *)Address; printf("0x%x data is 0x%x \r\n",Address,*temp); Address = Address + 4; } |
程序中橙色部分為與實驗一相比程序修改的部分
3. 實驗步驟:
(1) 先將實驗一完成,即是將資料寫入到0x0800 8000~0x0800 C000
(2) 修改實驗一的程式,將區塊三(清除page)刪除,以及區塊四的
FLASH_ProgramWord(Address, Data);刪除
(3) 重新燒入修改過的程式,即可將先前寫入的資料讀出來,並且檢查是否存在
實驗3: flash資料寫入鎖定
實驗說明:保護FLASH內的資料。
1. 程式說明:
#ifdef WriteProtection_Disable printf("WriteProtection_Disable \r\n"); if (ProtectedPages == 0x00) {/* Pages are write protected */ /* Disable the write protection */ FLASHStatus = FLASH_EraseOptionBytes(); /* Generate System Reset to load the new option byte values */ NVIC_GenerateSystemReset(); } #else #ifdef WriteProtection_Enable printf("WriteProtection_Enable \r\n"); if (ProtectedPages != 0x00) {/* Pages not write protected */ /* Disable the write protection */ FLASHStatus = FLASH_EraseOptionBytes(); /* Enable the pages write protection */ //FLASHStatus = FLASH_EnableWriteProtection(FLASH_WRProt_Pages12to13 |FLASH_WRProt_Pages14to15); // 0x00006000 / 0x800 =12 0x0000 8000 / 0x800= 16 FLASHStatus = FLASH_EnableWriteProtection(FLASH_WRProt_Pages24to27 |FLASH_WRProt_Pages28to31); // 0x00006000 / 0x400 = 24 0x0000 8000 / 0x400 = 32 /* Generate System Reset to load the new option byte values */ NVIC_GenerateSystemReset();
}
#endif
#endif
|
定義上述巨集對特定Page進行寫入保護的動作
範例中是以一個page為2K來計算,因為SIOC的一個page為
1K,因此0x00006000/ 0x400 =24,若要保護0x00006000之後的資料,
就必須對page24進行寫入的保護,請讀者自行求出實驗一的資料是在那些Page,然後對那些page進行寫入的保護
請使用預設0x08006000之後的位置,避免覆蓋DFU與使用者程式碼區塊
更改寫入位置測試寫入鎖定時需同時更改 FLASH_EnableWriteProtection鎖定之Page