微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > 中断多任务+状态机 单片机软件结构设计

中断多任务+状态机 单片机软件结构设计

时间:11-27 来源:互联网 点击:

习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)

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top