Herramientas de usuario

Herramientas del sitio


octaviotron:semeruco

SEMERUCO MIDI

Qué es Semeruco

“Semeruco” es una baya (berry, en inglés) propia del centro-occidente de Venezuela, en los estados Falcón, Lara y Zulia, aunque se da en otros estados del país.

Esta es una iniciativa colectiva de la Comuna Itinerante del Sonido y del Audio “CISA” y la Comuna Don Luis Zambrano y el objetivo es crear una fábrica de hardware libre donde se produzcan estas propuestas de innovación nacional.

El objetivo de este proyecto es hacer una Superficie de Control MIDI con las siguientes características y objetivos para su primer prototipo:

  • Matriz de teclado de capacidad hasta de 64 pulsadores organizados en una rejilla de 8×8
  • Entradas analógicas (las 8 de la tarjeta Pingüino)
  • 16 leds, asignados sobre un teclado de 4×4 pulsadores.
  • Reloj para el tempo, asignado a una línea de 4 leds.
  • Comunicación USB y/o MIDI

Resultado esperado

Una superficie de control MIDI con una matriz de 4×4 teclas para la secuencia y controles diversos, tanto analógicos (potenciómetros/sensores) como digitales (prendido/apagado).

En el gráfico se muestra el diseño conceptual, esquematizado para su comprensión. La manufactura tendrá variación.

Los controles pueden añadirse o quitarse de la matriz, así como esta puede redimensionarse y alcanzar las 8×8 teclas. Inicialmente y en el prototipo planteado, se pueden implementar sensores de luz, de proximidad y de cualquier dispositivo que envíe una variación de valores, los cuales podrán ser leídos en un rango entre 0 y 1023. Una precisión de 1024 valores es bastante suficiente para casi cualquier necesidad de lectura diferencial, al menos para las señales MIDI las cuales tienen todas un rango de variación entre 0 y 127.

La matriz trabajará como escritorios virtuales: se pueden administrar muchas matrices que forman cuadrados de 2 o más matrices, de manera de poder, con 4×4 elementos, manejar rejillas con formas arbitrarias como 8×8, 4×16 o cualquier patrón con un múltiplo de 4×4 de estos pulsadores con led.

La pantalla ayudará a ubicarse en qué “escritorio” se está, aunque la pantalla LCD no forma parte necesariamente del primer resultado de este proyecto. Se puede sustituir por una matriz de 4×4 leds que muestren en qué espacio virtual se encuentre.

El Controlador

El controlador principal de Semeruco MIDI es la tarjeta “geÑUino”, basada en el micro-controlador AVR Atmel de 8-bit. Este controlador posee tecnología basada en arquitectura RISC Para profundizar más sobre sus características, las cuales están públicamente documentadas, se pueden ver su (hoja de especificaciones técnicas).

Es una tarjeta 100% compatible con Arduino y su placa base está diseñada a una sola cara, de manera de poderla reproducir con cualquier procedimiento artesanal de creación de PCB.

La tarjeta geÑUino ha sido totalmente diseñada desde cero y acá están sus esquemáticos, diseñados con Fritzing. Las imágenes pueden ampliarse al accionar el puntero sobre ellas.

Este controlador ha sido inspirado en el proyecto Pingüino-VE del “maestro” Joan Espinoza.

Este micro-controlador, servirá tanto de procesador de señales como de interfaz tanto por USB como por MIDI directamente. Este será el cerebro de la superficie de control. Se encargará de las siguientes funciones:

  • Recibir la señal de los pulsadores de la matriz (señales binarias)
  • Leer valores de entrada analógica (perillas, potenciometros, sensores de proximidad, de luz, de presión, etc)
  • Enviar las señales MIDI tanto por DIN-5 como USB
  • Dirigir todos los dispositivos y poder acoplarse a señales maestras

Acá podemos ver cómo se interconecta nuestra tarjeta con los demás elementos de la superficie de control:

Lectura del Teclado

La superficie de control se compone de un teclado en forma de matriz de 8×8, lo cual se logra multiplexando 3 bits con el integrado 74138 para enviar un pulso a cada una de sus 8 salidas (eje X) lo cual será leído en cada una de las 8 entradas (eje Y) del integrado 74151.

La lectura funciona de la siguiente manera: se envía la sucesión binaria-octal 000,001,010,011,100,101,110,111 en sus 3 puertos de entrada. Esto hará que se encienda, en cada una de esas combinaciones, una salida del 74138 (eje x).

Es decir: enviando en las entradas del 74138 el binario “010” activará el pin de salida 2, enviando el binario “011” activará la salida 3, etc.

Por cada uno de los envíos de estos octales, se leen secuencialmente las 8 entradas del 74151 para registrar cual está activo en ese momento, es decir, si hay o no un pulsador presionado. Esto sucede muchas veces por segundo, por lo cual presionarlo momentáneamente será suficiente para obtener una lectura.

Por un proceso inverso al funcionamiento del 74138, cuando se detecta la activación de un pulsador, el pin marcado como “R” del 74151 se activará y en sus salidas “abc” estará el octal correspondiente a cual pin ha sido detectado. Esta lectura también debe hacerse secuencialmente.

Así se recorre toda la matriz y se lee el teclado.

Los puntos que se ven en la matriz corresponden a los pulsadores que estarán en el circuito. Están colocados arbitrariamente, por lo cual este diseño da para hasta 64 pulsadores.

Es importante acotar que este teclado lee sólo una pulsación a la vez y pulsar dos teclas a la misma vez no dará el efecto deseado. Este diseño no necesita esa funcionalidad. Para permitir pulsaciones simultáneas es necesario acompañar cada pulsador con un diodo o una resistencia que haga unívoca la presencia de más de una pulsación simultánea. Más información aquí.

La lógica de leer el teclado y enviar la respuesta se resolverá con un FOR anidado, como se expresa en el siguiente pseudo-código:

apagar todos los pines
for (x=0; x<8; x++) 
  { 
  enviar octal "x" al 74138
  for (y=0; y<8; y++)
    {
    leer la entrada octal "y" del 74151
    if y==HIGH
      {
      salida MIDI: "activo x en y"
      }
    apagar el pin encendido
    }
  }

Como se lee en el código, se hará una lectura en barrido secuencial por el teclado enviando pulsos en los 8 pines del eje X, leyendo coincidencias por cada uno de esos pines en los 8 pines del eje Y.

Y aquí el esquema y el PCB de este circuito.

En el esquema eléctrico no está reflejada la presencia de las resistencias “pull-up” pero en el circuito impreso o PCB está un componente llamado “arr” el cual es un arreglo de resistencias de 1k.

Matriz de Leds

Este circuito compuesto por un integrado 74154 es un multiplexor de 4 a 16 que servirá para colocar sus salidas en una matriz de 4×4 que sirvan para indicar qué teclas se han pulsado. Al pulsarlas de nuevo su led se apaga.

La escritura no es en forma de matriz (componente x,y) sino que es una secuencia constante de 16 leds que se llamará desde los 4 bits (nibble) enviado a la entrada del 74154.

Para mantener el estado encendido/apagado de cada led, no se realiza como es común un proceso de round-robin sobre cada uno para activarlos por una fracción de segundo muy rápidamente, dando la ilusión de estar continuamente encendidos.

En este caso, se usarán flip-flops 74LS273 los cuales contienen ocho tipo D. Con esto, sólo es necesario enviar un pulso para encender o apagar el led deseado, manteniendo su estado hasta que un segundo pulso lo cambie a su contrario.

La entrada “CLK” habilita la escritura para el cambio de estado. En este caso se conectan en paralelo pues no se separarán en dos grupos lógicos de ocho, sino que se activará todo el circuito para escribir o apagar arbitrariamente el led deseado.

La entrada “RST” al recibir un pulso colocará todos los estados en cero, es decir, se apagarán todos los leds.

Acá los esquemas del PCB (dos caras) y el circuito.

Entradas Analógicas

Este es un proceso más simple: son 8 entradas que pueden registrar valores del 0 al 1023. Cualquier dispositivo que arroje valores análogos podrán luego ser interpretados dentro de esa escala. Acá pueden colocarse potenciómetros (lineales o logarítmicos), sensores de proximidad, sensores de luz, etc.

El valor que arroje esta entrada tendrá que ser interpretada por el programa para enviarse posteriormente como una señal MIDI (velocidad, portamento, pitch, etc)

Inicialmente este diseño tendrá dos potenciómetros y una fotoresistencia.

Fotografías del Proceso

Tenemos un primer prototipo 100% funcional de SEMERUCO MIDI.

Programa

Este código, tomado de http://playground.arduino.cc/Main/RGBLEDPWM hace que tres potenciómetros regulen un led RGB, toca modificarlo para que pase con un solo potenciómetro de verde (o azul) a rojo:

// Init the Pins used for PWM
const int redPin = 9;
const int greenPin = 10;
const int bluePin = 11;
 
// Init the Pins used for 10K pots
const int redPotPin = 0;
const int greenPotPin = 1;
const int bluePotPin = 2;
 
// Init our Vars
int currentColorValueRed;
int currentColorValueGreen;
int currentColorValueBlue;
 
void setup()
{
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);
}
 
void loop()
{
// Read the voltage on each analog pin then scale down to 0-255 and inverting the value for common anode
  currentColorValueRed = (255 - map( analogRead(redPotPin), 0, 1024, 0, 255 ) );
  currentColorValueBlue = (255 - map( analogRead(bluePotPin), 0, 1024, 0, 255 ) );
  currentColorValueGreen = (255 - map( analogRead(greenPotPin), 0, 1024, 0, 255 ) );
 
// Write the color to each pin using PWM and the value gathered above
  analogWrite(redPin, currentColorValueRed);
  analogWrite(bluePin, currentColorValueBlue);
  analogWrite(greenPin, currentColorValueGreen);
}

Este código es Software Libre que proviene de http://www.ingeniuswil.com/2015/08/09/controlador-midi-casero-con-arduino y ha sido usado como base del proyecto

// Entradas y salidas digitales
#define BUTTON1 2
#define BUTTON2 3
#define BUTTON3 4
#define BUTTON4 5
#define BUTTON5 6
#define BUTTON6 7
#define BUTTON7 8

// Usamos AO como tecla “shift” para duplicar
// la cantidad de entradas de la interfaz
#define SHIFT_KEY A0

// Por lo pronto se usará sólo el canal 1 MIDI
#define CMD_1 0x90
#define CMD_2 0x90
#define CMD_3 0x90
#define CMD_4 0x90
#define CMD_5 0x90
#define CMD_6 0x90
#define CMD_7 0x90

//Con una velocidad media
#define VEL_1 0x45
#define VEL_2 0x45
#define VEL_3 0x45
#define VEL_4 0x45
#define VEL_5 0x45
#define VEL_6 0x45
#define VEL_7 0x45

// Definimos las notas
#define PITCH_1 0x3F //D#
#define PITCH_2 0x40 //E
#define PITCH_3 0x41 //F
#define PITCH_4 0x3E //D
#define PITCH_5 0x3D //C#
#define PITCH_6 0x3C //Middle C (C4)
#define PITCH_7 0x45 //A
#define PITCH_8 0x46 //A#
#define PITCH_9 0x47 //B
#define PITCH_10 0x44 //G#
#define PITCH_11 0x43 //G
#define PITCH_12 0x42 //F#

boolean changeState1 = false;
boolean changeState2 = false;
boolean changeState3 = false;
boolean changeState4 = false;
boolean changeState5 = false;
boolean changeState6 = false;
boolean changeState7 = false;
boolean currentState1 = false;
boolean currentState2 = false;
boolean currentState3 = false;
boolean currentState4 = false;
boolean currentState5 = false;
boolean currentState6 = false;
boolean currentState7 = false;

void setup() {
  //Entradas
  pinMode(BUTTON1, INPUT);
  pinMode(BUTTON2, INPUT);
  pinMode(BUTTON3, INPUT);
  pinMode(BUTTON4, INPUT);
  pinMode(BUTTON5, INPUT);
  pinMode(BUTTON6, INPUT);
  pinMode(BUTTON7, INPUT);

  // Pin analogo 0 será la tecla SHIFT
  pinMode(SHIFT_KEY, INPUT);

  // MIDI baud rate:
  Serial.begin(31250);
  }

void loop() {
  // Leemos todos los botones y guardamos su valor booleano
  if(digitalRead(BUTTON1) == HIGH) {currentState1 = true;}
    else {currentState1 = false;}
  if(digitalRead(BUTTON2) == HIGH) {currentState2 = true;}
    else {currentState2 = false;}
  if(digitalRead(BUTTON3) == HIGH) {currentState3 = true;}
    else {currentState3 = false;}
  if(digitalRead(BUTTON4) == HIGH) {currentState4 = true;}
    else {currentState4 = false;}
  if(digitalRead(BUTTON5) == HIGH) {currentState5 = true;}
    else {currentState5 = false;}
  if(digitalRead(BUTTON6) == HIGH) {currentState6 = true;}
    else {currentState6 = false;}
  if(digitalRead(BUTTON7) == HIGH) {currentState7 = true;}
    else {currentState7 = false;}

  //Limpiamos el buffer serial
  Serial.flush();

  //Si hay un botón presionado
 if(currentState1 == true) {
   if(currentState1 != changeState1){
     // enviar la nota correspondiente
     writeNote(CMD_1, PITCH_1, VEL_1);
     }
   }
  //Si no estaba presionado ese botón
  else {
    if(currentState1 != changeState1){
    // enviamos la nota off
    stopNote(CMD_1, PITCH_1);
    }
  }

// Repetimos lo mismo para cada botón
  if(currentState2 == true) { if(currentState2 != changeState2) { writeNote(CMD_2, PITCH_2, VEL_2);} }
  else{ if(currentState2 != changeState2){ stopNote(CMD_2, PITCH_2);} }

  if(currentState3 == true) { if(currentState3 != changeState3) { writeNote(CMD_3, PITCH_3, VEL_3);} }
  else{ if(currentState3 != changeState3){ stopNote(CMD_3, PITCH_3);} }

  if(currentState4 == true) { if(currentState4 != changeState4) { writeNote(CMD_4, PITCH_4, VEL_4);} }
  else{ if(currentState4 != changeState4){ stopNote(CMD_4, PITCH_4);} }

  if(currentState5 == true) { if(currentState5 != changeState5) { writeNote(CMD_5, PITCH_5, VEL_5);} }
  else{ if(currentState5 != changeState5){ stopNote(CMD_5, PITCH_5);} }

  if(currentState6 == true) { if(currentState6 != changeState6) { writeNote(CMD_6, PITCH_6, VEL_6);} }
  else{ if(currentState6 != changeState6){ stopNote(CMD_6, PITCH_6);} }

  //Guardamos el registro del estado previo, para poder monitorear cambios
  changeState1 = currentState1;
  changeState2 = currentState2;
  changeState3 = currentState3;
  changeState4 = currentState4;
  changeState5 = currentState5;
  changeState6 = currentState6;
  changeState7 = currentState7;
  }

  void writeNote(byte command, byte pitch, byte velocity) {
  //Verificamos si está presionada la tecla SHIFT
  if(digitalRead(SHIFT_KEY) == HIGH) {
    // De ser cierto pasamos a la octava superior
    Serial.write(command);
    Serial.write(pitch+6);
    Serial.write(velocity);
    }
  else {
    Serial.write(command);
    Serial.write(pitch);
    Serial.write(velocity);
  }
}

void stopNote(byte command, byte pitch) {
  // no podemos saber que octava ha sido tocada, así que apagamos ambas
  Serial.write(command);
  Serial.write(pitch);
  Serial.write(0x00);
  Serial.write(command);
  Serial.write(pitch+6);
  Serial.write(0x00);
  }

Documentación Complementaria

octaviotron/semeruco.txt · Última modificación: 2016/03/23 16:03 por octaviotron