最近在做freescale HCS12系列單片機的bootloader,畢竟是新手,對這方面的入門知識還是欠缺。盡管NXP官網已經有了相當豐富的文檔與例程,可是無奈例程太過復雜,不知道如何下手第一行代碼。這里先感謝經驗豐富的某位高人醍醐灌頂的指導,為了讓再入門的新學者能夠有個參考,所以就將本次開發bootloader的過程學習過程記錄下來以其能夠對大家有所啟發。因為本人自己也是新學者,所以有問題可以留言討論。
這里單片機的bootloader與我們廣義的Linux以及windows的bootloader是不同的。這種大型操作系統的bootloader是為了做操作系統與硬件之間的橋梁:初始化硬件系統、為操作系統的引導做准備。但是單片機的bootloader主要作用卻是為燒錄程序提供一種新的方式,即可以讓單片機可以通過串口,藍牙,CAN, LIN等通訊方式更新燒寫在Flash中的程序以及參數。這樣做的好處很多,比如一個串口燒錄的bootloader可以通過串口向單片機的Flash燒錄程序而不需要再額外用專用的燒錄器來操作,豐富了修改程序的方式,使燒錄更加方便。我們常使用的慧凈51系列單片機以及Arduino能夠通過一根簡單的串口線就燒錄程序原理都是如此,在這些板子出廠時的原程序是通過專門燒錄器燒錄的,但由於bootloader的存在,后續用戶的程序就可以通過串口燒錄。再比如單片機的bootloader在實際使用時可以遠程更新單片機程序,為實用的場景提供程序更新的功能,盡管做到這一點對通用計算機非常簡單,無非是卸載程序重新安裝,但是對於Flash空間較小的單片機而言,就需要通過bootloader這樣的程序來提供這種功能。
單片機的bootloader基本概念實際上也非常的簡單,就是把單片機的存儲空間(Memory,包括RAM以及Flash)都合理的分配。把Flash的程序存儲空間分為bootloader區域以及user application區域。bootloader程序一般是不修改的,一次燒錄后可以一直使用(當然也可以有bootloader程序自身的更新,但是原理是一樣的),但是user application區域卻是可以重復由bootloader修改的。這里的user application區域就是我們通常的應用代碼,它是為了實現單片機的功能的主要代碼。bootloader區域代碼卻是為了實現對user application區域的控制而存在的,它的程序邏輯是明確的:與上位機建立bootloader專用的通訊,通過串口等簡單通訊方式修改Flash或者EEPROM中的程序內容,完成修改后跳出bootloader執行user application的程序內容。
這里筆者要假設讀者對freescale的HCS12單片機有了一定程度的了解,關於Bootloader的內容以及文本中的部分實例程序均來自NXP公開的AN3275以及AN4258文檔以及文檔的樣例程序,更多的介紹可以去NXP官網下載。
一個HCS12系列單片機的工程是由多個文件類型構成的。
.c ——程序文件
.h ——頭文件
.prm ——內存划分,內存塊名稱聲明以及中斷向量聲明文件
所以這里很明顯的,開發bootloader最重要的就是對.prm文件進行操作了。這里使用AN3275的例程中prm文件
NAMES END SEGMENTS //RAM = READ_WRITE 0x5000 TO 0x57FF; RAM = READ_WRITE 0x5000 TO 0x5322; FLASHROUTINES = READ_WRITE 0x5323 TO 0x53C8; //157 bytes Flash_Cmd & Unsecure /* unbanked FLASH ROM */ //ROM_4000 = READ_ONLY 0x4000 TO 0x7FFF; ROM_UPDATE = READ_ONLY 0xF000 TO 0xF0AF RELOCATE_TO 0xE000; /*Update Section, 224 bytes*/ ROM_ERASELOADER = READ_ONLY 0xF0B0 TO 0xF0BF; ROM_C000 = READ_ONLY 0xF0C0 TO 0xFE50; /* Code may reach only up to 0xFE50 because of limitation on * the amount of Flash that is backed-up */ /* banked FLASH ROM */ /* PAGE_3F = READ_ONLY 0x3F8000 TO 0x3FBFFF; not used: equivalent to ROM_C000 */ END PLACEMENT _PRESTART, /* Used in HIWARE format: jump to _Startup at the code start */ STARTUP, /* startup data structures */ ROM_VAR, /* constant variables */ STRINGS, /* string literals */ VIRTUAL_TABLE_SEGMENT, /* C++ virtual table segment */ NON_BANKED, /* runtime routines which must not be banked */ DEFAULT_ROM, COPY, /* copy down information: how to initialize variables */ /* in case you want to use ROM_4000 here as well, make sure that all files (incl. library files) are compiled with the option: -OnB=b */ INTO ROM_C000/*, ROM_4000*/; // DEFAULT_ROM INTO PAGE_30,PAGE_31,PAGE_32,PAGE_33,PAGE_34,PAGE_35,PAGE_36,PAGE_37,PAGE_38,PAGE_39,PAGE_3A,PAGE_3B,PAGE_3C,PAGE_3D; DEFAULT_RAM INTO RAM; UPDATE_SECTION INTO ROM_UPDATE; ERASELOADER_SECTION INTO ROM_ERASELOADER; ROUTINESINRAM INTO FLASHROUTINES; END ENTRIES vfnpUpdateFn; gi16FlashSecurity; END STACKTOP 0x53FF VECTOR 0 _Startup /* reset vector: this is the default entry point for a C/C++ application. */ //VECTOR 0 Entry /* reset vector: this is the default entry point for a Assembly application. */ //INIT Entry /* for assembly applications: that this is as well the initialisation entry point */
這里主要先介紹內存划分的內容:
1)SEGMENTS關鍵詞包含的內容是對Memory(存儲空間)的分配,先看第一句:
RAM = READ_WRITE 0x5000 TO 0x5322;
這個第一句是將所有存儲空間0x5000到0x5322的部分命名為RAM, 這部分的操作方式為讀寫(READ_WRITE)
FLASHROUTINES = READ_WRITE 0x5323 TO 0x53C8; //157 bytes Flash_Cmd & Unsecure /* unbanked FLASH ROM */ //ROM_4000 = READ_ONLY 0x4000 TO 0x7FFF; ROM_UPDATE = READ_ONLY 0xF000 TO 0xF0AF RELOCATE_TO 0xE000; /*Update Section, 224 bytes*/ ROM_ERASELOADER = READ_ONLY 0xF0B0 TO 0xF0BF; ROM_C000 = READ_ONLY 0xF0C0 TO 0xFE50;
后面部分是一致的,關於RELOCATE_TO語句,help文檔中搜索可以得到如下說明
Defining a Relocation Rule Use the relocation rule if a segment is moved to a different location at runtime. With the relocation rule, you instruct the linker to use different runtime addresses for all objects in a segment. 當一個segment在運行時需要移動到一個不同的位置可以使用重定位規則。通過使用重定位規則,你可以指示連接器讓某個segment中的程序在運行時使用不同的程序地址 This is useful when at runtime the code is copied and executed at a different address than the linked location. One example is a Flash programmer which must run out of RAM. Another example is a boot loader, which moves the actual application to a different address before running it. Specify a relocation rule as follows: RELOCATE_TO Address Use <Address> to specify the runtime address of the object. Example SEGMENTS CODE_RELOC = READ_ONLY 0x8000 TO 0x8FFF RELOCATE_TO 0x1000; ... END In this example, references to functions in CODE_RELOC use addresses from 0x1000 to 0x1FFF area, but the code is programmed from 0x8000 to 0x8FFF. 在這個例子中,CODE_RELOC中的內容在運行時使用地址0x1000到0x1FFF的區域,但實際程序是放在0x8000到0x8FFF的區域中 With RELOCATE_TO, you can execute code at an address different from where it was allocated. The code need not be position independent (PIC), however, non-PIC code may not run at its allocation address, as all references in the code refer to the RELOCATE_TO address.
2)在SEGMENTS模塊中將存儲空間分配以后,在PLACEMENTS中對已經定義的SEGMENTS進行進一步的定義,這里先看PLACEMENTS中的第一項
_PRESTART, /* Used in HIWARE format: jump to _Startup at the code start */ STARTUP, /* startup data structures */ ROM_VAR, /* constant variables */ STRINGS, /* string literals */ VIRTUAL_TABLE_SEGMENT, /* C++ virtual table segment */ NON_BANKED, /* runtime routines which must not be banked */ DEFAULT_ROM, COPY, /* copy down information: how to initialize variables */ /* in case you want to use ROM_4000 here as well, make sure that all files (incl. library files) are compiled with the option: -OnB=b */ INTO ROM_C000/*, ROM_4000*/;
上述的_PRESTART,STARTUP等內容是預定義的程序塊,這條語句實際上就是指示連接器將上述的這些定義的塊放在SEGMENTS中已經定義過的ROM_C000區域。當中的NON_BANKED塊熟悉S12的讀者應該都知道,我們通常定義的中斷函數都放在這個區域內。PALCEMENTS中其余語句為:
DEFAULT_RAM INTO RAM;
UPDATE_SECTION INTO ROM_UPDATE;
ERASELOADER_SECTION INTO ROM_ERASELOADER;
ROUTINESINRAM INTO FLASHROUTINES;
即,將SEGMENTS中定義的RAM,ROM_UPDATE等區域依次命名為DEFAULT_RAM,UPDATE_SECTION等塊。
到這里,實際上我們基本上已經明確了SEGMENTS語句以及PLACEMENTS語句的作用了,這里總結如下:
1)SEGMENTS語句按照程序的存儲空間定義(datasheet中有物理空間對應存儲器分布)將存儲空間聲明為不同的存儲區域
2)PLACEMENTS語句按照SEGMENTS中的定義對各塊進行進一步的划分以及布置
這也就是bootloader的第一步,划分儲存空間。
那么,存儲空間划分好了以后我們要怎么做才能將我們的程序放到這些塊中並讓它們依次執行呢?
(未完待續)
注: 本系列文章均為原創,如有轉載引用請標明來源