Как подключить 2 мультиплексора?

gatsby163
Offline
Зарегистрирован: 10.03.2014

Здравтсвуйте, собираю 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;
} 

 

gatsby163
Offline
Зарегистрирован: 10.03.2014
Вроде заработали оба канала, сделал явно не рационально...тупо скопировал алгоритм обработки первого канала, назначил еще кучу переменных. Возможно как то упростить код? и еще переживаю по поводу конфликта крутилок. Например назначу на 5 крутилку первого мкльтиплексора действие и не будет ли оно выполняться если я буду крутить 5 крутилку второго мультиплексора))
#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

//byte analogueInputMapping[NUM_AI] = { A0, A1, A2, A3, A4, A5 };

// Содержит текущее состояние цифровых входов.
byte digitalInputs[NUM_DI];
// Содержит текущее состояние аналоговых входов.
byte analogueInputs[8];
byte analogueInputs1[8];

//Переменая для проведения опроса цифровых пинов, используя функцию DEBOUNCE
byte tempDigitalInput;
//Переменная для проведения опроса аналоговых пинов, используя функцию аналоговой фильтрации
byte tempAnalogueInput;
byte tempAnalogueInput1;


int i = 0;
// Переменная нужна что бы провести различие между текущими и новыми значениями аналоговых входов.
int analogueDiff = 0;
int analogueDiff1 = 0;
// Флаг изменения на аналоговом пине.
boolean analogueInputChanging;
boolean analogueInputChanging1;

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);
    pinMode(1, 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;
    analogueInputs1[i] = analogRead(0)/8;
  }
  
  // Предположим нет активности на аналоговых входах
  analogueInputChanging = false;
  analogueInputChanging1 = 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
    }
  }
 

    // Управление мультиплексором 1  
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]);
    }
  }
 // Управление мультиплексором 2   
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 1, делим получившееся значение на 8 чтобы 10-битное значение АЦП (0-1023) перевести в 7-битное значение MIDI (0-127).
    tempAnalogueInput1 = analogRead(1) / 8;
    
    
    #ifdef ANALOGUE_FILTER
    // Возьмем абсолютное значение разности между текущим и новым значениями 
    analogueDiff1 = abs(tempAnalogueInput1 - analogueInputs1[i]);
    // Продолжаем только если порог был превышен или вход уже меняется
    if ((analogueDiff1 > 0 && analogueInputChanging1 == true) || analogueDiff1 >= FILTER_AMOUNT)
    {
    #else
    if (analogueInputs1[i] != tempAnalogueInput1)
    {
    #endif
      // Если изменений не было, снова запускаем таймер
      if (analogueInputChanging1 == false)
      {
        Timer1.start();
      }
      // Если аналоговое значение изменилось таймер перезапускается. Только перезапустить его, если мы уверены, что вход не 'между' значение
      // т.е.. Это переехал более FILTER_AMOUNT
      else if (analogueDiff1 >= FILTER_AMOUNT)
      {
        Timer1.restart();
      }
      
      // Аналоговое значение изменяется
      analogueInputChanging1 = true;
      
      // Записываем новое аналоговое значение
      analogueInputs1[i] = tempAnalogueInput1;
      
      // Отправить аналоговое значение на общем MIDI CC (см определения на начало этого файла)
      controlChange(0, MIDI_CC_GENERAL1 + i, analogueInputs1[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;
}