Arduino Мигает двумя светодиодами без задержки (количество повторений)

Сообщество Arduino. У меня есть опыт работы с Arduino уже 3 года, и я, наконец, пришел к выводу, что нужно полностью избавиться от функции задержки.

Ниже приведен код для полного 10-кратного мигания двух светодиодов. Проблема в том, что setup() не повторяется как цикл (), что означает, что он длится недостаточно долго, чтобы найти разницу во времени между предыдущими миллионами и текущими миллионами, потому что он выполняется один раз; таким образом, устраняется идея мигания светодиода в setup(). Но я исследовал способы имитации цикла() с помощью циклов while; Я использовал метод Adafruit, чтобы оживить вещи(конструкторы и классы!), https://learn.adafruit.com/multi-tasking-the-arduino-part-1/overview.

Но мне нужна небольшая помощь, потому что это не работает, вместо этого он мигает вечно(Миссия наполовину завершена =)).

Обратите внимание, как я использовал циклы while в Update (), который находится в классе Flasher, чтобы имитировать цикл (), пока условие не станет истинным.

Проблема в том, что я не знаю, почему этот код не работает и что делать дальше.

Конечная цель состоит в том, чтобы мигать светодиодами на выводах 12 и 13 в общей сложности 10 раз, вместо того, чтобы постоянно мигать целую вечность. Я использую Arduino Uno R3 с последовательной скоростью передачи данных 115200.

Игнорируйте то, что я сделал в цикле(). Это та же функция, что и код setup (), вместо этого светодиод мигает целую вечность (к сожалению), когда вводится значение "1".

    class Flasher{
    // Class Member Variables
    // These are initialized at startup
    int ledPin;      // the number of the LED pin
    long OnTime;     // milliseconds of on-time
    long OffTime;    // milliseconds of off-time

    // These maintain the current state
    int ledState;                 // ledState used to set the LED
    unsigned long previousMillis;   // will store last time LED was updated
    unsigned long currentMillis = 0;

  public: Flasher (int pin, long on, long off)    
{            // Constructor - creates a Flasher
    // and initializes the member variables and state


      ledPin = pin;
      pinMode(ledPin, OUTPUT);

      OnTime = on;
      OffTime = off;

      ledState = LOW;
      previousMillis = 0;
    }

    void Update(int a){
      // check to see if it's time to change the state of the LED
      for (int i = 0; i < a; i++) {
        while ((currentMillis - previousMillis <= OnTime) || ((currentMillis - previousMillis <= OffTime))) {
          currentMillis = millis();

          if ((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
          {
            ledState = LOW;  // Turn it off
            previousMillis = currentMillis;  // Remember the time
            digitalWrite(ledPin, ledState);  // Update the actual LED
          }
          else if ((ledState == LOW) && (currentMillis - previousMillis >=OffTime))
          {
            ledState = HIGH;  // turn it on
            previousMillis = currentMillis;   // Remember the time
            digitalWrite(ledPin, ledState);   // Update the actual LED
          }
        }
      }
    }
};


Flasher led1(12, 100, 400);
Flasher led2(13, 350, 350);

void setup(){

  Serial.begin(115200);
  led1.Update(10);

}

void loop(){

   /* if (Serial.available()) {
      String str = Serial.readString();

      if (str.substring(0, 1).equals("1")) {
        led1.Update(10);

      }
  }*/
}

, 👍1


4 ответа


Лучший ответ:

1

Библиотеки таймеров SimpleTimer и аналогичные библиотеки таймеров на основе millis () созданы именно для такого рода задач. SimpleTimer даже позволяет вам указать количество повторений. Суть в том, что вы устанавливаете таймер на интервал и, возможно, на количество повторений, а также функцию обратного вызова, которая вызывается в конце каждого интервала. Ваш основной код просто очень часто вызывает функцию обновления таймера "run()" в этом случае. Ваша функция обратного вызова будет вызвана в соответствующее время. Это один из самых простых способов реализации такого рода заданий, имитирующий передний/задний план, но без сложностей прерываний и процедур обслуживания прерываний.

,

0

Для такой простой задачи код очень сложен.

Во-первых, напишите фрагмент кода, который мигает светодиодами, если прошло достаточно времени.

Вы можете реализовать счетчик мигания либо в основном цикле, либо в процедуре мигания, упомянутой ранее.

Должно быть довольно просто.

редактировать: вот как бы я это реализовал.

//blink the led for a given number of times
//cnt: number of times of the blinks needed
uint8_t led_blinky(uint8_t cnt) {
    static uint8_t led_cnt=0;       //blinky count
    static uint32_t millis_prev=0;  //previous millies

    if (led_cnt>=cnt) return led_cnt;   //return blink count
    if (millis() > millis_prev + LED_ms) {    //enough time has passed
        millis_prev += LED_ms;                      //update the time
        digitalWrite(LED, (digitalRead(LED)==HIGH)?LOW:HIGH);       //flip led
        led_cnt+=1;                 //increment the led count
    }
    return led_cnt;                 //return blinky count
}

сам код довольно понятен- > единственная (незначительная) сложная часть заключается в том, как он поддерживает точность синхронизации в течение длительного периода времени. в этой конкретной реализации тест выполняется внутри процедуры led_blinky (), поэтому он является автономным- > он возвращает количество выполненных миганий.

Вот выполнение выполнения 100 мс мигает 4 раза:

,

1

Я изменил ваш скетч так, чтобы при создании экземпляра объекта светодиодной мигалки с 4-м аргументом он мигал вечно. Если вы создаете экземпляр объекта мигалки с 3 аргументами, используйте функцию Start(), чтобы индикатор начал мигать указанное вами количество раз. Просто вызовите его один раз, а не повторно в функции loop () (например, при нажатии кнопки). Вы можете снова вызвать функцию Start() и указать другое количество вспышек. У вас может быть несколько объектов светодиодной мигалки. У каждого объекта может быть разное время включения/выключения. Некоторые светодиоды могут мигать вечно, а некоторые-указанное количество раз.

Обновление

Я добавил несколько функций в класс, чтобы сделать его более универсальным: Stop (), SetPinHighTime (), SetPinLowTime() и SetFlashForever().

class Flasher{

  // Переменные - члены класса. Они инициализируются при запуске.
  byte ledPin;
  byte ledState;
  byte flashForever;
  unsigned int flashCounter;
  unsigned long highTime;
  unsigned long lowTime;
  unsigned long previousMillis;

  // Конструктор. Создайте объект светодиодной мигалки.
  // Инициализируйте переменные-члены и состояние светодиода.
  public:

  Flasher(byte pin, unsigned long on, unsigned long off, byte continueFlashing = 0){
    ledPin = pin;
    pinMode(ledPin, OUTPUT);
    highTime = on;
    lowTime = off;      
    ledState = LOW;
    flashForever = continueFlashing;
    previousMillis = 0;
  }

  void Update(){

    // Проверка, не пришло ли время изменить состояние светодиода.
    unsigned long currentMillis = millis();

    if((ledState == HIGH) && (currentMillis - previousMillis >= highTime) && ((flashCounter > 0) || (flashForever == 1))){
      ledState = LOW;
      previousMillis = currentMillis;        // Запомните время.
      digitalWrite(ledPin, ledState);        // Обновите фактический светодиод.
      if(flashCounter > 0){flashCounter--;}  // Обновите счетчик вспышек.
    }
    else if((ledState == LOW) && (currentMillis - previousMillis >= lowTime) && ((flashCounter > 0) || (flashForever == 1))){
      ledState = HIGH;
      previousMillis = currentMillis;        // Запомните время.
      digitalWrite(ledPin, ledState);        // Обновите фактический светодиод.
      if(flashCounter > 0){flashCounter--;}  // Обновите счетчик вспышек.
    }
  }

  void Start(unsigned int flashNumberOfTimes){
    flashCounter = flashNumberOfTimes * 2;
    flashForever = 0;
    ledState = LOW;
    digitalWrite(ledPin, ledState);
  }

  void Stop(){
    Start(0);
  }

  void SetPinHighTime(unsigned long pinHighTime){
    highTime = pinHighTime;
  }

  void SetPinLowTime(unsigned long pinLowTime){
    lowTime = pinLowTime;
  }

  void SetFlashForever(byte oneOrZero){
    flashForever = oneOrZero;
    if(oneOrZero == 0){Start(0);}
  }

  void SetPinToInput(){
    pinMode(ledPin, INPUT);
  }

};

// Используется только в целях тестирования.
byte doOnce = 0;
const byte led1PinNumber = 2;
const byte led2PinNumber = 3;
const byte led3PinNumber = 4;

// Создать экземпляр объекта(ов) светодиодной мигалки.
// например, Flasher object_name(Pin Number, ON Time, OFF Time, OPTIONAL Flash Forever);

// Конструктор с тремя аргументами.
// Указано количество раз, когда должен мигать светодиод
// при вызове функции Start (). Светодиод НЕ будет
// начинайте мигать, пока не вызовете команду Start().
Flasher led1(led1PinNumber, 10, 990);
Flasher led2(led2PinNumber, 100, 400);

// Конструктор с четырьмя аргументами.
// Мигайте светодиодом вечно. Это немедленно запустит мигание светодиода.
Flasher led3(led3PinNumber, 1000, 1000, 1);

void setup(){}

void loop(){

  // Вызовите функцию обновления как можно быстрее.
  led1.Update();
  led2.Update();
  led3.Update();

  // Тестовый вывод. Мигают 3 светодиода с разным временем включения/выключения.
  // Используйте функцию Start() для мигания 2 светодиодов X номер
  // раз, затем перезапустите каждый объект светодиодной мигалки с
  // другое количество вспышек. Установите 1 светодиод на вечную вспышку,
  // затем остановите его и начните вечно мигать другим светодиодом,
  // изменяя частоту вспышки одного светодиода перед его перезапуском.
  // Наконец, установите 3 светодиодных вывода на ВХОД.

  // Запустите объект led1 через 4 секунды после запуска Arduino.
  if(millis() > 4000 && millis() < 5000 && doOnce == 0){
    led1.Start(3);  // Flash the LED 3 times.
    doOnce = 1;
  }

  // Запустите объект led2 через 5 секунд после запуска Arduino.
  if(millis() > 5000 && millis() < 6000 && doOnce == 1){
    led2.Start(5);  // Flash the LED 5 times.
    doOnce = 0;
  }

  // Перезагрузите объект led1 через 10 секунд после запуска Arduino.
  if(millis() > 10000 && millis() < 11000 && doOnce == 0){
    led1.Start(7);  // Flash the LED 7 times.
    doOnce = 1;
  }

  // Перезагрузите объект led2 через 15 секунд после запуска Arduino.
  if(millis() > 15000 && millis() < 16000 && doOnce == 1){
    led2.Start(9);  // Flash the LED 9 times.
    doOnce = 0;
  }

  // Прекратите мигать объектом led3.
  // Начните мигать объектом led1 вечно.
  if(millis() > 30000 && millis() < 31000 && doOnce == 0){
    led3.SetFlashForever(0);
    led1.SetFlashForever(1);
    doOnce = 1;
  }

  // Прекратите мигать объектом led1.
  // Начните мигать объектом led3 навсегда и измените
  // это скорость вспышки.
  if(millis() > 50000 && millis() < 51000 && doOnce == 1){
    led3.SetPinHighTime(250);
    led3.SetPinLowTime(500);
    led3.SetFlashForever(1);
    led1.SetFlashForever(0);
    doOnce = 0;
  }

  // Прекратите мигать объектом led3. Никакие светодиоды не должны мигать.
  if(millis() > 60000 && millis() < 61000 && doOnce == 0){
    led3.Stop();
    doOnce = 1;

    // Мой тестовый экран имеет один высокий и один низкий светодиод на вывод.
    // Установите контакты на вход, чтобы выключить светодиоды низкого уровня.
    led1.SetPinToInput();
    led2.SetPinToInput();
    led3.SetPinToInput();
  }

}
,

С точки зрения чистого C++, эта реализация может быть правильной, однако, 1) для Arduino, "setup ()" получает вызов в Arduino [main()](https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/main.cpp#L33-L51) после некоторой инициализации аппаратного обеспечения, именно поэтому Arduino выступает за инициализацию аппаратного обеспечения методом " begin ()", а не с использованием конструкции (это произошло до инициализации аппаратного обеспечения)., @hcheung

2) Для C++, если ваша реализация методов класса в классе, по умолчанию, все методы будут рассматриваться как " встроенные` методы. это означает, что, например, "update ()" будет вставлен как встроенный в том месте, где он вызывается. Поэтому необходимо отделить прототип класса от реализации., @hcheung

@hcheung - "конструктор" заботится об инициализации требуемых переменных, когда объект(ы) " создается`. Если бы вы могли ответить на вопрос ОП с кодом, необходимым для реализации функции " begin ()", я бы проголосовал за такой ответ., @VE7JRO

@hcheung - 2) "Поэтому необходимо отделить прототип класса от реализации. - Кто сказал? Я могу написать "код Arduino" любым удобным мне способом, если он компилируется, имеет наименьший размер компиляции и отлично работает. Я думаю, что компилятор умнее меня и может взять код, написанный миллионами разных пользователей по всему миру, и заставить его работать просто отлично :) Не стесняйтесь ответить на этот вопрос лучшим ответом., @VE7JRO

Ну, я не говорил, что это не работает, и для Arduino людей может не сильно волновать размер кода, на самом деле большинство пользователей Arduino думают, что изучение C++ необязательно. Это не часть оптимизации компилятора, это то, как определяется C++. "Функции-члены класса могут быть объявлены встроенными либо с помощью ключевого слова inline, либо путем помещения определения функции в определение класса". Вы можете проверить любую книгу о C++ от Бьярне Страуструпа., @hcheung

@hcheung - Мне все равно, что написано в книгах. Я на пенсии, поэтому пишу код так, как мне нравится. Если у вас есть "правильный" ответ, который согласуется с вашей книгой(книгами), скажите..., @VE7JRO

Давайте [продолжим это обсуждение в чате](https://chat.stackexchange.com/rooms/130826/discussion-between-ve7jro-and-hcheung)., @VE7JRO


-1

Я хотел бы внести пару изменений в реализацию @VE7JRO.

Для C++, это совершенно нормально для инициализации экземпляра класса через его построение, тем не менее, для Arduino, на настройки() сделать звонок в Arduino функции main() после инициализации аппаратного обеспечения, в том числе определение PIN и т. д., именно поэтому для Arduino выступает в Arduino стиле руководство для написания библиотек для использования начать() , чтобы инициализировать библиотеку экземпляр (это совершенно нормально, чтобы использовать конструкцию для установки некоторых переменных, но не для чего-то вроде pinMode() в конструкции). Потому что очень часто создание экземпляра класса происходило до вызова setup ().

С учетом этого, предлагаемые изменения:

class Flasher{

  // Переменные - члены класса. Они инициализируются в begin().
  byte ledPin;
  byte ledState;
  byte flashForever;
  unsigned int flashCounter;
  unsigned long highTime;
  unsigned long lowTime;
  unsigned long previousMillis;

  public:

  Flasher() {}
  
  begin(byte pin, unsigned long on, unsigned long off, byte continueFlashing = 0){
    ledPin = pin;
    pinMode(ledPin, OUTPUT);
    highTime = on;
    lowTime = off;      
    ledState = LOW;
    flashForever = continueFlashing;
    previousMillis = 0;
  }
  ....
};

Чтобы использовать класс:

Flasher led1;
Flasher led2;
Flasher led3;

setup() {
  led1.begin(led1PinNumber, 10, 990);
  led2.begin(led2PinNumber, 100, 400);
  led3.begin(led3PinNumber, 1000, 1000, 1);
}

Еще одно изменение связано с тем, как C++ определяет встроенную функцию или метод. "Функции-члены класса могут быть объявлены встроенными либо с помощью ключевого слова inline, либо путем помещения определения функции в определение класса". Это означает, что все функции, определенные в классе Flasher в данном случае, подразумеваются как встроенные методы. поэтому, когда led.update() получает вызов, компилятор помещает его в виде встроенного кода там, где он получает вызов (и несколько раз). Существуют определенные методы, которые вполне подходят и на самом деле должны быть встроенными, например, SetPinHighTime ()и SetPinToInput() на самом деле выиграли бы от того, чтобы быть встроенными, но вы, вероятно, не захотите, чтобы метод update() был встроенным. Для этого создайте прототип класса и используйте методы, которые вы хотели бы встроить в определение класса, но разделите реализацию метода update ().

#ifndef FLASHER_
#define FLASHER_
#include "Arduino.h"

class Flasher{

  // Переменные - члены класса. Они инициализируются при запуске.
  byte ledPin;
  byte ledState;
  byte flashForever;
  unsigned int flashCounter;
  unsigned long highTime;
  unsigned long lowTime;
  unsigned long previousMillis;

  public:

  // Все методы являются встроенными, кроме update()

  Flasher() {}
  void begin(byte pin, unsigned long on, unsigned long off, byte continueFlashing = 0){
    ledPin = pin;
    pinMode(ledPin, OUTPUT);
    highTime = on;
    lowTime = off;      
    ledState = LOW;
    flashForever = continueFlashing;
    previousMillis = 0;
  }

  void Update();

  void Start(unsigned int flashNumberOfTimes){
    flashCounter = flashNumberOfTimes * 2;
    flashForever = 0;
    ledState = LOW;
    digitalWrite(ledPin, ledState);
  }

  void Stop(){ Start(0); }

  void SetPinHighTime(unsigned long pinHighTime){
    highTime = pinHighTime;
  }

  void SetPinLowTime(unsigned long pinLowTime){
    lowTime = pinLowTime;
  }

  void SetFlashForever(byte oneOrZero){
    flashForever = oneOrZero;
    if(oneOrZero == 0){Start(0);}
  }

  void SetPinToInput(){ pinMode(ledPin, INPUT); }

};
#endif

Определите метод update() отдельно в реализации Flasher.cpp:

#include "Flasher.h"

Flasher::update() {

    // Проверьте, не пришло ли время изменить состояние светодиода.
    unsigned long currentMillis = millis();

    if((ledState == HIGH) && (currentMillis - previousMillis >= highTime) && ((flashCounter > 0) || (flashForever == 1))){
      ledState = LOW;
      previousMillis = currentMillis;        // Запомните время.
      digitalWrite(ledPin, ledState);        // Обновите фактический светодиод.
      if(flashCounter > 0){flashCounter--;}  //Обновите счетчик вспышек.
    }
    else if((ledState == LOW) && (currentMillis - previousMillis >= lowTime) && ((flashCounter > 0) || (flashForever == 1))){
      ledState = HIGH;
      previousMillis = currentMillis;        // Запомните время.
      digitalWrite(ledPin, ledState);        // Обновите фактический светодиод.
      if(flashCounter > 0){flashCounter--;}  // Обновите счетчик вспышек.
}

Я надеюсь, что это поможет сделать класс лучше и лучше понимать C++ и Arduino, и в следующий раз, когда вы прочтете источник библиотеки Arduino, вы лучше поймете, почему библиотека реализует свой класс и методы так, как она это делает.

,

Итак, вы взяли мои 3 строки кода, используемые для создания экземпляров 3 объектов (с помощью конструктора), добавили к ним еще 3 строки, что делает то же самое, что и исходные 3 строки. Вы сравнивали размер компиляции / использование памяти для вашего метода С моим методом? -1 за то, что взял простой код и сделал его более сложным., @VE7JRO