freescale飛思卡爾 HC9S12 系列單片機 Flash擦寫詳解(三)之Flash控制器指令執行


  前面我們介紹了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
View Code

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
View Code

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
View Code

ROM中內容復制到RAM中函數

void MoveCodeIntoRam(byte *source, byte *dest, unsigned int size)
{
    while (size --)
    {
        *dest ++ = *source ++;
    }
}
View Code

對應本prm文件調用復制函數

MoveCodeIntoRam((byte *)0x4400, (byte *)0x3C00, 0x400);

 

 本節內容小結:

  1. 通過實例展示了S12系列單片機通過Flash指令寄存器讓Flash控制器執行操作的過程

  2. 給出了在擦寫Flash的四個重要問題

  3. 給出了可以調用的實例程序

 

注: 本系列文章均為原創,如有轉載引用請標明來源 

 


免責聲明!

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



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