Как подключить 2 мультиплексора?
- Войдите на сайт для отправки комментариев
Вс, 21/02/2016 - 11:21
Здравтсвуйте, собираю MIDI контроллер, но встрял в тупик по программе...планирую использовать 2 мультиплексора СD4051 на пинах A0 и A1. Железо 100 процентов работает, так как проверял по одному каналу, менял в рабочем скетче A0 на A1. взгляните пожалуйста опытным глазом, исходный скетч на один мультиплексор и тот что я пытался переделать на два.
#include <TimerOne.h> #define DEBUG // MIDI карта взята с http://www.nortonmusic.com/midi_cc.html #define MIDI_CC_MODULATION 0x01 #define MIDI_CC_BREATH 0x02 #define MIDI_CC_VOLUME 0x07 #define MIDI_CC_BALANCE 0x08 #define MIDI_CC_PAN 0x0A #define MIDI_CC_EXPRESSION 0x0B #define MIDI_CC_EFFECT1 0x0C #define MIDI_CC_EFFECT2 0x0D #define MIDI_CC_GENERAL1 0x0E #define MIDI_CC_GENERAL2 0x0F #define MIDI_CC_GENERAL3 0x10 #define MIDI_CC_GENERAL4 0x11 #define MIDI_CC_GENERAL5 0x12 #define MIDI_CC_GENERAL6 0x13 #define MIDI_CC_GENERAL7 0x14 #define MIDI_CC_GENERAL8 0x15 #define MIDI_CC_GENERAL9 0x16 #define MIDI_CC_GENERAL10 0x17 #define MIDI_CC_GENERAL11 0x18 #define MIDI_CC_GENERAL12 0x19 #define MIDI_CC_GENERAL13 0x1A #define MIDI_CC_GENERAL14 0x1B #define MIDI_CC_GENERAL15 0x1C #define MIDI_CC_GENERAL16 0x1D #define MIDI_CC_GENERAL17 0x1E #define MIDI_CC_GENERAL18 0x1F #define MIDI_CC_GENERAL1_FINE 0x2E #define MIDI_CC_GENERAL2_FINE 0x2F #define MIDI_CC_GENERAL3_FINE 0x30 #define MIDI_CC_GENERAL4_FINE 0x31 #define MIDI_CC_GENERAL5_FINE 0x32 #define MIDI_CC_GENERAL6_FINE 0x33 #define MIDI_CC_GENERAL7_FINE 0x34 #define MIDI_CC_GENERAL8_FINE 0x35 #define MIDI_CC_GENERAL9_FINE 0x36 #define MIDI_CC_GENERAL10_FINE 0x37 #define MIDI_CC_GENERAL11_FINE 0x38 #define MIDI_CC_GENERAL12_FINE 0x39 #define MIDI_CC_GENERAL13_FINE 0x3A #define MIDI_CC_GENERAL14_FINE 0x3B #define MIDI_CC_GENERAL15_FINE 0x3C #define MIDI_CC_GENERAL16_FINE 0x3D #define MIDI_CC_GENERAL17_FINE 0x3E #define MIDI_CC_GENERAL18_FINE 0x3F #define MIDI_CC_SUSTAIN 0x40 #define MIDI_CC_REVERB 0x5B #define MIDI_CC_CHORUS 0x5D #define MIDI_CC_CONTROL_OFF 0x79 #define MIDI_CC_NOTES_OFF 0x78 // Функция контроля дребезга контактов кнопки. // Подробнее про DEBOUNCE http://arduino.cc/en/Tutorial/Debounce #define DEBOUNCE // время debounce в миллисекундах #define DEBOUNCE_LENGTH 5 // Фильтрация аналоговых значений #define ANALOGUE_FILTER // Что бы движение потенциометра было распознано как ввод, текущее аналоговое значение должно превысить это. #define FILTER_AMOUNT 5 // Таймаут в микросекундах #define ANALOGUE_INPUT_CHANGE_TIMEOUT 1000000 // Число цифровых входов. Может быть от 0 до 18. #define NUM_DI 9 // Число аналоговых входов от 0 до 6. //Закоментированно из-за мультиплексирования аналогового pin0. //#define NUM_AI 6 //Массив хранящий карту цифровых пинов в индекс канала. Размер этого массива должен соответствовать NUM_DI выше. byte digitalInputMapping[NUM_DI] = { 5, 6, 7, 8, 9, 10, 11, 12, 13 }; //Массив хранящий карту аналоговых пинов в индекс канала. Размер этого массива должен соответствовать NUM_AI выше. //Line commented out because of the use multeplexing to the analogue pin0 //Строка закоментирована из-за мультиплексирования только одного A0 //byte analogueInputMapping[NUM_AI] = { A0, A1, A2, A3, A4, A5 }; // Содержит текущее состояние цифровых входов. byte digitalInputs[NUM_DI]; // Содержит текущее состояние аналоговых входов. byte analogueInputs[8]; //Переменая для проведения опроса цифровых пинов, используя функцию DEBOUNCE byte tempDigitalInput; //Переменная для проведения опроса аналоговых пинов, используя функцию аналоговой фильтрации byte tempAnalogueInput; int i = 0; // Переменная нужна что бы провести различие между текущими и новыми значениями аналоговых входов. int analogueDiff = 0; // Флаг изменения на аналоговом пине. boolean analogueInputChanging; int r0 = 0; //значения адреса аналогового входа мультиплексора 4051 (s0) int r1 = 0; //значения адреса аналогового входа мультиплексора 4051 (s1) int r2 = 0; //значения адреса аналогового входа мультиплексора 4051 (s2) void setup() { // Включить скорость последовательного ввода / вывода на скорости 57600 кбит. Это быстрее, чем по стандартной скорости MIDI 31250 кбит. // The PC application which we connect to will automatically take the higher sample rate and send MIDI // messages out at the correct rate. We only send things faster in case there is any latency. // Приложение PC, к которому мы подключаемся автоматически сделает высокую частоту дискретизации и отправит MIDI // Сообщения с правильной скоростью. Мы посылаем только данные быстрее в случае какой-либо задержки. Serial.begin(57600); pinMode(2, OUTPUT); // s0 4051 pinMode(3, OUTPUT); // s1 4051 pinMode(4, OUTPUT); // s2 4051 // Инициализтруем каждый цифровой пин из массива выше, как вход. for (i = 0; i < NUM_DI; i++) { pinMode(digitalInputMapping[i], INPUT); // Don't enable pullup resistor on pin 13, as the LED and resistor will always pull it low, meaning the input won't work. // Instead an external pulldown resistor must be used on pin 13. // инвертируем значения HIGH\LOW на 13 входе изза подключенного к нему резистора со светодиодом. if (digitalInputMapping[i] != 13) { // Включить нагрузочный резистор. Этот вызов должен прийти после вышеуказанного вызова pinMode. digitalWrite(digitalInputMapping[i], HIGH); } // Инициализируем чтение состояния цифровых входов. digitalInputs[i] = digitalRead(digitalInputMapping[i]); } // Инициализируем как вход нужные аналоговые пины. pinMode(0, INPUT); for (i = 0; i <=7; i++) { // управление мультиплексором r0 = i & 0x01; r1 = (i>>1) & 0x01; r2 = (i>>2) & 0x01; digitalWrite(2, r0); digitalWrite(3, r1); digitalWrite(4, r2); // Инициализация чтения с аналоговых входов analogueInputs[i] = analogRead(0)/8; } // Предположим нет активности на аналоговых входах analogueInputChanging = false; // таймер запускается каждую секунду Timer1.initialize(ANALOGUE_INPUT_CHANGE_TIMEOUT); // По истечению времени вызывается функция Timer1.attachInterrupt(analogueInputStopped); // Запускается таймер Timer1.start(); } void loop() { for (i = 0; i < NUM_DI; i++) { // Считываем текущее состояние цифровых пинов и временно храним их. tempDigitalInput = digitalRead(digitalInputMapping[i]); // Проверяем, если последнее состояние отличается от текущего состояния. if (digitalInputs[i] != tempDigitalInput) { #ifdef DEBOUNCE // Ждем в течении короткого промежутка времени и снова считываем значение пина. delay(DEBOUNCE_LENGTH); // Если второе чтение является таким же, как начального чтения, предположим, что это правда. if (tempDigitalInput == digitalRead(digitalInputMapping[i])) { #endif // Запишем новое значение цифрового пина. digitalInputs[i] = tempDigitalInput; // переход из HIGH в LOW (кнопка нажата) if (digitalInputs[i] == 0) { // Все цифровые входы используют подтянуть резисторов, за исключением контактный 13 так логика переворачивается if (digitalInputMapping[i] != 13) { noteOn(0, 0x00 + i, 0x7F); // Channel 1, middle C, maximum velocity } else { noteOff(0, 0x00 + i); // Channel 1, middle C } } // Переход из LOW в HIGH (кнопка отпущена) else { // Все цифровые входы используют подтянуть резисторов, за исключением контактный 13 так логика переворачивается if (digitalInputMapping[i] != 13) { noteOff(0, 0x00 + i); // Channel 1, middle C } else { noteOn(0, 0x00 + i, 0x7F); // Channel 1, middle C, maximum velocity } } #ifdef DEBOUNCE } #endif } } for (i = 0; i <=7; i++) { // Управление мультиплексором r0 = i & 0x01; r1 = (i>>1) & 0x01; r2 = (i>>2) & 0x01; digitalWrite(2, r0); digitalWrite(3, r1); digitalWrite(4, r2); // Считываем аналоговый pin 0, делим получившееся значение на 8 чтобы 10-битное значение АЦП (0-1023) перевести в 7-битное значение MIDI (0-127). tempAnalogueInput = analogRead(0) / 8; #ifdef ANALOGUE_FILTER // Возьмем абсолютное значение разности между текущим и новым значениями analogueDiff = abs(tempAnalogueInput - analogueInputs[i]); // Продолжаем только если порог был превышен или вход уже меняется if ((analogueDiff > 0 && analogueInputChanging == true) || analogueDiff >= FILTER_AMOUNT) { #else if (analogueInputs[i] != tempAnalogueInput) { #endif // Если изменений не было, снова запускаем таймер if (analogueInputChanging == false) { Timer1.start(); } // Если аналоговое значение изменилось таймер перезапускается. Только перезапустить его, если мы уверены, что вход не 'между' значение // т.е.. Это переехал более FILTER_AMOUNT else if (analogueDiff >= FILTER_AMOUNT) { Timer1.restart(); } // Аналоговое значение изменяется analogueInputChanging = true; // Записываем новое аналоговое значение analogueInputs[i] = tempAnalogueInput; // Отправить аналоговое значение на общем MIDI CC (см определения на начало этого файла) controlChange(0, MIDI_CC_GENERAL1 + i, analogueInputs[i]); } } } // Передача Note On в MIDI сообщении void noteOn(int channel, int pitch, int velocity) { // 0x90 это первый из 16 note on каналов channel += 0x90; // Убедимся что мы между 1 и 16 каналами для note on сообщения if (channel >= 0x90 && channel <= 0x9F) { #ifdef DEBUG Serial.print("Button pressed: "); Serial.println(pitch); #else Serial.print(channel); Serial.print(pitch); Serial.print(velocity); #endif } } // Передаем MIDI note off сообщение void noteOff(int channel, int pitch) { // 0x80 первый из 16 note off каналов channel += 0x80; // убеждаемся что мы междц 1 и 16 каналами для note off сообщения if (channel >= 0x80 && channel <= 0x8F) { #ifdef DEBUG Serial.print("Button released: "); Serial.println(pitch); #else Serial.print(channel); Serial.print(pitch); Serial.print(0x00); #endif } } // Отправить сообщение об изменении управления MIDI void controlChange(int channel, int control, int value) { // 0xB0 первый из 16 каналов control change (СС) channel += 0xB0; // Убеждаемся что мы между 1 и 16 каналами для CC сообщения if (channel >= 0xB0 && channel <= 0xBF) { #ifdef DEBUG Serial.print(control - MIDI_CC_GENERAL1); Serial.print(": "); Serial.println(value); #else Serial.print(channel); Serial.print(control); Serial.print(value); #endif } } // Таймер истек void analogueInputStopped() { // Остановить таймер, чтобы он не раз вызывать эту функцию. Timer1.stop(); // когда аналоговый вход перестанет меняться analogueInputChanging = false; }
#include <TimerOne.h> //#define DEBUG // MIDI карта взята с http://www.nortonmusic.com/midi_cc.html #define MIDI_CC_MODULATION 0x01 #define MIDI_CC_BREATH 0x02 #define MIDI_CC_VOLUME 0x07 #define MIDI_CC_BALANCE 0x08 #define MIDI_CC_PAN 0x0A #define MIDI_CC_EXPRESSION 0x0B #define MIDI_CC_EFFECT1 0x0C #define MIDI_CC_EFFECT2 0x0D #define MIDI_CC_GENERAL1 0x0E #define MIDI_CC_GENERAL2 0x0F #define MIDI_CC_GENERAL3 0x10 #define MIDI_CC_GENERAL4 0x11 #define MIDI_CC_GENERAL5 0x12 #define MIDI_CC_GENERAL6 0x13 #define MIDI_CC_GENERAL7 0x14 #define MIDI_CC_GENERAL8 0x15 #define MIDI_CC_GENERAL9 0x16 #define MIDI_CC_GENERAL10 0x17 #define MIDI_CC_GENERAL11 0x18 #define MIDI_CC_GENERAL12 0x19 #define MIDI_CC_GENERAL13 0x1A #define MIDI_CC_GENERAL14 0x1B #define MIDI_CC_GENERAL15 0x1C #define MIDI_CC_GENERAL16 0x1D #define MIDI_CC_GENERAL17 0x1E #define MIDI_CC_GENERAL18 0x1F #define MIDI_CC_GENERAL1_FINE 0x2E #define MIDI_CC_GENERAL2_FINE 0x2F #define MIDI_CC_GENERAL3_FINE 0x30 #define MIDI_CC_GENERAL4_FINE 0x31 #define MIDI_CC_GENERAL5_FINE 0x32 #define MIDI_CC_GENERAL6_FINE 0x33 #define MIDI_CC_GENERAL7_FINE 0x34 #define MIDI_CC_GENERAL8_FINE 0x35 #define MIDI_CC_GENERAL9_FINE 0x36 #define MIDI_CC_GENERAL10_FINE 0x37 #define MIDI_CC_GENERAL11_FINE 0x38 #define MIDI_CC_GENERAL12_FINE 0x39 #define MIDI_CC_GENERAL13_FINE 0x3A #define MIDI_CC_GENERAL14_FINE 0x3B #define MIDI_CC_GENERAL15_FINE 0x3C #define MIDI_CC_GENERAL16_FINE 0x3D #define MIDI_CC_GENERAL17_FINE 0x3E #define MIDI_CC_GENERAL18_FINE 0x3F #define MIDI_CC_SUSTAIN 0x40 #define MIDI_CC_REVERB 0x5B #define MIDI_CC_CHORUS 0x5D #define MIDI_CC_CONTROL_OFF 0x79 #define MIDI_CC_NOTES_OFF 0x78 // Функция контроля дребезга контактов кнопки. // Подробнее про DEBOUNCE http://arduino.cc/en/Tutorial/Debounce #define DEBOUNCE // время debounce в миллисекундах #define DEBOUNCE_LENGTH 5 // Фильтрация аналоговых значений #define ANALOGUE_FILTER // Что бы движение потенциометра было распознано как ввод, текущее аналоговое значение должно превысить это. #define FILTER_AMOUNT 5 // Таймаут в микросекундах #define ANALOGUE_INPUT_CHANGE_TIMEOUT 1000000 // Число цифровых входов. Может быть от 0 до 18. #define NUM_DI 9 // Число аналоговых входов от 0 до 6. //Закоментированно из-за мультиплексирования аналогового pin0. #define NUM_AI 2 //Массив хранящий карту цифровых пинов в индекс канала. Размер этого массива должен соответствовать NUM_DI выше. byte digitalInputMapping[NUM_DI] = { 5, 6, 7, 8, 9, 10, 11, 12, 13 }; //Массив хранящий карту аналоговых пинов в индекс канала. Размер этого массива должен соответствовать NUM_AI выше. //Line commented out because of the use multeplexing to the analogue pin0 //Строка закоментирована из-за мультиплексирования только одного A0 byte analogueInputMapping[NUM_AI] = { A0, A1}; // Содержит текущее состояние цифровых входов. byte digitalInputs[NUM_DI]; // Содержит текущее состояние аналоговых входов. byte analogueInputs[8]; //Переменая для проведения опроса цифровых пинов, используя функцию DEBOUNCE byte tempDigitalInput; //Переменная для проведения опроса аналоговых пинов, используя функцию аналоговой фильтрации byte tempAnalogueInput; int i = 0; // Переменная нужна что бы провести различие между текущими и новыми значениями аналоговых входов. int analogueDiff = 0; // Флаг изменения на аналоговом пине. boolean analogueInputChanging; int r0 = 0; //значения адреса аналогового входа мультиплексора 4051 (s0) int r1 = 0; //значения адреса аналогового входа мультиплексора 4051 (s1) int r2 = 0; //значения адреса аналогового входа мультиплексора 4051 (s2) void setup() { // Включить скорость последовательного ввода / вывода на скорости 57600 кбит. Это быстрее, чем по стандартной скорости MIDI 31250 кбит. // The PC application which we connect to will automatically take the higher sample rate and send MIDI // messages out at the correct rate. We only send things faster in case there is any latency. // Приложение PC, к которому мы подключаемся автоматически сделает высокую частоту дискретизации и отправит MIDI // Сообщения с правильной скоростью. Мы посылаем только данные быстрее в случае какой-либо задержки. Serial.begin(31250); pinMode(2, OUTPUT); // s0 4051 pinMode(3, OUTPUT); // s1 4051 pinMode(4, OUTPUT); // s2 4051 // Инициализтруем каждый цифровой пин из массива выше, как вход. for (i = 0; i < NUM_DI; i++) { pinMode(digitalInputMapping[i], INPUT); // Don't enable pullup resistor on pin 13, as the LED and resistor will always pull it low, meaning the input won't work. // Instead an external pulldown resistor must be used on pin 13. // инвертируем значения HIGH\LOW на 13 входе изза подключенного к нему резистора со светодиодом. if (digitalInputMapping[i] != 13) { // Включить нагрузочный резистор. Этот вызов должен прийти после вышеуказанного вызова pinMode. digitalWrite(digitalInputMapping[i], HIGH); } // Инициализируем чтение состояния цифровых входов. digitalInputs[i] = digitalRead(digitalInputMapping[i]); } // Инициализируем как вход нужные аналоговые пины. pinMode(analogueInputMapping[i], INPUT); for (i = 0; i <=7; i++) { // управление мультиплексором r0 = i & 0x01; r1 = (i>>1) & 0x01; r2 = (i>>2) & 0x01; digitalWrite(2, r0); digitalWrite(3, r1); digitalWrite(4, r2); // Инициализация чтения с аналоговых входов analogueInputs[i] = analogRead(analogueInputMapping[i])/8; } // Предположим нет активности на аналоговых входах analogueInputChanging = false; // таймер запускается каждую секунду Timer1.initialize(ANALOGUE_INPUT_CHANGE_TIMEOUT); // По истечению времени вызывается функция Timer1.attachInterrupt(analogueInputStopped); // Запускается таймер Timer1.start(); } void loop() { for (i = 0; i < NUM_DI; i++) { // Считываем текущее состояние цифровых пинов и временно храним их. tempDigitalInput = digitalRead(digitalInputMapping[i]); // Проверяем, если последнее состояние отличается от текущего состояния. if (digitalInputs[i] != tempDigitalInput) { #ifdef DEBOUNCE // Ждем в течении короткого промежутка времени и снова считываем значение пина. delay(DEBOUNCE_LENGTH); // Если второе чтение является таким же, как начального чтения, предположим, что это правда. if (tempDigitalInput == digitalRead(digitalInputMapping[i])) { #endif // Запишем новое значение цифрового пина. digitalInputs[i] = tempDigitalInput; // переход из HIGH в LOW (кнопка нажата) if (digitalInputs[i] == 0) { // Все цифровые входы используют подтянуть резисторов, за исключением контактный 13 так логика переворачивается if (digitalInputMapping[i] != 13) { noteOn(0, 0x00 + i, 0x7F); // Channel 1, middle C, maximum velocity } else { noteOff(0, 0x00 + i); // Channel 1, middle C } } // Переход из LOW в HIGH (кнопка отпущена) else { // Все цифровые входы используют подтянуть резисторов, за исключением контактный 13 так логика переворачивается if (digitalInputMapping[i] != 13) { noteOff(0, 0x00 + i); // Channel 1, middle C } else { noteOn(0, 0x00 + i, 0x7F); // Channel 1, middle C, maximum velocity } } #ifdef DEBOUNCE } #endif } } for (i = 0; i <=7; i++) { // Управление мультиплексором r0 = i & 0x01; r1 = (i>>1) & 0x01; r2 = (i>>2) & 0x01; digitalWrite(2, r0); digitalWrite(3, r1); digitalWrite(4, r2); // Считываем аналоговый pin 0, делим получившееся значение на 8 чтобы 10-битное значение АЦП (0-1023) перевести в 7-битное значение MIDI (0-127). tempAnalogueInput = analogRead(analogueInputMapping[i]) / 8; #ifdef ANALOGUE_FILTER // Возьмем абсолютное значение разности между текущим и новым значениями analogueDiff = abs(tempAnalogueInput - analogueInputs[i]); // Продолжаем только если порог был превышен или вход уже меняется if ((analogueDiff > 0 && analogueInputChanging == true) || analogueDiff >= FILTER_AMOUNT) { #else if (analogueInputs[i] != tempAnalogueInput) { #endif // Если изменений не было, снова запускаем таймер if (analogueInputChanging == false) { Timer1.start(); } // Если аналоговое значение изменилось таймер перезапускается. Только перезапустить его, если мы уверены, что вход не 'между' значение // т.е.. Это переехал более FILTER_AMOUNT else if (analogueDiff >= FILTER_AMOUNT) { Timer1.restart(); } // Аналоговое значение изменяется analogueInputChanging = true; // Записываем новое аналоговое значение analogueInputs[i] = tempAnalogueInput; // Отправить аналоговое значение на общем MIDI CC (см определения на начало этого файла) controlChange(0, MIDI_CC_GENERAL1 + i, analogueInputs[i]); } } } // Передача Note On в MIDI сообщении void noteOn(int channel, int pitch, int velocity) { // 0x90 это первый из 16 note on каналов channel += 0x90; // Убедимся что мы между 1 и 16 каналами для note on сообщения if (channel >= 0x90 && channel <= 0x9F) { #ifdef DEBUG Serial.print("Button pressed: "); Serial.println(pitch); #else Serial.print(channel); Serial.print(pitch); Serial.print(velocity); #endif } } // Передаем MIDI note off сообщение void noteOff(int channel, int pitch) { // 0x80 первый из 16 note off каналов channel += 0x80; // убеждаемся что мы междц 1 и 16 каналами для note off сообщения if (channel >= 0x80 && channel <= 0x8F) { #ifdef DEBUG Serial.print("Button released: "); Serial.println(pitch); #else Serial.print(channel); Serial.print(pitch); Serial.print(0x00); #endif } } // Отправить сообщение об изменении управления MIDI void controlChange(int channel, int control, int value) { // 0xB0 первый из 16 каналов control change (СС) channel += 0xB0; // Убеждаемся что мы между 1 и 16 каналами для CC сообщения if (channel >= 0xB0 && channel <= 0xBF) { #ifdef DEBUG Serial.print(control - MIDI_CC_GENERAL1); Serial.print(": "); Serial.println(value); #else Serial.print(channel); Serial.print(control); Serial.print(value); #endif } } // Таймер истек void analogueInputStopped() { // Остановить таймер, чтобы он не раз вызывать эту функцию. Timer1.stop(); // когда аналоговый вход перестанет меняться analogueInputChanging = false; }
Вроде заработали оба канала, сделал явно не рационально...тупо скопировал алгоритм обработки первого канала, назначил еще кучу переменных. Возможно как то упростить код? и еще переживаю по поводу конфликта крутилок. Например назначу на 5 крутилку первого мкльтиплексора действие и не будет ли оно выполняться если я буду крутить 5 крутилку второго мультиплексора))