了解更多關於bootloader 的C語言實現,請加我QQ: 1273623966 (驗證信息請填 bootloader),歡迎咨詢或定制bootloader(在線升級程序)。
從15年12月份以來我的空余時間都在折騰15年底買的PIC32MZ EC Starter kit。並陸續寫了十多篇隨筆記錄我折騰的歷程。最近新完成的是用C語言實現了PIC32的UART bootloader, 采用串口通信,適用於Microchip的PIC32MZ EC,稍作對應的修改也可適用於PIC32MX, PIC32MZ EF等。Uart bootloader是用XC32編譯的,電腦端的通信軟件是用超級終端--HyperTerminal (也可以使用SecureCRT). 和之前我寫的HyperBootloader_PIC16/PIC18/dsPIC很類似,算是一個家族系列之一。為保證家族特性,我命其名為HyperBootloader_PIC32
BOOTLOADER PLACEMENT
HyperBootloader_PIC32 序放在引導閃存存儲器中,如下圖所示。
BOOTLOADER LINKER SCRIPT
由於需要將bootloader放置在特定位置,我們不能使用默認的linker script. 我們需要修改linker script來放置bootloader, 以下是修改后的主要部分。
_RESET_ADDR = 0xBFC00000; _BEV_EXCPT_ADDR = 0xBFC00380; _DBG_EXCPT_ADDR = 0xBFC00480; _SIMPLE_TLB_REFILL_EXCPT_ADDR = _ebase_address + 0; _CACHE_ERR_EXCPT_ADDR = _ebase_address + 0x100; _GEN_EXCPT_ADDR = _ebase_address + 0x180; /************************************************************************* * Memory Regions * * Memory regions without attributes cannot be used for orphaned sections. * Only sections specifically assigned to these regions can be allocated * into these regions. * * The Debug exception vector is located at 0x9FC00480. * * The config_<address> sections are used to locate the config words at * their absolute addresses. *************************************************************************/ MEMORY { /* Bootloader user code: Only in the Lower Boot Alias */ kseg0_program_mem (rx) : ORIGIN = 0x9FC02000, LENGTH = 0xDF00 /* Lower Boot Alias last(5th) page: Not used */ kseg0_lba_last_page (rx) : ORIGIN = 0x9FC10000, LENGTH = 0x4000 /* Interrupt Vector table */ kseg0_boot_mem : ORIGIN = 0x9FC01000, LENGTH = 0x1000 /* Reset and C Startup module */ kseg1_boot_mem : ORIGIN = 0xBFC00000, LENGTH = 0x480 /* Cache TLB Initialization Table */ kseg1_boot_mem_4B0 : ORIGIN = 0xBFC004B0, LENGTH = 0xB50
BOOTLOADER MAIN CODE
修改后的linker script加到bootloader的項目中后,接着是bootloader的coding,以下是bootloader code的主要部分
if (linereceived) { for (ix = 0; ix < incrbytes; ix++) { Uart_Putc(frameBuffer[ix]); } Uart_Putc('\n'); cksum = bcount = GetXbyte(frameBuffer[LEN_NIBBLE1_INDEX],frameBuffer[LEN_NIBBLE2_INDEX]); SourceAddr.v[1] = GetXbyte(frameBuffer[ADDRH_NIBBLE1_INDEX],frameBuffer[ADDRH_NIBBLE2_INDEX]); SourceAddr.v[0] = GetXbyte(frameBuffer[ADDRL_NIBBLE1_INDEX],frameBuffer[ADDRL_NIBBLE2_INDEX]); rectype = GetXbyte(frameBuffer[TYPE_NIBBLE1_INDEX],frameBuffer[TYPE_NIBBLE2_INDEX]); switch(rectype) { case LINEAR_ADDRESS: SourceAddr.v[3] = GetXbyte(frameBuffer[TYPE_NIBBLE2_INDEX+1],frameBuffer[TYPE_NIBBLE2_INDEX+2]); SourceAddr.v[2] = GetXbyte(frameBuffer[TYPE_NIBBLE2_INDEX+3],frameBuffer[TYPE_NIBBLE2_INDEX+4]); Checksum(); break; case DATA: if ((SourceAddr.word.HW == 0x1fc0)) break; if (SourceAddr.Val >= (EraseAddr.Val+ PAGE_SIZE)) { EraseAddr.Val = (SourceAddr.Val/PAGE_SIZE) * PAGE_SIZE; NVMErasePage(EraseAddr.Val); } linereceived = 0; for (ix=0; ix < 2*bcount;) { pData.byte.LB = GetXbyte(frameBuffer[TYPE_NIBBLE2_INDEX+1+ix+0],frameBuffer[TYPE_NIBBLE2_INDEX+1+ix+1]); pData.byte.HB = GetXbyte(frameBuffer[TYPE_NIBBLE2_INDEX+1+ix+2],frameBuffer[TYPE_NIBBLE2_INDEX+1+ix+3]); pData.byte.UB = GetXbyte(frameBuffer[TYPE_NIBBLE2_INDEX+1+ix+4],frameBuffer[TYPE_NIBBLE2_INDEX+1+ix+5]); pData.byte.MB = GetXbyte(frameBuffer[TYPE_NIBBLE2_INDEX+1+ix+6],frameBuffer[TYPE_NIBBLE2_INDEX+1+ix+7]); unsigned int error = NVMWriteWord(SourceAddr.Val, pData.Val); if ((error & 0x3000) > 0) Uart_Putc('N'); // for debug error = 0; SourceAddr.Val += 4; ix += 8; if (linereceived) Uart_Putc('X'); // for debug } Checksum(); //Buf_Init(); break; case END: Checksum(); U1MODE = 0x0; U1STA = 0x0; TRISCSET = 0x2000; /* RC13 Input */ LATCCLR = 0x6000; /* Clear LATC */ IPC28CLR = 0x1F00; /* Clear IPC28 */ IEC3bits.U1RXIE = 0; /* Clear IEC3 */ (*((void(*)(void))USER_APP_RESET_ADDRESS))(); break; } linereceived = 0; incrbytes = 0; }
Bootloader編譯成功后,使用programmer燒錄到target, 然后拔掉programmer。並且以后再更新application再也不需要programmer了。
APPLICATION LINKER SCRIPT
Bootloader和appication同時放置在閃存存儲器中,要兩者都可以正常工作,就必修確保兩者不能有overlap。所以application的放置需要避開bootloader,這樣需要需改application 的linker script。 以下是修改后的主要部分。
_RESET_ADDR = 0x9D001000; _SIMPLE_TLB_REFILL_EXCPT_ADDR = _ebase_address + 0; _CACHE_ERR_EXCPT_ADDR = _ebase_address + 0x100; _GEN_EXCPT_ADDR = _ebase_address + 0x180; /************************************************************************* * Memory Regions * * Memory regions without attributes cannot be used for orphaned sections. * Only sections specifically assigned to these regions can be allocated * into these regions. * * The Debug exception vector is located at 0x9FC00480. * The config_<address> sections are used to locate the config words at * their absolute addresses. *************************************************************************/ MEMORY { kseg1_boot_mem : ORIGIN = 0x9D001000, LENGTH = 0x480 kseg0_program_mem (rx) : ORIGIN = 0x9D000000 + 0x1000 + 0x480, LENGTH = 0x200000 - (0x1000 + 0x480) /* All C files will be located here */ kseg0_boot_mem : ORIGIN = 0x9D000000, LENGTH = 0x0 kseg0_data_mem (w!x) : ORIGIN = 0x80000000, LENGTH = 0x80000 sfrs : ORIGIN = 0xBF800000, LENGTH = 0x100000 kseg2_ebi_data_mem : ORIGIN = 0xC0000000, LENGTH = 0x4000000 kseg2_sqi_data_mem : ORIGIN = 0xD0000000, LENGTH = 0x4000000 kseg3_ebi_data_mem : ORIGIN = 0xE0000000, LENGTH = 0x4000000 kseg3_sqi_data_mem : ORIGIN = 0xF0000000, LENGTH = 0x4000000 }
HOW TO USE HYPERBOOTLOADER_PIC32
1. 使用XC32編譯HyperBootloader_PIC32(編譯前,需先修改linker script)。
2. 使用pickit3燒錄HyperBootloader_PIC32的Hex文件到目標板中。
3. 拔除pickit3燒錄器,連接目標板與PC的串口,打開超級終端,設置如下:115200-8-None-1-None, Line Delay-20ms。
4. 重啟目標板,超級終端會出現Booting... 字樣。
5. 6秒內,打開Send Text File對話框,選擇期望燒錄的application的hex文件(編譯前,需先修改linker script),點擊確認, HyperBootloader會將接收到的數據傳回到電腦超級終端上,並將數據燒錄到目標板程序存儲器的正確位置。
6. 燒錄完畢,自動跳轉去執行application。
之后每次更新application,只需重復步驟 4 ~ 6 就可以了。
NOTE
HyperBootloader不支持configuration bits的燒寫,需要application的configuration bits 和HyperBootloader_PIC32的configuration bits保持一致。