學習ARM開發20 21

2021-04-13 01:17:32 字數 4302 閱讀 2624

************************************/

學習arm開發(20)

os的任務切換

有了前面的tick中斷,那麼基本的任務切換條件已經是「萬事俱備,只欠東風」了。不過,這個「東風」也是很難搞得懂的,只有不斷地通過實踐才會找到合適的方法。現在我就需要去找這個東風了,就是解決不同的任務切換的問題。從簡單到複雜,這是任何事物的認識過程,也是行之有效的方法。絕對不要一上來就搞乙個很複雜的,因為人的理解能力還是有限的。最簡單的任務切換,就是我需要實現的:只需要實現兩個任務不斷地來回切換,就已經說明可行了。那我先把這兩個任務設定為最簡單的,因此,就把任務的棧定下來,因為每個任務的棧是肯定不同的,所以我選擇了固定地設定棧位址。比如第乙個任務的棧位址是0x0c700000,第二個任務的棧位址是0x0c700200。接著就需要把任務這兩個棧初始化成中斷返回的方式,就是需要儲存r0到r12,lr,pc,cpsr等暫存器的值。

這時就需要理解arm的幾種工作模式了,目前我使用到的只有兩種模式:irq和svc模式。在這兩種模式下,它的暫存器是有一些不同的。就是sp,lr,spcr的暫存器不同樣,並且lr與spcr在兩種模式中是有關聯的。當從svc模式轉換到irq模式時,它的lr,就是在svc下的執行的下一條指令位址減4;spsr就是svc模式下的cpsr暫存器的值。因此,在irq中斷之後,一定要想辦法把這兩個暫存器儲存下來,否則就返回不到先前的任務了。由於svc與irq的sp是不一樣的,並且在svc下的lr也沒有辦法儲存,那麼就一定要切換回到svc模式下,才能訪問這個任務的棧了,並且那時才能儲存lr的值。因此,特殊的要求就在這裡了。

由於我的os是採用時間片輪轉演算法,那麼當時間片到時,tick中斷就會中斷任務的執行,並且要切換到新的任務執行。具體過程是這樣的:啟動時第乙個任務執行,當時間片使用完了,那麼tick中斷就發生,接著就儲存irq模式下的lr,spcr暫存器到記憶體某個位置,並且把r0到r12的所有暫存器恢復,接著切換回到svc模式。接著儲存乙個暫存器的值,然後用這個暫存器從記憶體讀回來在irq方式儲存的lr值。然後把這個lr值壓入棧,這個lr值就是任務再回來時要執行的pc值。因此把它放到最先棧裡,到時出棧才方便。接著儲存svc模式下的lr值,儲存r0到r12的值,然後儲存spcr值。到這裡,就把所有任務恢復時所需要的暫存器儲存了。

到後面,接著再寫一段可以恢復任務的匯程式設計序就可以了,這個就是上面的壓棧反向過程。通過這樣的方法,就可以不斷來回地切換任務了。

學習arm開發(21)

os任務切換源程式分析

先要宣告任務指標,因為後面需要使用。

//任務指標.

volatile task_tcb* volatile g_pcurrenttask = null;

volatile task_tcb* volatile g_pcurrenttask1 = null;

volatile task_tcb* volatile g_pcurrenttask2 = null;

/////函式名稱:   taskinitstack

//函式功能:   分配任務的棧。

//輸入引數:

//輸出引數:

//返 回 值:   

//開發人員:   蔡軍生

//時    間:   2006/02/26

//修改說明:  

/////

void taskinitstack(void)

g_pcurrenttask1 = (ptask_tcb)0x0c700000;

g_pcurrenttask1->pstackstart = (uint*)(0x0c700000+0x200);

g_pcurrenttask1->pstacktop = g_pcurrenttask1->pstackstart + 0x100;

g_pcurrenttask2 = (ptask_tcb)(0x0c700000 + 0x400);

g_pcurrenttask2->pstackstart = (uint*)(0x0c700000+0x400 + 0x200);

g_pcurrenttask2->pstacktop = g_pcurrenttask2->pstackstart + 0x100;

接著再建立兩個簡單的任務,它們都是輸出一行字串,就等待一會,**如下:

void tasktest1(void)}//

void tasktest2(void)}

然後再初始化任務棧,**如下:

void taskstart(void)

");//關閉tick中斷.

intmsk |= bit_global|bit_tick;

//取得儲存irq的lr位址,並儲存lr.

a** volatile (" ldr     r0,=g_dwirqlr" );

a** volatile (" subs    lr,lr,#4 ");

a** volatile (" str           lr,[r0] ");

//取回前面儲存的r0-r12寄存的值.

a** volatile (" ldmia       sp!, ");

//取得最後返回的spsr,就是svc模式下的cpsr.

//從irq模式切換回到svc模式.

a** volatile (" mrs          lr,spsr ");

a** volatile (" orr           lr,#0xc0 ");

a** volatile (" msr          cpsr,lr ");

//現在已經轉換回到svc模式,取回irq模式下儲存的lr.

//把r0的值儲存到棧中第二個位置.

a** volatile (" str     r0,[sp,#-8] ");

//從儲存位置取回irq模式下儲存的lr值.

a** volatile (" ldr     r0,=g_dwirqlr" );

a** volatile (" ldr     r0,[r0] ");

//把irq模式下的lr儲存到棧裡,就是出棧時的pc值.

a** volatile (" s***b   sp!, ");

//從棧裡第二個位置取回先前儲存的r0值.

a** volatile (" subs    sp,sp,#4 ");

a** volatile (" ldmia   sp!, ");

//儲存r0-r12,lr到棧裡,入棧順序是剛好相反的.

a** volatile (" s***b   sp!, ");

//儲存cpsr的值到棧裡,就是spsr的位置.

a** volatile (" mrs     r4,cpsr ");

a** volatile (" bic     r4,#0xc0 ");

a** volatile (" s***b   sp!, ");

//儲存棧頂到任務指標裡.

a** volatile ( "ldr           r0, %0" : : "m" (g_pcurrenttask) );

a** volatile ( "str           sp, [r0]" );

//增加時鐘計數.

g_dwtickcount++;  

//    

printf("g_dwtickcount = (%d)/n",g_dwtickcount);

//    

//清除遮蔽位.

i_ispc = bit_tick;           

intmsk &= ~(bit_global|bit_tick); //

if (g_dwtickcount < 3)

else if (g_dwtickcount < 4)

else

//取得新任務指標

a** volatile ( "ldr           r0, %0" : : "m" (g_pcurrenttask) );  

a** volatile ( "ldr           sp, [r0]" );

//取回spsr值.

a** volatile ( "ldmia       sp!, " );

a** volatile ( "msr          spsr, r0" );

//取回r0-r12,lr,pc值,並執行最後的任務.

a** volatile ( "ldmia       sp!, ^" );

寫完上面的**,就可以真正地實現了任務排程了。有了以上的**,寫其它複雜的任務除錯都變得很容易了,這些**得來是不太容易的,我經歷了好幾個星期的除錯才通過的。

這些都是在我的s3c44b0開發板上除錯通過的,如果你沒有開發板,可以跟我購買。聯絡方法ccaimouse#gmail.com(請把#換成@)。

學習ARM開發 5

學習arm開發 5 蔡軍生 2005 07 16 寫於深圳 上一次說到要學習uboot的 但在看之前,首先要知道目標機器的程式設計資源,這裡的資源,是指s3c44b0所提供的執行程式的資源,對任何嵌入式軟體開發,都首先要對硬體有乙個很好的了解,這跟pc機的程式設計是大不一樣的。因為pc機都已經發展了...

學習ARM開發 9

學習arm開發 9 上一次把引導的彙編看完,已經準備c的執行環境,下面就開始學習c的源程式,從start.s檔案裡到跳檔案 lib arm board.c裡執行.引導程式從彙編start.s裡跳到這裡執行。蔡軍生 2005 07 19 void start armboot void 宣告乙個全域性指...

學習ARM開發 11

學習arm開發 11 昨天又是星期天,在家裡又可以對那塊開發板進行軟體研究了。由於前幾次,把編譯好的uboot寫到flash老是執行不了。那麼怎麼辦呢?思考了很久,也檢視 了源程式,還是沒有發現問題。也許那個uboot的源程式太大,有很多的編譯開關,還有很多驅 動程式選擇,所以一頭霧水,不知怎麼辦好...