************************************/
學習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的源程式太大,有很多的編譯開關,還有很多驅 動程式選擇,所以一頭霧水,不知怎麼辦好...