色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

單片機設計新思路:把主程序放入中斷如何?

GReq_mcu168 ? 來源:互聯網 ? 作者:佚名 ? 2017-11-20 18:27 ? 次閱讀

mcu由于內部資源的限制,軟件設計有其特殊性,程序一般沒有復雜的算法以及數據結構,代碼量也不大,通常不會使用OS (Operating System),因為對于一個只有若干K ROM,一百多byte RAM的mcu來說,一個簡單OS也會吃掉大部分的資源。

對于無os的系統,流行的設計是主程序(主循環) +(定時)中斷,這種結構雖然符合自然想法,不過卻有很多不利之處,首先是中斷可以在主程序的任何地方發生,隨意打斷主程序。其次主程序與中斷之間的耦合性(關聯度)較大,這種做法使得主程序與中斷纏繞在一起,必須仔細處理以防不測。

那么換一種思路,如果把主程序全部放入(定時)中斷中會怎么樣?這么做至少可以立即看到幾個好處:系統可以處于低功耗的休眠狀態,將由中斷喚醒進入主程序;如果程序跑飛,則中斷可以拉回;沒有了主從之分(其他中斷另計),程序易于模塊化。

(題外話:這種方法就不會有何處喂狗的說法,也沒有中斷是否應該盡可能的簡短的爭論了)

為了把主程序全部放入(定時)中斷中,必須把程序化分成一個個的模塊,即任務,每個任務完成一個特定的功能,例如掃描鍵盤并檢測按鍵。設定一個合理的時基(tick),例如5, 10或20 ms,每次定時中斷,把所有任務執行一遍,為減少復雜性,一般不做動態調度(最多使用固定數組以簡化設計,做動態調度就接近os了),這實際上是一種無優先級時間片輪循的變種。來看看主程序的構成:

void main()

{

….// Initialize

while (true) {

IDLE;//sleep

}

}

這里的IDLE是一條sleep指令,讓mcu進入低功耗模式。中斷程序的構成

void Timer_Interrupt()

{

SetTimer();

ResetStack();

Enable_Timer_Interrupt;

….

進入中斷后,首先重置Timer,這主要針對8051, 8051自動重裝分頻器只有8-bit,難以做到長時間定時;復位stack,即把stack指針賦值為棧頂或棧底(對于pic,TI DSP等使用循環棧的mcu來說,則無此必要),用以表示與過去決裂,而且不準備返回到中斷點,保證不會保留程序在跑飛時stack中的遺體。Enable_Timer_Interrupt也主要是針對8051。8051由于中斷控制較弱,只有兩級中斷優先級,而且使用了如果中斷程序不用reti返回,則不能響應同級中斷這種偷懶方法,所以對于8051,必須調用一次reti來開放中斷:

_Enable_Timer_Interrupt:

acall_reti

_reti:reti

下面就是任務的執行了,這里有幾種方法。第一種是采用固定順序,由于mcu程序復雜度不高,多數情況下可以采用這種方法:

Enable_Timer_Interrupt;

ProcessKey();

RunTask2();

RunTaskN();

while (1) IDLE;

可以看到中斷把所有任務調用一遍,至于任務是否需要運行,由程序員自己控制。另一種做法是通過函數指針數組:

#define CountOfArray(x) (sizeof(x)/sizeof(x[0]))

typedef void (*FUNCTIONPTR)();

const FUNCTIONPTR[] tasks = {

ProcessKey,

RunTask2,

RunTaskN

};

void Timer_Interrupt()

{

SetTimer();

ResetStack();

Enable_Timer_Interrupt;

for (i=0; i

(*tasks[i])();

while (1) IDLE;

}

使用const是讓數組內容位于code segment(ROM)而非data segment (RAM)中,8051中使用code作為const的替代品。

(題外話:關于函數指針賦值時是否需要取地址操作符&的問題,與數組名一樣,取決于compiler.對于熟悉匯編的人來說,函數名和數組名都是常數地址,無需也不能取地址。對于不熟悉匯編的人來說,用&取地址是理所當然的事情。Visual C++ 2005對此兩者都支持)

這種方法在匯編下表現為散轉,一個小技巧是利用stack獲取跳轉表入口:

movA, state

acallMultiJump

ajmpstate0

ajmpstate1

...

MultiJump:

popDPH

popDPL

rlA

jmp@A+DPTR

還有一種方法是把函數指針數組(動態數組,鏈表更好,不過在mcu中不適用)放在data segment中,便于修改函數指針以運行不同的任務,這已經接近于動態調度了:

FUNCTIONPTR[COUNTOFTASKS] tasks;

tasks[0] = ProcessKey;

tasks[0] = RunTaskM;

tasks[0] = NULL;

...

FUNCTIONPTR pFunc;

for (i=0; i< COUNTOFTASKS; i++)??{

pFunc = tasks[i]);

if (pFunc != NULL)

(*pFunc)();

}

通過上面的手段,一個中斷驅動的框架形成了,下面的事情就是保證每個tick內所有任務的運行時間總和不能超過一個tick的時間。為了做到這一點,必須把每個任務切分成一個個的時間片,每個tick內運行一片。這里引入了狀態機(state machine)來實現切分。關于state machine,很多書中都有介紹,這里就不多說了。

(題外話:實踐升華出理論,理論再作用于實踐。我很長時間不知道我一直沿用的方法就是state machine,直到學習UML/C++,書中介紹tachniques for identifying dynamic behvior,方才豁然開朗。功夫在詩外,掌握C++,甚至C# JAVA,對理解嵌入式程序設計,會有莫大的幫助)

狀態機的程序實現相當簡單,第一種方法是用swich-case實現:

void RunTaskN()

{

switch (state) {

case 0: state0(); break; case 1: state1(); break;

case M: stateM(); break;

default:

state = 0;

}

}

另一種方法還是用更通用簡潔的函數指針數組:

const FUNCTIONPTR[] states = { state0, state1, …, stateM };

void RunTaskN()

{

(*states[state])();

}

下面是state machine控制的例子:

void state0() { }

void state1() { state++; }//next state;

void state2() { state+=2; }//go to state 4;

void state3() { state--; }//go to previous state;

void state4() { delay = 100; state++; }

void state5() { delay--; if (delay <= 0) state++; }???//delay 100*tick

void state6() { state=0; }//go to the first state

一個小技巧是把第一個狀態state0設置為空狀態,即:

void state0() { }

這樣,state =0可以讓整個task停止運行,如果需要投入運行,簡單的讓state = 1即可。

以下是一個鍵盤掃描的例子,這里假設tick = 20 ms, ScanKeyboard()函數控制口線的輸出掃描,并檢測輸入轉換為鍵碼,利用每個state之間20 ms的間隔去抖動。

enum EnumKey {

EnumKey_NoKey =0,

};

struct StructKey {

intkeyValue;

boolkeyPressed;

} ;

struct StructKeyProcess key;

void ProcessKey() { (*states[state])(); }

void state0() { }

void state1() { key.keyPressed = false; state++; }

void state2() { if (ScanKey() != EnumKey_NoKey) state++; }//next state if a key pressed

void state3()

{//debouncing state

key.keyValue = ScanKey();

if (key.keyValue == EnumKey_NoKey)

state--;

else {

key.keyPressed = true;

state++;

}

}

void state4() {if (ScanKey() == EnumKey_NoKey) state++; }//next state if the key released

void state5() {ScanKey() == EnumKey_NoKey? state = 1 : state--; }

上面的鍵盤處理過程顯然比通常使用標志去抖的程序簡潔清晰,而且沒有軟件延時去抖的困擾。以此類推,各個任務都可以劃分成一個個的state,每個state實際上占用不多的處理時間。某些任務可以劃分成若干個子任務,每個子任務再劃分成若干個狀態。

(題外話:對于常數類型,建議使用enum分類組織,避免使用大量#define定義常數)

對于一些完全不能分割,必須獨占的任務來說,比如我以前一個低成本應用中紅外遙控器的軟件解碼任務,這時只能犧牲其他的任務了。兩種做法:一種是關閉中斷,完全的獨占;

void RunTaskN()

{

Disable_Interrupt;

Enable_Interrupt;

}

第二種,允許定時中斷發生,保證某些時基register得以更新;

void Timer_Interrupt()

{

SetTimer();

Enable_Timer_Interrupt;

UpdateTimingRegisters();

if (watchDogCounter = 0) {

ResetStack();

for (i=0; i

(*tasks[i])();

while (1) IDLE;

}

else

watchDogCounter--;

}

只要watchDogCounter不為0,那么中斷正常返回到中斷點,繼續執行先前被中斷的任務,否則,復位stack,重新進行任務循環。這種狀況下,中斷處理過程極短,對獨占任務的影響也有限。

中斷驅動多任務配合狀態機的使用,我相信這是mcu下無os系統較好的設計結構。對于絕大多數mcu程序設計來說,可以極大的減輕程序結構的安排,無需過多的考慮各個任務之間的時間安排,而且可以讓程序簡潔易懂。缺點是,程序員必須花費一定的時間考慮如何切分任務。

下面是一段用C改寫的CD Player中檢測disc是否存在的偽代碼,用以展示這種結構的設計技巧,原源代碼為Z8 mcu匯編,基于Sony的DSP, Servo and RF處理芯片,通過送出命令字來控制主軸/滑板/聚焦/尋跡電機,并讀取狀態以及CD的sub Q碼。這個處理任務只是一個大任務下用state machine切開的一個二級子任務,tick = 20 ms。

state1() { InitializeMotor(); state++; }

state2() {

if (innerSwitch != ON) {

SendCommand(EnumCommand_SlidingMotorBackward);

timeout = MILLISECOND(10000);

state++;//滑板電機向內運動,直至觸及最內開關。

}

else

state +=2;

}

state3() {

if ((--timeout) == 0) {//note: some C compliers do not support (--timeout) ==

SendCommand(EnumCommand_SlidingMotorStop)

systemErrorCode = EnumErrorCode_InnerSwitch;

state = 0;// 10 s超時錯誤,

}

else {

if (innerSwitch == ON) {

SendCommand(EnumCommand _SlidingMotorStop)

timeout = MILLISECOND(200);// 200ms電機停止時間

state++;

}

}

}

state4() { if ((--timeout) == 0) state++; }//等待電機完全停止

state5() {

SendCommand(EnumCommand_SlidingMotorForward);

timeout = MILLISECOND(2000);

state++;

}//滑板電機向外運動,脫離inner switch

state6() {

if ((--timeout) == 0) {

SendCommand(EnumCommand_SlidingMotorStop)

systemErrorCode = EnumErrorCode_InnerSwitch;

state = 0;// 2 s超時錯誤,

}

else {

if (innerSwitch == OFF) {

SendCommand(EnumCommand_SlidingMotorStop)

timeout = MILLISECOND(200);// 200ms電機停止時間

state++;

}

}

}

state7() { state4(); }

state8() { LaserOn(); state++; retryCounter = 3;}//打開激光器

state9() {

SendCommand(FocusUp);

state++;

timeout = MILLISECOND(2000);

}//光頭上舉,檢測聚焦過零3次,判斷cd是否存在

state10() {

if (FocusCrossZero){

systemStatus.Disc = EnumStatus_DiscExist;

SendCommand(EnumCommand_AutoFocusOn);//有cd,打開自動聚焦。

state = 0;//本任務結束。

playProcess.state = 1;//啟動play任務

}

else if ((--timeout) == 0) {

SendCommand(EnumCommand_ FocusClose);//光頭聚焦復位

if ((--retryCounter) == 0) {

systemStatus.Disc = EnumStatus_Nodisc;//無盤

displayProcess.state = EnumDisplayState_NoDisc;//顯示閃爍的無盤

LaserOff();

state = 0;//任務停止

}

else

state--;//再試

}

}

stateStop() {

SendCommand(EnumCommand_SlidingMotorStop);

SendCommand(EnumCommand_FocusClose);

state = 0;

}

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 中斷系統
    +關注

    關注

    1

    文章

    96

    瀏覽量

    61015
  • 單片機系統
    +關注

    關注

    1

    文章

    73

    瀏覽量

    103842

原文標題:把主程序放入中斷如何?單片機的一種軟件設計新思路

文章出處:【微信號:mcu168,微信公眾號:硬件攻城獅】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    使用硬件中斷的方法實現單片機設計的0到60秒的計時器電路圖和程序

    本文檔的主要內容詳細介紹的是使用硬件中斷的方法實現單片機設計的0到60秒的計時器電路圖和程序資料免費下載。
    發表于 09-05 17:27 ?12次下載
    使用硬件<b class='flag-5'>中斷</b>的方法實現<b class='flag-5'>單片機設</b>計的0到60秒的計時器電路圖和<b class='flag-5'>程序</b>

    Arduino中的中斷函數詳細說明

    單片機中斷可概述為:由于某一隨機事件的發生,單片機暫停原程序的運行,轉去執行另一程序(隨機事件),處理完畢后又自動返回原
    發表于 08-20 17:31 ?1次下載
    Arduino中的<b class='flag-5'>中斷</b>函數詳細說明

    不利用中斷使用單片機設計數字時鐘的程序免費下載

    本文檔的主要內容詳細介紹的是不利用中斷使用單片機設計數字時鐘的程序免費下載。
    發表于 07-26 17:36 ?1次下載

    使用單片機設計蜂鳴器的程序免費下載

    本文檔的主要內容詳細介紹的是使用單片機設計蜂鳴器的程序免費下載。
    發表于 07-19 17:38 ?4次下載
    使用<b class='flag-5'>單片機設</b>計蜂鳴器的<b class='flag-5'>程序</b>免費下載

    51單片機中斷服務子程序的設計

    由于各中斷人口地址是固定的,而程序又必須先從主程序起始地址OOOOH執行。所以,在OOOOH起始地址的幾個字節中,要用無條件轉移指令,跳轉到主程序。另外,各
    發表于 06-18 15:33 ?7447次閱讀
    51<b class='flag-5'>單片機</b><b class='flag-5'>中斷</b>服務子<b class='flag-5'>程序</b>的設計

    單片機中斷程序如何運行

    單片機中斷就是類似的一個過程,發生中斷時,就會打斷正在執行的主程序,先處理完中斷任務,返回主程序
    的頭像 發表于 01-27 17:11 ?1.1w次閱讀
    <b class='flag-5'>單片機</b>的<b class='flag-5'>中斷</b><b class='flag-5'>程序</b>如何運行

    單片機主程序中斷程序是怎么樣運行的

    過程,發生中斷時,就會打斷正在執行的主程序,先處理完中斷任務,返回主程序繼續運行,當然在執行中斷函數之前,
    的頭像 發表于 01-04 10:11 ?1.4w次閱讀
    <b class='flag-5'>單片機</b>的<b class='flag-5'>主程序</b>和<b class='flag-5'>中斷</b><b class='flag-5'>程序</b>是怎么樣運行的

    單片機設程序30例資料

    單片機設程序30例資料免費下載。
    發表于 05-30 09:38 ?15次下載

    單片機中斷的基本概念

    時,可以讓單片機工作在休眠的低功耗方式。通常,恢復到正常工作方式往往也是利用中斷信號來喚醒。《中斷處理過程》中斷系統中,MCU正常情況下運行的程序
    發表于 11-15 13:21 ?155次下載
    <b class='flag-5'>單片機</b><b class='flag-5'>中斷</b>的基本概念

    【51單片機】有關單片機執行中斷無法恢復主程序探討

    Author: Manba Cople專業:IOT說明:記錄和輸出學習內容文章目錄問題思考修改進階聲明問題??最近在給小伙伴培訓單片機中斷時,小伙伴寫了一段中斷的代碼(代碼如下),出現每次執行完
    發表于 11-22 12:06 ?12次下載
    【51<b class='flag-5'>單片機</b>】有關<b class='flag-5'>單片機</b>執行<b class='flag-5'>中斷</b>無法恢復<b class='flag-5'>主程序</b>探討

    MCS-51單片機中斷系統

    ,CPU暫時中斷當前程序而轉去執行相應的處理程序,待處理程序執行完畢后,CPU再繼續執行原來被中斷程序
    發表于 11-23 16:20 ?7次下載
    MCS-51<b class='flag-5'>單片機</b>的<b class='flag-5'>中斷</b>系統

    謹慎處理單片機中斷中斷等價于比主程序優先級更高的線程

    ?有些小伙伴喜歡在單片機中斷里做任務,殊不知可能會因此遇到棘手的bug,然后查半天查不出個所以然。本文為了糾正這個不良習慣,對單片機中斷進行闡述。?無
    發表于 01-14 14:54 ?2次下載
    謹慎處理<b class='flag-5'>單片機</b><b class='flag-5'>中斷</b>,<b class='flag-5'>中斷</b>等價于比<b class='flag-5'>主程序</b>優先級更高的線程

    51單片機中斷程序示例

    51單片機中斷程序示例
    發表于 05-17 18:03 ?0次下載

    基于單片機的外部中斷實驗 中斷系統知識介紹

    單片機中有兩個重要的概念分別叫做中斷中斷系統,那么他們分別又代表什么意義呢?當單片機CPU正在運行主程序時外界發生了緊急事件請求,要求
    的頭像 發表于 07-26 17:23 ?2748次閱讀
    基于<b class='flag-5'>單片機</b>的外部<b class='flag-5'>中斷</b>實驗 <b class='flag-5'>中斷</b>系統知識介紹

    單片機中斷機制

    單片機中斷機制是一種重要的處理方式,它允許單片機在執行主程序的過程中,能夠暫停當前任務,轉而處理外部或內部緊急事件。這種機制極大地提高了系統的響應速度和處理能力,使得
    的頭像 發表于 10-17 18:03 ?620次閱讀
    主站蜘蛛池模板: 驯服有夫之妇HD中字日本| 男男h开荤粗肉h文1v1| 精品网站一区二区三区网站| 国产自产视频在线观看香蕉| 国产在线综合色视频| 精品久久久久久久高清| 久久精品日本免费线| 男人和女人全黄一级毛片| 日本A级作爱片金瓶双艳| 我要搞av| 一品探花论坛| ABO成结顶腔锁住| 国产 亚洲 中文在线 字幕| 国产在线亚洲精品观| 领导边摸边吃奶边做爽在线观看| 男人扒开添女人屁股| 秋秋影视午夜福利高清| 午夜神器18以下不能进免费| 欲香欲色天天影视大全| chinesevideoshd性舞| 国产盗摄一区二区| 久久九九有精品国产23百花影院| 捏揉舔水插按摩师| 忘忧草在线社区WWW日本-韩国| 一级毛片视频免费| 边摸边吃奶边做带声音| 国产在线精品视频免费观看| 免费三级黄色| 亚洲高清一区二区三区电影| 5G在线观看免费年龄确认18| 国产精品96久久久久久AV网址| 久久精品99热超碰| 色婷婷亚洲五月| 又紧又大又爽精品一区二区| 芳草地社区在线视频| 久久精品国产亚洲AV天美18 | 中文字幕1| 单亲妈妈3韩国电影免费观看| 精品欧美一区二区三区四区 | 日本老人oldmantv乱| 亚洲日韩天堂在线中文字幕|