Siloged

logoVolume MIDI

rack

Cette page décrit une réalisation de pédale de volume pouvant s'insérer dans un rack d'effets MIDI.
Le module doit intégrer :

  • Le réglage de volume
  • un bargraph présentant le niveau de volume
  • une fonction BYPASS
  • une fonction MUTE
  • possibilité de modifier le gabari de variation de volume

Il dispose :
  • d'une LED rouge qui indique une activité MIDI
  • une LED verte qui indique l'état MUTE
  • une LED verte qui indique l'état BYPASS
  • un bouton poussoir permettant de passer d'un gabari à l'autre
  • une prise d'entrée MIDI
  • une prise de sortie MIDI
  • une prise d'entrée jack 6.3mm
  • une prise de sortie jack 6.3mm

Principe de fonctionnement

ldr mli

La variation de volume est faite grâce à deux photo-résistances (LDR) en série qui sont éclairées par deux diodes électroluminescentes dont l'intensité lumineuse est inversement proportionnelle grâce à une porte NON qui inverse leur signal d'alimentation.
Ce dernier est un signal modulé en largeur d'impulsion.

Schéma structurel

schéma

Le coeur du système est un microcontroleur PIC 16877 de 20MHz.
Le choix du canal MIDI se fait grâce à 4 dip switch qui permettent de sélectionner les canaux 1 à 16. les autres dip-switch permettent de choisir la courbe de variation du volume par défaut.

A la mise sous tension, le fait de garder le bouton poussoir enfoncé lance un programme de test des éléments du circuit. Le même bouton poussoir permet également de changer de courbe de variation.
codes Les fonctions MUTE et BYPASS sont assurées par deux relais.
L'afficheur LCD porte la référence PC1602-LRS de la marque POWERTIP

Réalisé en C avec le logiciel d'essai MikroC de Mikroelectronica.

//************** Volume MIDI ********************************
//* Norbert BRAUN *
//* CPU PIC16F877 cadencé a 20MHz *
//* version 2 mars 2016 *
//**********************************************************

//variables utilisees pour afficheur LCD
sbit LCD_RS at RB0_bit;
sbit LCD_EN at RB2_bit;
sbit LCD_D4 at RB3_bit;
sbit LCD_D5 at RB4_bit;
sbit LCD_D6 at RB5_bit;
sbit LCD_D7 at RB6_bit;
sbit LCD_RS_Direction at TRISB0_bit;
sbit LCD_EN_Direction at TRISB2_bit;
sbit LCD_D4_Direction at TRISB3_bit;
sbit LCD_D5_Direction at TRISB4_bit;
sbit LCD_D6_Direction at TRISB5_bit;
sbit LCD_D7_Direction at TRISB6_bit;
// End LCD module connections
//-----Constantes d'affichage------------------------------------------------
const char *msg[] = {
"Volume MIDI v.2 ", //0
"-----NB 2016----", //1
"----- TEST -----", //2
"canal: ", //3
" V", //4
" F", //5
" ", //6
"linear", //7
" log1 ", //8
" log2 ", //9
" log3 ", //10
"lead1 ", //11
"lead2 ", //12
"lead3 ", //13
"Mut/Bp"}; //14
//-------Tableaux de valeurs----------------------------------------------
const int nbCourbe=8;
unsigned char step[nbCourbe][10] =
{ //courbes de variations différentes
{0,40,70,100,130,155,185,210,234,255}, //0 : linear
{40,150, 163,176,189,202,215,228,241,255}, //1 : log1
{40,150, 180,200,210,220,230,240,248,255}, //2 : log2
{40,170, 205,220,230,238,245,250,253,255}, //3 : log3
{200,200, 200,200,200,200,255,255,255,255}, //4 : lead1
{210,210, 210,210,210,210,255,255,255,255}, //5 : lead2
{220,220, 220,220,220,220,255,255,255,255}, //6 : lead3
{0,0, 0,0,0,0,255,255,255,255} //7 : bypass
};
//---- Variables globales ------------------------------------------------
int i; // Loop variable
unsigned char config;
unsigned char mode;
unsigned char canal;//canal MIDI (numero du canal MIDI -1)
unsigned char courbe; // courbe de progression
unsigned char codeMidi;//code midi
unsigned char Msg_PC;//Program change
unsigned char Msg_CC;//Control change
char canalTxt[4]; //canal Midi en texte
char codeTxt[10];
unsigned char oldNVol=0; //valeur precedente de volume
unsigned char paramCC;

//-----------------------------------------------------------------------------
// Fonction qui prend le texte en ROM pour l'utiliser
// car Lcd_Out ne gere pas les constantes (Sources Mikroelectronica)
char* message(const char* ctxt)
{
static char txt[18];
char j;
for (j=0; txt[j]=ctxt[j]; j++);
return txt;
}
//------------------------------------------------------------------------------
void affichercanal()
{
Lcd_Out(1, 1,message(msg[3])); // Ecrit le mot canal
Lcd_Chr(1, 7, canalTxt[1]); Lcd_Chr(1, 8, canalTxt[2]);
}
//------------------------------------------------------------------------------
void afficherCourbe()
{
Lcd_Out(1, 10,message(msg[7+courbe])); // Ecrit le nom de la courbe
}
//------------------------------------------------------------------------------
void initialisation()
{
IRP_bit=0;
//config du port A : SWITCH
adcon1=6;//Port A utilise en logique et non analogique
adcon0=0;
TRISA = 0x3F;//Port A en entree
//Config du port B : LCD
TRISB = 0;//Port B en sortie
Delay_ms(1000);
Lcd_Init(); // Initialize LCD
Delay_ms(1000);
Lcd_Cmd(_Lcd_CURSOR_OFF); // Cursor off
Lcd_Cmd(_Lcd_CLEAR);
//Config du port D
TRISD = 0x01;//Port D en sortie sauf D0 qui est en entrée
PortD.F1 = 0;PortD.F2 = 0;//initialisation des relais
//Config du PWM
PWM1_Init(30000); // Initialize PWM1 :frequence 30000Hz
//Config liaison serie
UART1_init(31250); // Vitesse du port MIDI : 31250 bauds
Delay_ms(100);
//lecture du canal
config=PortA;
canal=config & 0x0F;
courbe=(config & 0xF0)/16;
Msg_PC=192+canal;
Msg_CC=176+canal;
ByteToStr(canal+1,canalTxt);
Lcd_Out(1,1,message(msg[0])); // Ecrit sur la 1ere ligne
Lcd_out(2,1,message(msg[1])); //ecrit version
delay_ms(1000);
}
//**************** bar graph ************************************************
char* bargraph(int blocs)
{
static char txt[16]="-- --";
int i;
for (i=2;i for (i=blocs+2;i<12;i++)txt[i]=' ';
return txt;
}
//********* Changement de volume ***********************************
void ChangeVol(int niveau)
{ //en fonction de la valeur, le niveau de PWM change ainsi que le bargraph
int nbrBlocs;
int level;
if ((niveau>=0) && (niveau<24))
{nbrblocs=1;level=step[courbe][0];}
else
if ((niveau>=24) && (niveau<48))
{nbrblocs=2;level=step[courbe][1];}
else
if ((niveau>=48) && (niveau<72))
{nbrblocs=3;level=step[courbe][2];}
else
if ((niveau>=72) && (niveau<96))
{nbrblocs=4;level=step[courbe][3];}
else
if ((niveau>=96) && (niveau<120))
{nbrblocs=5;level=step[courbe][4];}
else
if ((niveau>=120) && (niveau<144))
{nbrblocs=6;level=step[courbe][5];}
else
if ((niveau>=144) && (niveau<168))
{nbrblocs=7;level=step[courbe][6];}
else
if ((niveau>=168) && (niveau<192))
{nbrblocs=8;level=step[courbe][7];}
else
if ((niveau>=192) && (niveau<216))
{nbrblocs=9;level=step[courbe][8];}
else
if (niveau>=216)
{nbrblocs=10;level=step[courbe][9];}
PWM1_Set_Duty(level);
Lcd_Out(2,1,bargraph(nbrBlocs));
}
//************Gestion du PROGRAM CHANGE *************************
void programChange()
{ //gestion du PC
while (UART1_Data_Ready()==0); // on attend la donnee
codeMidi=UART1_Read();
switch(codeMidi)
{ //2eme octet on change le relais 0
case 0 :
{
PortD.F1 = ~(PortD.F1) ;
if (PortD.F1)
Lcd_Out(2,15,message(msg[4]));
else
Lcd_Out(2,15,message(msg[6]));
break; //F1
}
case 1 :
{
PortD.F2 = ~(PortD.F2) ;
if (PortD.F2)
Lcd_Out(2,15,message(msg[5]));
else
Lcd_Out(2,15,message(msg[6]));
break; //F2
}
}
}
//******* Gestion du CONTROL CHANGE ********************************
void controlChange()
{ //gestion du CC
unsigned char NVol;
unsigned char cdg;
unsigned char paramCC2;
cdg=0;
while ((UART1_Data_Ready()==0) && (cdg<0xFF))
cdg++;
if (cdg==0xFF) return;
paramCC=UART1_Read();
switch (paramCC)
{
case 0x07 :
{//chgt de volume
//on attend la donnee de volume
cdg=0;
while ((UART1_Data_Ready()==0) && (cdg<0xFF))
cdg++;
if (cdg==0xFF) return;//timeout écoulé
paramCC2=UART1_Read();
NVol=paramCC2;
NVol=NVol & 0xFC;
if (NVol!=oldNVol)
{
oldNVol=NVol;
ChangeVol(NVol*2);
}
break;
}
case 0x01 :
{//Fonction 2 par CC
while (UART1_Data_Ready()==0);
paramCC2=UART1_Read();
switch(paramCC2)
{
case 0:
{
PortD.F2=0;
Lcd_Out(1,14,message(msg[6]));
break;
}
case 127:
{
PortD.F2=1;
Lcd_Out(1,14,message(msg[5]));
break;
}
default : break;
}
break;
}
case 0x00 :
{//Fonction 1 par CC
while (UART1_Data_Ready()==0);
paramCC2=UART1_Read();
switch(paramCC2)
{
case 0:
{
PortD.F1=0;
Lcd_Out(2,15,message(msg[6]));
break;
}
case 127:
{
PortD.F1=1;
Lcd_Out(2,15,message(msg[4]));
break;
}
default : break;
}
break;
}
}//de switch
} //de controlChange
//************** fonctionnement test ****************************
modeTest()
{
int i,j;
while(1)
{
Lcd_Out(1,1,message(msg[2])); // écrit TEST
// test led
Lcd_Out(2,1,message(msg[9]));
for (i=0;i<4;i++)
{
PortD.F1=~PortD.F1;
PortD.F2=~PortD.F2;
delay_ms(500);
}

// rampe PWM
Lcd_Out(1,1,message(msg[3]));
affichercanal();
afficherCourbe();

PWM1_Start();
for (i=0;i<255;i++) //255 = 100%
{
PWM1_Set_Duty(i);
j=i/25;
delay_ms(50);
Lcd_Out(2,1,bargraph(j));
}
PWM1_stop();
} //fin de while
}
//************ Changement de courbe ******************************
void changerCourbe()
{ //l'utilisateur change de courbe
delay_ms(200);
while (portD.F0==0); //on attend le relachement
courbe++;
if (courbe>nbCourbe-1) courbe=0;
afficherCourbe();
}
//*********** fonctionnement normal ***********************
void modeRun()
{
Lcd_Out(1,1,message(msg[0]));
Lcd_Out(2,1,message(msg[1]));
delay_ms(1000);
affichercanal();
afficherCourbe();
PWM1_Start(); // start PWM1
while (1)
{
if (portD.F0==0) changerCourbe();//l'utilisateur décide de changer de courbe
if (UART1_Data_Ready())
{ //une donnee est recue
codeMidi=UART1_Read();
if (codeMidi==Msg_CC)
{
controlChange(); // c'est un Control Change Msg sur 3 octets
}
else
if (codeMidi==Msg_PC)
{
programChange();
}
else UART1_write(codeMidi); //la donnée n'est pas pour cet esclave MIDI
}
} //end de While()
PWM1_Stop(); // start PWM1
}
//********** Programme principal ***********************************
void main()
{
initialisation();
mode=PortA & 0xF0;
delay_ms(1000);
if (PortD.F0==0) //le bouton est appuyé à la mise sous tension
modeTest();
else
modeRun();//fonctionnement normal
}