Siloged

logoVolume MIDI / Cry Baby 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
En avril 2025, j'ai piloté une pédale Wah-Wah cry baby en remplaçant le potentiomètre de la Cry Baby de Dunlop par la sortie du VolumeMidi Les fichiers de fabrication se trouvent plus bas dans cette page.
Hormis le titre, le programme ne nécessite pas de modification.
De la même manière il n'y a pas de modification à apporter à la partie électronique.

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 / Cry Baby Midi******************************** //* Norbert BRAUN * //* CPU PIC16F877 cadencé a 20MHz * //* version 2 mars 2016 / 19 avril 2025 * //************************************************************************** //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<br> //-----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() {<br> 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<blocs+2;i++)txt[i]=0xFF; 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 du bouton 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 } Télécharger les documents de fabrication.