0

根据我的阅读,我的问题的解决方案是使用中断,但如果我理解正确,我不能在中断调用的例程中使用延迟。我有一个大按钮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);
    }
 }
4

3 回答 3

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 中断的好教程。这是您尝试做的另一个很好的例子。

根据您使用的开关类型,您可能还想研究如何消除您的开关压力。如果不是错过第一次印刷,而是印刷太多,则需要实施某种类型的去抖动。

于 2012-11-27T13:16:56.200 回答
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,从而恢复心跳。

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

于 2015-09-03T12:19:26.663 回答
0

Arduino 确实只能使用 attachInterrupt 进行 2 个中断,但其他板如 Arduino Mega / Due / ... 有更多引脚可用于使用 attachInterrupt 进行中断。最重要的是,如果您使用直接寄存器写入(低级操作)而不是 attachInterrupts,您可以使用 Arduino Uno 上的任何数字输入进行中断处理。请参阅https://thewanderingengineer.com/2014/08/11/arduino-pin-change-interrupts/以获得对引脚更改中断和https://ukmars.org/projects/ukmarsbot/developer-notes/encoders的良好解释-on-ukmarsbot/有关外部中断的信息。这种直接方法也比 attachInterrupt 更快,因此我可以将它与 Uno 一起使用,以计算高达 100kcounts/sec 的正交编码器输入,同时执行 pid 循环/串行通信等其他操作。在主循环中轮询数字线路是不可能的。

于 2022-02-16T17:50:19.283 回答