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! |


Le code est divisé en trois sections :
/*
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);
}
}
}
}
/*
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;
}
}
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;
}
}
}