mcu因为内部资源的约束,软件规划有其特殊性,程序一般没有杂乱的算法以及数据结构,代码量也不大,一般不会运用OS (Operating System),
关于无os的体系,盛行的规划是主程序(主循环) +(守时)中止,这种结构尽管契合天然主意,不过却有许多晦气之处,首要是中止能够在主程序的任何地方产生,随意打断主程序。其次主程序与中止之间的耦合性(关联度)较大,这种做法使得主程序与中止环绕在一起,有必要细心处理以防不测。
那么换一种思路,假如把主程序悉数放入(守时)中止中会怎么样?这么做至少能够当即看到几个优点:体系能够处于低功耗的休眠状况,将由中止唤醒进入主程序;假如程序跑飞,则中止能够拉回;没有了主从之分(其他中止另计),程序易于模块化。
(题外话:这种办法就不会有何处喂狗的说法,也没有中止是否应该尽可能的简略的争辩了)
为了把主程序悉数放入(守时)中止中,有必要把程序化分红一个个的模块,即使命,每个使命完结一个特定的功用,例如扫描键盘并检测按键。设定一个合理的时基(tick),例如
这儿的IDLE是一条sleep指令,让mcu进入低功耗形式。中止程序的构成
进入中止后,首要重置Timer,这首要针对8051, 8051主动重装分频器只要8-bit,难以做到长时刻守时;复位stack,即把stack指针赋值为栈顶或栈底(关于pic,TI DSP等运用循环栈的mcu来说,则无此必要),用以表明与曩昔分裂,并且不准备回来到中止点,确保不会保存程序在跑飞时stack中的遗体。Enable_Timer_Interrupt也首要是针对8051。8051因为中止操控较弱,只要两级中止优先级,并且运用了假如中止程序不必reti回来,则不能呼应同级中止这种偷闲办法,所以关于8051,有必要调用一次reti来敞开中止:
下面便是使命的履行了,这儿有几种办法。榜首种是选用固定次序,因为mcu程序杂乱度不高,大都情况下能够选用这种办法:
…
能够看到中止把一切使命调用一遍,至于使命是否需求运转,由程序员自己操控。另一种做法是经过函数指针数组:
typedef void (*FUNCTIONPTR)();
const FUNCTIONPTR[] tasks = {
ProcessKey,
RunTask2,
…
RunTaskN
};
}
运用const是让数组内容坐落code segment(ROM)而非data segment (RAM)中,8051中运用code作为const的替代品。
(题外话:关于函数指针赋值时是否需求取地址操作符&的问题,与数组名相同,取决于compiler.关于了解汇编的人来说,函数名和数组名都是常数地址,无需也不能取地址。关于不了解汇编的人来说,用&取地址是天经地义的工作。Visual C++ 2005对此两者都支撑)
这种办法在汇编下表现为散转,一个小技巧是使用stack获取跳转表进口:
MultiJump:
还有一种办法是把函数指针数组(动态数组,链表更好,不过在mcu中不适用)放在data segment中,便于修正函数指针以运转不同的使命,这现已挨近于动态调度了:
FUNCTIONPTR[COUNTOFTASKS] tasks;
经过上面的手法,一个中止驱动的结构形成了,下面的工作便是确保每个tick内一切使命的运转时刻总和不能超过一个tick的时刻。为了做到这一点,有必要把每个使命切分红一个个的时刻片,每个tick内运转一片。这儿引入了状况机(state machine)来完成切分。关于state machine,
(题外话:实践提高出理论,理论再作用于实践。我很长时刻不知道我一向沿袭的办法便是state machine,直到学习UML/C++,书中介绍tachniques for identifying dynamic behvior,刚才恍然大悟。功夫在诗外,把握C++,乃至C# JAVA,对了解嵌入式程序规划,会有极大的协助)
状况机的程序完成适当简略,榜首种办法是用swich-case完成:
}
另一种办法仍是用更通用简练的函数指针数组:
const FUNCTIONPTR[] states = { state0, state1, …, stateM };
void RunTaskN()
{
(*states[state])();
}
下面是state machine操控的比如:
void state0() { }
void state1() { state++; }
void state2() { state+=2; }
void state3() { state–; }
void state4() { delay = 100; state++; }
void state5() { delay–; if (delay <= 0) state++; }
void state6() { state=0; }
一个小技巧是把榜首个状况state0设置为空状况,即:
这样,state =0能够让整个task中止运转,假如需求投入运转,简略的让state = 1即可。