Ошибки при чтении 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 кГц).