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);
}
}*/
}
4 ответа
Лучший ответ:
Библиотеки таймеров SimpleTimer и аналогичные библиотеки таймеров на основе millis () созданы именно для такого рода задач. SimpleTimer даже позволяет вам указать количество повторений. Суть в том, что вы устанавливаете таймер на интервал и, возможно, на количество повторений, а также функцию обратного вызова, которая вызывается в конце каждого интервала. Ваш основной код просто очень часто вызывает функцию обновления таймера "run()" в этом случае. Ваша функция обратного вызова будет вызвана в соответствующее время. Это один из самых простых способов реализации такого рода заданий, имитирующий передний/задний план, но без сложностей прерываний и процедур обслуживания прерываний.
Для такой простой задачи код очень сложен.
Во-первых, напишите фрагмент кода, который мигает светодиодами, если прошло достаточно времени.
Вы можете реализовать счетчик мигания либо в основном цикле, либо в процедуре мигания, упомянутой ранее.
Должно быть довольно просто.
редактировать: вот как бы я это реализовал.
//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 раза:
Я изменил ваш скетч так, чтобы при создании экземпляра объекта светодиодной мигалки с 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();
}
}
Я хотел бы внести пару изменений в реализацию @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
- Использовать timer0, не влияя на millis() и micros().
- Как запустить 4 светодиода последовательно на основе кнопочного входа?
- Как повторить кусок кода
- Торговый автомат Arduino для мониторинга ввода монет в слот во время ожидания ввода пользователя
- Сброс Arduino с помощью ПО (каждый день)
- Та же кнопка одним кликом и двойным кликом
- Как сбросить millis()?
- Как увеличить срок службы EEPROM?
С точки зрения чистого 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