"Виснет" выполнение loop, помогите найти косяк

Kyouma
Offline
Зарегистрирован: 12.09.2018

Скетч должен по сигналу с ИК-пульта плавно зажигать два светодиода, включать воспроизведение аудиозаписи на мини 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();
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Не очень ясно для чего Вы используете прерывание, но уж раз используете,то все переменные, которые там могут поменяться (ir_dt, ir_kod, >results, irrecv) должны быть объявлены как volatile. А там дальше видно будет

sadman41
Offline
Зарегистрирован: 19.10.2016

Тут вообще какое-то оливье вместо кода. И, мне кажется, что volatile не особо облегчит жись, потому что чтение длинных переменных неатомарно. Надо от идеи с прерыванием отказываться - будет проще жить. В данном случае тут стопроцентный race condition в процессе захвата сигнала рисуется. Я бы на месте МК такой алгоритм вообще не стал исполнять.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, идея прерывания, тут да - изначально хреновая, но уже если делать, то делать правильно. Атомарность можно будет специальными макросами добавить.

sadman41
Offline
Зарегистрирован: 19.10.2016

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

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;
  }

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

Green
Offline
Зарегистрирован: 01.10.2015

"Никому нельзя верить!"

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Green пишет:

"Никому нельзя верить!"

Эт тошна!

sadman41
Offline
Зарегистрирован: 19.10.2016

А как вам такое?

 unsigned long temp;
  temp = ((channel0 * b) - (channel1 * m));

  /* Do not allow negative lux value */
  if (temp < 0) temp = 0;

https://github.com/adafruit/Adafruit_TSL2561/blob/master/Adafruit_TSL256... -> #445...449

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

sadman41 пишет:

А как вам такое?

Ну, хуже-то не будет, так ведь?

Green
Offline
Зарегистрирован: 01.10.2015

"Береженого бог бережет".)

sadman41
Offline
Зарегистрирован: 19.10.2016

Я определенное время пытался понять основополагающую идею этой программной конструкции. 

С одной стороны, конечно, отрицательная сила света маловероятна, так как из показаний ADC Fullspectrum канала вычитаются показания ADC Infrared канала (получается Visible канал, т.е. результат ближе к человеческому зрению). Однако, если такое условие было введено, то, видимо, предполагалось (об этом далее), что IR*m все же может превышать FS*b. Но в этом случае отрицательное число , рассматриваемое как unsigned, дает результат чуть меньший, чем UINT32_MAX, что в последствии ведет к получению адского значения силы света в люксах. А уже эта ситуация вообще никак не обрабатывается...

Возникает вопрос - почему я считаю, что разница значений таки может быть отрицательной? Дело в том, что так считаю не я, а инженеры из TAOS, код которых был передран из даташита 2005-го года на TSL2560/2561 погроммистами Adafruit без видимых изменений.

Может быть более опытные программисты поделятся своим видением в отношении данного случая? Если время найдут.

В этом драйвере есть еще забавные проколы типа неправильного запроса регистра I2C, который возвращает похожий на правду результат, но во всех ли ревизиях сенсора это поведение сохраняется - большой вопрос.