生活随笔
收集整理的這篇文章主要介紹了
解剖8051内核如何进行多任务切换
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
最近在玩新唐單片機,這個跟我之前用的51內核是一樣的,然后今天覺得跑下多任務,自己研究了下,跟幾個同學還討論了,發現有些人對切換過程還不是十分明白,所以發個文章出來。
直接上代碼
#include?"MS51_16K.h"/**?UART0?initial?setting*?include?sys.c?in?Library?for?modify?HIRC?value?to?24MHz*?include?uart.c?in?Library?for?UART?initial?setting*/
void?initialize_UART0(void)
{MODIFY_HIRC(HIRC_24);P06_PUSHPULL_MODE;UART_Open(24000000,UART0_Timer3,115200);ENABLE_UART0_PRINTF;
}void?delay_ms(unsigned?int?n)
{unsigned?int?i;for(i=0;i<n;i++)_delay_();
}void?Timer_ISR?(void)???interrupt?17?????//ISR?for?self?wake-up?timer
{_push_(SFRS);clr_WKTF;???????????????????????????????????//clear?interrupt?flag_pop_(SFRS);
}void?initialize_Timer(void)
{WKCON?=?0x00;?????????????????????//timer?base?10k,?Pre-scale?=?1/16//RWK?=?0XFF;?????????????????????//??if?prescale?is?0x00,?never?set?RWK?=?0xffRWK?=?0X00;ENABLE_WKT_INTERRUPT;?????????????//?enable?WKT?interruptENABLE_GLOBAL_INTERRUPT;set_EIPH1_PWKTH;set_WKCON_WKTR;
}#define?MAX_TASKS?2???????/*任務槽個數.必須和實際任務數一至*/
#define?MAX_TASK_DEP?100???/*最大棧深.最低不得少于2個,保守值為12*/
unsigned?char?idata?task_stack[MAX_TASKS][MAX_TASK_DEP];/*任務堆棧.*/
unsigned?int?task_id;????/*當前活動任務號*/
unsigned?int?max_task?=?0;unsigned?char?idata?task_sp[MAX_TASKS];void?task_switch()
{task_sp[task_id]?=?SP;if(++task_id?==?max_task)task_id?=?0;SP?=?task_sp[task_id];
}void?task_load(unsigned?int?fn,?int?tid)
{task_sp[tid]?=?task_stack[tid]+1;task_stack[tid][0]?=?(unsigned?int)fn?&?0xff;task_stack[tid][1]?=?(unsigned?int)fn?>>?8;++max_task;
}void?task1()
{static?unsigned?char?i;printf("task,SP:%x\n",(int)SP);while(1){i++;printf("task#1\n");delay_ms(100);task_switch();}
}void?task2()
{static?unsigned?char?j;while(1){j+=2;printf("task#2\n");delay_ms(100);task_switch();}
}void?switch_to(unsigned?int?tid)
{task_id?=?tid;SP?=?task_sp[tid];return;
}void?main(void)
{initialize_UART0();Disable_WDT_Reset_Config();printf("~~~~~~~~~~~~~~~~MainStart...\n");task_load(task1,?0);//將task1函數裝入0號槽task_load(task2,?1);//將task2函數裝入1號槽switch_to(0);printf("~~~~~~~~~~~~~~~~MainEnd...\n");?
}
單片機運行輸出
代碼很簡單,就是兩個任務進行不斷的切換,每個任務進行相應時間的延遲。
先說下第一個函數
把任務函數轉載到二維數組保存起來,而且任務函數地址和任務的tid編號要對應。
void?task_load(unsigned?int?fn,?int?tid)
{task_sp[tid]?=?task_stack[tid]+1;task_stack[tid][0]?=?(unsigned?int)fn?&?0xff;task_stack[tid][1]?=?(unsigned?int)fn?>>?8;++max_task;
}
task_sp 用來表示任務的數組
task_stack 用來保存任務函數的地址
說下這行代碼
task_sp[tid]?=?task_stack[tid]+1;
后面的 + 1 ,不知道大家有沒有疑惑。
+1 簡單理解就是指向了下一個位置
再解剖第二個函數,等下你就知道這個作用的奇特
用來做任務的切換,先保存之前運行的任務函數地址,再改變任務id,把對應任務id的函數地址賦值給SP。
void?task_switch()
{task_sp[task_id]?=?SP;if(++task_id?==?max_task)task_id?=?0;SP?=?task_sp[task_id];
}
它是妙處不是在這個函數的本身,而是只有比較深入的了解函數調用的過程,才明白其中的奧妙。
SP 是堆棧指針,用來保存當前堆棧的位置
上面的函數是在進入函數的時候,把當前堆棧的值保存在 stak_sp 中,然后更改stak_sp 的值,再賦值給SP。
說如何切換吧
void?switch_to(unsigned?int?tid)
{task_id?=?tid;SP?=?task_sp[tid];return;
}
調用函數?switch_to(0)?之前 堆棧和PC指針是這樣的
調用函數?switch_to(0)?之后
我們需要把PC之前的值,保存在SP里面,然后呢,PC就開始執行switch_to函數體里面的內容。
然后,改變SP的值,讓SP的值等于需要執行函數的地址
函數退出的時候,PC指針又會從SP堆棧位置拿到之前保存的那個地址「實際上已經被我們修改了」,去繼續執行。
就是通過這樣不斷的切換,完成了多個函數交換執行。
這是最基本的多任務系統,代碼也不是非常完整,喜歡研究的同學,可以再看看網上的例程。
我這次用的是芯唐 MS51FB9AE 芯片。
有做這方便的同學,歡迎一起討論~
推薦閱讀:
專輯|Linux文章匯總
專輯|程序人生
專輯|C語言
我的知識小密圈
關注公眾號,后臺回復「1024」獲取學習資料網盤鏈接。
歡迎點贊,關注,轉發,在看,您的每一次鼓勵,我都將銘記于心~
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔為你收集整理的解剖8051内核如何进行多任务切换的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。