【问题标题】:How can I blink a LED differently when I press a toggle button?当我按下切换按钮时,如何以不同的方式闪烁 LED?
【发布时间】:2018-05-09 11:06:24
【问题描述】:

我正在尝试根据切换按钮的按下来闪烁 LED。如果我第一次按下第一个拨动开关,LED 以 5Hz 闪烁,当我第二次按下拨动按钮时,LED 以 6Hz 闪烁,当我第三次按下时,LED 熄灭。

我尝试使用下面的程序,但它没有按我的意愿工作。

// constants won't change. They're used here to set pin numbers:
const int buttonPin = 7;     // the number of the pushbutton pin
const int ledPin =  6;      // the number of the LED pin
// variables will change:
int buttonState = 0;  

// variable for reading the pushbutton status
void setup() {
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);
   Serial.begin(9600); 
}

void loop() {
   int x=0; 
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);
  Serial.print(x);
  // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
  if (buttonState == HIGH &&  x==0) {
    // turn LED on:
    digitalWrite(ledPin, HIGH);
    delay(1000);
    digitalWrite(ledPin, LOW);
     delay(1000);
    Serial.print(x);
  } else {
    // turn LED off:
    x = x+1;
  }
  if (buttonState == HIGH && x==1) {
    // turn LED on:
    digitalWrite(ledPin, HIGH);
    delay(2000);
    digitalWrite(ledPin, LOW); 
    delay(2000);
     Serial.print(x);

  } else {
    // turn LED off:
    digitalWrite(ledPin, LOW);
    x = x+1;
  }
  if (buttonState == HIGH && x==2) {
    // turn LED on:
    digitalWrite(ledPin, HIGH);
    delay(3000);
    digitalWrite(ledPin, LOW);
    delay(3000);
     Serial.print(x);
  } else {
    // turn LED off:
    digitalWrite(ledPin, LOW);
    x = x+1;

  }
  if (buttonState == HIGH && x==3) {
    // turn LED off:
    digitalWrite(ledPin, LOW);
    x = 0;
  }
}

当我使用此代码时,它适用于第一种情况,即 LED 以 1000 毫秒延迟闪烁,但如果我切换开关,它再次适用于第一种情况。如何让它执行第二个条件,即以 2000 毫秒的延迟闪烁?

【问题讨论】:

  • 你的条件逻辑有点不对劲。您是否尝试对此进行调试并查看正在执行哪些“if”分支以及x 会发生什么?
  • 50Hz 和 60Hz,你确定这些频率是正确的吗?对led来说有点高。
  • 对不起,是 5 和 6,我没有关注那个
  • 正确的方法是使用硬件外设定时器,并消除按钮按下和延迟之间的紧密耦合。这只有在用户按住按钮超过 2 秒时才有效,这很糟糕。
  • 我可以去抖动一个中断触发器吗?

标签: c++ arduino


【解决方案1】:

首先这是你的电路。我尝试了这个电路和代码并为我工作。我使用中断来检查按钮状态。而且毫秒计算很简单。

频率 = 1 / 周期

周期 = Ton + Toff

6Hz = 1000 毫/T => T = 166 毫

166 = Ton + Toff(对于 %50 占空比 Ton=Toff)=> Ton = Toff = 83 毫秒

enter image description here

const int ledPin = 13;
const int buttonPin = 2;
int state = -1;
bool willLightOn = false;

unsigned long currentDelay = 0;
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(buttonPin), changeState, FALLING);
}

void loop() {
  if(state % 3 == 0) { //6Hz 
    currentDelay = 83;
    willLightOn = true;
  } else if (state % 3 == 1) { //5Hz
    currentDelay = 100;
    willLightOn = true;
  } else if (state % 3 == 2) { //LED off
    currentDelay = 0;
    willLightOn = false;
    digitalWrite(ledPin, LOW);
  }

  currentMillis = millis();
    if (currentMillis - previousMillis >= currentDelay && willLightOn) {
        previousMillis = currentMillis;
        digitalWrite(ledPin, !digitalRead(ledPin));
    } 
}

void changeState() {
  state++;
}

【讨论】:

    【解决方案2】:

    现在,您的逻辑在一个循环中检查了 3 次 x 的值。 每当 x 大于零时,下面的代码就会切换灯光。 x 的值在按下按钮时更改。

    但是这里有一个大问题:如果在处理器中发生其他事情或者它正在休眠时按下按钮(例如您想要使用的长时间延迟),它可能会被忽略。因此,您最好研究中断并使用它们来实现此行为。

    if (x > 0)
    {
        digitalWrite(ledPin, HIGH);
        delay(1000 * x);
        digitalWrite(ledPin, LOW);
    }
    if (buttonState == HIGH)
    {
        x++;
        if (x > 3)
            x = 0;
    }
    

    【讨论】:

    • 为什么不只删除delay(1000*x) 并删除所有switch()-case
    • @12431234123412341234123 哦,它甚至更好。解决这个问题
    • 据了解,闪烁应该一直持续到下一个按钮按下?
    • @12431234123412341234123 感谢您的提醒。我试图阅读 OP 提供的代码并假设情况并非如此,但你是对的。我希望将其转换为适合该要求。
    • 是的,你是对的,它应该按照当前延迟的速度进行,直到下一次按下 @12431234123412341234123
    【解决方案3】:

    您应该创建应用程序的全局状态。如果您以 50hz/60hz/off 的频率闪烁,您可以记住此状态。然后,您可以使用开关来做正确的事情。

    然后您检查按钮是否被按下并更改应用程序状态。

    请参阅下面的示例:

    // constants won't change. They're used here to set pin numbers:
    const int buttonPin = 7;     // the number of the pushbutton pin
    const int ledPin =  6;      // the number of the LED pin
    // variables will change:
    int applicationState = 0;  
    bool lightOn = true;
    
    int currentDelay = 1000;
    
    unsigned long currentMillis = 0;
    unsigned long previousMillis = 0;
    
    // variable for reading the pushbutton status
    void setup() {
      // initialize the LED pin as an output:
      pinMode(ledPin, OUTPUT);
      // initialize the pushbutton pin as an input:
      pinMode(buttonPin, INPUT);
    }
    
    void loop() {
    
        if (digitalRead(buttonPin) == HIGH) {
            applicationState++;
            if(applicationState >= 3) {
                applicationState = 0;
            }
            delay(100);
        }
    
        switch(applicationState){
            case 0:
                currentDelay = 1000;
                lightOn = true;
                break;
            case 1:
                currentDelay = 2000;
                lightOn = true;
                break;
            case 2:
                digitalWrite(ledPin, LOW);
                lightOn = false;
                break;
        }
    
    
        currentMillis = millis();
        if (currentMillis - previousMillis >= currentDelay && lightOn) {
            previousMillis = currentMillis;
            digitalWrite(ledPin, !digitalRead(ledPin));
        }      
    }
    

    我希望你能理解我想要表达的意思,并通过示例代码进行演示。

    【讨论】:

    • 这不像我想要的那样工作,每次按下按钮都会遵循相同的闪烁模式
    • @NikhilR 在 ledPin 从 LOW 切换到 HIGH 时必须按下按钮。 (那是因为 uC 正在休眠 2000 或 4000,无论您在这里使用什么单位)
    • 是的,我怎样才能使用millis()而不是延迟?
    • 我更改了代码,这应该可以。唯一的问题是你应该按住按钮太久,因为那样你会在状态之间不断切换。
    【解决方案4】:

    你的代码不能工作:

    1. 您确实需要检查按钮状态是否发生变化,检测何时有边缘。并确保您只检测一次边缘。

    2. 你必须循环重复闪烁直到按下按钮,然后你才能改变频率。

    3. 您必须在睡眠时检查该按钮,否则当您按下该按钮时您的程序无法识别。

    要使其工作,您必须更改完整的程序。

    #define BLINK_SLEEP_TIME <some value> // insert value for 16.6666ms
    
    //return 1 after a positive edge
    bool button_read(void)
    {
      static bool lastState=1; //set this to 1, so that a pressed button at startup does not trigger a instant reaction
      bool state = digitalRead(buttonPin);
      if(state != lastState)
        {
          state=lastState;
          return state;
        }
     return 0;
    }
    
    //Blink the LED with a given period, till button is pressed
    //Times are in x*16.666ms or x/60Hz
    //At least one time should be more than 0
    void blink(uint8_t ontime, uint8_t offtime) 
    {
      while(1)
      {
        for(uint8_t i=0;i<ontime;i++)
        {
          led_setOn();
          delay(BLINK_SLEEP_TIME);
          if(button_read())
          {
            return;
          }
        }
        for(uint8_t i=0;i<offtime;i++)
        {
          led_setOff();
          delay(BLINK_SLEEP_TIME);
          if(button_read())
          {
            return;
          }
        }
      }
    }
    
    const uint8_t time_table[][]=
    {
      {0,50},//LED is off
      {6,6}, //LED blinks with 5Hz, 60Hz/2/6=5Hz
      {5,5}, //LED blinks with 6Hz, 60Hz/2/5=6Hz
    }
    
    void endless(void)
    {
      uint8_t i=0;
      for(;;)
        {
          i++;
          if(i>2)
          {
            i=0;
          }
          blink(time_table[i][0],time_table[i][1]);
        }
    }
    

    更好的方法是使用硬件 PWM 模块并在按钮边缘后更改值。

    【讨论】:

    • 如何调用 led_setOn();和 led_setOff();在这里?
    • @NikhilR 要么用digitalWrite(ledPin, HIGH);/digitalWrite(ledPin, LOW); 替换它(不好),要么为它写一个宏或函数(更好)。当您直接使用digitalWrite(ledPin, HIGH); 时,您看不到它是打开还是关闭LED(高电平或低电平有效)。
    • 嘿,为什么我收到一个错误,因为退出状态 1 'time' 没有在这个范围内声明 for(uint8_t i=0;i
    • @NikhilR 因为它不能以这种方式工作,所以它是从一些编辑中遗留下来的。请在复制粘贴之前尝试理解代码。
    猜你喜欢
    • 2021-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多