本文詳細(xì)介紹了在某直放站監(jiān)控系統(tǒng)中實(shí)現(xiàn)的基于C8051F130 的遠(yuǎn)程在線程序升級(jí)系統(tǒng)的設(shè)計(jì)思路和方法。并提出了一些實(shí)際編程中需要注意的問(wèn)題如KEIL 中程序的定位和C8051F130 程序切換時(shí)的PLL 設(shè)置等。 1. 引言 目前,采用FLASH 存儲(chǔ)介質(zhì)來(lái)作為程序存儲(chǔ)器的單片機(jī)種類越來(lái)越多。和其他類型如OTP、EPROM 型單片機(jī)比較起來(lái),F(xiàn)LASH 具有可擦寫(xiě)方便,次數(shù)多,編程無(wú)需外加高電壓等特點(diǎn)。絕大多數(shù)FLASH 型單片機(jī)都可在運(yùn)行時(shí)通過(guò)指令來(lái)直接擦寫(xiě)內(nèi)部FLASH,提供了IAP、ISP 功能,借助這個(gè)功能,就可以實(shí)現(xiàn)系統(tǒng)底層固件的在線升級(jí)功能。 C8051F130 單片機(jī)是SILICON LABS推出的完全集成的混合信號(hào)片上系統(tǒng)型MCU 芯片,功能十分強(qiáng)大。其采用高速、流水線結(jié)構(gòu)的8051 兼容的CIP-51 內(nèi)核,內(nèi)部PLL可倍頻至100MIPS,具有2個(gè)串口,128KB 可在系統(tǒng)編程的FLASH 存儲(chǔ)器,8448(8K+256)字節(jié)的片內(nèi)RAM,并包含了片內(nèi)JTAG 調(diào)試電路。其功能完全滿足實(shí)現(xiàn)遠(yuǎn)程在線程序升級(jí)系統(tǒng)的條件。 在專用網(wǎng)絡(luò)系統(tǒng)中實(shí)現(xiàn)遠(yuǎn)程在線程序升級(jí),其優(yōu)點(diǎn)顯而易見(jiàn),不僅極大的為系統(tǒng)維護(hù)提供了方便,還節(jié)省了大量的人力財(cái)力。本文詳細(xì)介紹了在某直放站監(jiān)控系統(tǒng)中實(shí)現(xiàn)的基于C8051F130的遠(yuǎn)程在線程序升級(jí)系統(tǒng)的設(shè)計(jì)思路和方法。 2. 設(shè)計(jì)思路 系統(tǒng)組網(wǎng)拓?fù)浣Y(jié)構(gòu)如圖1 所示,正常工作時(shí),網(wǎng)管中心通過(guò)有線或無(wú)線通道對(duì)所屬各站點(diǎn)下位機(jī)狀態(tài)進(jìn)行監(jiān)控。網(wǎng)管中心通過(guò)查詢各站點(diǎn)下位機(jī)參數(shù)得到下位機(jī)固件版本號(hào),如果固件版本號(hào)和現(xiàn)有最新固件版本號(hào)不同,則直接發(fā)送第一幀程序升級(jí)數(shù)據(jù)啟動(dòng)遠(yuǎn)程程序升級(jí)過(guò)程。因?yàn)楸O(jiān)控系統(tǒng)需要實(shí)時(shí)工作,所以程序的升級(jí)也就必須在站點(diǎn)下位機(jī)系統(tǒng)工作過(guò)程中完成。在網(wǎng)管中心發(fā)送程序升級(jí)數(shù)據(jù)的過(guò)程中,站點(diǎn)下位機(jī)系統(tǒng)依然正常工作,只有全部接收完程序升級(jí)數(shù)據(jù)并校驗(yàn)通過(guò)后,才跳轉(zhuǎn)到Bootloader 程序進(jìn)行程序升級(jí)。 ![]() 下位機(jī)硬件框圖如圖2 所示,CPU 選擇C8051F130,其他部分包括控制輸入輸出部分,人機(jī)接口電路,RS232 接口,EEPROM 器件24C16 等。RS232 接口作為系統(tǒng)的通訊接口可以外接電話MODEM或短信MODEM 等標(biāo)準(zhǔn)模塊,和網(wǎng)管中心組成有無(wú)線網(wǎng)絡(luò),其所采用的具體形式不影響本文所述的遠(yuǎn)程升級(jí)系統(tǒng)。EEPOM 器件24C16 由于可擦寫(xiě)次數(shù)比FLASH 多,用來(lái)存儲(chǔ)系統(tǒng)的關(guān)鍵參數(shù)。 ![]() 由于51 系列單片機(jī)外部總線地址為16 位,能直接尋址的最大范圍為64KB,所以C8051F130 將內(nèi)部的128KB FLASH 程序存儲(chǔ)空間被分成了4 塊,BLOCK0、BLOCK1、BLOCK2、BLOCK3。每塊大小為32KB,0x0000~0x7FFF 地址空間始終為BLOCK0,作為公共段。可以通過(guò)設(shè)置程序存儲(chǔ)器空間塊選擇寄存器PSBANK 來(lái)選擇常量操作和取指操作地址在0x8000 ~0xFFFF 所指向的塊。 ![]() COBANK:常量操作存儲(chǔ)塊選擇位。 這兩位選擇常量操作(MOVC 和FLASH MOVX)地址在0x8000 ~0xFFFF 范圍的FLASH 存儲(chǔ)塊。 00:常量操作指向存儲(chǔ)塊0(注意,塊0 也映射到地址0x0000 ~0x7FFF)。 01:常量操作指向存儲(chǔ)塊1。 10:常量操作指向存儲(chǔ)塊2。 11:常量操作指向存儲(chǔ)塊3。 IFBANK:取指操作存儲(chǔ)塊選擇位這兩位選擇取指操作(地址在0x8000 ~0xFFFF 范圍)的FLASH 存儲(chǔ)塊。這兩位只能由位于BLOCK0 的程序改寫(xiě)。 00:從存儲(chǔ)塊0 取指令(注意,塊0 也映射到地址0x0000 ~0x7FFF)。 01:從存儲(chǔ)塊1 取指令。 10:從存儲(chǔ)塊2 取指令。 11:從存儲(chǔ)塊3 取指令。 FLASH 的設(shè)計(jì)分配方案如下: ![]() BLOCK0、BLOCK1 作為默認(rèn)的64KB 程序存儲(chǔ)區(qū)空間,存儲(chǔ)正常工作時(shí)的固件程序。我們所使用的程序不會(huì)超過(guò)60KB,0x0000~0xEFFF 地址區(qū)60KB 空間用于存儲(chǔ)系統(tǒng)主程序,0xF000~0xFFFF 地址地址區(qū)4KB 空間用于存儲(chǔ)Bootloader 程序。 BLOCK2、BLOCK3 作為64KB 常量存儲(chǔ)區(qū),用于存儲(chǔ)系統(tǒng)運(yùn)行過(guò)程中接收到的程序升級(jí)數(shù)據(jù)。 如果要遠(yuǎn)程對(duì)某指定站點(diǎn)的固件進(jìn)行升級(jí)時(shí),網(wǎng)管中心對(duì)該站點(diǎn)分幀發(fā)送程序升級(jí)數(shù)據(jù),升級(jí)數(shù)據(jù)協(xié)議格式如下: ![]() 網(wǎng)管中心發(fā)送程序升級(jí)數(shù)據(jù)幀時(shí),每幀數(shù)據(jù)大小固定為512 字節(jié),幀序號(hào)從1 開(kāi)始。從功能標(biāo)志處起到數(shù)據(jù)部分最后一字節(jié)做累加和作為校驗(yàn)碼。最后一幀不足512 字節(jié)部分加0xFF 補(bǔ)足。在發(fā)送過(guò)程中,由于每幀數(shù)據(jù)以0x1002 開(kāi)始,0x1003 結(jié)束,中間碰到0x10 時(shí),要再補(bǔ)發(fā)0x10。接收時(shí)除了幀頭幀尾,在幀中間時(shí)如果連續(xù)接收到兩個(gè)0x10,則需要去掉一個(gè)。 指定站點(diǎn)在成功接收完一幀程序升級(jí)數(shù)據(jù)后,如果校驗(yàn)通過(guò)則擦除對(duì)應(yīng)的BLOCK2 或者BLOCK3中的扇區(qū)。這里需要注意的是,C8051F130 的FLASH 扇區(qū)大小是1024 字節(jié),而我們的數(shù)據(jù)幀大小定為512 字節(jié),所以應(yīng)該接收到每?jī)蓭瑪?shù)據(jù)才擦除對(duì)應(yīng)的扇區(qū)。將程序升級(jí)數(shù)據(jù)寫(xiě)入FLASH 中,同時(shí)對(duì)每幀512 字節(jié)的程序升級(jí)數(shù)據(jù)做累加,并將其累加和作為校驗(yàn)碼寫(xiě)入EEPROM 指定位置,最后給出正確回應(yīng)。如果校驗(yàn)錯(cuò)誤,則回應(yīng)錯(cuò)誤。網(wǎng)管中心接收到錯(cuò)誤回應(yīng)時(shí)應(yīng)該重發(fā)上一幀數(shù)據(jù),否則繼續(xù)發(fā)送下一幀數(shù)據(jù)。如果幀序號(hào)等于總幀數(shù),說(shuō)明指定站點(diǎn)接收到最后一幀數(shù)據(jù),置需要進(jìn)行程序升級(jí)標(biāo)志,此標(biāo)志至關(guān)重要,因此將其存儲(chǔ)在EEPROM 中,為增加其可靠性,另外還存儲(chǔ)其反碼作為校驗(yàn), 然后直接跳轉(zhuǎn)Bootloader 程序,以升級(jí)主程序。 ![]() 程序跳轉(zhuǎn)到Bootloader 程序執(zhí)行后,首先必須要檢查程序升級(jí)標(biāo)志,如果為真,再檢查其校驗(yàn)碼,只有兩者都符合預(yù)設(shè)的值,才認(rèn)為確實(shí)需要進(jìn)行程序升級(jí)。否則跳轉(zhuǎn)到主程序區(qū)執(zhí)行。 程序升級(jí)過(guò)程如下:首先讀出24C16 中存儲(chǔ)的程序升級(jí)數(shù)據(jù)的總幀數(shù),為防止寫(xiě)入到BLOCK2/3中的程序升級(jí)數(shù)據(jù)有錯(cuò)誤,從BLOCK2/3 中讀出每一幀512 字節(jié)的數(shù)據(jù)進(jìn)行累加,檢查其是否和存儲(chǔ)在24C16 中的對(duì)應(yīng)數(shù)據(jù)幀的累加校驗(yàn)碼相等。如果所有的的程序升級(jí)數(shù)據(jù)校驗(yàn)都通過(guò),則說(shuō)明數(shù)據(jù)正確,開(kāi)始擦除主程序區(qū)FLASH,并再次讀出BLOCK2/3 中的程序升級(jí)數(shù)據(jù)寫(xiě)入主程序區(qū)FLASH。完成后擦除程序升級(jí)標(biāo)志,跳轉(zhuǎn)到主程序區(qū)開(kāi)始執(zhí)行新版本固件程序。在讀出校驗(yàn)過(guò)程中,只要有一幀數(shù)據(jù)校驗(yàn)通不過(guò),則認(rèn)為寫(xiě)入到BLOCK2/3 的程序升級(jí)數(shù)據(jù)發(fā)生錯(cuò)誤,為保證系統(tǒng)安全,擦除程序升級(jí)標(biāo)志,跳轉(zhuǎn)到主程序區(qū)執(zhí)行原版本程序,并等待下一次升級(jí)。 3. 需要注意的問(wèn)題 3.1 項(xiàng)目管理問(wèn)題 下位機(jī)固件程序中實(shí)際包含兩個(gè)獨(dú)立的部分,Bootloader 程序和主程序。我們?cè)贙EIL 中分別為這兩部分的建立獨(dú)立的項(xiàng)目文件,分別編譯。燒寫(xiě)編譯后產(chǎn)生的HEX 文件時(shí),應(yīng)該先擦除FLASH 后,燒寫(xiě)B(tài)ootloader 程序,然后在不擦除之前內(nèi)容的情況下燒寫(xiě)主程序。 3.2 Bootloader 的存儲(chǔ)位置 Bootloader 程序必須保證在上電過(guò)程后立即運(yùn)行,而51 單片機(jī)的中斷向量存放在低地址處。所以Bootloader 程序不能存儲(chǔ)在低地址處,必須存放到高地址處,本例中,留出0xF000~0xFFFF 4KB 的FLASH 空間作為Bootloader 程序存儲(chǔ)區(qū)。在KEIL 開(kāi)發(fā)環(huán)境中,默認(rèn)會(huì)為項(xiàng)目文件提供初始化文件STARTUP.A51 來(lái)清空RAM 空間,以及調(diào)用初始化全局變量代碼段。其默認(rèn)起始地址安排在0x0000處。為保證上電過(guò)程后立即運(yùn)行Bootloader,我們?cè)谄鹗继庍必須要手動(dòng)修改匯編指令如下: ![]() 以上代碼編譯時(shí)強(qiáng)制在0x0000 處放置一條跳轉(zhuǎn)到 0xF000 處的指令,這就保證了保證在上電過(guò)程后立即跳轉(zhuǎn)到Bootloader 程序運(yùn)行。為了將Bootloader 程序的所有代碼定位在0xF000~0xFFFF 范圍內(nèi),要對(duì)KEIL 的BL51 定位選項(xiàng)設(shè)置如下: ![]() 主程序同樣有代碼定位的問(wèn)題。為保證程序升級(jí)后能正常工作,修改主程序的STARTUP.A51 文件如下: ![]() 這里保證執(zhí)行主程序也是先跳轉(zhuǎn)到Bootloader 程序,而將0x0006 設(shè)為了主程序的起始地址,避免了對(duì)中斷向量表的占用。 另外還要將主程序的編譯代碼進(jìn)行定位。設(shè)置和圖4 中類似,只是將Code Ranger 設(shè)為主程序的代碼空間:0x0000~0xEFFF。 3.3 程序跳轉(zhuǎn)時(shí)的PLL 設(shè)置 C8051F130 內(nèi)部帶有PLL,最高主頻可達(dá)100MHz。在本系統(tǒng)設(shè)計(jì)中,外部晶振頻率為11.0592MHz, 在Bootloader 程序和主程序中都通過(guò)使能PLL,倍頻至99.5328MHz 作為系統(tǒng)時(shí)鐘。在上電后,C8051F130 默認(rèn)是以內(nèi)部時(shí)鐘作為系統(tǒng)時(shí)鐘的,通過(guò)執(zhí)行初始化程序,系統(tǒng)再切換到以PLL 輸出為系統(tǒng)時(shí)鐘的工作環(huán)境上來(lái)。如果通過(guò)Silicon Labs 公司提供的初始化軟件CONFIG2 來(lái)配置初始化C8051F130 的代碼的話,在初始化PLL 的過(guò)程中,會(huì)關(guān)閉PLL 模塊。 // 一個(gè)由 CONFIG2 生成的系統(tǒng)時(shí)鐘初始化程序, // 注意:在調(diào)用此函數(shù)時(shí),系統(tǒng)時(shí)鐘是由內(nèi)部振蕩器產(chǎn)生。 void Oscillator_Init() { int i = 0; SFRPAGE = CONFIG_PAGE; // 切換到對(duì)應(yīng)的寄存器頁(yè) OSCXCN = 0x67; // 選擇外部晶振,頻率11.0592MHz. for (i = 0; i < 3000; i++); // Wait 1ms for initialization while ((OSCXCN & 0x80) == 0); // 檢測(cè)晶振是否已穩(wěn)定,如果穩(wěn)定,繼續(xù)執(zhí)行下面的程序。 PLL0CN = 0x04; // 選擇PLL 源時(shí)鐘為外部晶振,PLL 保持在復(fù)位狀態(tài),偏置發(fā)生器被禁止 CCH0CN &= ~0x20; // 禁止預(yù)取引擎。 SFRPAGE = LEGACY_PAGE; // 切換到對(duì)應(yīng)的寄存器頁(yè) FLSCL = 0xB0; // FLASH 讀時(shí)間,SYSCLK <= 100 MHz SFRPAGE = CONFIG_PAGE; CCH0CN |= 0x20; // 允許預(yù)取引擎。 PLL0CN |= 0x01; // PLL 偏置發(fā)生器被使能 PLL0DIV = 0x01; // PLL 預(yù)分頻值 PLL0FLT = 0x07; // PLL 濾波器參數(shù) PLL0MUL = 0x09; // PLL 時(shí)鐘倍頻寄存器 for (i = 0; i < 15; i++); // 等待PLL 初始化。 PLL0CN |= 0x02; // PLL 被使能 while ((PLL0CN & 0x10) == 0); // 等待PLL 輸出頻率已經(jīng)鎖定。 CLKSEL = 0x02; // 選擇PLL 輸出為系統(tǒng)時(shí)鐘 OSCICN &= ~0x80; // 關(guān)閉內(nèi)部振蕩器。 } 如果使用這段代碼初始化PLL 模塊,在主程序跳轉(zhuǎn)到Bootloader 程序,或者由Bootloader 程序跳轉(zhuǎn)到主程序時(shí),必須考慮PLL 的設(shè)置問(wèn)題。因?yàn)樵ぷ鲿r(shí)鐘是PLL 的輸出,而直接切換到另外一個(gè)程序中時(shí),執(zhí)行以上代碼會(huì)關(guān)閉PLL 系統(tǒng)時(shí)鐘,導(dǎo)致系統(tǒng)時(shí)鐘丟失,工作不正常。一個(gè)更安全的做法是在程序間互相跳轉(zhuǎn)之前,將系統(tǒng)時(shí)鐘切換到上電后默認(rèn)的內(nèi)部時(shí)鐘上。代碼如下所示: // 跳轉(zhuǎn)到Bootloader 程序處開(kāi)始執(zhí)行, // 因?yàn)樵诖薆ootloader 程序中采用PLL 作為系統(tǒng)時(shí)鐘, // 所以跳轉(zhuǎn)之前,應(yīng)該將時(shí)鐘切換到內(nèi)部振蕩器或外部時(shí)鐘。 // 本函數(shù)切換到內(nèi)部時(shí)鐘 void GotoBootLoader(void) { #define BOOTLOADER_ADDR 0xF000 SFRPAGE = CONFIG_PAGE; OSCICN = 0xC0; // 使能內(nèi)部振蕩器,且8 分頻,其起振時(shí)間短,無(wú)需延時(shí)。 CLKSEL = 0x00; // 選擇內(nèi)部時(shí)鐘作為系統(tǒng)時(shí)鐘。 PLL0CN = 0x00; // 關(guān)閉PLL。 ((void (code *)(void)) BOOTLOADER_ADDR)(); // 程序跳轉(zhuǎn)到Loaded 的代碼運(yùn)行,正常情況下永遠(yuǎn)不會(huì)返回 } 3.4 寄存器頁(yè)的切換 C8051F130 內(nèi)部功能模塊多,其控制寄存器也多,為了合理安排控制寄存器位置,采用了寄存器頁(yè)控制寄存器SFRPAGE 來(lái)將相同的地址切換到不同的控制寄存器。除了中斷函數(shù)自動(dòng)切換到對(duì)應(yīng)的寄存器頁(yè)外,當(dāng)在程序中對(duì)某個(gè)寄存器操作前,需要設(shè)置SFRPAGE 切換到對(duì)應(yīng)的寄存器頁(yè)。因此,在C51 語(yǔ)言編寫(xiě)的程序中,涉及到寄存器操作的模塊函數(shù)中,需要在執(zhí)行之前保存SFRPAGE 寄存器值,處理完畢后再恢復(fù)先前的SFRPAGE 寄存器值。相當(dāng)于對(duì)SFRPAGE 寄存器值做一次出入棧。示例代碼如下: void PutUnchar(uchar ch) //顯示字符 { uchar SFRPAGE_SAVE = SFRPAGE; // 保存 SFRPAGE // 其他局部變量定義 // 函數(shù)執(zhí)行代碼部分,包含對(duì)指定寄存器的操作 SFRPAGE = SFRPAGE_SAVE; // 恢復(fù)SFRPAGE } 4. 總結(jié) 通過(guò)在系統(tǒng)工作過(guò)程中接收程序升級(jí)數(shù)據(jù),存入內(nèi)部FLASH,全部接收完成后再集中升級(jí)主程序的方法,充分利用了C8051F130 的內(nèi)部FLASH 空間,對(duì)系統(tǒng)的實(shí)時(shí)工作影響小,僅在Bootloader 程序運(yùn)行期間不能正常工作,減小了通訊網(wǎng)絡(luò)通訊狀況差時(shí)對(duì)遠(yuǎn)程在線更新程序造成的風(fēng)險(xiǎn)。 基于以上方法,已經(jīng)成功設(shè)計(jì)出可遠(yuǎn)程在線升級(jí)程序的直放站監(jiān)控系統(tǒng),使用情況表明,該系統(tǒng)能穩(wěn)定可靠工作,且為系統(tǒng)的維護(hù)節(jié)省了大量的工作量。本文介紹的在線程序升級(jí)設(shè)計(jì)方案,雖然是基于C8051F130 單片機(jī)的,但對(duì)其他類型的在線程序升級(jí)設(shè)計(jì)都有很好的參考作用。 參考文獻(xiàn): [1] Silicon Laboratories Inc.C8051F12X_13X DataSheet.2003. [2] Keil Software Inc. uVison3 HELP. 作者:泉州鐵通電子設(shè)備有限公司 高時(shí)兵 馬宏平 陳建福 來(lái)源:?jiǎn)纹瑱C(jī)與嵌入式系統(tǒng)應(yīng)用 2008 (9) |