Arduino中断替代方案

时间:2012-11-27 11:38:55

标签: c embedded arduino microcontroller interrupt

从我所读到的,我的问题的解决方案是使用中断,但如果我正确理解它,我不能在中断调用的例程中使用延迟。我有一个很大的按钮LED开关。我希望它在闲置时有一个心跳,但一旦推动它,保持绿色并执行代码。

如果我按下按钮足够次,我可以打破heartbeat()(我假设在完成heartbeat的循环时恰好在正确的时间进行状态更改,但我卡住了关于如何使它在第一次点击时工作。有没有其他方法可以做我正在尝试的事情?

void loop(){

heartbeat();                                    //Make LED beat.
buttonVal = digitalRead(buttonPin);             //Check the button.
    if (buttonVal != buttonState) {               //If the button state changed.
        if (buttonVal == HIGH){                   //Check if the button is pressed.
            analogWrite(greenPin, 255);         //Button stays green once pushed.
            functionA                           //Has some delays in it.
            functionB                           //Has some other delays.
        }
    }
}

void heartbeat(){
    for(i = 0; i < pmw; i++) {
        analogWrite(greenPin,i);
        delay(((60000/rate)*.1)/pmw);
    }

    for (i = pmw; i > 0; i--){
        analogWrite(greenPin,i);
        delay(((60000/rate)*.2)/pmw);
    }

    for(i = 0; i < pmw; i++) {
        analogWrite(greenPin,i);
        delay(((60000/rate)*.1)/pmw);
    }

    for (i = pmw; i > 0; i--){
        analogWrite(greenPin,i);
        delay(((60000/rate)*.6)/pmw);
    }
 }

2 个答案:

答案 0 :(得分:5)

你的大部分假设都是对的。处理此问题的正确方法是使用中断,并且在中断服务例程(ISR)中存在延迟并不是一个好主意。所以你想要做的是在ISR中设置一个标志,并在主循环中检查该标志。

// Flag needs to be volatile if used in an ISR
volatile int buttonFlag = 0; 

void loop()
{
    if (buttonFlag == 0)
    {
        heartbeat();                        //make led beat
    }
    else
    {
        analogWrite(greenPin, 255);         //button stays green once pushed
        functionA                           //has some delays in it
        functionB                           //has some other delays
        buttonFlag = 0;                     //clear flag after executing code
    }

}

// Interrupt Service Routine attached to INT0 vector
ISR(EXT_INT0_vect)
{
    buttonFlag = digitalRead(buttonPin);    //set flag to value of button
}

由于中断只会在按钮状态发生变化时触发,因此您无需检查。

确保您的标志变量是全局的,并声明volatile以便在ISR中使用。并确保使用正确的中断向量与您正在使用的引脚一起使用。

这是关于Arduino中断的good tutorial。这是你要做的另一个good example

您可能还想查看debouncing your switch印刷机,具体取决于您使用的开关类型。如果没有错过第一次按下,你得到太多印刷机,你需要实施某种类型的去抖动。

答案 1 :(得分:2)

我是一名专业程序员,但我是Arduino世界的新手。我注意到许多Arduino所有者不了解编程的基础知识,并且在使用这项神奇的技术时无法取得好成绩。

特别是,我想建议不要使用Arduino中断,如果没有必要,因为它们只有两个,即使你可以为一个传感器或执行器写一个“漂亮”的代码,当你有要实现更复杂的项目,就不能使用它们。当然你使用中断来处理单个按钮是正确的,但如果你有四个按钮要管理怎么办?

出于这个原因,我使用“时间片”或“单步”技术重写了“心跳”草图。使用这种技术,每次执行循环功能时,只运行控制功能的“单步”,因此您可以根据需要插入任意数量,并且同样快速响应您正在使用的所有传感器。 为此,我为每个必须实现的控制函数使用全局计数器,每次执行循环函数时,我执行每个函数的一个步骤。 这是新草图:

// Interrupt.ino - this sketch demonstrates how to implement a "virtual" interrupt using
// the technique of "single step" to avoid heavy duty cycles within the loop function.

int maxPwm = 128;   // max pwm amount
int myPwm = 0;      // current pwm value
int phase = 1;      // current beat phase
int greenPin = 11;  // output led pin
int buttonPin = 9;  // input button pin
int buttonFlag = 1; // button flag for debounce

int myDir[] = {0,1,-1,1,-1}; // direction of heartbeat loop
int myDelay[] = {0,500,1000,500,3000}; // delay in microseconds of a single step

void setup()
{
   pinMode(buttonPin, INPUT); // enable button pin for input
   // it's not necessary to enable the analog output
}

void loop()
{
   if(phase>0) heartbeat(); // if phase 1 to 4 beat, else steady
   buttonRead(); // test if button has been pressed
}

// heartbeat function - each time is executed, it advances only one step
// phase 1: the led is given more and more voltage till myPwm equals to maxPwm
// phase 2: the led is given less and less voltage till myPwm equals to zero
// phase 3: the led is given more and more voltage till myPwm equals to maxPwm
// phase 4: the led is given less and less voltage till myPwm equals to zero
void heartbeat() 
{
   myPwm += myDir[phase];
   analogWrite(greenPin, myPwm);
   delayMicroseconds(myDelay[phase]);
   if(myPwm==maxPwm||myPwm==0) phase = (phase%4)+1;
}

// buttonRead function - tests if the button is pressed; 
// if so, forces phase 0 (no beat) and enlightens the led to the maximum pwm
// and remains in "inoperative" state till the button is released
void buttonRead()
{
   if(digitalRead(buttonPin)!=buttonFlag)   // if button status changes (pressed os released)
   {
      buttonFlag = 1 - buttonFlag; // toggle button flag value
      if(buttonFlag) // if pressed, toggle between "beat" status and "steady" status
      {
         if(phase) myPwm = maxPwm; else myPwm = 0;
         phase = phase==0;
         analogWrite(greenPin, myPwm);
      }
   }
}

如您所见,代码非常紧凑且执行速度快。我将心跳循环划分为四个“阶段”,由myDelay数组调节,其计数方向由myDir数组调节。如果没有任何反应,pwm led引脚的电压在每一步增加,直到达到maxPwm值,然后环路进入第2阶段,其中电压递减到零,依此类推,实现原始心跳。

如果按下该按钮,则循环进入零相位(无心跳),并且LED使用maxPwm电压进行补偿。从现在开始,循环保持稳定的led,直到按钮被释放(这实现了“去抖”算法)。如果再次按下该按钮,buttonRead功能将重新启动心跳功能,使其再次进入阶段1,从而恢复心跳。

如果再次按下按钮,心跳停止,依此类推。所有人都没有任何兴奋或反弹。