<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 牛人業(yè)話(huà) > 51單片機多任務(wù)操作系統的原理與實(shí)現

51單片機多任務(wù)操作系統的原理與實(shí)現

作者: 時(shí)間:2017-01-06 來(lái)源:網(wǎng)絡(luò ) 收藏

 

本文引用地址:http://dyxdggzs.com/article/201701/342566.htm

  好了,現在要給大家潑冷水了,看下面兩個(gè)函數:

  void func1()

  {

  register char data i;

  i = 5;

  do{

  sigl = !sigl;

  }while(--i);

  }

  void func2()

  {

  register char data i;

  i = 5;

  do{

  func1();

  }while(--i);

  }

  父函數fun2()里調用func1(),展開(kāi)匯編代碼看看:

  193: void func1(){

  194: register char data i;

  195: i = 5;

  C:0x00C3 7F05 MOV R7,#0x05

  196: do{

  197: sigl = !sigl;

  C:0x00C5 B297 CPL sigl(0x90.7)

  198: }while(--i);

  C:0x00C7 DFFC DJNZ R7,C:00C5

  199: }

  C:0x00C9 22 RET

  200: void func2(){

  201: register char data i;

  202: i = 5;

  C:0x00CA 7E05 MOV R6,#0x05

  203: do{

  204: func1();

  C:0x00CC 11C3 ACALL func1(C:00C3)

  205: }while(--i);

  C:0x00CE DEFC DJNZ R6,C:00CC

  206: }

  C:0x00D0 22 RET

  看清楚沒(méi)?函數func2()里的變量使用了寄存器R6,而在func1和func2里都沒(méi)保護.

  聽(tīng)到這里,你可能又要跳一跳了:func1()里并沒(méi)有用到R6,干嘛要保護?沒(méi)錯,但編譯器是怎么知道func1()沒(méi)用到R6的呢?是從調用關(guān)系里推測出來(lái)的.

  一點(diǎn)都沒(méi)錯,KEIL會(huì )根據函數間的直接調用關(guān)系為各函數分配寄存器,既不用保護,又不會(huì )沖突,KEIL好棒哦!!等一下,先別高興,換到多任務(wù)的環(huán)境里再試試:

  void func1()

  {

  register char data i;

  i = 5;

  do{

  sigl = !sigl;

  }while(--i);

  }

  void func2()

  {

  register char data i;

  i = 5;

  do{

  sigl = !sigl;

  }while(--i);

  }

  展開(kāi)匯編代碼看看:

  193: void func1(){

  194: register char data i;

  195: i = 5;

  C:0x00C3 7F05 MOV R7,#0x05

  196: do{

  197: sigl = !sigl;

  C:0x00C5 B297 CPL sigl(0x90.7)

  198: }while(--i);

  C:0x00C7 DFFC DJNZ R7,C:00C5

  199: }

  C:0x00C9 22 RET

  200: void func2(){

  201: register char data i;

  202: i = 5;

  C:0x00CA 7F05 MOV R7,#0x05

  203: do{

  204: sigl = !sigl;

  C:0x00CC B297 CPL sigl(0x90.7)

  205: }while(--i);

  C:0x00CE DFFC DJNZ R7,C:00CC

  206: }

  C:0x00D0 22 RET

  看到了吧?哈哈,這回神仙也算不出來(lái)了.因為兩個(gè)函數沒(méi)有了直接調用的關(guān)系,所以編譯器認為它們之間不會(huì )產(chǎn)生沖突,結果分配了一對互相沖突的寄存器,當任務(wù)從func1()切換到func2()時(shí),func1()中的寄存器內容就給破壞掉了.大家可以試著(zhù)去編譯一下下面的程序:

  sbit sigl = P1^7;

  void func1()

  {

  register char data i;

  i = 5;

  do{

  sigl = !sigl;

  task_switch();

  } while (--i);

  }

  void func2()

  {

  register char data i;

  i = 5;

  do{

  sigl = !sigl;

  task_switch();

  }while(--i);

  }

  我們這里只是示例,所以仍可以通過(guò)手工分配不同的寄存器避免寄存器沖突,但在真實(shí)的應用中,由于任務(wù)間的切換是非常隨機的,我們無(wú)法預知某個(gè)時(shí)刻哪個(gè)寄存器不會(huì )沖突,所以分配不同寄存器的方法不可取.那么,要怎么辦呢?

  這樣就行了:

  sbit sigl = P1^7;

  void func1()

  {

  static char data i;

  while(1){

  i = 5;

  do{

  sigl = !sigl;

  task_switch();

  }while(--i);

  }

  }

  void func2()

  {

  static char data i;

  while(1){

  i = 5;

  do{

  sigl = !sigl;

  task_switch();

  }while(--i);

  }

  }

  將兩個(gè)函數中的變量通通改成靜態(tài)就行了.還可以這么做:

  sbit sigl = P1^7;

  void func1()

  {

  register char data i;

  while(1){

  i = 5;

  do{

  sigl = !sigl;

  }while(--i);

  task_switch();

  }

  }

  void func2()

  {

  register char data i;

  while(1){

  i = 5;

  do{

  sigl = !sigl;

  }while(--i);

  task_switch();

  }

  }

  即,在變量的作用域內不切換任務(wù),等變量用完了,再切換任務(wù).此時(shí)雖然兩個(gè)任務(wù)仍然會(huì )互相破壞對方的寄存器內容,但對方已經(jīng)不關(guān)心寄存器里的內容了.

  以上所說(shuō)的,就是"變量覆蓋"的問(wèn)題.現在我們系統地說(shuō)說(shuō)關(guān)于"變量覆蓋".

  變量分兩種,一種是全局變量,一種是局部變量(在這里,寄存器變量算到局部變量里).

  對于全局變量,每個(gè)變量都會(huì )分配到單獨的地址.

  而對于局部變量,KEIL會(huì )做一個(gè)"覆蓋優(yōu)化",即沒(méi)有直接調用關(guān)系的函數的變量共用空間.由于不是同時(shí)使用,所以不會(huì )沖突,這對內存小的來(lái)說(shuō),是好事.

  但現在我們進(jìn)入多任務(wù)的世界了,這就意味著(zhù)兩個(gè)沒(méi)有直接調用關(guān)系的函數其實(shí)是并列執行的,空間不能共用了.怎么辦呢?一種笨辦法是關(guān)掉覆蓋優(yōu)化功能.呵呵,的確很笨.

  比較簡(jiǎn)單易行一個(gè)解決辦法是,不關(guān)閉覆蓋優(yōu)化,但將那些在作用域內需要跨越任務(wù)(換句話(huà)說(shuō)就是在變量用完前會(huì )調用task_switch()函數的)變量通通改成靜態(tài)(static)即可.這里要對初學(xué)者提一下,"靜態(tài)"你可以理解為"全局",因為它的地址空間一直保留,但它又不是全局,它只能在定義它的那個(gè)花括號對{}里訪(fǎng)問(wèn).

  靜態(tài)變量有個(gè)副作用,就是即使函數退出了,仍會(huì )占著(zhù)內存.所以寫(xiě)任務(wù)函數的時(shí)候,盡量在變量作用域結束后才切換任務(wù),除非這個(gè)變量的作用域很長(cháng)(時(shí)間上長(cháng)),會(huì )影響到其它任務(wù)的實(shí)時(shí)性.只有在這種情況下才考慮在變量作用域內跨越任務(wù),并將變量申明為靜態(tài).

  事實(shí)上,只要編程思路比較清析,很少有變量需要跨越任務(wù)的.就是說(shuō),靜態(tài)變量并不多.

  說(shuō)完了"覆蓋"我們再說(shuō)說(shuō)"重入".

  所謂重入,就是一個(gè)函數在同一時(shí)刻有兩個(gè)不同的進(jìn)程復本.對初學(xué)者來(lái)說(shuō)可能不好理解,我舉個(gè)例子吧:

  有一個(gè)函數在主程序會(huì )被調用,在中斷里也會(huì )被調用,假如正當在主程序里調用時(shí),中斷發(fā)生了,會(huì )發(fā)生什么情況?

  void func1()

  {

  static char data i;

  i = 5;

  do{

  sigl = !sigl;

  }while(--i);

  }

  假定func1()正執行到i=3時(shí),中斷發(fā)生,一旦中斷調用到func1()時(shí),i的值就被破壞了,當中斷結束后,i == 0.

  以上說(shuō)的是在傳統的單任務(wù)系統中,所以重入的機率不是很大.但在多任務(wù)系統中,很容易發(fā)生重入,看下面的例子:

  void func1()

  {

  ....

  delay();

  ....

  }

  void func2()

  {

  ....

  delay();

  ....

  }

  void delay()

  {

  static unsigned char i;//注意這里是申明為static,不申明static的話(huà)會(huì )發(fā)生覆蓋問(wèn)題.而申明為static會(huì )發(fā)生重入問(wèn)題.麻煩啊

  for(i=0;i<10;i++)

  task_switch();

  }

  兩個(gè)并行執行的任務(wù)都調用了delay(),這就叫重入.問(wèn)題在于重入后的兩個(gè)復本都依賴(lài)變量i來(lái)控制循環(huán),而該變量跨越了任務(wù),這樣,兩個(gè)任務(wù)都會(huì )修改i值了.

  重入只能以防為主,就是說(shuō)盡量不要讓重入發(fā)生,比如將代碼改成下面的樣子:

  #define delay() {static unsigned char i; for(i=0;i<10;i++) task_switch();}//i仍定義為static,但實(shí)際上已經(jīng)不是同一個(gè)函數了,所以分配的地址不同.

  void func1()

  {

  ....

  delay();

  ....

  }

  void func2()

  {

  ....

  delay();

  ....

  }

  用宏來(lái)代替函數,就意味著(zhù)每個(gè)調用處都是一個(gè)獨立的代碼復本,那么兩個(gè)delay實(shí)際使用的內存地址也就不同了,重入問(wèn)題消失.

  但這種方法帶來(lái)的問(wèn)題是,每調用一次delay(),都會(huì )產(chǎn)生一個(gè)delay的目標代碼,如果delay的代碼很多,那就會(huì )造成大量的rom空間占用.有其它辦法沒(méi)?

  本人所知有限,只有最后一招了:

  void delay() reentrant

  {

  unsigned char i;

  for(i=0;i<10;i++)

  task_switch();

  }

  加入reentrant申明后,該函數就可以支持重入.但小心使用,申明為重入后,函數效率極低!

  最后附帶說(shuō)下中斷.因為沒(méi)太多可說(shuō)的,就不單獨開(kāi)章了.

  中斷跟普通的寫(xiě)法沒(méi)什么區別,只不過(guò)在目前所示例的多任務(wù)系統里因為有堆棧的壓力,所以要使用using來(lái)減少對堆棧的使用(順便提下,也不要調用子函數,同樣是為了減輕堆棧壓力)

  用using,必須用#pragma NOAREGS關(guān)閉掉絕對寄存器訪(fǎng)問(wèn),如果中斷里非要調用函數,連同函數也要放在#pragma NOAREGS的作用域內.如例所示:

  #pragma SAVE

  #pragma NOAREGS //使用using時(shí)必須將絕對寄存器訪(fǎng)問(wèn)關(guān)閉

  void clock_timer(void) interrupt 1 using 1 //使用using是為了減輕堆棧的壓力

  }

  #pragma RESTORE


上一頁(yè) 1 2 3 下一頁(yè)

關(guān)鍵詞: 51 操作系統

評論


相關(guān)推薦

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>