<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > μC/OS-II 移植筆記 1(FreeScale 68HCS12 核單片機(jī))

          μC/OS-II 移植筆記 1(FreeScale 68HCS12 核單片機(jī))

          作者: 時(shí)間:2016-11-20 來(lái)源:網(wǎng)絡(luò) 收藏
          μC/OS-II移植筆記 1(移植到FreeScale 68HCS12 核單片機(jī),Small Memory Model)


          最近閑暇下來(lái),花了些時(shí)間研究了如何將 μC/OS-II 移植到 FreeScale 68HCS12 內(nèi)核的單片機(jī)。其實(shí)這個(gè)工作前年做過(guò)一次,當(dāng)時(shí)是在網(wǎng)上找的相近的移植代碼(68HC11核,Bank Memory Model,METROWERKS 編譯器)自己做了些修改,內(nèi)核已經(jīng)跑起來(lái)了,但是在跑串口測(cè)試程序(ESBB書(shū)上的那個(gè)串口模塊)時(shí),程序運(yùn)行一段時(shí)間就會(huì)跑飛。當(dāng)時(shí)調(diào)試了許久也沒(méi)有找到問(wèn)題。這次就是接著上次的工作繼續(xù)深入的往下做,消除錯(cuò)誤。其實(shí)前年調(diào)試時(shí)就已經(jīng)隱約的想到了錯(cuò)誤可能的地方,只是當(dāng)年對(duì) 68HCS12 內(nèi)核還有 CodeWarrior IDE 附帶的 C 編譯器、匯編器了解的有限,尤其是對(duì)編譯器的 C Calling Convention、還有 C 編譯器對(duì)代碼中內(nèi)嵌匯編的處理幾乎完全不知,這種水平下調(diào)試不出錯(cuò)誤也是理所當(dāng)然的。
          這次由于有了以前的基礎(chǔ),這次著眼點(diǎn)直接就放在了編譯器對(duì)我寫(xiě)的移植代碼的處理上,在匯編語(yǔ)言的層面上對(duì)移植代碼進(jìn)行了剖析,很快(其實(shí)也不算快了,花了我整整 2 天時(shí)間)就找到了問(wèn)題所在,并給出了一個(gè)初步的解決方案?,F(xiàn)在的移植代碼談不上完美,但至少是正確的。
          1. μC/OS-II 版本的選擇
          這次的移植代碼主要針對(duì) μC/OS-II V2.52,2.52 之后的版本對(duì)移植代碼的要求大體相同,因此這個(gè)移植代碼稍加修改就應(yīng)該能在新版本上運(yùn)行,并且我相信修改的難度應(yīng)該很小。 μC/OS-III還沒(méi)有研究過(guò),因此移植代碼是否適合 μC/OS-III 我就不得而知了。
          之所以選擇μC/OS-II V2.52這個(gè)版本主要基于兩個(gè)方面的考慮。首先,Jean J.Labrosse寫(xiě)的那本大名鼎鼎的《MicroC/OS-II The Real-Time Kernel Second Edition》就是根據(jù)這個(gè)版本寫(xiě)成的,移植過(guò)程中遇到問(wèn)題至少可以翻翻書(shū)。另外,這個(gè)版本確實(shí)也稱得上經(jīng)典,每一行代碼都經(jīng)過(guò)經(jīng)得起推敲。因此,我選擇了這個(gè)版本。
          2. 移植代碼詳解
          μC/OS-II 移植主要需要重寫(xiě) 3 個(gè)代碼文件:
          OS_CPU.H
          OS_CPU_C.C
          OS_CPU_A.S
          下面就對(duì)移植代碼進(jìn)行詳細(xì)的說(shuō)明。開(kāi)發(fā)環(huán)境采用:CodeWarrior Development Studio V5.9.0
          2.1 OS_CPU.H
          OS_CPU.H 中的代碼主要有兩部分,第一部分 typedef 了一系列的基本數(shù)據(jù)類型和幾個(gè)宏定義。具體代碼如下:

          本文引用地址:http://www.ex-cimer.com/article/201611/318813.htm
          1. /*
          2. ******************************************************************************
          3. *DATATYPES
          4. ******************************************************************************
          5. */
          6. typedefunsignedcharBOOLEAN;
          7. typedefunsignedcharINT8U;/*Unsigned8bitquantity*/
          8. typedefsignedcharINT8S;/*Signed8bitquantity*/
          9. typedefunsignedintINT16U;/*Unsigned16bitquantity*/
          10. typedefsignedintINT16S;/*Signed16bitquantity*/
          11. typedefunsignedlongINT32U;/*Unsigned32bitquantity*/
          12. typedefsignedlongINT32S;/*Signed32bitquantity*/
          13. typedeffloatFP32;/*Singleprecisionfloatingpoint*/
          14. typedefdoubleFP64;/*Doubleprecisionfloatingpoint*/
          15. #defineBYTEINT8S/*Definedatatypesforbackwardcompatibility..*/
          16. typedefcharSBYTE;
          17. #defineUBYTEINT8U/*...touC/OSV1.xx*/
          18. #defineWORDINT16S
          19. #defineUWORDINT16U
          20. #defineLONGINT32S
          21. #defineULONGINT32U
          22. typedefunsignedcharOS_STK;/*Eachstackentryis8-bitwide*/
          23. typedefunsignedcharOS_CPU_SR;/*DefinesizeofCPUstatusregister(CCR=8bits)*/
          24. /*
          25. ******************************************************************************
          26. *CONSTANTS
          27. ******************************************************************************
          28. */
          29. #ifndefFALSE
          30. #defineFALSE0
          31. #endif
          32. #ifndefTRUE
          33. #defineTRUE1
          34. #endif


          基本數(shù)據(jù)類型的長(zhǎng)度查編譯器手冊(cè)都有詳細(xì)的說(shuō)明。
          有點(diǎn)難度的是堆棧和狀態(tài)寄存器,需要查68HCS12內(nèi)核手冊(cè),雖然68HCS12內(nèi)核是16位的內(nèi)核,但是堆棧是以字節(jié)為單位的,所以有如下代碼:
          typedef unsigned char OS_STK;

          68HCS12內(nèi)核的狀態(tài)寄存器稱為 CCR,也是8位了,因此:
          typedef unsigned char OS_CPU_SR;

          OS_CPU.H 中還包含對(duì)臨界區(qū)的處理:


          1. #defineOS_CRITICAL_METHOD3
          2. #ifOS_CRITICAL_METHOD==3
          3. OS_CPU_SROSCPUSaveSR(void);
          4. voidOSCPURestoreSR(OS_CPU_SRos_cpu_sr);
          5. #endif
          6. #ifOS_CRITICAL_METHOD==1
          7. #defineOS_ENTER_CRITICAL()__asmsei;
          8. #defineOS_EXIT_CRITICAL()__asmcli;
          9. #endif
          10. #ifOS_CRITICAL_METHOD==2
          11. #defineOS_ENTER_CRITICAL()__asmpshc;__asmsei;
          12. #defineOS_EXIT_CRITICAL()__asmpulc;
          13. #endif
          14. #ifOS_CRITICAL_METHOD==3
          15. #defineOS_ENTER_CRITICAL()(cpu_sr=OSCPUSaveSR())/*Disableinterrupts*/
          16. #defineOS_EXIT_CRITICAL()(OSCPURestoreSR(cpu_sr))/*Enableinterrupts*/


          上述代碼中雖然列出了三種對(duì)臨界區(qū)的不同處理方法,但實(shí)際只有第三種是正確的。
          第一種方法直接開(kāi)關(guān)中斷,這種方法的問(wèn)題在于退出臨界區(qū)后中斷就被強(qiáng)制性的打開(kāi)了,及時(shí)在進(jìn)入臨界區(qū)前中斷是關(guān)著的。在任務(wù)級(jí)代碼中這樣做沒(méi)有太大的問(wèn)題,因?yàn)樵趫?zhí)行任務(wù)代碼是中斷基本都是打開(kāi)的,中斷幾乎只有在臨界區(qū)中才是關(guān)閉的。但是在中斷處理函數(shù)中情況就不是這樣的了,68HCS12 內(nèi)核在進(jìn)入中斷處理函數(shù)后中斷是關(guān)閉的,中斷處理函數(shù)中要調(diào)用 OSIntExit(),OSIntExit()中是有臨界區(qū)的,一退出臨界區(qū)就會(huì)導(dǎo)致中斷打開(kāi),CPU 處理新的中斷,也就是形成所謂的中斷嵌套。而中斷嵌套是我們不希望發(fā)生的,因?yàn)樵试S中斷嵌套并不能顯著提升系統(tǒng)的性能,還會(huì)導(dǎo)致各個(gè)任務(wù)的堆棧使用量加大,,對(duì)于內(nèi)存緊張的單片機(jī)來(lái)說(shuō),絕對(duì)弊大于利。因此,在我的移植代碼中不允許中斷嵌套,也就否掉了第一種臨界區(qū)的處理方式。
          第二種方法看似很好,進(jìn)入臨界區(qū)時(shí)先將 CCR 的值存入堆棧然后關(guān)閉中斷,退出臨界區(qū)時(shí)直接將堆棧的內(nèi)容恢復(fù)到 CCR。看似很完美的解決方法,實(shí)際上卻行不通。C 編譯器要利用堆棧指針的地址來(lái)尋址局部變量,而匯編語(yǔ)句 pshc 卻改變了堆棧指針的指向,導(dǎo)致對(duì)局部變量的訪問(wèn)產(chǎn)生了錯(cuò)位。
          要想說(shuō)明這個(gè)問(wèn)題還要從 C 編譯器對(duì)局部變量的處理方式說(shuō)起,我使用的 C 編譯器將局部變量放到堆棧上,利用堆棧指針(SP)間接尋址局部變量。如果堆棧指針指向的地址變了,訪問(wèn)局部變量時(shí)就要出問(wèn)題。下面用個(gè)例子來(lái)說(shuō)明:

          volatile char a = 1;
          volatile char b = 2;

          __asm pshc; __asm sei;

          a = 3;
          b = 4;

          __asm pulc;

          將其轉(zhuǎn)化為匯編代碼后如下:

          PSHD
          volatile char a = 1;
          LDAB #1
          STAB 1,SP
          volatile char b = 2;
          LSLB
          STAB 0,SP

          __asm pshc; __asm sei;
          PSHC
          SEI
          a = 3;
          INCB
          STAB 1,SP
          b = 4;
          INCB
          STAB 0,SP
          __asm pulc;
          PULC

          PSHD 指令調(diào)整堆棧指針,在堆棧上空出 2 個(gè)字節(jié)存放 a 和 b 的值。a 的地址為[SP+1],b 的地址為[SP],然后給 a 和 b 賦初值。PSHC 首先調(diào)整堆棧指針(SP=SP-1),然后將 CCR 寄存器的值存入堆棧,這里需要注意的是68HCS12核的堆棧是倒生堆棧,實(shí)棧頂(也就是說(shuō)SP指向的是有數(shù)據(jù)的地方,而不是個(gè)空位),而老的68HC11內(nèi)核是虛棧頂?shù)模⊿P指向的是個(gè)空位)。這時(shí) [SP] 中存的是 CCR 的值, [SP+1]指向 b,[SP+2]指向a。因此, STAB, 0,SP 將原本存的 CCR 的值改成了 4, b 的值卻沒(méi)有任何改變,說(shuō)明這款編譯器無(wú)法感知堆棧的變動(dòng)生成正確的代碼。因此第二種方法會(huì)導(dǎo)致錯(cuò)誤的結(jié)果,不能采用。
          這里多說(shuō)兩句,熟悉 x86 體系的讀者可以將 68HCS12 核與 x86 內(nèi)核做個(gè)對(duì)比。這兩個(gè)核都是馮諾依曼結(jié)構(gòu)體系,復(fù)雜指令集,從某種意義上可以說(shuō)這兩種內(nèi)核具有某種神似。在 x86 內(nèi)核上大多數(shù)編譯器也是將局部變量存放到堆棧上,但是不同的是訪問(wèn)局部變量時(shí)用的是 BSP 寄存器而不直接使用 ESP 寄存器,因此在 x86 內(nèi)核上用第二種方法處理臨界區(qū)是沒(méi)有問(wèn)題的,并且可以認(rèn)為是一種相當(dāng)好的方式。從這也可以看出來(lái)寄存器多一些確實(shí)是有好處的。

          第三種方式是實(shí)現(xiàn)兩個(gè)函數(shù) OSCPUSaveSR 和 OSCPURestoreSR。雖然這樣會(huì)多些函數(shù)調(diào)用產(chǎn)生的額外開(kāi)銷,卻沒(méi)有了前兩種方法的問(wèn)題。這兩個(gè)函數(shù)具體的實(shí)現(xiàn)可以放到OS_CPU_A.S 中,也可以在 OS_CPU_C.C。

          如果用 C 語(yǔ)言實(shí)現(xiàn),可以如下:

          1. OS_CPU_SROSCPUSaveSR(void)
          2. {
          3. __asm
          4. {
          5. tfrccr,b//copythevalueofCCRtotheregisterB
          6. sei//Disableinterrupts
          7. }
          8. }
          9. voidOSCPURestoreSR(OS_CPU_SRos_cpu_sr)
          10. {
          11. __asm
          12. {
          13. tfrb,ccr//BcontainstheCCRvaluetorestore,movetoCCR
          14. }
          15. }


          想要理解上面代碼,除了要知道匯編指令 tfr 和 sei 的含義。還要知道所謂的 C 語(yǔ)言調(diào)用約定,對(duì)于當(dāng)前代碼來(lái)說(shuō)需要知道 C 語(yǔ)言編譯器使用何種方式傳遞函數(shù)的參數(shù)和返回值。 這些知識(shí)在 編譯器附帶的文檔《S12(X)Build Tools Reference Manual》可以查到。我這里只介紹直接相關(guān)的幾條,其余的請(qǐng)自己查閱手冊(cè)。

          我所采用的編譯器對(duì)參數(shù)固定的 C 函數(shù)采用 PASCAL 調(diào)用約定,參數(shù)采用堆棧傳遞,傳遞順序?yàn)閺淖蟮接乙来稳霔?,如果最后一個(gè)參數(shù)小于 4 個(gè)字節(jié)則最后一個(gè)參數(shù)采用寄存器傳遞。1 字節(jié)的參數(shù)存放到寄存器 B 中,OSCPURestoreSR 就是這種情況。函數(shù)的返回值如果也是 1 個(gè)字節(jié),那么也通過(guò) 寄存器 B 傳出來(lái),OSCPUSaveSR 就是這種情況。關(guān)于這兩個(gè)函數(shù)我就說(shuō)這么多了。

          如果想直接寫(xiě)匯編代碼的話也很簡(jiǎn)單,下面是例子:

          xdef OSCPUSaveSR
          xdef OSCPURestoreSR
          OSCPUSaveSR:
          tfr ccr,b ; Its assumed that 8-bit return value is in register B
          sei ; Disable interrupts
          rts ; Return to caller with D containing the previous CCR

          OSCPURestoreSR:
          tfr b, ccr ; B contains the CCR value to restore, move to CCR
          rts

          和上面 C 函數(shù)的代碼幾乎一樣,沒(méi)什么可介紹的了。

          另外多說(shuō)一句,大家可能覺(jué)得上面的代碼可以直接寫(xiě)成內(nèi)聯(lián)匯編語(yǔ)句,就不用函數(shù)調(diào)用了。比如下面的代碼:

          #define OS_ENTER_CRITICAL() asm ("tpa; staa cpu_sr; sei")
          #define OS_EXIT_CRITICAL() asm ("ldaa cpu_sr; tap")

          上面的代碼看似很好,進(jìn)入臨界區(qū)時(shí)將 CCR 的值放到 寄存器 a 中,然后存到 cpu_sr 中,再關(guān)中斷。退出臨界區(qū)是恢復(fù) CCR。問(wèn)題在于 CodeWarrior Development Studio 中帶的 C 編譯器太弱智了,無(wú)法感知 寄存器 a 被改變了,更無(wú)法添加相應(yīng)的處理代碼。自己保存寄存器 a 的內(nèi)容又很麻煩,不能直接放到堆棧中,否則會(huì)影響局部變量的訪問(wèn),只能存在 C 編譯器可以感知的地方。比如用下面的方法:
          char tmp_a;
          asm ("staa tmp_a, tpa; staa cpu_sr; sei; ldaa tmp_a")
          asm ("staa tmp_a, tpa; ldaa cpu_sr; tap; ldaa tmp_a")

          這樣的開(kāi)銷也不小,不如直接寫(xiě)兩個(gè)函數(shù)方便。

          OS_CPU.H 還有幾行代碼:

          1. #defineOS_TASK_SW()__asmswi;
          2. #defineOS_STK_GROWTH/*Definestackgrowth:1=Down,0=Up*/
          3. #pragmaCODE_SEGNON_BANKED
          4. voidOSStartHighRdy(void);
          5. voidOSIntCtxSw(void);
          6. voidOSCtxSw(void);
          7. #pragmaCODE_SEGDEFAULT


          都比較簡(jiǎn)單,OS_TASK_SW()將操作系統(tǒng)使用的中斷指定為 SWI 中斷,也就是 software interrupt。
          堆棧生長(zhǎng)方向?yàn)橄蛳律L(zhǎng)。

          至此,OS_CPU.H 就寫(xiě)完了。



          評(píng)論


          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();