"Виснет" выполнение 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 и внезапно увидел такое:
/* Make sure we're actually connected */ uint8_t x = read8(TSL2561_REGISTER_ID); if (x & 0xF0 != 0x10) { // ID code for TSL2561 return false; }Долго всматривался, даже в справочник полез для проверки приоритета операций. Вот не ожидал я от адафрута такого )) Хотя оно, конечно, работает, но исключительно по счастливому стечению обстоятельств.
"Никому нельзя верить!"
"Никому нельзя верить!"
Эт тошна!
А как вам такое?
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, который возвращает похожий на правду результат, но во всех ли ревизиях сенсора это поведение сохраняется - большой вопрос.