IceAge1

    1. 求助linux下任务调度的问题 8/5102 Linux开发 2009-05-08
      呵呵,30多个任务啊,貌似任务切分的有点儿问题 ... 基本上所有的操作系统的 tcp/ip 协议栈处理都在核心, 你那个 802.15 的协议处理最好也作为核心模块加载 运行在核心空间吧...
    2. 只要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;     }
    3. 一个小技巧是把第一个状态 state0 设置为空状态,即:                 void state0() { } 这样,state =0可以让整个task 停止运行,如果需要投入运行,简单的让 state = 1 即可。 以下是一个键盘扫描的例子,这里假设 tick = 20 ms, ScanKeyboard() 函数控制口线的输出扫描,并检测输入转换为键码,利用每个state 之间 20 ms 的间隔去抖动。                 enum EnumKey { EnumKey_NoKey =  0, …     };                 struct StructKey {                                 int                keyValue;                                 bool                keyPressed;     } ; 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
    4. 使用const 是让数组内容位于 code segment (ROM) 而非 data segment (RAM) 中,8051 中使用 code 作为 const 的替代品。 (题外话:关于函数指针赋值时是否需要取地址操作符 & 的问题,与数组名一样,取决于 compiler. 对于熟悉汇编的人来说,函数名和数组名都是常数地址,无需也不能取地址。对于不熟悉汇编的人来说,用 & 取地址是理所当然的事情。Visual C++ 2005对此两者都支持) 这种方法在汇编下表现为散转, 一个小技巧是利用 stack 获取跳转表入口:                                                  mov                A, state                                              acall                MultiJump                                              ajmp               state0                                              ajmp               state1                                     ... MultiJump:                  pop                DPH                                  pop                DPL                                  rl                    A                                  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);                           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

最近访客

< 1/1 >

统计信息

已有73人来访过

  • 芯积分:--
  • 好友:--
  • 主题:1
  • 回复:4

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言