freescale飛思卡爾 HCS12 系列單片機bootloader詳解(三)


    在完成內存映射的內容后,接下來我們將進入一個簡單Bootloader的實際設計中來。在第一節內容中,我們已經簡單介紹了bootlaoder的作用,它實際上就是在單片機重啟過程中的一個步驟:如果有bootloader的啟動信號,則進入bootloader模式開始新程序的接收與flash的擦寫,若沒有bootloader的啟動信號,則直接進入用戶程序執行用戶程序內容。

 bootloader的啟動信號一般有如下兩種:

 1)外部引腳接地或者拉高電平;每次啟動時先監測某一已經設定的引腳是否已經被操作到了bootloader啟動電平位,如果是希望bootloader啟動的電平,則需要跳轉入bootloader程序,否則直接進入用戶程序。

   2)重啟后先開啟通訊,通過串口,LIN或者CAN,網絡等方式先於外部設備交互,若能夠完成已經設計好的握手內容,則進入bootloader繼續后續操作,否則等待一段時間比如10ms后直接跳轉至用戶程序。

   讀取引腳信息跳轉bootloader與單片機的類型緊密相關,因而這里僅介紹開啟通訊后的bootloader實現(實際上實現的過程大同小異,可以舉一反三)。

   這里講解的內容即為上述(2)中方式,這個方式的開機后處理邏輯為:

  1. 初始化總線時鍾

  2. 初始化通訊方式(初始化串口,CAN通訊,網絡通訊或其他)

  3. 初始化Flash擦寫內容(這一步也可以在確認要進入bootloader后進行)

  4. 將Flash擦寫必須的程序從ROM中的復制到RAM中

  5. 進入大循環中從通訊方式中發出握手信號判斷是否進入Bootloader,握手成功則進入Bootloader否則進入用戶程序

  在了解整個開機后處理邏輯后,首先就需要考慮划分存儲空間的事。下面是我的一個例程的prm文件的設置內容(本例程使用飛思卡爾HC9S12G128單片機為例):

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

    這段內存分塊中可以看出,全部的RAM空間地址從0x2000-0x3FFF。其中最高位的1KB (地址從 0x3C00到0x3FFF)內容用於運行Flash讀寫時的程序(注意,Flash的讀寫不能同步進行,僅當Flash不在寫入時才能從中讀取程序)。我們將ROM邏輯地址中未分頁的區域0x4400-0x47FF定義為Flash寫入必不可少的程序存儲區域,並使用 RELOCATE_TO 語句將其映射至0x3C00。這樣的話,存儲在ROM_FLASH區域中的內容將在運行時使用CODE_RAM中的地址(當然程序需要在運行以前從ROM中先復制到RAM里,單片機不會自動幫你完成)。除了以上的操作外,我們分別定義了bootloader的存儲區域ROM_BOOT並定義了相關存儲區域的名稱。

   我們通過如下代碼的方式定義了Flash擦寫的庫函數,通過#pragma關鍵詞將其定位至不同的存儲空間

 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區域的擦除與寫入函數我們將其放在ROM的Flash區域,這兩個函數都將會被復制到RAM中運行,復制ROM中的函數到RAM中我們只需要將每個字節都對應的復制過去即可。代碼復制函數及其調用方式如下:

1 void MoveCodeIntoRam(byte *source, byte *dest, unsigned int size)
2 {
3     while (size --)
4     {
5         *dest ++ = *source ++;
6     }
7 }
View Code

    此函數以復制的源起始地址,目標空間起始地址,復制地址內容大小為參數,調用格式為:

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

     在main()函數中添加如下的內容:

 1 Init_PLL();   // 初始化時鍾
 2   
 3 Init_SCI0();  // 初始化串口
 4   
 5 Init_Flash();  // 初始化Flash擦寫
 6   
 7 MoveCodeIntoRam((byte *)0x4400, (byte *)0x3C00, 0x400); // 從ROM中復制程序至RAM
 8   
 9 for (;;)
10 {
11       // 串口發送握手信息判斷是否進入bootloader,若握手失敗,進入用戶程序
12       // 若進入bootloader在此處擦寫Flash 
13 }

    主要的操作思路為上述所示,再有就是關於用戶程序的跳轉,可以直接通過指針函數的形式將用戶程序調用。

    如下所示在分頁區域中定義了用戶程序:

 1 #pragma CODE_SEG USER_APP
 2 void application(void)
 3 {
 4     /*Do application works here*/
 5     
 6     for (;;)
 7     {  
 8            // 用戶大循環
 9     }    
10 
11 }
12 #pragma CODE_SEG DEFAULT

     跳轉時可以先定義函數指針,然后調用即可:

1 void (*__far ifunction)(void) = application;

    總結一下本篇的內容:

    1. 簡要的給出了具體的HC9S12的Bootloader設計思路並提供了具體實現的方法

    2. 給出了從ROM中復制地址到RAM中運行的方法及其簡單解釋

    3. 給出了bootloader程序跳轉至用戶程序的函數指針方法

    后續我們會再介紹HCS12系列單片機Bootloader中的重中之重:Flash擦寫思路,S19記錄文件的解析

    (未完待續)

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

 


免責聲明!

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



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