Коллеги, добрый день. Прошу вашего опыта. Для озвучивания успешного события (считывание RFID карты) хочу динамиком издавать звук "Бии-боо" длительностью до 0,5 - 0,8 сек. "Бии" - примерно 800 ... 1000Гц, "Боо" - 200 ... 300 Гц Как бы мне так сделать, что бы на время бибиканья не тормозить основной код? Я наверное какой то асинхронности хочу, но может есть решение, что бы не ставить delay()?
каждое событие (чтение карты, управление бипером и пр.) записать в виде отдельной функции. Эти функции постоянно крутятся в loop, неиспользуемые в данный момент (определять можно по флагам) просто игнорируются. Типичный пример (только здесь функции не вынесены отдельно, а всунуты в switch): Код (C++): enum { LED_PIN = 13 }; enum LedState { LED_ON, LED_OFF, LED_BLINK }; LedState led_state; void setup() { led_state = LED_OFF; pinMode(LED_PIN, OUTPUT); Serial.begin(115200); } void loop() { if (Serial.available()) { char command = Serial.read(); switch (command) { case '1': led_state = LED_ON; break; case '0': led_state = LED_OFF; break; case '*': led_state = LED_BLINK; break; default: { for (int i = 0; i < 5; ++i) { digitalWrite(LED_PIN, HIGH); delay(50); digitalWrite(LED_PIN, LOW); delay(50); } } } } switch (led_state) { case LED_ON: digitalWrite(LED_PIN, HIGH); break; case LED_OFF: digitalWrite(LED_PIN, LOW); break; case LED_BLINK: { static unsigned long start_millis = 0; if (millis() - start_millis >= 300) { start_millis = millis(); digitalWrite(LED_PIN, !digitalRead(LED_PIN)); } } } }
Подтверждаю -- асинхрон полный, вот только нужно будет почитать документацию на ATmega328, т.к. стандартные Arduino'вские библиотеки не умеют менять частоту ШИМ. Поэтому частоту нужно будет задавать при помощи изменения регистров напрямую (или найти стороннюю библиотеку, которая это сможет делать). И желательно не трогать 'Timer 0' -- пины 5 и 6, т.к. он используется часами реального времени, которые программно реализованы в Arduino. 'Timer 1' имеет диапазон от 15 Гц до 62 кГц, 'Timer 2' -- от 61 Гц до 62 кГц. PS: Всё сказанное для Arduino на базе ATmega328.
Зачем активировать прерывание, если нужно просто выдать сигнал нужной частоты и скважности? Выдачу сигнала определённой частоты можно сделать без всяких прерываний и влияния на выполнение основного кода.
Коллеги, спасибо. Вы помогли сформулировать задачу в голове, осталось только решить. Важно! нужен звук БииБоо (две тональности последовательно) общей продолжительностью 0,6 - 0,8 сек вот скелет тестового примера Код (C++): // LIBRARY #include <RFID.h> #include <SPI.h> // SETUP PINS RC522 #define RFID_SS 4 // slave select #define RFID_RST 3 // reset // SETUP PINS BUZZER #define BUZZER_PIN 2 // CREATE INSTANS RFID rfidReader = RFID(RFID_SS, RFID_RST); // GLOBAL VAR char rfidTagID[14]; // id RFID tag /////////////////////////////////////////////////////// void prepareRFID() { SPI.begin(); rfidReader.init(); } void setup() { prepareRFID(); pinMode(BUZZER_PIN, OUTPUT); } void loop() { if (rfidReader.isCard()) { beepOK(); // БииБоо однократно, когда считана карта и не дожидаясь // окончания сигнала продолжить выполнять какой то важный код } // ... какой то важный код ... } void beepOK() { // 1. тут нужно бикнуть Бии - 0,4сек // тут после того, как закончится п.1, нужно биккнуть Боо - 0,4сек // !!! и не тормозить основной loop() !!! } Важные моменты: 1. БииБоо делать однократно только после успешного чтения карты rfidReader.isCard(). Постоянно сигналить не нужно. 2. БииБоо не тормозит основной цикл 3. Хочется именно БииБоо а не просто Бии Продолжаю копать. Пока идея вырисовывается такая: Код (C++): void beepOK() { // 1. Установить таймер на вызыване прерывания каждые 0,4сек // 2. Если это первое срабатывание таймера, то заставить ШИМ генерить // на ножку 1000Гц (Бии). // ?? ШИМ же может генерить самостоятельно, по заданным параметрам ?? // 3. Если это второе срабатывание таймера, то заставить ШИМ генерить // на ножку 300Гц (Боо). // 4. Если это треье срабатывание таймера, то сбросить таймер }
Коллеги, спасибо всем за помощь. Толкнули в нужном направлении. БииБоо готово. Бибикает при поднесении карточки и не тормозит программу: Код (C++): // LIBRARY #include <RFID.h> #include <SPI.h> #include <TimerOne.h> // SETUP PINS RC522 #define RFID_SS 4 // slave select #define RFID_RST 2 // reset // SETUP PINS BUZZER #define BUZZER_PIN 3 // CREATE INSTANS RFID rfidReader = RFID(RFID_SS, RFID_RST); /////////////////////////////////////////////////////// void prepareRFID() { SPI.begin(); rfidReader.init(); } void setup() { prepareRFID(); pinMode(BUZZER_PIN, OUTPUT); } void loop() { if (rfidReader.isCard()) { beeBoo(); } // ... какой то важный код ... } void beeBoo() { Timer1.initialize(); Timer1.attachInterrupt(beepPWM,400000); } void beepPWM(){ static byte count = 0; switch(count){ case 0: TCCR2B = TCCR2B & 0b11111000 | 0x03; analogWrite(BUZZER_PIN,128); count++; break; case 1: TCCR2B = TCCR2B & 0b11111000 | 0x05; analogWrite(BUZZER_PIN,128); count++; break; default: Timer1.detachInterrupt(); digitalWrite(BUZZER_PIN,LOW); count = 0; } }
Добрый день! Сейчас в loop использую код, в котором через каждые 9 секунд, светодиод начинает моргать определенным образом в течении последующей 1 секунды: Код (C++): msErr = millis(); if((msErr - ms2Err) > 9000){ if((msErr - ms1Err) > 125 || msErr < ms1Err){ ms1Err = msErr; if(blink_mode_err & 1<<(blink_loop&0x07) ) mcp.digitalWrite(p_ledErr, HIGH); else mcp.digitalWrite(p_ledErr, LOW); blink_loop++; } if((msErr - ms2Err) > 10000) ms2Err = msErr; } Естественно, при увеличении кол-ва других операций в loop, стали вылазить проблемы. Пришлось обратиться к TimerOne.h Но не могу понять алгоритм действий. Ну вызываем мы прерывание через каждые 9 сек, а дальше то нужно выполнить не какую то быструю операцию, а посвятить целую секунду на работу со светодиодом, не нарушая в это время работу других операций в loop. Подскажите плз
А что там за операции, которые вам доставляют проблемы? Может их оптимизировать? Мигание у вас какое хитрое…
может. но меньше их не станет точно, и в любом случае не сегодня так завтра вопрос снова возникнет код из статьи https://habr.com/ru/post/357904/
И что же ты такое делать собираешься со светодиодом целую секунду? Тереть его как лампу Алладина? Как раз и нужно выполнить быструю операцию и завести таймер на время, когда потребуется новая быстрая операция. Хотя, если бы описал что тебе надо чуть подробнее, чем "мигать определённым образом", то было бы проще. А так, мог только сказать: чтобы светодиод мигал определённым образом, нужно определённым образом написать программу.
вот именно Сначала читаем невнимательно, а потом вопросы... Таймер в статье вызывается не на "1 секунду раз в 9 секунд" - а 8 раз в секунду. Прям ностальгией повеяло - я тоже мигал светиком по битовой матрице в своем самом первом проекте
а я и не говорил что это из статьи. из статьи я взял код управления светодиодом по битовой матрице. а интервал в 9 секунд - это уже по моей задаче нарисовалось сейчас это уже не модно?
нужен конечный автомат состояний. что-то подобное я описывал здесь http://forum.amperka.ru/threads/Пощебечем-Часть-i.19009/
так работает (светодиод моргает как указано): Код (C++): #include "TimerOne.h" #include "Wire.h" uint8_t blink_loop = 0; uint8_t blink_mode = 0; uint8_t modes_count = 0; // Callback функция по таймеру void timerIsr() { if( blink_mode & 1<<(blink_loop&0x07) ) digitalWrite(13, HIGH); else digitalWrite(13, LOW); blink_loop++; } void setup() { Serial.begin(9600); pinMode(13,OUTPUT); // blink_mode = 0B00001010; Timer1.initialize(125000); Timer1.attachInterrupt( timerIsr ); } void loop() { blink_mode = 0B00001111; //Мигание по 0.5 сек delay(5000); Serial.println("1"); blink_mode = 0B00000001; //Короткая вспышка раз в секунду delay(5000); Serial.println("2"); blink_mode = 0B00000101; //Две короткие вспышки раз в секунду delay(5000); Serial.println("3"); blink_mode = 0B00010101; //Три короткие вспышки раз в секунду delay(5000); Serial.println("4"); blink_mode = 0B01010101; //Частые короткие вспышки (4 раза в секунду) delay(5000); Serial.println("5"); } а так не работает (убрал только строчки Serial): Код (C++): #include "TimerOne.h" #include "Wire.h" uint8_t blink_loop = 0; uint8_t blink_mode = 0; uint8_t modes_count = 0; // Callback функция по таймеру void timerIsr() { if( blink_mode & 1<<(blink_loop&0x07) ) digitalWrite(13, HIGH); else digitalWrite(13, LOW); blink_loop++; } void setup() { Serial.begin(9600); pinMode(13,OUTPUT); // blink_mode = 0B00001010; Timer1.initialize(125000); Timer1.attachInterrupt( timerIsr ); } void loop() { blink_mode = 0B00001111; //Мигание по 0.5 сек delay(5000); // Serial.println("1"); blink_mode = 0B00000001; //Короткая вспышка раз в секунду delay(5000); // Serial.println("2"); blink_mode = 0B00000101; //Две короткие вспышки раз в секунду delay(5000); // Serial.println("3"); blink_mode = 0B00010101; //Три короткие вспышки раз в секунду delay(5000); // Serial.println("4"); blink_mode = 0B01010101; //Частые короткие вспышки (4 раза в секунду) delay(5000); // Serial.println("5"); } почему?
Потому, что неправильно написано. Переменная blink_mode используется в обработчике прерывания, а изменяется вне его. Она сама должна сообщать об этом компилятору? Так сама она этого не делает - это работа программиста. Исправь и проблема уйдёт. И ещё. Прямо к твоей проблеме не относится, но из общих соображений: переменная blink_loop используется только в функции timerIsr, так какого рожна она у тебя глобальна? Чтобы пудрить мозги любому, кто будет разбираться в коде? Если переменная используется только в одной функции - она должна быть локально ней объявлена. Если её при этом нужно сохранять значение, то объявляй статической. Ну, и ... "if( blink_mode &1<<(blink_loop&0x07))" формально то правильно, но ... имей совесть, тебе что скобок жалко?