Siloged

Volume MIDI

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

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

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.
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
    }