使用 millis() 函数在 Arduino 中进行多任务处理

Multitasking in Arduino using millis() function

我正在考虑用报废的汽车零件制作一辆 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 秒的阻止功能。将其更改为较小的值(例如在我的情况下为 10 毫秒)就足够了并解决了问题。所有功劳都归功于解决了这个问题的@CherryDT。非常感谢。

顺便说一下,代码在 10 毫秒内应该是这样的: pulseIn(echoPin, HIGH, 10000)

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

编辑: 原来我误解了这个问题。不管怎样,把这个答案留在这里,因为它回答了 "how to blink without blocking" 这可能也很有用。

主要问题的答案是:您正在使用 pulseIn,它也会阻塞直到收到脉冲。 pulseIn 的默认超时为一秒,因此如果您的停车传感器视野内没有物体,您每次都会阻塞 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

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

这基本上是 500 毫秒的低电平和 500 毫秒的高电平,因为将 millis() 除以 500(不会有任何小数部分,因为我们在这里处于整数世界)会给出一个递增的数字500 毫秒,% 2 取模 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 实际上非常慢,因此您可以避免每次 调用它 除非需要实际更改。