Ошибки при чтении MIDI-порта
- Войдите на сайт для отправки комментариев
Источник сигнала: MIDI-клавиатура Roland PC-200mkII
Приемник сигнала: Arduino Mega 2560 (китайский аналог)
Схема включения:
Инициализация и чтение:
001 | void setup () { |
002 | ... |
003 | Serial2.begin(31250); |
004 | } |
005 |
006 | void loop () { |
007 | CheckMIDI(); |
008 | ... |
009 | } |
010 |
011 | ////////////////////////////////////////////////////////////////////// |
012 | // функция разбора MIDI сообщений |
013 | byte comm[3]; // буфер для команды с параметрами |
014 | byte index; // насколько заполнен буфер |
015 |
016 | void CheckMIDI() { // взаимодействие с MIDI-портом и выдача исполнительных команд после завершения приема сообщения |
017 | static int counter_n = 0; |
018 | byte b = 0; |
019 | if (Serial2.available()) { |
020 | counter_n++; |
021 | b = Serial2.read(); |
022 | if (b & 0x80) { // команда |
023 | comm[0] = b; |
024 | index = 1; |
025 | } else { // данные |
026 | if (index <=2) { |
027 | comm[index] = b; |
028 | index++; |
029 | } else { |
030 | Serial .print( " !!! ERROR : command > 3 bytes, Command: " ); |
031 | Serial .println(b, HEX); |
032 | } |
033 | } |
034 | // анализ - только если что-то поступило новое |
035 | if ((comm[0] & 0xF0) < 0xF0) { // channel messages |
036 | if (index == 0) return ; // все канальные команды сопровождаются данными |
037 | b = comm[0] & 0xF0; // Выделяем команду |
038 | if (b == 0x90){ // 9x command : Note On |
039 | if (index == 3) { // конец команды |
040 | // Serial.print("MIDI: Note On "); |
041 | #ifdef DEBUG_1 |
042 | Serial .print(comm[1]); |
043 | Serial .print( ", " ); |
044 | Serial .println(comm[2]); |
045 | #endif |
046 | if (comm[2] == 0) { |
047 | if (twoVoices) |
048 | two_NoteOff(comm[1]); |
049 | else |
050 | one_NoteOff(comm[1]); |
051 | } else { |
052 | if (twoVoices) |
053 | two_NoteOn(comm[1], comm[2]); |
054 | else |
055 | one_NoteOn(comm[1], comm[2]); |
056 | } |
057 | index = 1; |
058 | } |
059 | return ; |
060 | } else if (b == 0x80){ // 8x command : Note Off |
061 | if (index == 3) { // конец команды |
062 | // Serial.print("MIDI: Note Off "); |
063 | #ifdef DEBUG_1 |
064 | Serial .print(comm[1]); |
065 | Serial .print( ", " ); |
066 | Serial .println(comm[2]); |
067 | #endif |
068 | if (twoVoices) |
069 | two_NoteOff(comm[1]); |
070 | else |
071 | one_NoteOff(comm[1]); |
072 | index = 1; |
073 | } |
074 | return ; |
075 | } else if (b == 0xA0){ // Ax command : Key after-touch |
076 | if (index == 3) { // конец команды |
077 | ; |
078 | index = 1; |
079 | } |
080 | return ; |
081 | } else if (b == 0xB0){ // Bx command : Control change |
082 | if (index == 3) { // конец команды |
083 | ; |
084 | index = 1; |
085 | } |
086 | return ; |
087 | } else if (b == 0xC0){ // Cx command : Programm change |
088 | if (index == 2) { // конец команды |
089 | ; |
090 | index = 1; |
091 | } |
092 | return ; |
093 | } else if (b == 0xD0){ // Dx command : Channel after-touch |
094 | if (index == 2) { // конец команды |
095 | ; |
096 | index = 1; |
097 | } |
098 | return ; |
099 | } else if (b == 0xE0){ // Ex command : Pitch wheel change |
100 | if (index == 3) { // конец команды |
101 | ; |
102 | index = 1; |
103 | } |
104 | return ; |
105 | } else { |
106 | Serial .print( " !!! ERROR : strange command : " ); |
107 | Serial .println(b, HEX); |
108 | return ; |
109 | } |
110 | } else { // system messages |
111 | switch (b) { |
112 | case 0xFE : { |
113 | return ; |
114 | } |
115 | case 0xFF : { |
116 | Serial .print( " !!! ERROR : command FF : " ); |
117 | Serial .println(b, HEX); |
118 | return ; |
119 | } |
120 | default : { |
121 | Serial .print(counter_n); |
122 | Serial .print( " !!! ERROR : unsupported command : " ); |
123 | Serial .print(b, HEX); |
124 | Serial .print( ", index: " ); |
125 | Serial .print(index); |
126 | Serial .print( ", data: " ); |
127 | Serial .println(comm[index]); |
128 | return ; |
129 | } |
130 | } |
131 | } |
132 | } |
133 | } |
Т.е. читается по 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 кГц).