前面我們介紹了Flash主要的幾個寄存器,如果還有其他的疑問可以再返回去看看或者直接查閱S12系列單片機的手冊中相關的內容。這一節我將介紹Flash控制器指令執行的過程,並舉出相關的例子,希望能夠起到舉一反三的效果。
在手冊中FCCOB寄存器下面,介紹了Flash寄存器執行指令的過程是這樣的:
我們解釋下:NVM執行指令時將通過FCCOB寄存器向存儲控制器提供一個指令碼還有其相關的參數,也就是說先根據指令碼對FCCOB寄存器進行設置,然后通過對FSTAT寄存器中的CCIF位寫入1從而使Flash控制器開始執行FCCOB中的指令(當向CCIF寫1后,讀取CCIF時其值將為0)。當用戶向CCIF中寫1后,FCCOB指令寄存器內的內容將被鎖定直到此次指令執行結束。如果該指令碼有返回值,則返回信息將會在FCCOB寄存器內。
我們稍微總結下Flash寄存器執行指令的過程:
1. 先通過調整FCCOBIX寄存器向FCCOB寄存器各個word中寫入指令碼與相關的參數
2. 向FSTAT的CCIF位中寫入1,開始指令執行
3. 讀取CCIF的值直到指令執行結束,若指令碼有返回值則讀取並返回(還有FSTAT中的其他位可能表示了寫入過程中的錯誤信息)
這樣就可以讓Flashi控制器完成一次指令的執行了。下面我們看一下一個手冊中的典型FCCOB寄存器的用法:
在上面的典型用法表中我們可以看到向Flash中寫入8個Byte的過程應該如下方程序所示:
1 unsigned long address = 0x020000 //將要在Flash中寫入的數據首地址(全局地址) 2 unsigned int *ptr = data // word長度的數據指針 3 byte i 4 5 // 讀取CCIF的值來判斷Flash 6 while ((FSTAT & FSTAT_CCIF_MASK) == 0); 7 8 9 // 按照格式要求選擇0x00 10 FCCOBIX = 0x00; // choose the 000 FCCOB 11 12 /* 000 FCCOB HI is 8 bits commands and LO is physical address Highest byte*/ 13 /* command 0x06 means program flash */ 14 /* warning: before program, the area must be erased*/ 15 FCCOB = 0x0600 | ((address & 0x00030000) >> 16); 16 17 FCCOBIX = 0x01; //choose the 001 FCCOB 18 /* 001 FCCOB is the least 16bits physical address*/ 19 FCCOB = (address & 0x0000FFFF); 20 21 /* 0x02 - 0x05 FCCOB are 4x2 bytes write data*/ 22 for (i = 2; i < 6; i++) 23 { 24 FCCOBIX = i; 25 FCCOB = *ptr; /* 2 bytes data each FCCOB */ 26 ptr ++; /* next 2 bytes */ 27 } 28 29 FSTAT = 0x80; /* set CCIF to clear CCIF and launch command */ 30 while ((FSTAT & FSTAT_CCIF_MASK) == 0); /* wait command done */ 31
請仔細閱讀上方的代碼,在代碼中我使用了向Flash中寫入8個Byte的指令碼0x06,完成的效果就是將上方表格所示的FCCOB中的內容設置滿,然后啟動並執行指令,直到指令被完全執行成功。
這里有四點需要強調,
首先是不同的指令碼對應着FCCOB中不同的內容要求,具體設置情況根據需要,參照手冊中對每個指令碼的要求來編寫。
其次是Flash的操作地址模式為全局地址,也就是上面表格中Global address,先把全局地址與分頁地址的關系搞清楚了再做Flash的操作。
第三是Flash的擦寫程序必須要復制到RAM中執行才可以執行,復制代碼到RAM中執行前要在prm文件中使用RELOCATE_TO關鍵詞,將代碼拷貝至內存中運行(在bootloader詳解教程中有寫如何將ROM中的代碼拷貝至RAM中),這是因為Flash的操作不能讀寫同步的,當Flash控制器在執行指令時,單片機無法從Flash讀出指令,一定要先將Flash擦寫的代碼拷貝至RAM中運行才可以。
第四是,在寫入Flash之前,務必要確定該片Flash區域已經被擦除了,否則會出現寫入錯誤。
后面我還會展示一點代碼,對於初學者,將下面的代碼每一句都弄懂,對照着器件手冊理解了,基本上Flash這部分內容就算是勉強出師了。其間涉及到了錯誤信息的處理,在手冊里查一查都有的。
還想說一下本人寫這個教程的初衷,因為首先自己搞的時候確實很頭大,這樣把過程寫出來十分暢快,其次就是在下目前在下的環境,完全不參與什么行業競爭,這些只是需要讀一讀器件手冊就能完成的事情,發出來對大家有用就好。下面貼代碼,代碼包括prm文件設置,Flash擦寫頭文件與實現,還有個將ROM中程序復制到RAM中運行的程序。要是根據我這個教程將Flash調通的,在教程下評論下吧,並不是要推薦,只是想看看幫到了多少人。
prm文件設置

1 NAMES END 2 3 SEGMENTS 4 /* Register space */ 5 /* IO_SEG = PAGED 0x0000 TO 0x03FF; intentionally not defined */ 6 7 /* RAM */ 8 RAM = READ_WRITE 0x2000 TO 0x3BFF; 9 CODE_RAM = READ_WRITE 0x3C00 TO 0x3FFF; /*1 kB for flash read and write*/ 10 11 /* D-Flash */ 12 DFLASH = READ_ONLY 0x000400 TO 0x0013FF; 13 14 /* non-paged FLASHs */ 15 ROM_1400 = READ_ONLY 0x1400 TO 0x1FFF; 16 17 ROM_BOOT = READ_ONLY 0x4000 TO 0x43FF; // 1KB for boot loader 18 ROM_FLASH = READ_ONLY 0X4400 TO 0x47FF RELOCATE_TO 0x3C00; // 1KB for necessary flash operation 19 20 ROM_C000 = READ_ONLY 0xC000 TO 0xFEFF; 21 /* VECTORS = READ_ONLY 0xFF00 TO 0xFFFF; intentionally not defined: used for VECTOR commands below */ 22 //OSVECTORS = READ_ONLY 0xFF80 TO 0xFFFF; /* OSEK interrupt vectors (use your vector.o) */ 23 24 /* paged FLASH: 0x8000 TO 0xBFFF; addressed through PPAGE */ 25 PAGE_08 = READ_ONLY 0x088000 TO 0x08BFFF; 26 PAGE_09 = READ_ONLY 0x098000 TO 0x09BFFF; 27 PAGE_0A = READ_ONLY 0x0A8000 TO 0x0ABFFF; 28 PAGE_0B = READ_ONLY 0x0B8000 TO 0x0BBFFF; 29 PAGE_0C = READ_ONLY 0x0C8000 TO 0x0C93FF; 30 PAGE_0C_A000 = READ_ONLY 0x0CA000 TO 0x0CBFFF; 31 PAGE_0E = READ_ONLY 0x0E8000 TO 0x0EBFFF; 32 /* PAGE_0D = READ_ONLY 0x0D8000 TO 0x0DBFFF; not used: equivalent to ROM_4000 */ 33 /* PAGE_0F = READ_ONLY 0x0F8000 TO 0x0FBEFF; not used: equivalent to ROM_C000 */ 34 END 35 36 PLACEMENT /* here all predefined and user segments are placed into the SEGMENTS defined above. */ 37 _PRESTART, /* Used in HIWARE format: jump to _Startup at the code start */ 38 STARTUP, /* startup data structures */ 39 ROM_VAR, /* constant variables */ 40 STRINGS, /* string literals */ 41 VIRTUAL_TABLE_SEGMENT, /* C++ virtual table segment */ 42 //.ostext, /* OSEK */ 43 NON_BANKED, /* runtime routines which must not be banked */ 44 COPY /* copy down information: how to initialize variables */ 45 /* in case you want to use ROM_4000 here as well, make sure 46 that all files (incl. library files) are compiled with the 47 option: -OnB=b */ 48 INTO ROM_C000/*, ROM_1400, ROM_4000*/; 49 50 BOOTLOADER INTO ROM_BOOT; 51 FLASH_CODE INTO ROM_FLASH; 52 53 54 USER_APP INTO PAGE_08; 55 TEST_AREA INTO PAGE_09; /* physical address from 0x2_4000 */ 56 DEFAULT_ROM INTO PAGE_0A, PAGE_0B, PAGE_0C, PAGE_0C_A000, PAGE_0E ; 57 58 //.stackstart, /* eventually used for OSEK kernel awareness: Main-Stack Start */ 59 SSTACK, /* allocate stack first to avoid overwriting variables on overflow */ 60 //.stackend, /* eventually used for OSEK kernel awareness: Main-Stack End */ 61 DEFAULT_RAM INTO RAM; 62 63 //.vectors INTO OSVECTORS; /* OSEK */ 64 END 65 66 ENTRIES /* keep the following unreferenced variables */ 67 /* OSEK: always allocate the vector table and all dependent objects */ 68 //_vectab OsBuildNumber _OsOrtiStackStart _OsOrtiStart 69 END 70 71 STACKSIZE 0x100 72 73 VECTOR 0 _Startup /* reset vector: this is the default entry point for a C/C++ application. */ 74 //VECTOR 0 Entry /* reset vector: this is the default entry point for an Assembly application. */ 75 //INIT Entry /* for assembly applications: that this is as well the initialization entry point */ 76 77 VECTOR ADDRESS 0xFFD6 SCI0_INT_receive
Flash擦寫頭文件

1 #ifndef _FLASH_LIB_H 2 #define _FLASH_LIB_H 3 4 #include <mc9s12g128.h> 5 6 typedef enum 7 { 8 NoError = 0, 9 FlashProgramError = 1, 10 FlashEraseError = 2 11 } FlashMsg; 12 13 #pragma CODE_SEG BOOTLOASER 14 15 void Init_Flash(void); 16 17 #pragma CODE_SEG DEFAULT 18 19 #pragma CODE_SEG FLASH_CODE 20 21 FlashMsg Flash_Program(unsigned long address, unsigned int *ptr); 22 23 FlashMsg Flash_EraseSector(unsigned long address); 24 25 26 #pragma CODE_SEG DEFAULT 27 28 #endif
Flash擦寫實現

1 #include "FLASH_LIB.h" 2 3 /* a sector contains 512 bytes */ 4 #define FLASH_SECTOR_SIZE 0x200 5 6 7 #pragma CODE_SEG BOOTLOASER 8 9 void Init_Flash(void) 10 { 11 // bus clock is 8MHz 12 // read the manual reference get the respective value 13 14 // check if the flash controller is busy or not 15 while ((FSTAT & FSTAT_CCIF_MASK) == 0); 16 17 // set the clock divider according to bus clock 18 FCLKDIV = 0x07; 19 20 // lock the divider 21 FCLKDIV_FDIVLCK = 1; 22 23 } 24 25 #pragma CODE_SEG DEFAULT 26 27 28 #pragma CODE_SEG FLASH_CODE 29 30 FlashMsg Flash_Program(unsigned long address, unsigned int *ptr) 31 { 32 byte i; 33 34 /*CCIF is the 7th bit of FSTAT 35 CCIF is 0, the flash controller is busy 36 wait until CCIF become 1*/ 37 while ((FSTAT & FSTAT_CCIF_MASK) == 0); 38 39 /*clear ACCERR (flash access error) 5th bit 40 clear PVIOL (protection violation error) 4th bit 41 , or the CCIF cannot be cleared*/ 42 FSTAT = 0x30; 43 44 /*write flash command, operation address and input parameters*/ 45 FCCOBIX = 0x00; // choose the 000 FCCOB 46 47 /* 000 FCCOB HI is 8 bits commands and LO is physical address Highest byte*/ 48 /* command 0x06 means program flash */ 49 /* warning: before program, the area must be erased*/ 50 FCCOB = 0x0600 | ((address & 0x00030000) >> 16); 51 52 FCCOBIX = 0x01; //choose the 001 FCCOB 53 /* 001 FCCOB is the least 16bits physical address*/ 54 FCCOB = (address & 0x0000FFFF); 55 56 /* 0x02 - 0x05 FCCOB are 4x2 bytes write data*/ 57 for (i = 2; i < 6; i++) 58 { 59 FCCOBIX = i; 60 FCCOB = *ptr; /* 2 bytes data each FCCOB */ 61 ptr ++; /* next 2 bytes */ 62 } 63 64 FSTAT = 0x80; /* set CCIF to clear CCIF and launch command */ 65 while ((FSTAT & FSTAT_CCIF_MASK) == 0); /* wait command done */ 66 67 /*read the error flag and return error message*/ 68 if ((FSTAT & (FSTAT_ACCERR_MASK | FSTAT_FPVIOL_MASK)) != 0) 69 return FlashProgramError; 70 else 71 return NoError; 72 73 } 74 75 FlashMsg Flash_EraseSector(unsigned long address) 76 { 77 while ((FSTAT & FSTAT_CCIF_MASK) == 0); 78 FSTAT = 0x30; 79 80 /* command 0x0A means erased all address in the sector*/ 81 FCCOBIX = 0x00; 82 FCCOB = 0x0A00 | ((address & 0x00030000) >> 16); 83 84 FCCOBIX = 0x01; 85 FCCOB = (address & 0x0000FFF8); /*get the sector*/ 86 87 FSTAT = 0x80; /* launch command*/ 88 while ((FSTAT & FSTAT_CCIF_MASK) == 0); /*wait for done*/ 89 90 if ((FSTAT & (FSTAT_ACCERR_MASK | FSTAT_FPVIOL_MASK | 91 FSTAT_MGSTAT_MASK)) != 0) 92 return FlashEraseError; 93 else 94 return NoError; 95 } 96 97 98 #pragma CODE_SEG DEFAULT
ROM中內容復制到RAM中函數

void MoveCodeIntoRam(byte *source, byte *dest, unsigned int size) { while (size --) { *dest ++ = *source ++; } }
對應本prm文件調用復制函數
MoveCodeIntoRam((byte *)0x4400, (byte *)0x3C00, 0x400);
本節內容小結:
1. 通過實例展示了S12系列單片機通過Flash指令寄存器讓Flash控制器執行操作的過程
2. 給出了在擦寫Flash的四個重要問題
3. 給出了可以調用的實例程序
注: 本系列文章均為原創,如有轉載引用請標明來源