Мультиплексирование клавиатуры пианино с помощью arduino nano

Я пытаюсь использовать дешевую клавиатуру с 37 клавишами в качестве MIDI-контроллера, считывая мультиплексированные клавиши с помощью Arduino nano. Ключи сгруппированы в шесть групп, максимум по 8 ключей в каждой группе. Что приводит к тому, что я настраиваю 8 цифровых ВХОДОВ, которые вытягиваются ВЫСОКО, и 6 ВЫХОДОВ, которые вытягиваются НИЗКО, при считывании этой группы ключей. Это отлично работает, когда у меня в цикле всего 3 группы. Однако всякий раз, когда я добавляю другую группу, она больше не работает. Не имеет значения, какие группы активированы.

Одно групповое чтение выглядит так:

digitalWrite(A0, LOW);
for (byte j = 12; j > 4; j--)
{
  uint8_t note = offset + 20 + (12-j);
  uint8_t key_state = digitalRead(j);

  if ( (key_state == 0) && (notes_pressed[note] == 1) )
  {
    notes_pressed[note] = 0;
    Serial.write(note + 100);
  } else if ( (key_state == 1) && (notes_pressed[note] == 0) )
  {
    notes_pressed[note] = 1;
    Serial.write(note);
  }
}
digitalWrite(A0, HIGH);

С A0, являющимся выводом группы.

Я думаю, что это связано с тем, что контакты не могут быть опущены так быстро из-за слабых драйверов вывода или чего-то подобного. К сожалению, у меня нет осциллографа, чтобы проверить уровни. Я добавил задержки, чтобы убедиться, что уровни установились, когда происходит считывание, но безрезультатно.

Редактировать: Схема в значительной степени заключается именно в этом. Диоды на клавиатуре я опустил, но они есть по мере необходимости: schematic

#include "HardwareSerial.h"

#define offset 48

void midi_note_off(byte channel, byte key, byte velocity);
void midi_note_on(byte channel, byte key, byte velocity);
void midi_command(byte command, byte channel, byte param1, byte param2);

byte notes_pressed[37];
void setup() {
  Serial.begin(115200);
  pinMode(12, INPUT_PULLUP);
  pinMode(11, INPUT_PULLUP);
  pinMode(10, INPUT_PULLUP);
  pinMode(9, INPUT_PULLUP);
  pinMode(8, INPUT_PULLUP);
  pinMode(7, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(4, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(A2, OUTPUT);
  pinMode(A1, OUTPUT);
  pinMode(A0, OUTPUT);

  digitalWrite(4, HIGH);
  digitalWrite(3, HIGH);
  digitalWrite(2, HIGH);
  digitalWrite(A0, HIGH);
  digitalWrite(A1, HIGH);
  digitalWrite(A2, HIGH);
}

void loop() {
  digitalWrite(4, LOW);
  for (byte j = 8; j > 4; j--)
  {
    uint8_t note = offset + (8-j);
    uint8_t key_state = digitalRead(j);
    
    if ( (key_state == 0) && (notes_pressed[note] == 1) )
    {
      notes_pressed[note] = 0;
      Serial.write(note + 100);
    } else if ( (key_state == 1) && (notes_pressed[note] == 0) )
    {
      notes_pressed[note] = 1;
      Serial.write(note);
    }
  }
  digitalWrite(4, HIGH);

  digitalWrite(3, LOW);
  for (byte j = 12; j > 4; j--)
  {
    uint8_t note = offset + 4 + (12 - j);
    uint8_t key_state = digitalRead(j);
    
    if ( (key_state == 0) && (notes_pressed[note] == 1) )
    {
      notes_pressed[note] = 0;
      Serial.write(note + 100);
    } else if ( (key_state == 1) && (notes_pressed[note] == 0) )
    {
      notes_pressed[note] = 1;
      Serial.write(note);
    }
  }
  digitalWrite(3, HIGH);

  digitalWrite(2, LOW);
  for (byte j = 12; j > 4; j--)
  {
    uint8_t note = offset + 12 + (12-j);
    uint8_t key_state = digitalRead(j);
    
    if ( (key_state == 0) && (notes_pressed[note] == 1) )
    {
      notes_pressed[note] = 0;
      Serial.write(note + 100);
    } else if ( (key_state == 1) && (notes_pressed[note] == 0) )
    {
      notes_pressed[note] = 1;
      Serial.write(note);
    }
  }
  digitalWrite(2, HIGH);

  digitalWrite(A0, LOW);
  for (byte j = 12; j > 4; j--)
  {
    uint8_t note = offset + 20 + (12-j);
    uint8_t key_state = digitalRead(j);
    
    if ( (key_state == 0) && (notes_pressed[note] == 1) )
    {
      notes_pressed[note] = 0;
      Serial.write(note + 100);
    } else if ( (key_state == 1) && (notes_pressed[note] == 0) )
    {
      notes_pressed[note] = 1;
      Serial.write(note);
    }
  }
  digitalWrite(A0, HIGH);

  digitalWrite(A1, LOW);
  for (byte j = 12; j > 4; j--)
  {
    uint8_t note = offset + 28 + (12-j);
    uint8_t key_state = digitalRead(j);
    
    if ( (key_state == 0) && (notes_pressed[note] == 1) )
    {
      notes_pressed[note] = 0;
      Serial.write(note + 100);
    } else if ( (key_state == 1) && (notes_pressed[note] == 0) )
    {
      notes_pressed[note] = 1;
      Serial.write(note);
    }
  }
  digitalWrite(A1, HIGH);

  digitalWrite(A2, LOW);
  for (byte j = 5; j > 4; j--)
  {
    uint8_t note = offset + 36 + (5-j);
    uint8_t key_state = digitalRead(j);

    if ( (key_state == 0) && (notes_pressed[note] == 1) )
    {
      notes_pressed[note] = 0;
      Serial.write(note + 100);
    } else if ( (key_state == 1) && (notes_pressed[note] == 0) )
    {
      notes_pressed[note] = 1;
      Serial.write(note);
    }
  }
  digitalWrite(A2, HIGH);
}

Я знаю, что последняя часть, зацикливающаяся на одном значении, не является обязательной, но она все равно должна работать.

, 👍0

Обсуждение

Выходы Arduino составляют около 25 Ом, тогда как внутреннее подтягивание составляет примерно 30 Ком, поэтому один ВЫХОД НИЗКИЙ может тянуть много контактов INPUT_PULLUP. Не могли бы вы показать схему настройки клавиатуры + Arduino?, @Edgar Bonet

В строке 82 у вас несбалансированные круглые скобки., @Edgar Bonet

Спасибо, это, должно быть, произошло во время вставки сюда!, @multikeys

ваши циклы for перепутались ...порядок ключей в столбцах не одинаков (если ваша схема верна) ... первая колонка-от 5 до 8 ... вторая колонка-от 12 до 5 ... третий столбец-от 5 до 12 ... Четвертый столбец-от 5 до 12 ... пятая колонна - 12 к 5 ... шестой столбец-это один ключ, который не требует цикла "for", @jsotola

прекратите вычислять примечание ... примечание = смещение + 20 + (12-j) ... вместо этого используйте инкремент ... `примечание++, @jsotola


2 ответа


0

При разработке матрицы коммутатора для встроенного процессора необходимо учитывать несколько вопросов, независимо от того, является ли это музыкальная или компьютерная клавиатура:

  1. Контактный отскок: Это более важно для компьютерной клавиатуры , чем для музыкальной клавиатуры. Однако, если переключатель особенно плох, он может быть слышен. Программное обеспечение может быть использовано для устранения проблемы , но также добавит задержку между нажатием клавиши и началом звука ноты.
  2. Подтягивание: Любой логический вход не должен оставаться отключенным или плавающим. Это может привести к неожиданным результатам. Используйте подтягивающие резисторы (или потяните вниз в зависимости от вашей конструкции), чтобы смягчить эту проблему. Встроенный процессор Arduino Uno (the ATmega328P) поставляется со встроенными подтягивающими резисторами. IDE Arduino абстрагирует эту функцию, включив параметр в вызов функции pinMode() для вызова этой функции.
  3. Опрокидывание: В матрице переключателей может быть однозначно обнаружено 1 или 2 одновременных замыкания переключателей. Однако, если 3 замыкания переключателя соединяют 2 строки и 2 столбца вместе, они не могут быть однозначно обнаружены. Это связано с тем, что ток может протекать через 3 закрытых переключателя, активирующих обе строки и оба столбца, что сводит на нет возможность встроенного процессора сканировать отдельные строки или столбцы. Чтобы уменьшить эту проблему, ко всем переключателям в матрице переключателей добавляется диод, так что между строками или столбцами нет пути тока.

Со страницы ролловера в Википедии:

Большинство музыкальных клавиатур используют изолирующие диоды в матрице клавиатуры для реализации полного опрокидывания n-клавиш, что делает их невосприимчивыми как к появлению призраков , так и к заклиниванию клавиш

Это изображение, если из этого вопроса об обмене стеками: This image is from this stack exchange question.

,

Спасибо за ваш вклад. 1. Это то, что я хочу смягчить позже, если это необходимо. Однако сейчас я не получаю никакой реакции на нажатия или отпускания кнопок. 2. Я использую подтягивания Arduino 3. Клавиатура, которую я купил, имеет эти встроенные диоды, @multikeys

Похоже, вы уже приняли во внимание основы. Подожди ... Я думал, вам удалось создать несколько заметок? Если вы не создаете нот, это означает, что что-то может быть не так, включая MIDI-плеер! Хммм ... тогда тебе следует начать все сначала. Проверьте всю вашу проводку (используя тестовое оборудование, если оно у вас есть). Затем используйте версию вашего кода, которая не меняется между разными столбцами. Затем попробуйте 2 колонки. Добавьте задержки в код после изменения столбцов, чтобы увидеть, поможет ли это., @st2000

Извините, что я неправильно сформулировал это. Я не реагирую на нажатия кнопок всякий раз, когда раскомментирую более трех столбцов в цикле. Не имеет значения, какие из них комментируются, другие реагируют должным образом. Я пробовал с некоторыми задержками, но постараюсь добавить их в разных местах, чтобы, возможно, получить другое поведение., @multikeys

Re “_builded in Pull Up and Pull Down resistors_”: Uno имеет встроенные подтягивания, но не подтягивания. Re “_если два замыкания переключателя происходят в одной строке или столбце, они не могут быть однозначно обнаружены_”: Мне кажется, вам нужно нажать как минимум три переключателя, чтобы создать такую двусмысленность., @Edgar Bonet

Никаких спусков? Виноват, - поправил я ответ. Если сканирование путем понижения напряжений столбцов и 2 столбца электрически соединены 2 замкнутыми переключателями в одном ряду (без диодов), то я не вижу способа определить разницу ... о, я понимаю, что вы имеете в виду. Вам не нужно видеть разницу. Вам просто нужно обнаружить, что они оба закрыты. Ладно, это я тоже исправлю., @st2000


1

Несколько выдержек из программы:

#define offset 48
//...
byte notes_pressed[37];  // допустимые индексы: 0 – 36
//...
uint8_t note = offset + (8-j);  // j ≤ 8, таким образом, примечание ≥ 48
//...
notes_pressed[note] = 0;

Эта последняя строка записывается после конца массива notes_pressed и перезаписывает неизвестно что в памяти. Это может быть источником проблемы, с которой вы столкнулись.

,