Официальный сайт компании Arduino по адресу arduino.cc
"Виснет" выполнение loop, помогите найти косяк
- Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии
Скетч должен по сигналу с ИК-пульта плавно зажигать два светодиода, включать воспроизведение аудиозаписи на мини mp3 плеере(при начале воспроизведения включается реле усилителя, по окончании - выключается), в определенный момент воспроизведения включать реле дым машины на короткий промежуток времени, по окончании воспризведения и выключении реле дым машины - плавно тушить светодиоды.
На ИК пульте кроме кнопок №1 и №2 включения определенной аудиозаписи, используются кнопки регулировки громкости, кнопка эквалайзера, и кнопка для сброса плеера (reset).
Проблема заключается в том, что после того как я решил сделать управления с ИК пульта через прерывания - после приема какого-то количества сигналов с пульта основной цикл loop перестает выполняться (вставлял маркеры для проверки), хотя сигналы с пульта ардуина принимает (определил по блинку). Плюс к этому - сигналы с пульта искажаются, смотрел по последовательному порту - идет какой-то мусор, не пойму откуда он. Прошу помощи, ибо я новичок, возможно что-то где-то сделал не так.
#include "Arduino.h" #include "SoftwareSerial.h" #include "DFRobotDFPlayerMini.h" #include <IRremote.h> #define Bitrate 4500 #define Speed 1 IRrecv irrecv(3); // вход ИК-приемника unsigned long ir_dt, ir_kod; decode_results results; SoftwareSerial mySoftwareSerial(4, 11); //RX, TX связь с mp3 плеером DFRobotDFPlayerMini myDFPlayer; const byte led1 = 9; const byte led2 = 10; const byte rele1 = 5; //реле дым машины const byte rele4 = 8; //включение-выключение усилителя const int interva1 = 3600; //задержка включения, интервал между включениями дыма const int interva2 = 500; //время удержания во включенном состоянии дым-машины unsigned long previousMillis = 0; unsigned long previousMillis1 = 0; bool releSrab = false; bool finishPlay = false; bool zaderjka = false; bool mp3_flag = true; uint8_t eq; void setup() { mySoftwareSerial.begin(9600); Serial.begin(115200); irrecv.enableIRIn(); // включить ИК-приемник Serial.println(); Serial.println(F("DFRobot DFPlayer Mini Demo")); Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)")); while (!myDFPlayer.begin(mySoftwareSerial)) { //Use softwareSerial to communicate with mp3. Serial.println(F("Unable to begin:")); Serial.println(F("1.Please recheck the connection!")); Serial.println(F("2.Please insert the SD card!")); myDFPlayer.reset(); mySoftwareSerial.begin(9600); } Serial.println(F("DFPlayer Mini online.")); myDFPlayer.volume(30); //Set volume value. From 0 to 30 myDFPlayer.EQ(DFPLAYER_EQ_BASS); digitalWrite (rele1, HIGH); digitalWrite (rele4, HIGH); pinMode (3, INPUT); // ИК приемник pinMode (9, OUTPUT); //светодиод pinMode (10, OUTPUT); //светодиод pinMode (5, OUTPUT); //реле дым машины pinMode (8, OUTPUT); //реле усилителя pinMode (13, OUTPUT); //сигнализация приема сигнала с пульта ДУ attachInterrupt(1,get_ir_kod,FALLING); // назначим прерывание } void loop() { Serial.println(ir_kod, HEX); if (ir_kod > 0) { //прием данных с пульта ДУ switch (ir_kod) { case 0xFF30CF: //кнопка "1" нажата if (mp3_flag == true) { led_ON(); digitalWrite (rele4, LOW); //включение усилителя myDFPlayer.play(1); delay (100); mp3_flag = false; zaderjka = true; } break; case 0xFF18E7: //кнопка "2" нажата if (mp3_flag == true) { led_ON(); digitalWrite (rele4, LOW); //включение усилителя myDFPlayer.play(2); delay (100); mp3_flag = false; zaderjka = true; } break; case 0xFFE01F: //кнопка звук- нажата myDFPlayer.volumeDown(); delay (100); break; case 0xFFA857: //кнопка звук+ нажата myDFPlayer.volumeUp(); delay (100); break; case 0xFF906F: //кнопка "EQ" нажата myDFPlayer.readEQ(); delay (100); eq = myDFPlayer.read()+1; //переключение эквалайзера на следующий режим myDFPlayer.EQ(eq); delay (100); break; case 0xFFC23D: //кнопка play/pause нажата myDFPlayer.reset(); //перезагрузка плеера delay (100); myDFPlayer.volume(30); delay (100); break; default: break; } ir_kod = 0; irrecv.resume(); } unsigned long currentMillis = millis(); if (myDFPlayer.readState() == 0x201 && currentMillis - previousMillis >= interva1) { //плеер играет, задержка включения реле дыма (чтобы под конец песни) if (zaderjka == true) { previousMillis = currentMillis; zaderjka = false; } else { digitalWrite (rele1, LOW); releSrab = true; previousMillis = currentMillis; } } if (releSrab == true && currentMillis - previousMillis >= interva2) { digitalWrite (rele1, HIGH); previousMillis = currentMillis; releSrab = false; } if(myDFPlayer.readState() == 0x0200 && mp3_flag == false) { //плеер закончил воспроизведение трека. digitalWrite (rele4, HIGH); mp3_flag = true; finishPlay = true; myDFPlayer.stop(); } if (finishPlay == true && releSrab == false) { //плеер закончил воспроизведение трека и реле дым машины выключено. сделал подобную конструкцию ибо плеер в последовательный порт как-то перестал передавать что он окончил воспроизведение трека, вместо этого пишет о том что он в режиме "стоп" как при начальной загрузке led_OFF(); } } void led_ON() { for(uint16_t i = 0; i<Bitrate; i+=Speed) { uint16_t del = pow(i,2) / Bitrate; PORTB |= (1 << PB1)|(1 << PB2); //включение 9й и 10й ноги delayMicroseconds(del); PORTB &= (~((1 << PB1)|(1 << PB2))); //выключение 9й и 10й ноги delayMicroseconds(Bitrate - del); } PORTB |= (1 << PB1)|(1 << PB2); } void led_OFF() { for(uint16_t i = Bitrate; i>Speed; i-=Speed) { uint16_t del = pow(i,2) / Bitrate; PORTB |= (1 << PB1)|(1 << PB2); //включение 9й и 10й ноги delayMicroseconds(del); PORTB &= (~((1 << PB1)|(1 << PB2))); //выключение 9й и 10й ноги delayMicroseconds(Bitrate - del); } PORTB &= (~((1 << PB1)|(1 << PB2))); } void get_ir_kod() { // получить код, переданный с ИК-пульта cli(); if (irrecv.decode(&results)) { irrecv.blink13(1); if (results.value > 0 && results.value != REPEAT) { ir_dt = results.value; ir_kod = ir_dt; } else if (results.value == REPEAT){ ir_kod = ir_dt; } } sei(); }
Не очень ясно для чего Вы используете прерывание, но уж раз используете,то все переменные, которые там могут поменяться (ir_dt, ir_kod, >results, irrecv) должны быть объявлены как volatile. А там дальше видно будет
Тут вообще какое-то оливье вместо кода. И, мне кажется, что volatile не особо облегчит жись, потому что чтение длинных переменных неатомарно. Надо от идеи с прерыванием отказываться - будет проще жить. В данном случае тут стопроцентный race condition в процессе захвата сигнала рисуется. Я бы на месте МК такой алгоритм вообще не стал исполнять.
Ну, идея прерывания, тут да - изначально хреновая, но уже если делать, то делать правильно. Атомарность можно будет специальными макросами добавить.
Пока не вижу здесь правильного пути при сохранении обработчика прерывания даже с привнесенной атомарностью.
IRremote захватывает пакеты по таймеру. Пока МК за непонятным хе... висит в обработчике внешнего прерывания - часть импульсов будет пропущена. А это сводит на "нет" весь охренительный замысел по перехвату сигнала без потерь путем дерганья за МК ногу. Т.е. в данном случае нужно от этой либы отказаться, а делать другую - interrupt driven. Ну или не страдать фигней, а как все проверять в лупе что дает decode().
Кстати, к вопросу о бессмысленности ардуинного бытия. Ковырял я сегодня библиотеку от Adafruit и внезапно увидел такое:
Долго всматривался, даже в справочник полез для проверки приоритета операций. Вот не ожидал я от адафрута такого )) Хотя оно, конечно, работает, но исключительно по счастливому стечению обстоятельств.
"Никому нельзя верить!"
"Никому нельзя верить!"
Эт тошна!
А как вам такое?
https://github.com/adafruit/Adafruit_TSL2561/blob/master/Adafruit_TSL256... -> #445...449
А как вам такое?
Ну, хуже-то не будет, так ведь?
"Береженого бог бережет".)
Я определенное время пытался понять основополагающую идею этой программной конструкции.
С одной стороны, конечно, отрицательная сила света маловероятна, так как из показаний ADC Fullspectrum канала вычитаются показания ADC Infrared канала (получается Visible канал, т.е. результат ближе к человеческому зрению). Однако, если такое условие было введено, то, видимо, предполагалось (об этом далее), что IR*m все же может превышать FS*b. Но в этом случае отрицательное число , рассматриваемое как unsigned, дает результат чуть меньший, чем UINT32_MAX, что в последствии ведет к получению адского значения силы света в люксах. А уже эта ситуация вообще никак не обрабатывается...
Возникает вопрос - почему я считаю, что разница значений таки может быть отрицательной? Дело в том, что так считаю не я, а инженеры из TAOS, код которых был передран из даташита 2005-го года на TSL2560/2561 погроммистами Adafruit без видимых изменений.
Может быть более опытные программисты поделятся своим видением в отношении данного случая? Если время найдут.
В этом драйвере есть еще забавные проколы типа неправильного запроса регистра I2C, который возвращает похожий на правду результат, но во всех ли ревизиях сенсора это поведение сохраняется - большой вопрос.