1. gzyueqian
      18078865874
      首頁 > 新聞中心 > > 正文

      uC/OS II針對TMS320C32的移值

      更新時間: 2007-06-28 10:41:45來源: 粵嵌教育瀏覽量:911

        嵌入式操作系統uc/os ii是一個可移植可裁剪可固化的、占先式多任務的微os。它的源代碼結構清晰、明了,注釋詳盡、易懂,組織有條理,而且大部分源代碼都是用大多數編譯器都支持的ansi c語言寫的,只有很少的一部分與微處理器相關的代碼是用匯編語言寫的。這給我們學習和理解內核代碼帶來了很大的方便,也使得移值工作得到的簡化。
      下面介紹如何將uc/os-ii移值到ti的浮點dsp芯片tms320c32上;(注:基于tms320c32的移值也適合于tms320c3x系列dsp芯片)。

        一、tms320c32 dsp芯片介紹

        tms320c3x是ti公司的第三代數字信號處理芯片(簡稱dsp芯片),也是其代浮點dsp芯片。tms320c32是其中的一款,能工作在60mhz的時鐘頻率下,指令運行速度達到60 mflops,是一款性價比很高的浮點處理器,在工業控制及其其他領域有著廣泛的應用。

        tms320c32芯片由如下幾個模塊組成:浮點cpu、512字節的片上ram、2個dma通道、1個串口、2個定時器、片上固化的引導程序等。另外c32內部還有如下的一些全局的、通用的寄存器:1)8個40-bit的寄存器(r0~r7),可以用來存放32-bit的整數,同時也可以用來存放40-bit的擴展精度的浮點數;2)8個32-bit的輔助寄存器(ar0~ar7),它們的主要作用是存儲地址、參與各種模式的尋址等,當然也可以作為一般的寄存器來使用;3)狀態寄存器st(含有全局中斷使能位global interrupt-enable)、堆棧寄存器sp、中斷標志寄存器if、中斷使能寄存器ie、i/o標志寄存器iof、數據頁指針寄存器dp(每頁容量為64k,跨也操作時必須更新dp寄存器)、索引寄存器ir0、ir1(用來參與復雜的尋址)、塊大小寄存器bk(循環尋址時指定數據塊大小的寄存器)、重復執行寄存器rs(重復塊起始地址)、re(重復塊終止地址)、rc(重復次數);這些寄存器都是全局的,可以看成c語言中各種類型的變量,用作不同的用途;

        要實現uc/os-ii向tms320c32的移值,需要c3x的c編譯器支持,否則無從下手。我們使用的是ti公司的c編譯器coder composer v4.10.36。這個c編譯器允許嵌入行匯編,另外還具有強大的優化c編譯的功能。

        二、移值中所需修改的文件

        和cpu相關的文件主要有三個:c語言文件os_cpu_c32.c、頭文件os_cpu_c32.h和匯編文件os_cpu_c32.asm,我們的主要移值工作就是針對這三個文件做一些變動。

        1.os_cpu_c32.h
        
        os_cpu_c32.h文件包括了用typedef、#define定義的與cpu相關的基本信息,其具體內容如下:

        #ifndef __os_cpu_h__
        #define __os_cpu_h__
        typedef unsigned char boolean; /*布爾量 */
        typedef unsigned char int8u; /* 8位無符號數 */
        typedef signed char int8s; /* 8位有符號數 */
        typedef unsigned int int16u; /* 16位無符號數 */
        typedef signed int int16s; /* 16位有符號數 */
        typedef unsigned long int32u; /* 32位無符號數 */
        typedef signed long int32s; /* 32位有符號數 */
        typedef float fp32; /* 32位單精度浮點數 */
        typedef long double fp40; /*40位擴展精度浮點數 */
        typedef unsigned int os_stk; /*堆棧入口寬度位32位 */
        #define os_stk_growth 0 /*堆棧由低地址向高地址增長*/
        #define os_critical_method 1
        #if os_critical_method == 1 /*方法一*/
        #define os_enter_critical() asm (` and 0dfffh,st `)
        /*關全局中斷,進入臨界區*/
        #define os_exit_critical() asm (` or 02000h,st `)
        /*開全局中斷,退出臨界區*/
        #endif
        #if os_critical_method == 2 /*方法二*/
        /*保存中斷禁止狀態到堆棧,關全局中斷,進入臨界區*/
        #define os_enter_critical() { \
        asm(` push st`); \
        asm(` and 0dfffh,st`); \
        }
        #define os_exit_critical() asm(` pop st`) /* 恢復中斷禁止狀態*/
        #endif
        #define os_task_sw() asm(` trap 27`) /*用于任務切換的軟中斷*/
        (1)數據類型

        由于不同的處理器有不同的字長,所以uc/os-ii的移值包括了一系列的數據類型的定義,以確保其可移值性。這里我們定義一些c32以及code composer都能識別、處理的一些數據類型。

        type size range
        minimum maximum
        char,signed char 32-bit -2147483648(-231) 2147483647(231-1)
        unsigned char 32-bit 0 4294967295(232-1)
        short 32-bit -2147483648(-231) 2147483647(231-1)
        unsigned short 32-bit 0 4294967295(232-1)
        int,signed int 32-bit -2147483648(-231) 2147483647(231-1)
        unsigned int 32-bit 0 4294967295(232-1)
        long,signed long 32-bit -2147483648(-231) 2147483647(231-1)
        unsigned long 32-bit 0 4294967295(232-1)
        float 32-bit 5.877472e-39 3.4028235e38
        double 32-bit 5.877472e-39 3.4028235e38
        long double 40-bit 5.87747175e-39 3.4028236684e38
        tms320c32的數據類型(表i)
        從上表我們可以看出,c32本質上只有4種數據類型:32位的無符號整數:0~4294967295;32位的有符號整數:-2147483648~2147483647;32位的浮點單精度浮點數:5.877472e-39~3.4028235e38;40位的擴展進度浮點數5.87747175e-39~3.4028236684e38;我們上面定義的8、16位數實際上都是32位的。另外c32中,堆棧都是按32位數據類型進行操作的,所以堆棧數據類型os_stk申明為32位無符號整數;

        (2)代碼的臨界區
       
        uc/os-ii在進入系統臨界代碼區之前要關中斷,避免臨界區代碼受多任務或中斷服務程序的破壞,等到臨界區代碼執行完畢之后,該怎么處理呢?有兩種方案可以供選擇:1)不管關中斷前中斷使能情況是什么樣子的,一律開中斷;2)恢復關中斷前中斷使能情況,從一定程度上保證任務執行環境的完整性。這兩種方案分別與方法一和方法二相對應。
      c32中,狀態寄存器st的第13位是全局中斷使能位gie(global interrupt-enable),把該位置0,那么不管什么中斷都不去被響應,直到臨界區代碼執行完畢為止。(注:c32沒有不可屏蔽的中斷nmi,對于別的芯片來說,如果有nmi的話,處理辦法就是在這個中斷服務程序isr中對st中的gie位進行判斷,如果置0,那么這個isr簡單響應一下這個中斷,大部分處理工作放到gie置1后馬上去執行)。宏os_enter_critical()把gie位置0而關閉所有中斷。

        (3)堆棧增長方向

        c32處理器的堆棧是由低地址向高地址遞增,所以os_stk_growth應該設置為1;
        
        (4)進入任務切換函數os_task_sw()的定義

        uc/os-ii中,進入任務切換是用函數os_task_sw()來實現的。這個函數通過軟中斷模擬了一次中斷過程,在這個中斷服務程序isr中實現任務的切換,切換的具體實現在介紹任務切換函數osctxsw()時詳細闡述。c32共有28個軟中斷可供使用,其編號為0~27,通過執行匯編指令 trap 0、trap 1、……、trap27來產生軟中斷,也稱為trap陷阱調用。這里,我們選擇編號為27的軟中斷作為進入任務切換的中斷,是由下面的語句完成這個定義的:

        #define os_task_sw() asm(` trap 27`)
        還要注意的一點這個中斷服務程序的入口必須指向函數osctxsw()。

        2.includes.h文件

        includes.h是主要的頭文件,在大都后綴名為.c的文件的開始都包含includes.h文件,使得我們的程序變得簡潔、可讀性強。對于不同的處理器、不同的編譯器以及提供的不同的庫文件,我們需要對includes.h文件進行修改,刪除不使用的頭文件,添加自己的頭文件。而且如果頭文件之間有包含關系、條件編譯關聯的,一定要排好他們之間的先后順序。我們為c32編寫的移值實例都放在一個目錄下面,includes.h文件修改如下:

        #ifndef __includes_h__
        #define __includes_h__
        #include `os_cfg.h`
        #include `os_cpu.h`
        #include `ucos_ii.h`
        #include `c32.h`
        #endif
        其中c32.h文件包含了4個頭文件:
        #include `timerdef.h`
        #include `serialport.h`
        #include `dma.h`
        #include `bus.h`

        這四個文件分別對c32的定時器、串口、dma通道、總線編程時要用到的數據結構進行定義;可以說,這也是與cpu相關的一個頭文件吧。

        3.os_cpu_c32.asm文件

        本來,這個匯編文件里面要實現4個函數:多任務啟動函數中調用的osstarthighrdy()、中斷任務切換函數osintctxsw()、任務切換函數osctxsw()、時鐘節拍服務函數ostickisr();但是我這里只實現后面兩個函數。前兩個函數我們在os_cpu_c32.c中實現,順便借此提及一點有關堆棧調整的知識。

        (1)任務切換函數:osctxsw()

        該函數是由于執行進入任務切換函數os_task_sw()而進入的,它是一個任務級的切換函數,不同與中斷程序中調用的切換函數osintctxsw()。uc/os-ii中,如果任務執行了某個函數,而這個函數的執行結果是:或者改變當前任務的狀態(執行了掛起任務函數ostasksuspend()、任務延時函數ostimedly())、或者是改變了別的任務的狀態(恢復任務函數ostaskresume()、任務延時結束函數ostimedlyresume())都要引起新的任務調度:ossched();而任務調度函數查找出需要調度執行的任務的控制塊地址放到ostcbhigrdy,然后執行進入任務切換函數os_task_sw()執行軟中斷,繼而進行任務切換。任務切換流程:1)硬件進行進入中斷處理工作:全局中斷使能位置0、返回地址壓棧。需要注意的是:有些cpu進入中斷時會把一些全局的寄存器也壓棧,但是這里,c32沒有這么做,它只把返回地址壓入當前任務的堆棧;2)保護上下文環境變量:把全局的寄存器中的值壓入當前任務堆棧,這個是由一系列的push、pushf指令完成;3)修改當前任務控制塊指針ostcbcur和當前任務優先級ospriocur;4)恢復當前任務的上下文環境:把堆棧中的值彈到全局寄存器中,這個是由一系列的pop、popf指令完成;5)開始執行當前任務,由一條指令完成:reti;
      上下文環境切換的部分代碼。

        (2)時鐘節拍函數:ostickisr()

        uc/os-ii中,時鐘節拍中斷是一個非常重要的中斷,因為整個操作系統的活動都受到它的激勵。系統利用時鐘中斷來維持任務的延時、等待以及切換等操作,以保證多有任務都能平等的得到cpu的擁有權。可以說,它是整個os的脈搏。

        ostickisr()的執行流程:1)硬件進行進入中斷處理工作,同上;2)保護上下文環境,同上,有一點不同的就是當前任務的堆棧指針還沒有保存到任務控制塊相應的域中去;3)調用執行osintenter(),記錄中斷嵌套層數;4)調用ostimetick(),檢查處理各個任務的延時,并根據情況修改就緒任務表;5)調用中斷任務切換函數osintexit(),檢查就緒任務表,看是否由比當前任務優先級更高的任務就緒,如果有,則進行調度;這里要提及的是,3)~5)三步執行時對環境的影響不波及到其他任何一個任務的環境,也就是3)~5)三步所形成的新的環境,不管系統進行不進行任務調度,必須全部舍棄而別別的任務的執行環境所覆蓋。所以,如果沒有比當前任務優先級更高的任務就緒,osintexit()返回并恢復2)所保存的上下文環境,并執行reti回到被中斷的那個任務里繼續運行;如果有,那么osintexit()就不返回到這里,具體的情況后面介紹osintexit()時具體闡述。

        4.os_cpu_c32.c文件

        這個文件里,主要實現3個函數:堆棧初始化函數ostaskinit()、中斷任務切換函數osintctxsw()、多任務啟動函數中調用的osstarthighrdy(),另外還有5個擴展外掛函數:
       
        void ostaskcreatehook(os_tcb ptcb){} /*任務創建擴展外掛函數*/
        void ostaskswhook(void){} /*任務切換擴展外掛函數*/
        void ostaskdelhook(os_tcb *ptcb){} /*任務刪除擴展外掛函數*/
        void ostaskstathook(void){} /*統計任務擴展外掛函數*/
        void ostimetickhook(void){} /*時鐘節拍創建擴展外掛函數*/

        這幾個函數我們這里都處理為空函數,而且還可以通過在文件os_cfg.h中設置os_cpu_hooks_en為0而不使用這些函數。我們主要來討論前三個函數:

        (1)堆棧初始化函數ostaskinit()

        堆棧初始化函數ostaskinit()是由任務創建函數ostaskcreate()或ostaskcreateext()來調用,用來初始化任務的堆棧。初始化后的堆棧保存著任務次執行時的上下文環境,它和中斷后的堆棧神似!這個函數關鍵的兩個參數就是任務的起始地址void(* task)(void *pd)和任務使用堆棧的棧頂指針void *ptos;需要注意的是,任務次執行時的某些全局寄存器的值由特殊要求:1)狀態寄存器st的初始值必須保證中斷全局使能位gie為1,從而保證時鐘節拍中斷不會長時間被屏蔽,這里我選擇初值為0x2000;2)頁指針寄存器dp的初始值:如果你選擇small-memory模式進行編譯時,那么它的初始值應該和建立c環境時對dp的初始化值一樣;否則就不要對這個dp寄存器進行任何保護處理,這樣也可以的,不過這樣的話,別的函數也就要做相應的改動了;如果選擇了large-memory模式的話,那么這個值的初始化就可以不進行了,因為編譯系統在編譯時會自動插入更新dp的指令的;

        (2)中斷任務級切換函數osintctxsw()

        uc/os-ii中,中斷的產生可能會引起任務的切換,在中斷服務程序的會調用osintexit()檢查任務就緒狀態。如果需要進行任務切換,將調用osintctxsw()。所以osintctxsw()又稱為中斷級的任務切換函數。需要注意的是,任何中斷服務程序isr前面都要像時鐘節拍函數:ostickisr()流程的第2)步那樣保存上下文環境。osintctxsw()和osctxsw()的后半部分幾乎相同,不同之處是:對當前任務的堆棧指針進行調整!其代碼如下:
      asm(` subi 5,sp `);
       
        asm(` ldi @_ostcbcur,ar0 `);
        asm(` sti sp,*ar0 `);

        這里我們把堆棧指針sp減去5,就是調整的結果。

        下面我們來分析一下這個“5”是怎么得來的(我們針對時鐘節拍中斷isr來進行說明):

        任務調用函數osintexit()之前時,當前任務堆棧的情況如圖a)所示;調用osintexit函數后,當前任務堆棧的情況如圖b)所示,為什么會這樣呢?我們可以看看osintexit()函數編譯后的匯編文件就明白了,這個函數入口的地方有如下幾條語句:

        _osintexit:
        push fp
        ldiu sp,fp
        push ar4

        然后該函數又調用中斷切換函數osintctxsw(),這時當前任務堆棧的的情況如圖c)所示;為什么會這樣呢?我們看看osintctxsw()編譯后的匯編文件就明白了,這個函數入口的地方有如下幾條語句:

        _osintctxsw:
        push fp

        由此可見,當別的任務需要調度時,當前任務需要把自己的堆棧指針sp調整到調用osintexit()之前的值,從圖上可以看出只要把當前任務的堆棧指針的值減去“5”便可。

        (3)多任務啟動函數中調用的osstarthighrdy()

        這個函數只在多任務啟動函數中調用一次,主要用來把多任務啟動時優先級的就緒任務的上下文環境從堆棧恢復過來。uc/os-ii系統啟動時至少創建了一個任務――空閑任務,實際應用當然還要創建別的用戶任務。osstart()首先從這些任務中查找出優先級的就緒任務,并把它的任務控制塊的地址賦給ostcbhighrdy,然后調用osstarthighrdy()來運行ostcbhighrdy指向的那個任務控制塊所對應的任務。其流程是:1)該函數的返回地址壓入堆棧,注意,這里提到的堆棧是uc/os-ii系統使用的堆棧,而與其他任何任務使用的堆棧沒有任何關系,而且這個地址壓不壓棧意義已經不大,因為不可能再從返回這里返回回去;2)變量osrunning賦值為true,標志多任務已經啟動;3)從任務初始化過的堆棧中恢復上下文環境,代碼和上面的雷同;4)執行reti指令運行這個任務;

        三、兩點補充說明

        1.編譯器的編譯選項

        在移值過程中,除了要熟悉uc/os-ii內核原理和目標芯片之外,熟悉使用相應的c編譯器也非常重要。通常c編譯器都提供一些編譯選項,在移值的過程中,要注意這些選項的使用。使用不當,會給移值帶來難移想象的麻煩。上面在介紹堆棧初始化函數也提及到有關dp寄存器的處理問題上,就是涉及到編譯選項的選擇:是選用small-memory還是large-memory模式,這個取決于編譯選項-mb的打開和關閉。
        2.中斷函數的編寫 

        在分析函數osintctxsw()時,我們提到分析的結果是針對時鐘中斷服務函數得出的。大家應該注意到這個函數時匯編語言寫的,那么我們寫別的中斷處理程序時,如果用c語言來寫的話,要不要注意些什么呢?回答是肯定的。因為編譯器在處理c語言寫的中斷服務函數時會作出一些特殊的處理:在這個isr入口處插入壓棧指令,把部分全局寄存器的值壓入堆棧,具體那些,會有因函數的不同而有所不同。這就干擾了我們保存上下文環境的工作,如果不進行處理,任務調度時會出現難以想象的問題。解決這個問題的辦法就是要讓c編譯器認為這個中斷服務函數是一般的函數,那么它就不會在函數入口處插入一系列的壓棧指令。c編譯器code composer規定,凡是函數名為c_intnm(其中n、m是小于9正整數)的函數都是中斷函數,在編譯這些函數時是都作出特殊的處理,為此,我們避免為中斷處理函數取這樣的名字就可以了。
      但是,編譯在處理一般函數時還是要做一定的處理的,譬如:

        push fp
        ldiu sp,fp
        push ar4

        為此,我們仔細分析編譯器編譯生成的匯編代碼,并對堆棧作相應的調整。然后在函數結束的地方嵌入asm(` reti`);語句結束。這樣中斷程序就可以正確執行。 它的框架如下:

        void int0isr(void)
        {
        asm(` subi n,sp`) /*這個n的值要看具體的程序來定,一般等于1或2*/
        …………
        asm(` reti`) /*用自己的返回指令返回*/
        }

        總結:在移值和運行的uc/os-ii過程中,也許還會有新的問題出現,遇到問題時要仔細分析,分析堆棧的使用、中斷的影響,分析編譯器生成的匯編代碼,就可以解決這些問題,從而實現uc/os-ii的可*運行。

        參考文獻

        1.《uc/os-ii——源碼公開的實時嵌入式操作系統》 邵貝貝譯 中國電力出版社 2001
        2.tms320c3x/4x optimizing c compiler user’s guide texas instruments
        3.《單片機與嵌入式系統應用》no.12 何立民編著

      免費預約試聽課

      亚洲另类欧美综合久久图片区_亚洲中文字幕日产无码2020_欧美日本一区二区三区桃色视频_亚洲AⅤ天堂一区二区三区

      
      

      1. 欧美大片在线视观看喷水 | 日韩女同中文字幕永久在线 | 亚洲国产精品中文乱码AV | 你懂的网站亚洲欧美另类在线 | 亚洲AV极品视觉盛宴 | 在线观看片免费人成视频网 |