一、80251CPU的寄存器
(1)STC32G單片機與Keil的C251編譯器都是基于80251CPU內(nèi)核的。當(dāng)單片機的某個任務(wù)運行時,最重要的資源就是全部寄存器。80251CPU的寄存器見下圖:
從圖中可以看到80251CPU的寄存器分為32位(雙字)寄存器10個,16位(字)寄存器16個,8位(字節(jié))寄存器16個,圖中表格中的名稱就是80251匯編語言中的寄存器名稱。
80251和8051是Intel經(jīng)過精心設(shè)計的兩款CPU內(nèi)核, 80251CPU中包含了一個完整的8051CPU,80251指令集中包含了一個完整的8051指令集。8051的8位累加器A和B對應(yīng)80251的R11和R10,這與80251CPU是大端數(shù)據(jù)存儲模式對應(yīng)。
80251的所有寄存器并不位于內(nèi)存地址空間,它們位于專門的“寄存器文件組”空間,除了“R0~R7”寄存器,其余寄存器只能用寄存器尋找模式對它們進行賦值和讀取,不能用對內(nèi)存地址空間的尋址方式來操作它們,在上圖中用淺綠色表示。
(2)上圖中深綠色的8051的通用8位寄存器R0~R7是一組很特殊的寄存器,它是位于內(nèi)存地址空間的4組寄存器中的一組,見下圖所示:
8051的4組寄存器Bank0~Bank3,位于內(nèi)存DATA區(qū)域,占頭32個字節(jié)。在單片機運行時根據(jù)程序狀態(tài)字“PSW”中的“RS1”和“RS0”兩位來決定8051指令中的“R0~R7”寄存器對應(yīng)的是哪個地址的數(shù)據(jù)。
比如加法指令“ADD A, R4”,當(dāng)RS1RS0=00時,是將存在DATA空間地址04H的8位數(shù)據(jù)與A寄存器內(nèi)容相加,但是當(dāng)RS1RS0=10時,卻是將存在DATA空間地址14H的8位數(shù)據(jù)與A寄存器內(nèi)容相加。
8051CPU采用這樣結(jié)構(gòu)兩個最重要的原因一是初期的51單片機運行速度很低,二是1981年左右受工藝限制在集成電路中集成RAM很難很貴,因此對于要使用多個變量的程序沒有那么多內(nèi)存空間可以使用。采用的是4組寄存器的方法,本質(zhì)上是讓用戶在同一時刻擁有8個寄存器和24個字節(jié)的臨時寄存器變量,經(jīng)過優(yōu)化編譯,以較小的RAM空間實現(xiàn)大數(shù)據(jù)處理的需求。
(3)重要:在80251指令中涉及“R0~R7”寄存器的操作,并不唯一對于8個內(nèi)存地址,而是依賴于PSW中的字段“RS1RS0”的值將DATA空間的4組地址映射為“R0~R7”寄存器。
(4)在80251CPU中,8位、16位和32位寄存器不是獨立的,而是像金字塔一樣組成的。
下圖為80251通用寄存器的組成方法
比如32位的DR4寄存器由兩個16位的寄存器WR4和WR6組成,而16位的WR6寄存器由兩個8位的寄存器R6和R7組成。每一組的寄存器中一個發(fā)生變化,則這一組的寄存器都按組成方式變化。
(5)重要提示:從上面的組成圖可以看出,DR0和DR4、WR0~WR6寄存器依賴“R0~R7”寄存器,即使你沒有改變它們,但只要PSW中的字段“RS1RS0”的值發(fā)生變化,那么R0~R7就會被映射到不同的DATA區(qū)域,它們的值就會發(fā)生變化。
因此為了避免這種歧義,Keil的C251編譯器在對C語言程序進行編譯的時候不會對PSW的字段“RS1RS0”進行設(shè)置(其他字段在各種操作時由硬件設(shè)置)。
由于80251有很多的寄存器和DATA空間了,特別建議用戶如果沒有特別的目的不要用匯編語言去設(shè)置“RS1RS0”字段。
(6)由于80251指令集完整包含8051指令集,因此為了保證8051指令也能正確地運行,80251CPU的特殊寄存器在SFR區(qū)域中也有一個對應(yīng)的映射。標(biāo)準(zhǔn)的80251CPU映射見下圖,具體的采用80251內(nèi)核的單片機的映射應(yīng)參考其技術(shù)手冊。
二、FreeRTOS任務(wù)切換時的現(xiàn)場保護與恢復(fù)
(7)當(dāng)前任務(wù)現(xiàn)場是指任務(wù)程序執(zhí)行到切換點時程序進一步正確執(zhí)行時所需要的資源。FreeRTOS主要依靠定時器0的1KHz的中斷來掃描和調(diào)度任務(wù),因此當(dāng)前執(zhí)行的實時任務(wù)的切換點就是定時器0的中斷點。
定時器0中斷發(fā)生時,24位的PC值(用戶下一條要執(zhí)行的程序地址)和PSW1程序狀態(tài)字已經(jīng)被組合位4個字節(jié)32位由硬件壓入堆棧了,其余的中斷服務(wù)程序由移植的FreeRTOS程序來完成。其中涉及中斷現(xiàn)場保護和恢復(fù)功能用匯編語言寫在“portasm.h”文件中。
(8)寄存器現(xiàn)場的保存與恢復(fù)。STC官方移植的FreeRTOS定時器0中斷現(xiàn)場的寄存器保護與恢復(fù)的程序見下圖:
上圖中第81行到第90行依次將9個32位的寄存器壓入堆棧保存,在將8051的程序狀態(tài)字PSW壓入堆棧保存。
在中斷退出時或者任務(wù)恢復(fù)時,第97行到第106行程序?qū)⑦@些寄存器原樣從堆棧中恢復(fù)。
(9)關(guān)于R0~R7寄存器的討論。注意第88行和第89行是將R0~R7寄存器用對應(yīng)的32位寄存器DR0和DR4推入堆棧的,然后把PSW推入堆棧,這樣按照前面對R0~R7寄存器映射的討論,不管當(dāng)前任務(wù)用戶對字段“RS1RS0”的設(shè)置如何,這3條指令都已把當(dāng)前任務(wù)的正在使用的R0~R7寄存器現(xiàn)場保存了,無論它們處于哪個Bank。
要點:對于STC官方移植的FreeRTOS,不需要像8051單片機一樣,在第90行后面加“PSW=0”這樣的語句來對字段“RS1RS0”進行設(shè)置了。
(10)任務(wù)切換時對于任務(wù)堆棧的保存與恢復(fù)。
80251的系統(tǒng)SPX堆棧運行在也只能運行在EDATA內(nèi)存區(qū)域中,任務(wù)切換點的現(xiàn)場不但包括寄存器,還包括整個堆棧里面的內(nèi)容。比如在進行函數(shù)訪問和嵌套時,函數(shù)的返回地址就被LCALL或者ECALL指令保存在堆棧中,另外,可重入函數(shù)的參數(shù)和局部變量也放在了堆棧中,最后,為了實現(xiàn)某些算法,編程者需要把某些變量和數(shù)據(jù)臨時存放在堆棧中,當(dāng)然,還包括中斷發(fā)生時被壓入堆棧的寄存器數(shù)據(jù)。
由于STC官方移植的FreeRTOS運行的內(nèi)存模式為“XSmall”模式,在這個模式下,用戶定義的任務(wù)的堆棧也放在了EDATA區(qū)域中。因此FreeRTOS采取了哪個任務(wù)運行,就將SPX堆棧運行在那個任務(wù)的任務(wù)堆棧中的方法,任務(wù)切換時只需要保存和恢復(fù)SPX的值,整個任務(wù)堆棧的內(nèi)容就被保存和恢復(fù)了。下圖是任務(wù)堆棧保存與恢復(fù)的程序:
其中DR60包括了SPX,第49行將其賦值到DR0,第50行將當(dāng)前任務(wù)的任務(wù)表地址賦值給DR4,然后第51行和第52行分兩個16位用間接尋址的方式保存SPX的值,實現(xiàn)了任務(wù)堆棧的保存。第59行到第62行是恢復(fù)任務(wù)SPX的值,完成恢復(fù)任務(wù)的功能。
采用這種將用戶任務(wù)堆棧設(shè)置在EDATA空間的方法,由于只保存和恢復(fù)SPX寄存器的值,不用進行堆棧內(nèi)容復(fù)制,所以任務(wù)切換速度極快。但也注定STC官方移植的FreeRTOS V1.02版本只能運行在“XSmal”模式。
三、C251中斷時的現(xiàn)場保護與恢復(fù)
在實際的FreeRTOS項目中,除了系統(tǒng)的定時器0中斷外,還有其他的中斷,在這些中斷中,如何保存中斷保存和恢復(fù)當(dāng)前任務(wù)現(xiàn)場,如何協(xié)調(diào)這些中斷與FreeRTOS任務(wù)調(diào)度之間的關(guān)系,對于保障RTOS程序的正確運行很重要。
(11)C251無局部變量中斷服務(wù)函數(shù)。下圖為一個典型的例子:
這個例子里,實際的中斷服務(wù)程序寫在另一個程序文件中,這里就只有一個函數(shù)訪問語句,在中斷函數(shù)里沒有使用局部變量。下面是對應(yīng)的編譯后的匯編語言列表:
對比前面FreeRTOS的定時器0系統(tǒng)中斷,一個是沒有保存DR12,另一個是DR56中,只保存了DPTR部分,沒有保存DPXL8位寄存器部分。
這是C251的約定:首先DR12(包括WR12、WR14、R12~R15)寄存器,保留給中斷函數(shù)中的局部變量使用,其他地方對C語言的編譯都不會使用,用戶編程時可以把它作為臨時變量使用;其次,DPXL的值由Keil的C251編程環(huán)境在單片機啟動時設(shè)置,用戶程序和C251編譯器自己都不得改變它。
(12)C251有局部變量中斷服務(wù)函數(shù)。下圖為一個典型的例子:
這是CAN1的中斷服務(wù)程序,里面有兩個8位的變量“isr”和“store”,下圖是編譯后的中斷現(xiàn)場保存程序:
從上圖中可以看到比無變量情況,增加了“PUSH R15”指令。這是因為C251編譯器將R15作為“store”變量使用。由于本中斷程序使用了“R15”,所以將用戶任務(wù)的“R15”作為現(xiàn)場保存,待中斷任務(wù)完成后再與其他寄存器一起恢復(fù)出來。
(13)C251簡單中斷服務(wù)函數(shù)。下圖為一個典型的例子:
這是定時器1的中斷程序,它只是將P66的LED燈的電平翻轉(zhuǎn)。下圖是對應(yīng)的編譯后匯編語言列表:
從圖中可以看到,除了硬件中斷本身推入堆棧的24位PC和PSW1外,沒有再保存任何寄存器,包括PSW。這是因為“CPL P66”這條指令,不涉及任何寄存器,也不改變?nèi)魏蜳SW中的標(biāo)志位,所以不需要保存和恢復(fù)硬件中斷外的其他現(xiàn)場。
(14)結(jié)論。對于C251編譯器,如果用戶打開高級別優(yōu)化選項,那么編譯器就會分析中斷程序的內(nèi)容,自動地決定要保存或恢復(fù)多少中斷現(xiàn)場。
(15)下面是C51編譯器對STC8H8K單片機鍵盤掃描范例中的中斷程序:
其對應(yīng)的編譯結(jié)果為:
對比上面C251和這里C51保存中斷現(xiàn)場的程序,這里在保存寄存器現(xiàn)場中明顯第加入了“MOV PSW, #00”指令,而C251在任何情況下都沒有這條指令,這是為什么?限于篇幅,后文將給出詳細的原因分析。
]]>三菱PLC的中斷分為三種:輸入中斷,計數(shù)中斷和定時器中斷,這個和單片機有點類似,本小節(jié)主要以介紹一下輸入中斷,通過PLC的輸入端子觸發(fā)的中斷。
什么是中斷?中斷有什么用?
什么是中斷:中斷就是當(dāng)PLC正在執(zhí)行某一個動作時,突然收到中斷觸發(fā)信號,立即停止當(dāng)前執(zhí)行動作,去執(zhí)行中斷程序中的動作,中斷程序執(zhí)行完成后返回被打斷的地方繼續(xù)執(zhí)行之前動作,中斷程序的優(yōu)先級最高,不受掃描周期的影響;就比如你正在洗衣服,正洗了一半,突然你媳婦喊你打王者榮耀,于是你立即停止洗衣,開始打游戲,打完王者后,你又返回繼續(xù)洗衣服。你媳婦喊你打王者是觸發(fā)中斷,你打游戲是中斷運行程序,你洗衣服時主程序;
中斷參考示意圖
中斷有什么用:中斷程序一般情況下很少用到,簡單了解一下即可。PLC是通過不斷執(zhí)行輸入掃描,程序執(zhí)行,輸出刷新三個動作,執(zhí)行完一個周期所用的時間稱為一個掃描周期,F(xiàn)X系列PLC掃描周期通常10-30ms;假如X0作為外部輸入計數(shù)用,掃描周期是20ms,在一個掃面周期內(nèi)X0變化了多次,這個時候,計數(shù)就不準(zhǔn)確了,如果引入中斷就可以解決掃描周期帶來的影響;
FX3U中輸入中斷對應(yīng)的指針編號:
輸入中斷指針及編號
應(yīng)用案例:X0作為外部輸入脈沖計數(shù),X0的下降沿時觸發(fā)中斷進行計數(shù);在三菱PLC的梯形圖編程中程序如下圖所示;
參考程序