Ошибки при чтении MIDI-порта
- Войдите на сайт для отправки комментариев
Источник сигнала: 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.
Кто-нибудь сталкивался с чем-нибудь подобным или у кого есть предположения, с чем это может быть связано?
Немного поэкспериментировал.
Наличие либо отсутствие диода, а также резистора в базе фототранзистора на наличие сбоев не влияют. Уменьшение резистора в коллекторе до 2к2 также не влияет.
Замена MIDI-кабеля тоже ни какому результату не привела.
Перестановка входа между Serial1 и Serial2 также не имела ощутимых результатов.
Как выяснилось, практически всегда сбой случается со вторым байтом последовательности. Возможно, с третьим. Но не было ни разу зафиксировано, чтобы с первым. Дело в том, что в MIDI байты идут "пачками" длиной от 1 до 3 байт. Так вот, первый байт из этой пачки всегда принимается правильно, второй и третий могут содержать ошибки либо вовсе быть пропущены. При этом ошибка проявляется, как правило, в том, что первые биты байта принимаются установленными в 1, тогда как, минимум, первый бит должен быть нулевой.
PS. Дополню, что и замена Ардуины на аналогичную ни на что не повлияла. (Увы, у меня все Меги от одного производителя)
PPS. Еше были заменены:
- все детали схемы, включая монтажною плату и подводящие провода, фильтрующие конденсаторы питания, диод, все резисторы и, разумеется, оптопара,
- питание MIDI-клавиатуры: сначала на другой 9-вольтовый блок питания, а потом - на питание от батарей,
- питание Ардуины (а вместе с ней MIDI-порта): от внешнего питания (до этого питалась от USB).
Результата нет.
Вот ведь, интересно: на любой дурацкий вопрос типа "как объединить два скетча" или "памагите навичку" куча ответов, а тут - молчок. :(
Проверяем дальше.
Обозначения:
In1 - входной MIDI порт разрабатыватываемого устройcтва,
In2 - входной порт аппаратного MIDI-синтезатора платы Creative SB Live, устанговленной в ПК,
Out1 - выходной MIDI порт клавиатуры Roland PC-200mkII,
Out2 - выходной MIDI порт платы Creative SB Live, устанговленной в ПК.
Out1+In1 - наблюдаются проблемы в виде неправильно распознанных байтов, проявляются в виде пропуска звуков,
Out1+In2 - проблем не обнаружено,
Out2+In1- проблем не обнаружено.
Куда копать?
Экспериментально обнаружил, что при установке скорости MIDI порта 31500 вместо положенных 31250 проблемы исчезают: входной MIDI порт теперь уверенно без ошибок читает данные как с MIDI-клавиатуры, так и с MIDI-выхода ПК (воспроизведение MIDI-файлов).
Теперь хотелось бы все это осмыслить.
PS. На всякий случай упомяну, что перед этим так же безрезультатно, как и раньше, MIDI-кабель был еще раз заменен - на этот раз на фирменный, а также между выходом фототранзистора и входом Ардуино включался триггер Шмитта, точнее, 2 штуки последовательно, в том числе с интегрирующей цепью между ними (постоянная времени порядка 200 кГц).