Avant la description, le fonctionnement général est à voir ici :
{youtube}wbgVoboskgY{/youtube}
Le circuitA 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.RésultatLe PCB : Double face assez simple. Les composants passifs sont tous de récup 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 ! |
The circuitAtmega328 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. ResultThe PCB: 2 simple layers.The passive components are all second hands. This gives a very low cost. Consumption about 35 mA on + 12V, 5 mA on -12V, it works very well! |
BOM
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; } } }
- Clics : 9385