瞭解更多關於bootloader 的C語言實現,請加我QQ: 1273623966 (驗證資訊請填 bootloader),歡迎諮詢或定製bootloader(線上升級程式)。
為什麼自己寫bootloader
我的第一款自己的serial bootloader是為Microchip PIC16微控制器寫的UART bootloader,我命其名為HyperBootloader_PIC16. 為什麼取這個名字,下面會講。很多朋友可能會問為什麼要自己寫bootloader, 百度上一搜,有不少下載下來直接就可以用。比如ds30_Loader 就很不錯,免費,還支援Microchip很多系列的微控制器。是沒錯,但是網上搜到的bootloader用C語言寫的少得可憐,或者不能用,或者不是用XC8編譯的(Microchip 的C編譯器)。它們絕大多數都是用匯編寫的,包括ds30_Loader。對於不怎麼用匯編的我感覺很頭疼,這些bootloader也不太好修改,比如增加個自己需要的功能都比較困難。所以我決定自己用C語言寫PIC16 serial bootloader。說幹就幹,本來以為是小菜一碟,沒想到寫一個穩定好用又節省空間的bootloader也不簡單,這是後話。在講如何實現自己的PIC16 serial bootloader之前,我先講下serial bootloader的基礎知識。如果不需要了解的,請跳過這部分。
Serial bootloader之ABC
Serial bootloader是一種非常方便使用並且低成本的程式燒寫的方法。一般情況,每次燒寫Microchip微控制器我們都需要將燒錄器PICKit3或ICD3接上目標板,然後在電腦上使用Microchip IPE或MPLAB X就可以直接燒寫Hex檔案到目標板中。使用serial bootloader 就可以不需要插拔燒錄器,對開發工程師來講非常的方便。Serial bootloader需要用到微控制器的串列埠,所以微控制器端需要如下硬體電路,其中DB9串列埠是和電腦的串列埠相連。
Serial bootloader 和應用程式一樣也是燒錄到程式儲存器中,serial bootloader 和應用程式在程式儲存器中需要分開放置。所以serial bootloader一般有兩種放置方式,一種是放置在程式儲存器頭部,另一種是放置在程式儲存器底部,如下圖所示。
Serial bootloader 可以使用燒錄器PICkit3或ICD3燒到目標板上,之後更新應用程式就不需要燒錄器了。目標板和電腦通過串列埠相連,電腦上執行一個串列埠通訊程式,將應用程式的Hex檔案通過串列埠傳給serial bootloader, serial bootloader 再將接收到的Hex資料燒錄到程式儲存器的正確的位置上。接下來就是講今天的主角HyperBootloader_PIC16——我自己寫的第一款PIC16微控制器C語言 serial bootloader。
HyperBootloader_PIC16
HyperBootloader_PIC16我是模仿HI-TECH的PICC bootloader,由於我是用XC8的編譯器的,所以有很多改動。上面有提到bootloader在程式儲存器中要麼是在頭部要麼是在底部,而HyperBootloader_PIC16 是在程式儲存器的底部。與它通訊的電腦端的串列埠通訊程式是超級終端——HyperTerminal. 這也是它命名的由來。
主要程式碼段
HyerBootloader_PIC16是一款用C語言寫的只佔很少空間的serial bootloader。不到0x200程式字空間. 實現邏輯也很簡單,主要程式碼段如下。

/* receive a hex file via the serial port and write it to program memory */
for(;;) // loop until end of file
{
typedef union {
unsigned int word;
unsigned char byte[2];
} wordbyte;
static persistent unsigned char inx;
static persistent BANKX wordbyte addr; while (comms_getch()!=':'); // wait for start of hex file line words2write = cksum = bytecount = getXbyte();
words2write >>= 1; // convert byte count to word count
addr.byte[1] = getXbyte(); // get the high byte of address
addr.byte[0] = getXbyte(); // get the low byte of the address
addr.word >>= 1; // convert byte address to word address EEADR = addr.byte[0];
EEADRH = addr.byte[1];
if(addr.byte[1] == 0x21) // unless this case,
EEPGD = 0; // when EEPROM should be selected
else
{
EEPGD = 1; // else destination is flash memory
// ignore code that overlaps loader or data such as CONF/IDLOC
#ifdef _BANK_ALIGNED
if( addr.byte[1] >= (BOOT_START>>8) )
{
#else
if( addr.word >= (BOOT_START) ){
#endif
while (!TRMT);
TXREG='\r';
while (!TRMT);
TXREG='\n';
continue;
}
}
rectype = getXbyte(); // get the record type
inx=0;
while(bytecount--)
{
databuff[inx++]=getXbyte(); // read all bytes in this record
}
checksum(); // test the checksum of this record if(rectype==1)
{ // if this was an END record: prepare to run new program
comms_puts("\r\nDone\r\n");
#asm
GOTO run_program
#endasm
}
// else ... INHX8M data record
ix=0;
isStartup=0; // Initiate a write to memory - EEADR has already been loaded
if (((tmpadrh=addr.byte[1])|(addr.byte[0] & 0xFC)) == 0)
{ // is this address < 0004h?
EEADRH = BOOT_START >> 8;
#ifndef _BANK_ALIGNED
tmpadr = EEADR;
EEADR = ((BOOT_START & 0xFF));
#endif
isStartup=1; // Yes - this is the reset vector code of new app
}
#ifdef _WRITE_4_WORDS
prewrite = EEADR & _FWMASK;
EEADR &= ~_FWMASK; // to start of flash-write boundary
do
{
if(prewrite){
prewrite--;
RD=1;
NOP();
NOP();
}else{
EEDATA=databuff[ix++];
EEDATH=databuff[ix++];
if(words2write==0){
prewrite=((~EEADR)&_FWMASK);
}
}
#else
do
{
EEDATA=databuff[ix++];
EEDATH=databuff[ix++];
#endif
while(WR);
WREN=1; // commit this word to flash
EECON2=0x55;
EECON2=0xAA;
WR=1; //initiate the write
NOP();
NOP();
while (WR);
WREN=0;
#ifdef _BANK_ALIGNED
if(isStartup && (EEADR==3)){
#else
if(isStartup && ((EEADR&3)==3)){
#endif
isStartup = 0;
EEADRH = tmpadrh; //swap back the address
#ifndef _BANK_ALIGNED
EEADR = (tmpadr|3);
#endif
}
if(++EEADR == 0)
EEADRH++;
#ifdef _WRITE_4_WORDS
}while((prewrite)||words2write--);
#else
}while(words2write--);
#endif
}
如何使用
1. 使用XC8編譯HyperBootloader_PIC16, 由於HyperBootloader_PIC16將放在程式儲存器底部,佔0x200程式字,編譯前,需先將Code Offset編譯引數設到正確值。例如,某PIC16 微控制器的程式儲存器空間為0x2000程式字,Code Offset = 0x2000 - 0x200 = 0x1E00, 所以只需設定Code offset為1E00, 然後編譯。
2. 使用pickit3燒錄HyperBootloader_PIC16的Hex檔案到目標板中。
3. 拔除pickit3燒錄器,連線目標板與PC的串列埠,開啟超級終端,設定如下:9600-8-None-1-None, Line Delay-20ms。
4. 重啟目標板,超級終端會出現Booting... 字樣。
5. 6秒內,在超級終端視窗中按下鍵盤上任何按鍵,會出現">"(6秒內沒按鍵,會自動跳轉到使用者的應用程式中去)。
6. 開啟Send Text File對話方塊,選擇期望燒錄的應用程式hex檔案,點選確認, HyperBootloader會將接收到的資料傳回到電腦超級終端上,並將資料燒錄到目標板程式儲存器的正確位置。
7. 燒錄完畢,再次重啟目標板,超級終端顯示完Booting ......,就自動跳到應用程式中,目標板開始正常執行應用程式。
之後每次更新應用程式,只需重複步驟 4 ~ 7 就可以了。
效果展示
下面是我使用HyperBootloader_PIC16給PIC16F877A燒錄應用程式的效果, HyperBootloader_PIC16會將接收到的資料先發送回電腦,同時再完成燒錄。如果有問題,這樣非常利於debug.
Booting.... >
:100000000A128A1104283FFF0A128A11522EFF1B7E
:10001000112883137F1883170008840A0319FF0A25
:1000200008007F087F398A000408840A0319FF0A40
:1000300082003FFF3FFF3FFF3FFF3FFF3FFF3FFF8C
:10020000533465347434743469346E346734203450
:1002100054344D3452343034203474346F342034F8
:10022000753473346534203469346E347434653411
:1002300072346E3461346C34203469346E34733407
:100240007434723475346334743469346F346E3496
:1002500020346334793463346C346534203463344B
:100260006C346F3463346B342E342E342E342E348D
:100270002E342E34203400347534743469346C34A4
:1002800069347A346134743469346F346E342034B0
:100290006F346E3420344D346934633472346F34C7
:1002A0006334683469347034203438342D34623423
:1002B0006934743420346D34693464342D347234C8
:1002C00061346E346734653420344D3443345534EE
:1002D000203450344934433431343634463438349D
:1002E0003734373441340D340A3400341B345B3432
:1002F00031343B34343430343B34333431346D3482
:1003000020344E344F34543445341B345B34313450
:100310003B34343430343B34333434346D3420346F
:1003200043344F344C344F3452341B345B34313407
:100330003B34343430343B34333432346D34203451
:100340004334483441344E34473445341B345B34F1
:1003500030346D3420340D340A3400342D342D34CF
:100360002D342D342D342D342D342D342D342D3485
:100370002D342D342D342D342D342D342D342D3475
:100380002D342D342D342D342D342D342D342D3465
:100390002D342D342D342D342D342D342D342D3455
:1003A0002D342D342D342D342D342D342D342D3445
:1003B0002D342D342D342D342D342D342D342D3435
:1003C0002D342D340D340A340034543468346934F7
:1003D00073342034703472346F346734723461345F
:1003E0006D342034693473342034613420347334F0
:1003F00069346D3470346C34653420346534783449
:1004000061346D3470346C34653420346F34663448
:100410002034733465347234693461346C3420347C
.
.
.
:100CA000FE0A452E8313E830FD000530FE00F930C2
:100CB000FF00A03084000A128A11452683010A121F
:100CC0008A115C2D20308316980090308312980092
:100CD000673083169900181508008C1E00341A0816
:100CE000F0008C1208003FFF3FFF3FFF3FFF3FFF38
:02400E
:00000001FF
Done
主要特性
HyperBootloader_PIC16有以下主要特性
1. C語言寫的,XC8 編譯(只有一點彙編在裡面)。
2. 非常容易移植。
3. 支援FLASH燒寫
4. 可支援EEPROM燒寫。
5. 不支援CONFIG BITS/IDLOC 燒寫, 保持應用程式的Configuration Bits和Bootloader的一致。
如果你有什麼疑問,或有興趣瞭解更多關於bootloader 的C語言實現,請加我QQ: 1273623966 (驗證資訊請填 bootloader 或 cnblogs)。
想了解dsPIC bootloader 請閱讀我的隨筆《自己用C語言寫dsPIC / PIC24 serial bootloader》
想了解PIC18 bootloader 請閱讀我的隨筆《自己用C語言寫微控制器PIC18 serial bootloader》