Ошибки при чтении MIDI-порта

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Источник сигнала: MIDI-клавиатура Roland PC-200mkII

Приемник сигнала: Arduino Mega 2560 (китайский аналог)

Схема включения:

Инициализация и чтение:

void setup() {
...
  Serial2.begin(31250);
}

void loop() {
  CheckMIDI();
...
}

//////////////////////////////////////////////////////////////////////
//  функция разбора MIDI сообщений
byte comm[3]; // буфер для команды с параметрами
byte index;   // насколько заполнен буфер

void CheckMIDI() { // взаимодействие с MIDI-портом и выдача исполнительных команд после завершения приема сообщения
  static int counter_n = 0;
  byte b = 0;
  if (Serial2.available()) {
    counter_n++;
    b = Serial2.read();
    if(b & 0x80) {     // команда
      comm[0] = b;
      index = 1;
    } else {           // данные
      if(index <=2) {
        comm[index] = b;
        index++;
      } else {
        Serial.print("   !!! ERROR : command > 3 bytes, Command: ");
        Serial.println(b, HEX);
      }
    }
// анализ - только если что-то поступило новое
    if((comm[0] & 0xF0) < 0xF0) { // channel messages
      if(index == 0) return; // все канальные команды сопровождаются данными
      b = comm[0] & 0xF0;  // Выделяем команду
      if(b == 0x90){                    // 9x command : Note On
        if(index == 3) { // конец команды
//          Serial.print("MIDI: Note On ");
#ifdef DEBUG_1
          Serial.print(comm[1]);
          Serial.print(", ");
          Serial.println(comm[2]);
#endif            
          if(comm[2] == 0) {
            if(twoVoices)
              two_NoteOff(comm[1]);
            else
              one_NoteOff(comm[1]);
          } else {
            if(twoVoices)
              two_NoteOn(comm[1], comm[2]);
            else
              one_NoteOn(comm[1], comm[2]);
          }
          index = 1;
        }
        return;
      } else if(b == 0x80){             // 8x command : Note Off
        if(index == 3) { // конец команды
//          Serial.print("MIDI: Note Off ");
#ifdef DEBUG_1
          Serial.print(comm[1]);
          Serial.print(", ");
          Serial.println(comm[2]);
#endif            
          if(twoVoices)
            two_NoteOff(comm[1]);
          else
            one_NoteOff(comm[1]);
          index = 1;
        }
        return;
      } else if(b == 0xA0){             // Ax command : Key after-touch
        if(index == 3) { // конец команды
          ;
          index = 1;
        }
        return;
      } else if(b == 0xB0){             // Bx command : Control change
        if(index == 3) { // конец команды
          ;
          index = 1;
        }
        return;
      } else if(b == 0xC0){             // Cx command : Programm change
        if(index == 2) { // конец команды
          ;
          index = 1;
        }
        return;
      } else if(b == 0xD0){             // Dx command : Channel after-touch
        if(index == 2) { // конец команды
          ;
          index = 1;
        }
        return;
      } else if(b == 0xE0){             // Ex command : Pitch wheel change
        if(index == 3) { // конец команды
          ;
          index = 1;
        }
        return;
      } else {
        Serial.print("   !!! ERROR : strange command : ");
        Serial.println(b, HEX);
        return;
      }
    } else {                      // system messages
      switch (b) {
        case 0xFE : {
          return;
        }
        case 0xFF : {
          Serial.print("   !!! ERROR : command FF : ");
          Serial.println(b, HEX);
          return;
        }
        default : {
          Serial.print(counter_n);
          Serial.print("   !!! ERROR : unsupported command : ");
          Serial.print(b, HEX);
          Serial.print(", index: ");
          Serial.print(index);
          Serial.print(", data: ");
          Serial.println(comm[index]);
          return;
        }
      }
    }
  }
}

Т.е. читается по 1 байту за одно обращение к CheckMIDI(), а когда приняты все параметры команды, наступает реакция.

Проблема в том, что время от времени (один раз на несколько десятков) получается неверный код. По крайней мере, код команды. Но, возможно, искажаются также и данные - но это труднее проверить.

Основная используемая команда 0x94 - клавиатура в настоящее время настроена на выдачу по 4 каналу.

Время от времени проскакивают левые команды типа 0xF4 или 0xFA.

Кто-нибудь сталкивался с чем-нибудь подобным или у кого есть предположения, с чем это может быть связано?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Немного поэкспериментировал.

Наличие либо отсутствие диода, а также резистора в базе фототранзистора на наличие сбоев не влияют. Уменьшение резистора в коллекторе до 2к2 также не влияет.

Замена MIDI-кабеля тоже ни какому результату не привела.

Перестановка входа между Serial1 и Serial2 также не имела ощутимых результатов.

Как выяснилось, практически всегда сбой случается со вторым байтом последовательности. Возможно, с третьим. Но не было ни разу зафиксировано, чтобы с первым. Дело в том, что в MIDI байты идут "пачками" длиной от 1 до 3 байт. Так вот, первый байт из этой пачки всегда принимается правильно, второй и третий могут содержать ошибки либо вовсе быть пропущены. При этом ошибка проявляется, как правило, в том, что первые биты байта принимаются установленными в 1, тогда как, минимум, первый бит должен быть нулевой.

 

PS. Дополню, что и замена Ардуины на аналогичную ни на что не повлияла. (Увы, у меня все Меги от одного производителя)

 

PPS. Еше были заменены:

- все детали схемы, включая монтажною плату и подводящие провода, фильтрующие конденсаторы питания, диод, все резисторы и, разумеется, оптопара,

- питание MIDI-клавиатуры: сначала на другой 9-вольтовый блок питания, а потом - на питание от батарей,

- питание Ардуины (а вместе с ней MIDI-порта): от внешнего питания (до этого питалась от USB).

Результата нет.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Вот ведь, интересно: на любой дурацкий вопрос типа "как объединить два скетча" или "памагите навичку" куча ответов, а тут - молчок. :(

Проверяем дальше.

 

Обозначения:

In1 - входной MIDI порт разрабатыватываемого устройcтва,

In2 - входной порт аппаратного MIDI-синтезатора платы Creative SB Live, устанговленной в ПК,

Out1 - выходной MIDI порт клавиатуры Roland PC-200mkII,

Out2 - выходной MIDI порт платы Creative SB Live, устанговленной в ПК.

 

Out1+In1 - наблюдаются проблемы в виде неправильно распознанных байтов, проявляются в виде пропуска звуков,

Out1+In2 - проблем не обнаружено,

Out2+In1- проблем не обнаружено.

 

Куда копать?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Экспериментально обнаружил, что при установке скорости MIDI порта 31500 вместо положенных 31250 проблемы исчезают: входной MIDI порт теперь уверенно без ошибок читает данные как с MIDI-клавиатуры, так и с MIDI-выхода ПК (воспроизведение MIDI-файлов).

Теперь хотелось бы все это осмыслить.

PS. На всякий случай упомяну, что перед этим так же безрезультатно, как и раньше, MIDI-кабель был еще раз заменен - на этот раз на фирменный, а также между выходом фототранзистора и входом Ардуино включался триггер Шмитта, точнее, 2 штуки последовательно, в том числе с интегрирующей цепью между ними (постоянная времени порядка 200 кГц).