【问题标题】:Multitasking in Arduino using millis() function使用millis()函数在Arduino中进行多任务处理
【发布时间】:2020-04-20 21:02:26
【问题描述】:

我正在考虑用废弃的汽车零件制造一辆 DIY 卡丁车越野型汽车。作为电子产品,我计划使用我的 Arduino Uno 来控制所有东西。所以我只是做了一个齿轮显示,让我知道我目前在哪个齿轮上。还有一个简单的停车传感器。

如果我离物体太近,停车传感器蜂鸣器会闪烁。而且我不能用 delay() 函数闪烁,因为如果我在闪烁时尝试向上移动,输入将不会注册,因为延迟是一个阻塞函数。所以我尝试改用millis,但似乎它也没有注册。

这个概念很简单。我的变速杆两侧各有两个按钮。当我向上移动时,按钮被按下,因此 7 段显示器增加了一个。另一个用于降档。

我还没有加倒档,但是当倒车雷达功能起作用的时候,我仍然很长时间无法按下按钮。

我希望你们能理解我想说的话。我刚开始用 Arduino 编码,所以我没有足够的经验。我将 .ino 文件作为代码块保留。我试图解释代码中的所有内容。希望有人知道解决方案。提前谢谢...



// Setting up the pins for 7-Segment-Display.
const int E = 13;
const int D = 12;
const int C = 11;
const int DP = 10;
const int G = 9;
const int F = 8;
const int A = 7;
const int B = 6;


// Setting up the counter for gear shifting.

int gearCount = 0;

// Setting up the button states and button pin for state-change detectors.

int buttonAddPin = 5;
int buttonAddState = 0;
int previousButtonAddState = 0;

// And this is for downshifting.

int buttonSubstractPin = 4;
int buttonSubstractState = 0;
int previousButtonSubstractState = 0;


// Adding buzzer for parking sensor.

const int buzzerPin = 3;
int buzzerTone = 0;


// Adding parking sensor.

const int trigPin = 2;
const int echoPin = 1;
long duration;
int distance;


// Adding non-delay blinker with millis for buzzer to blink when sensor detects an object.

unsigned long previousTime = 0;




/*---------------------------------- FUNCTIONS FOR SHIFTING -------------------------------------------------*/


// "zero" function is the series of commands for displayıng 0 on the 7-Segment corresponding to neutral gear.

void zero()
{
  digitalWrite(A, LOW);
  digitalWrite(B, LOW);
  digitalWrite(C, LOW);
  digitalWrite(D, LOW);
  digitalWrite(E, LOW);
  digitalWrite(F, LOW);
  digitalWrite(G, HIGH);
  digitalWrite(DP, HIGH);

}


// "one" function is the series of commands for displaying 1 on the 7-Segment-Display corresponding to 1st gear.

void one()
{
  digitalWrite(A, HIGH);
  digitalWrite(B, LOW);
  digitalWrite(C, LOW);
  digitalWrite(D, HIGH);
  digitalWrite(E, HIGH);
  digitalWrite(F, HIGH);
  digitalWrite(G, HIGH);
  digitalWrite(DP, HIGH);

}


// "two" function is the series of commands for displaying 2 on the 7-Segment-Display corresponding to 2nd gear.

void two()
{
  digitalWrite(A, LOW);
  digitalWrite(B, LOW);
  digitalWrite(C, HIGH);
  digitalWrite(D, LOW);
  digitalWrite(E, LOW);
  digitalWrite(F, HIGH);
  digitalWrite(G, LOW);
  digitalWrite(DP, HIGH);
}



// "three" function is the series of commands for displaying 3 on the 7-Segment-Display corresponding to 3rd gear.

void three()
{
  digitalWrite(A, LOW);
  digitalWrite(B, LOW);
  digitalWrite(C, LOW);
  digitalWrite(D, LOW);
  digitalWrite(E, HIGH);
  digitalWrite(F, HIGH);
  digitalWrite(G, LOW);
  digitalWrite(DP, HIGH);
}


// And now the main function that we are going to call in "void loop" to determine which gear we are on.

void gearDisplay()
{

  buttonAddState = digitalRead(buttonAddPin);          // And now the button state change detector for shifting up is ready. As you can see when I push the button
                                                       // program adds 1 to gearCount variable. So now gearCount is 1. Therefore the 7-Segment-Display will show
  if(buttonAddState != previousButtonAddState){        // 1 on it. We will do this a bit later.

    if(buttonAddState == HIGH){
      gearCount++;
    }

   delay(50);

  }

  previousButtonAddState = buttonAddState;


// Now for downshifting.


  buttonSubstractState = digitalRead(buttonSubstractPin);

  if(buttonSubstractState != previousButtonSubstractState) {

     if(buttonSubstractState == HIGH){
        gearCount--;
     }

  delay(50);


  }

  previousButtonSubstractState = buttonSubstractState;



// Now we will call one of the 7-Segment-Display number functions according to the gearCount variable.


  if(gearCount == 0){
    zero();
  }

  else if(gearCount == 1){
    one();
  }


  else if(gearCount == 2){
    two();
  }


  else if(gearCount == 3){
    three();
  }


  else if(gearCount > 3){                 // Just making sure if we press buttons accidentally more than enough, it sets gearCount back to closest gear available.
    gearCount--;
  }


  else if(gearCount < 0){                 // Same with this one.
    gearCount++;
  }

}


// That was al for the gear panel. Now the hard part. The parking sensor.


/*---------------------------------------------- SHIFTING DONE ----------------------------------------------------*/












/*--------------------------------------------- PARKING SENSOR ------------------------------------------------------*/



void parkingSensor()
{

  digitalWrite(trigPin, LOW);                 // Resetting trigPin

  delayMicroseconds(2);                       // Delaying to prevent any issiues. ( I saw this online, most people do it like this so I also did.)


  digitalWrite(trigPin, HIGH);                // Creating a short soundwave.
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  duration = pulseIn(echoPin, HIGH);          // Detecting the time that passes for the soundwave to reach back to the receiver.

  distance = duration*0.034/2;                // Simple math for calculating the distance between the obstacle and sensor.


  unsigned long currentTime = millis();       // Store the current time for blinking the buzzers.


  if((distance > 0) && (distance < 31)){              // If the distance is in between 0-31 buzz non-stop.

    buzzerTone = 1000;
    tone(buzzerPin, buzzerTone);
  }


  else if((distance > 30) && (distance < 76)){        // If the distance is between 30-76 buzz every 100 miliseconds.

      if(currentTime - previousTime >= 100){

        previousTime = currentTime;

        if(buzzerTone == 1000){

          buzzerTone = 0;
        }

        else {

          buzzerTone = 1000;
        }

       tone(buzzerPin, buzzerTone);
      }
  }


  else if((distance > 75) && (distance < 101)){      // If the distance is between 75-101 buzz every 300 miliseconds.

      if(currentTime - previousTime >= 300){

        previousTime = currentTime;


          if(buzzerTone == 1000){

            buzzerTone = 0;
          }

          else {

           buzzerTone = 1000;
          }

       tone(buzzerPin, buzzerTone);

      }
  }



  else if(distance > 100){                         // If the distance is more than 100 don't buzz.

    buzzerTone = 0;

    tone(buzzerPin, buzzerTone);
  }

}

/*------------------------------- PARKING SENSOR DONE -----------------------------------*/




// And this was, I guess, all I had to do but unfortunately it's not working...



void setup() {


  pinMode(A, OUTPUT);
  pinMode(B, OUTPUT);
  pinMode(C, OUTPUT);
  pinMode(D, OUTPUT);
  pinMode(E, OUTPUT);
  pinMode(F, OUTPUT);
  pinMode(G, OUTPUT);
  pinMode(DP, OUTPUT);
  pinMode(buttonAddPin, INPUT);
  pinMode(buttonSubstractPin, INPUT);
  pinMode(buzzerPin, OUTPUT);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
}

void loop() {

  gearDisplay();

  parkingSensor();
}

编辑:原来pulseIn() 的默认持续时间是 1 秒。它也是一个阻止功能,可阻止按钮在每个循环上注册输入 1 秒。将其更改为较小的值,例如在我的情况下为 10ms,就足够了并且解决了这个问题。所有功劳归于解决此问题的@CherryDT。非常感谢。

顺便说一句,这就是代码在 10 毫秒内的样子: pulseIn(echoPin, HIGH, 10000)

// 请注意pulseIn() 中的持续时间以微秒为单位。

【问题讨论】:

  • “基本上控制一切” - 您可能希望将工作分解为两个不同的问题:1. 上下移动并在 LCD 上显示活动档位; 2. 根据压电或扬声器发出声音到感应到的距离。获取 1. 和 2. 以使用基于非阻塞 millis() 的代码。然后将两者结合起来。

标签: function arduino sensors arduino-uno multitasking


【解决方案1】:

编辑:原来我误解了这个问题。无论如何,将这个答案留在这里,因为它回答了“如何在不阻塞的情况下眨眼”,这也可能有用。

主要问题的答案是:您正在使用pulseIn,它也会阻塞直到接收到脉冲。 pulseIn 的默认超时时间是 1 秒,所以如果您的停车传感器视野中没有物体,您每次都会阻塞 1 秒!因为你可能只对相当小的距离感兴趣,你可以设置一个超时,比如说,10ms(10000us):

pulseIn(echoPin, HIGH, 10000)

如果这不符合您的要求或仍然有太多延迟,请考虑编写您自己的代码,而不是在不阻塞的情况下测量时间,同时处理按钮按下。请参阅this answer 有人遇到完全相同的问题并以这种方式解决。

基本上,你可以这样做:

unsigned long pulseTimer = 0;
int lastEchoState = HIGH; // Initially HIGH so that the actual LOW triggers a "change"

void loop () {
  int currentEchoState = digitalRead(echoPin);

  // To set a timeout of the pulse, simulate a pulse if already waiting for too long
  if (lastEchoState == LOW && micros() - pulseTimer > 1000000ul) {
    currentEchoState = HIGH;
  }

  if (currentEchoState != lastEchoState) {
    lastEchoState = currentEchoState;
    if (currentEchoState == LOW) {
      // Echo pulse over (or it's the initial loop iteration)

      // Start counting time until echo
      // We do this before sending the trigger because pulseIn would
      // normally measure until the pulse is *over*, but for simplicity
      // we measure until the pulse *starts*, so this implicitly shifts
      // the result by our pulse duration, as trigger and echo pulse are
      // usually of the same length
      pulseTimer = micros();

      // Send new trigger pulse
      delayMicroseconds(2);
      digitalWrite(trigPin, HIGH);
      delayMicroseconds(10);
      digitalWrite(trigPin, LOW);
    } else {
      // Pulse came in, measure time
      unsigned long pulseDelayTime = micros() - pulseTimer;

      // **** DO SOMETHING WITH pulseDelayTime HERE! ****
    }
  }

  // **** DO YOUR REGULAR BUTTON PROCESSING HERE! ****
}

您可以通过将 LED 输出设置为:

(millis() / 500) % 2

...在每次循环迭代时闪烁它*。

这基本上是 500ms 的 LOW 和 500ms 的 HIGH,因为将 millis() 除以 500(它没有任何小数部分,因为我们在这里是整数世界)会得到一个每 500ms 递增的数字,并且 @ 987654329@ 取模 2,即奇数为 1,偶数为 010 与 Arduino 的 HIGHLOW 相同。

例子:


+----------+--------------+------------------+
| millis() | millis()/500 | (millis()/500)%2 |
+----------+--------------+------------------+
|      ... |              |                  |
|     1499 |            2 |         0 (LOW)  |
|     1500 |            3 |         1 (HIGH) |
|     1501 |            3 |         1 (HIGH) |
|      ... |              |                  |
|     1999 |            3 |         1 (HIGH) |
|     2000 |            4 |         0 (LOW)  |
|     2001 |            4 |         0 (LOW)  |
|      ... |              |                  |
|     2499 |            4 |         0 (LOW)  |
|     2500 |            5 |         1 (HIGH) |
|     2501 |            5 |         1 (HIGH) |
|      ... |              |                  |
+----------+--------------+------------------+

顺便说一句,很酷的项目。

*:如果您想从中获得更多性能,您可以将最后一个引脚状态保存在一个单独的变量中,并仅在它发生更改时执行digitalWrite。那是因为 digitalWrite 实际上很慢,所以您可以避免每次都调用它,除非需要进行实际更改。

【讨论】:

  • 我想我已经完成了闪烁的部分。不过感谢您的回答,这绝对是一种更好的眨眼方式。但是我想问的主要问题是,例如,当我尝试升档时,有时按钮不会注册推动。你有没有机会知道问题出在哪里?
  • 哦,那我看错了你的问题。我认为问题在于您不确定如何添加闪烁,因为delay 会阻塞。您提到它也不适用于millis,但我认为您以某种方式错误地使用了它,因为我没有意识到您显示的代码已经闪烁。
  • 啊,我现在看到了这个问题。您正在使用pulseIn,它也会阻塞直到接收到脉冲。考虑编写自己的代码,而不是在不阻塞的情况下测量时间,同时处理按钮按下。请参阅this answer 有人遇到完全相同的问题并以这种方式解决。
  • 或者可以只学习 C。切换引脚需要两个时钟周期。看DigitalWrite的代码,然后学C。
  • (millis() / 500) % 2 ... modulo 是一个非常昂贵的函数,只是让 LED 闪烁。尤其是当你可以用更少的周期用减法解决同样的问题时。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-13
  • 1970-01-01
相关资源
最近更新 更多