MIDI - клавиатура

Всем привет! Пытаюсь переделать синтезатор Юность-21 в миди-клавиатуру. Клавиши соединены с Arduino Uno через 4 расширителя порта PCA9555. На каждый по 10 музыкальных клавиш и по 2 функциональных. Но функциональные не рассматриваем.

структура расширителя:

#define   ADDR_Block_1   0x24

bool flag1_0;

union PCA9555_Byte 
 {
   uint16_t   Value;
   struct  
   {
    bool Bit0 : 1;
    bool Bit1 : 1;
    bool Bit2 : 1;
    bool Bit3 : 1;
    bool Bit4 : 1;
    bool Bit5 : 1;
    bool Bit6 : 1;
    bool Bit7 : 1;

    bool Bit8 : 1;
    bool Bit9 : 1;
    bool Bit10 : 1;
    bool Bit11 : 1;
    bool Bit12 : 1;
    bool Bit13 : 1;
    bool Bit14 : 1;
    bool Bit15 : 1;
   }Byte;
 };

PCA9555_Byte Block_1; 

#define Btn_C     Block_1.Byte.Bit0
#define Btn_Db    Block_1.Byte.Bit1
#define Btn_D     Block_1.Byte.Bit2
#define Btn_Eb    Block_1.Byte.Bit3
#define Btn_E     Block_1.Byte.Bit4
#define Btn_F     Block_1.Byte.Bit5
#define Btn_Fd    Block_1.Byte.Bit6
#define Btn_G     Block_1.Byte.Bit7
#define Btn_Ab    Block_1.Byte.Bit8
#define Btn_A     Block_1.Byte.Bit9
#define Btn_Hb    Block_1.Byte.Bit10
#define Btn_H     Block_1.Byte.Bit11

#define Btn_S_1  Block_1.Byte.Bit12
#define Btn_S_2  Block_1.Byte.Bit13

Чтение расширителя

void beginTransmission(int8_t addr)
 {
  Wire.begin();
  Wire.beginTransmission(addr);
  Wire.write(B11111111); 
  Wire.endTransmission();
 }

int16_t pca9555_read(int8_t addr) 
 {
  uint16_t rv = 0;
  uint8_t byte1, byte2;
 
  Wire.beginTransmission(addr);
  Wire.write(0x00);
  Wire.endTransmission();
  Wire.requestFrom(addr,1);
  byte1 = (Wire.read()); 
  
  Wire.beginTransmission(addr);
  Wire.write(0x01);
  Wire.endTransmission();
  Wire.requestFrom(addr,1);
  byte2 = (Wire.read());
  
  rv |= (byte2 << 8); 
  rv |= byte1;
  return rv;
 }
//Передача миди команды
void Command3(int cmd, int pitch, int velocity) 
{
  Serial.write(cmd);
  Serial.write(pitch);
  Serial.write(velocity);  
}

Сетап и луп

void setup() {

  Serial.begin(31250);
  Wire.begin();
  beginTransmission(ADDR_Block_1);
}
loop()
{
Block_1.Value  = pca9555_read(ADDR_Block_1);
//Опрос каждой кнопки
if(!Btn_С && flag1_0  == 0){Command3(0x90, 0x24, 0x6E);flag1_0  = 1;} else if(Btn_C && flag1_0  == 1){Command3(0x80, 0x24, 0x40); flag1_0  = 0;}
}

В принципе все работает, но клавиш 40 и писать обработку каждой не очень удобно. У каждой три состояния(нажата, удерживается, отпущена). К тому же на каждую клавишу нужно назначать несколько нот для красоты. Понимаю, что тут можно сделать класс кнопки. Но вопрос в том, как значения входов присваивать к этому классу? Если делать это в цикле for(), то когда делать остальные дела? так же одновременно могут быть нажаты 10 клавиш максимум.
Натолкните, пожалуйста на мысль.

Хм… а вы не тот Жека, который про таймеры утром спрашивал - @Geka777 ?

Нет. Я другой)

хм… тогда я пас.
отписался.

В прерывании опрашивать кнопки , а в main проводить анализ, и тормозить не будет.

Если просто - массив из структуры, которая содержит код нажатия и код отпускания. Номер кнопки - индекс в массиве. В цикле бегаем по кнопкам, проверяем их переходы и вызываем подпрограмму с параметрами, которые берутся из массива структур.

Так у меня и так значение каждой кнопки в двух байтах содержится. Эти байты в массив записать?

Можно и просто в массив. Но, если переменных, относящейся к кнопке будет больше, чем одна, то понятней будет структура.

А, в смысле - индекс большой? Тогда неплохо бы его к десятичной форме привести.

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

Не надо всё в кучу мешать.
Сначала сделайте для своих PCA функцию проверки: активна ли кнопка #17 (или #25).
Затем определитесь, какие переменные требуются для того, чтобы выполнить “Нажато” и “отпущено.”

Наверное что-то типа такого выйдет:

typedef struct {
  uint8_t onCode;
  uint8_t offCode;
  bool keyState;
} midiKey_t;

Из такой структуры делаете массив из N (кол-во кнопок) и в лупе организуете цикл 0…N-1. В нем проверяете с помощью функции из начала поста состояние кнопки и, если оно изменилось, шлёте onCode или offCode.

как и когда сделать присвоение двух байт со значениями кнопок экземпляру структуры?

Они у вас на ходу меняются? Если нет, то при описании массива.

У меня такое ощущение что я не верно сформулировал свой вопрос. Спасибо за помощь.

Ну, я хз как понятней объяснить.
В начале .ino файла, в отдельном .h файле-конфиге. Главное - чтобы до начала использования.

midiKey_t midiKey[] = {
 // Кл. #1 
 { 
   .onCode  = 0x0A,
   .offCode = 0x0B,
 },
 // Кл. #2
 { 
   .onCode  = 0x0C,
   .offCode = 0x0D,
 },
};
1 лайк

Вот вопрос

Совершенно логично, что это надо делать один в начале работы программы, предположительно в СЕТАП.
Поскольку это надо сделать один раз - то второй вопрос отпадает. Во время работы программы этот процесс время у “других дел” отнимать не будет.

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

вы предлагаете все 40 кнопок так описать? так я их уже через if…else все описал. какой в этом смысл?

Вот класс клавиши, как я вижу.

class Midi_Button {
  public:
  uint8_t note =0x00; // как присвоить значение ноты???
  Midi_Button(bool bit) {
  bit = _bit;
  }
  
 void state()
 {
  if(!_bit && flag == 0){Command3(0x90, note, 0x6E); flag  = 1;} 
  else if(_bit && flag  == 1){Command3(0x80, note, 0x40); flag  = 0;}
 }
  private:
  bool _bit;
  bool flag = 0;
  
  void Command3(int cmd, int pitch, int velocity) {
   Serial.write(cmd);
   Serial.write(pitch);
   Serial.write(velocity);  
  }
};

Как создать допустим 10 экземпляров этого класса и подружить с двумя байтами, где текущее состояние всех клавиш? Как передать классу код ноты? Ноты все идут по порядку. начиная от 0х20 и т.д. так же как и значения клавиш в байте. Как их приравнять?

Байт.Бит[i] = Midi_Button.note[i]; ?

Цитирую: "В принципе все работает, но клавиш 40 и писать обработку каждой не очень удобно. "

В for обработка один раз пишется. И потом можете заниматься другими делами, как и хотели.

Если думаете, что есть способ нигде не описывать 40 клавиш и чтобы всё работало, то ошибаетесь.

А никак иначе, кроме описывания 40 кнопок, вы эту задачу не решите.
Но описывать значения в массиве, подряд, в разы проще и компактнее, чем писать для каждой кнопки if…else со всеми вариантами

вы про то, что у конструктора могут параметры и список инициализации, почитайте в книжке