Vote utilisateur: 5 / 5

Etoiles activesEtoiles activesEtoiles activesEtoiles activesEtoiles actives
 

Nouveau module DIY : un Dual Quad Switch, pour les gates/triggers, les CV ou l'audio de environ -10V à +10V

Avant la description, le fonctionnement général est à voir ici :

 

flag fr

Le circuit

A base d'atmega328 comme d'habitude, facile à programmer. 2 * 2 * 4 sorties pilotées par deux DG509 (analog switch 1:4) ayant une entrée/sortie en commun. Des potentiomètres permettent de choisir le nombre de voies (0 à 4, 0 signifiant qu'aucune sortie n'est active) et le mode (up, down, pendulum, random). Il y a deux entrées clock indépendantes et deux entrées reset. Un mode 1*8 permet d'avoir un switch bidirectionnel 1 commun <-> 8 sorties, avec les mêmes modes de fonctionnement, le potentiomètre de la voie 1 permettant alors de choisir entre 0 et 8 sorties.

Cliquez pour le schéma.

Résultat

Le PCB : Double face assez simple. Les composants passifs sont tous de récup (merci Philippe !) ce qui donne un cout de revient très faible.

Consommation environ 35 mA sur +12V, 5 mA sur -12V, ça fonctionne très bien !

flag fr

The circuit

Atmega328 based as usual, for it's very easy to program. 2 * 2 * 4 outputs controlled by two DG509s (analog switch 1: 4) having an input / output in common. Potentiometers let you choose the number of channels (0 to 4) and the mode (up, down, pendulum, random). There are two independent clock inputs and two reset inputs. A 1 * 8 mode allows you to have a common bidirectional switch 1 <-> 8 outputs, with the same modes of operation. The left pot allows you to choose from 0 to 8 active outputs.

Schematics

Result

The PCB: 2 simple layers.The passive components are all second hands (thank you Philippe!). This gives a very low cost.

Consumption about 35 mA on + 12V, 5 mA on -12V, it works very well!

DualQuadSwitchimplant

 

DualQuadSwitchFacade

 

BOM

BOM switch

 

Code

Le code est divisé en trois sections :

main :
/*

   DualQuadSwitch

   2018-2020 LC

   -------------------------------------------------------
  D0: port D bit 0
  D1: port D bit 1
  D2: port D bit 2   CLK 0   sur interruptions
  D3: port D bit 3   CLK 1
  D4: port D bit 4   RST 0
  D5: port D bit 5   RST 1
  D6: port D bit 6
  D7: port D bit 7   MODE GENERAL (2*4 / 1*8)

  D8: port B bit 0    A0-0   1er DG509 adressé avec 2 bits
  D9: port B bit 1    A0-1
  D10: port B bit 2   A1-0   2ème
  D11: port B bit 3   A1-1
  D12: port B bit 4   EN0    EN = 1 pour compter, 0 pour OFF
  D13: port B bit 5   EN1

  A0: port C bit 0
  A1: port C bit 1
  A2: port C bit 2   MODE 0 (up, down, etc.)
  A3: port C bit 3   nbrePas 0 (0 à 4 ou 0 à 8 suivant le mode général)
  A4: port C bit 4   MODE 1
  A5: port C bit 5   nbrePas 1
*/

// pin en entrées
#define pinINTERRUPT0 2
#define pinINTERRUPT1 3
#define pinRESET0     4
#define pinRESET1     5
#define pinGENERAL    7
// de D9 à D13 : gestion des 2 DG509

// pins des potards
#define potSTEP0  2
#define potMODE0  3
#define potSTEP1  4
#define potMODE1  5

// modes de fonctionnement
#define modeUP    0
#define modeDOWN  1
#define modePEND  2
#define modeRND   3

struct {
  unsigned long lastInterrupt;
  byte mode, aMode;
  byte nbrePas, aNbrePas, nbrePas8, aNbrePas8;
  int compteur, compteurPendulum;
  boolean reset, on;
  volatile boolean clockInInterrupt;
} voie[2];

// variables pour la lecture régulière des potards et des interrupteurs
unsigned long dateLecture;
byte compteurPotard;

boolean mode8, aMode8;

void setup() {
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);
  
  pinMode(pinRESET0, INPUT_PULLUP);
  pinMode(pinRESET1, INPUT_PULLUP);

  pinMode(pinGENERAL, INPUT_PULLUP);
  
  for (int i = 0; i < 2; i++) {
    voie[i].clockInInterrupt = false;
    voie[i].compteur = 0;
    voie[i].compteurPendulum = 0;
    voie[i].reset = false;
  }
  
  initLecturePanel();
  majVoie();
  initInterrupt();
}


void loop() {
  lecturePanel();
  if (mode8) {
    majReset(0);
    if (voie[0].clockInInterrupt == true) {
      voie[0].clockInInterrupt = false;
      majSortie8();
    }
  } else {
    for (int i = 0; i < 2; i++) {
      majReset(i);
      if (voie[i].clockInInterrupt == true) {
        voie[i].clockInInterrupt = false;
        majSortie(i);
      }
    }
  }
}
entrées/sorties :
/*

   gestion des pin

*/
// si reset sur une voie : on remet le compteur à 0 (1ere position)
void majReset(int laVoie) {
  if (voie[laVoie].reset == true) {
    voie[laVoie].compteur = 0;
    majVoie();
  }
}

void majSortie(int laVoie) {
  int ns;
  if (voie[laVoie].on) {
    if (voie[laVoie].nbrePas == 1) {
      voie[laVoie].compteur = 0;
    } else {
      switch (voie[laVoie].mode) {
        case modeUP:
          voie[laVoie].compteur++;
          if (voie[laVoie].compteur >= voie[laVoie].nbrePas) {
            voie[laVoie].compteur = 0;
          }
          break;

        case modeDOWN:
          voie[laVoie].compteur--;
          if (voie[laVoie].compteur < 0) {
            voie[laVoie].compteur = voie[laVoie].nbrePas - 1;
          }
          break;

        case modePEND:
          ns = 2 * (voie[laVoie].nbrePas - 1);
          voie[laVoie].compteurPendulum = (voie[laVoie].compteurPendulum + 1) % ns;
          if (voie[laVoie].compteurPendulum < voie[laVoie].nbrePas) {
            voie[laVoie].compteur = voie[laVoie].compteurPendulum;
          } else {
            voie[laVoie].compteur = ns - voie[laVoie].compteurPendulum;
          }
          break;

        case modeRND:
          voie[laVoie].compteur = random(voie[laVoie].nbrePas);
          break;

      }
    }
  }
  majVoie();
}

void majSortie8() {
  int ns;
  if (voie[0].on) {
    if (voie[0].nbrePas == 1) {
      voie[0].compteur = 1;
    } else {
      switch (voie[0].mode) {
        case modeUP:
          voie[0].compteur = (voie[0].compteur + 1) % voie[0].nbrePas;
          break;

        case modeDOWN:
          voie[0].compteur--;
          if (voie[0].compteur < 0) {
            voie[0].compteur = voie[0].nbrePas - 1;
          }
          break;

        case modePEND:
          ns = 2 * (voie[0].nbrePas - 1);
          voie[0].compteurPendulum = (voie[0].compteurPendulum + 1) % ns;
          if (voie[0].compteurPendulum < voie[0].nbrePas) {
            voie[0].compteur = voie[0].compteurPendulum;
          } else {
            voie[0].compteur = ns - voie[0].compteurPendulum;
          }
          break;

        case modeRND:
          voie[0].compteur = random(voie[0].nbrePas);
          break;
      }
    }
    majVoie();
  }
}



void majVoie() {
  // on n'a qu'une voie de 8 inter
  if (mode8) {
    if (voie[0].on) {
      if (voie[0].compteur < 4) {
        // premier DG509 ON et deuxième OFF
        PORTB = (PORTB & B11011100) | B00010000 | voie[0].compteur;
      } else {
        // premier DG509 OFF et deuxième ON
        PORTB = (PORTB & B11100011) | B00100000 | ((voie[0].compteur - 4) << 2);
      }
    } else {
      // les deux voies OFF
      PORTB = PORTB & B11001111;
    }
  } else {
    // mode 2 * 4 voies indépendantes
    if (voie[0].on) {
      // 1er DG509, EN = PORT B bit n°4 et l'adresse sur bits 0 et 1
      // il faut mettre à 0 les 2 bits avant de les lever
      PORTB = (PORTB & B11111100) | B00010000 | voie[0].compteur;
    } else {
      // 1er DG509, EN = PORT B bit n°4
      PORTB = PORTB & B11101111;
    }

    if (voie[1].on) {
      // 2eme DG509, EN = PORT B bit n°5 et l'adresse sur bits 2 et 3
      PORTB = (PORTB & B11110011) | B00100000 | (voie[1].compteur << 2);
    } else {
      // 2eme DG509, EN = PORT B bit n°5
      PORTB = PORTB & B11011111;
    }
  }
}

void lecturePanel() {
  unsigned long tempo = millis();
  voie[0].reset = (digitalRead(pinRESET0) == LOW);   // c'est un pullup...
  voie[1].reset = (digitalRead(pinRESET1) == LOW);

  if (tempo - dateLecture > 50) {
    dateLecture = tempo;

    compteurPotard ++;
    if (compteurPotard > 4) {
      compteurPotard = 0;
    }
    switch (compteurPotard) {
      case 0:
        // le mode de la voie 0
        voie[0].mode = analogRead(potMODE0) >> 8;
        if (voie[0].mode != voie[0].aMode) {
          voie[0].aMode = voie[0].mode;
          majVoie();
        }
        break;
      case 1:
        // le mode de la voie 1
        voie[1].mode = analogRead(potMODE1) >> 8;
        if (voie[1].mode != voie[1].aMode) {
          voie[1].aMode = voie[1].mode;
          majVoie();
        }
        break;
      case 2:
        // le nbre de pas de la voie 0
        if (mode8) {
          voie[0].nbrePas = analogRead(potSTEP0) / 114;
          voie[1].on = false;
        } else {
          voie[0].nbrePas = analogRead(potSTEP0) / 205;
        }
        voie[0].on = voie[0].nbrePas > 0 ? true : false;
        if (voie[0].nbrePas != voie[0].aNbrePas) {
          voie[0].aNbrePas = voie[0].nbrePas;
          if (voie[0].compteur >= voie[0].nbrePas) {
            voie[0].compteur = voie[0].nbrePas - 1;
          }
          majVoie();
        }
        break;
      case 3:
        // le nbre de pas de la voie 1
        if (mode8) {
          voie[1].on = false;
        } else {
          voie[1].nbrePas = analogRead(potSTEP1) / 205;
          voie[1].on = voie[1].nbrePas > 0 ? true : false;
          if (voie[1].nbrePas != voie[1].aNbrePas) {
            voie[1].aNbrePas = voie[1].nbrePas;
            if (voie[1].compteur >= voie[1].nbrePas) {
              voie[1].compteur = voie[1].nbrePas - 1;
            }
            majVoie();
          }
        }
        break;
      case 4:
        // le pot de sélection entre "1x8" et "2x4"
        mode8 = (digitalRead(pinGENERAL) == LOW);
        if (mode8 != aMode8) {
          aMode8 = mode8;
          majVoie();
        }
        break;
    }
  }
}

void initLecturePanel() {
  compteurPotard = 0;
  voie[0].mode = analogRead(potMODE0) >> 8;
  voie[1].mode = analogRead(potMODE1) >> 8;

  mode8 = (digitalRead(pinGENERAL) == HIGH);

  if (mode8) {
    voie[0].nbrePas = analogRead(potSTEP0) / 114;
    voie[0].on = voie[0].nbrePas > 0 ? true : false;
    voie[1].nbrePas = analogRead(potSTEP1) / 205;
    voie[1].on = false;
  } else {
    voie[0].nbrePas = analogRead(potSTEP0) / 205;
    voie[0].on = voie[0].nbrePas > 0 ? true : false;
    voie[1].nbrePas = analogRead(potSTEP1) / 205;
    voie[1].on = voie[1].nbrePas > 0 ? true : false;
  }
}
interruptions :
void initInterrupt() {
  // broches UNO
  pinMode(pinINTERRUPT0, INPUT_PULLUP);
  pinMode(pinINTERRUPT1, INPUT_PULLUP);

  attachInterrupt(0, clockIn0, FALLING);    //    attention : gate inverseuse
  attachInterrupt(1, clockIn1, FALLING);

}

// front montant sur la broche D2 = CLOCK IN 0
void clockIn0() {
  unsigned long tempo = millis();
  // antirebonds basic si appui sur le BP plutôt que jack
  if (tempo - voie[0].lastInterrupt > 10) {
    voie[0].lastInterrupt = tempo;
    voie[0].clockInInterrupt = true;
  }
}

// front montant sur la broche D3 = CLOCK IN 1
void clockIn1() {
  if (!mode8) {
    unsigned long tempo = millis();
    if (tempo - voie[1].lastInterrupt > 10) {
      voie[1].lastInterrupt = tempo;
      voie[1].clockInInterrupt = true;
    }
  }
}