
Avant la description, le fonctionnement général est à voir ici :
Il existe des schémas basés sur des portes AND, OR etc, mais je voulais quelque chose de simple et de souple : 4 paires d'entrées sont associées à 4 sorties via 8 fonctions logiques indépendantes, et 2 sorties supplémentaires sont disponibles. Au prix des atmega328, c'est même moins cher qu'un montage à base de portes logiques. C'est moins rapide, mais largement suffisant pour mon usage (environ 80 ns) ! L'avantage est que je dispose de 2 sorties programmables selon mes besoins.
Les 9 fonctions sont accessibles indépendamment avec les potards : AND, NAND, OR, NOR, XOR, NXOR, NOT (voie 1), Set/Reset, ALEAS (sur front voie 1).


Le code est divisé en trois sections :
/*
QuadLogic v0.4
Laurent Chaté 2019-2020
www.la-roue-tourne.fr
4 paires d'entrée sont logiquement liées sur 4 sorties
à l'aide de 7 opérations logiques + 1 aléas + S/R
durée d'un cycle complet de scan/calcul/sortie < 80 ns
-------------------------------------------------------
D0 : out G1
D1 : out G2
D2 : in A1
D3 : in A2
D4 : in B1
D5 : in B2
D6 : in C1
D7 : in C2
D8 : in D1
D9 : in D2
D10: out A
D11: out B
D12: out C
D13: out D
A0 : potard MODE 0 (23)
A1 : potard MODE 1
A2 : potard MODE 2
A3 : potard MODE 3
A4 : N/A
A5 : N/A
*/
// entrées
#define pinA1 2
#define pinA2 3
#define pinB1 4
#define pinB2 5
#define pinC1 6
#define pinC2 7
#define pinD1 8
#define pinD2 9
// sorties
#define pinGP1 0
#define pinGP2 1
#define pinA 10
#define pinB 11
#define pinC 12
#define pinD 13
byte pinIn[] = {pinA1, pinA2, pinB1, pinB2, pinC1, pinC2, pinD1, pinD2};
byte entree[8];
byte pinOut[] = {pinA, pinB, pinC, pinD, pinGP1, pinGP2};
byte sortie[4];
// --------------------------------------------------- lecture potentiometres
byte compteurPot;
unsigned long timePot;
// --------------------------------------------------- les modes des 4 voies
enum modes {
MODE_AND,
MODE_NAND,
MODE_OR,
MODE_NOR,
MODE_XOR,
MODE_NXOR,
MODE_NOT,
MODE_SR,
MODE_ALEAS
};
modes mode[4];
// variables temporaires pour la gestion de l'aléas
byte oldX1[4], oldY[4];
// les seuils pour les aléas
int seuil[4] = {25, 50, 75, 90};
void setup() {
int i;
initPotard();
randomSeed(mode[0] + mode[1] + mode[2] + mode[3]);
for (i = 0; i < 8; i++) {
pinMode(pinIn[i], INPUT);
}
for (i = 0; i < 6; i++) {
pinMode(pinOut[i], OUTPUT);
}
}
void loop() {
gererPotard();
lectureEntrees();
calculSorties();
}
/*
les fonctions
*/
void lectureEntrees() {
byte pd = PIND,
pb = PINB;
entree[0] = (pd & B00000100) >> 2; // D2
entree[1] = (pd & B00001000) >> 3; // D3
entree[2] = (pd & B00010000) >> 4; // D4
entree[3] = (pd & B00100000) >> 5; // D5
entree[4] = (pd & B01000000) >> 6; // D6
entree[5] = (pd & B10000000) >> 7; // D7
entree[6] = pb & B00000001 ; // D8
entree[7] = (pb & B00000010) >> 1; // D9
}
/*
les calculs logiques
0 AND
1 NAND
2 OR
3 NOR
4 XOR
5 NXOR
6 NOT : on inverse l'entrée n°1 indépendemment de la 2
7 SR : SET/RESET : 1 sur la voie 1 SET, puis 1 sur la voie 2 RESET
8 ALEAS : on déclanche 1 bit aléatoire sur apparition de 1 sur l'entrée 1
*/
void calculSorties() {
byte x1, x2, y;
byte g1 = 1,
g2 = 0;
for (int i = 0; i < 4; i++) {
x1 = entree[ 2 * i ];
x2 = entree[ 2 * i + 1];
switch (mode[i]) {
case MODE_AND:
y = x1 & x2;
break;
case MODE_NAND:
y = 1 - (x1 & x2);
break;
case MODE_OR:
y = x1 | x2;
break;
case MODE_NOR:
y = 1 - (x1 | x2);
break;
case MODE_XOR:
y = x1 ^ x2;
break;
case MODE_NXOR:
y = 1 - (x1 ^ x2);
break;
case MODE_NOT:
y = 1 - x1;
break;
case MODE_ALEAS:
if (x1 == 1) {
if (oldX1[i] == 1) {
y = oldY[i];
} else {
y = (random(100) > seuil[i]);
oldY[i] = y;
oldX1[i] = 1;
}
} else {
y = oldY[i];
oldX1[i] = 0;
}
break;
case MODE_SR:
y = 1 - sortie[i];
if (x1 == 1) {
y = 1;
}
if (x2 == 1) {
y = 0;
}
break;
}
/* inversion du signe pour l'électronique des portes de sortie
* à modifier pour une future version à base de suiveurs
*/
sortie[i] = 1 - y;
g1 &= y;
g2 |= y;
}
// Ecriture directe des ports
// D0 et D1
PORTD = (PORTD & 0b11111100) | (1 - g1) | ((1 - g2) << 1);
// D10 à D13
PORTB = (PORTB & 0b11000011) | (sortie[0] << 2) | (sortie[1] << 3) | (sortie[2] << 4) | (sortie[3] << 5) ;
}
void gererPotard() {
unsigned long tempo = millis();
// un scan toutes les 100 ms : très suffisant
if (tempo - timePot > 100) {
timePot = tempo;
compteurPot = (compteurPot + 1) & 0x03;
// j'ai soudé mes potards à l'envers sur mon module, d'ou le calcul foireux
// normalement, c'est :
// mode[compteurPot] = analogRead(compteurPot) / 114;
mode[compteurPot] = MODE_ALEAS - analogRead(compteurPot) / 114;
}
}
void initPotard() {
timePot = millis();
mode[0] = MODE_ALEAS - analogRead(A0) / 114;
mode[1] = MODE_ALEAS - analogRead(A1) / 114;
mode[2] = MODE_ALEAS - analogRead(A2) / 114;
mode[3] = MODE_ALEAS - analogRead(A3) / 114;
}

