Archivi tag: ttl

Arduino – algebra booleana e funzioni logiche – dai componenti discreti, agli integrati, alla codifica in C

Nuovo Istituto, nuove avventure didattiche. Con le classi di questo nuovo anno scolastico più elettronica e più automazione. Tra gli argomenti che ho rivisto negli scorsi mesi in virtù delle attività che vorrò svolgere, molte sperimentazioni dedicata all’elettronica digitale e all’algebra di Boole, ho riformulato le esercitazioni con componenti discreti che utilizzano i classici integrati TTL 74XX: 7404, 7432, 7408, 7402, 7400, 7486, 74266 corrispondenti alle porte logiche: NOT, OR, AND, NOR, NAND, XOR, XNOR a queste attività aggiungerò, in una fase successiva, la realizzazione delle porte logiche con Arduino, quindi progettazione di semplici shield per la dimostrazione delle tabelle di verità degli operatori logici fondamentali e derivati.

Per rendere più interessante il laboratorio di elettronica, oltre che usare la breadboard, realizzare pcb e saldare, cercherò se il tempo lo permetterà, di far realizzare la scheda Arduino prima su breadboard e poi realizzare una nostra personalissima scheda Arduino con protezioni specifiche sulle uscite digitali ed altro… sogni nel cassetto… vedremo, un passettino alla volta.

Questo articolo è da intendersi come esercitazione e di supporto alla parte teorica ed è dedicata agli studenti delle classi 3′ e 4′ automazione, per lo svolgimento si richiede che gli allievi abbiano seguito un corso base su Arduino. Di seguito senza dilungarmi riprendo alcuni concetti di base che hanno solo l’obiettivo di comprendere meglio la parte sviluppata su Arduino, al fondo due circuiti e due sketch di esempio che potrete migliorare ed espandere.

Avviso per gli studenti

Quest’anno faremo automazione, non solo con PLC ma anche con microcontrollori (Arduino), quindi incominciare a strutturare in C le funzioni logiche sarà essenziale per iniziare a costruire i mattoncini di base dell’automazione, vedremo come costruire altre funzioni nel corso dell’anno.

Premessa (per ripassare velocemente)

I circuiti digitali sono presenti in moltissimi strumenti che utilizzimo ogni giorno, i più noti sono i computer costituiti principalmente da circuiti digitali che, come già sapete, elaborano segnali logici 0 e 1.

Qualsiasi calcolo all’interno di un computer utilizza l’aritmetica binaria e l’adozione di questa aritmetica è stata fatta perché i bit sono rappresentabili in modo semplice tramite dispositivi elettronici in cui è possibile distinguere i 2 stati del potenziale elettrico: high e low a cui si associano i numeri 1 e 0.

Partendo da questi presupposti è possibile costruire un sistema di calcolo che impiega i soli due simboli 0 e 1 che viene chiamato sistema logico binario.

Fu George Boole (1815-1864) che per primo costruì un modello matematico fondato su una logica di tipo binario, tale modello prende il nome di: algebra di Boole.

Come diffusamente esposto durante le lezioni di teoria, l’algebra booleana utilizza equazioni ed espressioni, ma segue le leggi della logica e non quelle dell’aritmetica, per cui le operazioni seguono regole differenti dall’algebra convenzionale.

Le porte logiche realizzano le operazioni logiche dell’algebra binaria.

Porte logiche fondamentali

Somma logica OR

L’operazione può essere effettuata su due o più variabili di ingresso. La somma logica OR assumerà il valore 1 se almeno una delle variabili di ingresso è al valore 1.

Se chiamiamo con A e B le variabili di ingresso e con Y la variabile di uscita, la somma logica assumerà lo stato logico 1 se almeno una delle due variabili assume lo stato logico 1.


si legge A OR B

Nella figura che segue è mostrata la tabella della verità con le quattro possibili combinazioni delle variabili di ingresso A e B è il simbolo logico corrispondente. Nella colonna Y sono indicati i valori dalla variabile di uscita Y che soddisfa la definizione della porta logica OR.

Prodotto logico AND

L’operazione può essere effettuata su due o più variabili di ingresso. Il prodotto logico AND assumerà il valore 1 se tutte le variabili di ingresso assumeranno il valore 1.

Se chiamiamo con A e B le variabili di ingresso e con Y la variabile di uscita, il prodotto logico assumerà lo stato logico 1 solo se tutte le variabili di ingresso sono allo stato 1.

si legge A AND B

Nella figura che segue è mostrata la tabella della verità con le quattro possibili combinazioni delle variabili di ingresso A e B è il simbolo logico corrispondente. Nella colonna Y sono indicati i valori dalla variabile di uscita Y che soddisfa la definizione della porta logica AND.

Negazione NOT

L’operazione può essere effettuata su una sola variabile di ingresso. Se chiamiamo con A la variabile di ingresso e con Y la variabile di uscita, la negazione farà assumere all’uscita il valore opposto a quello applicato all’ingresso.

si legge A NEGATO oppure A COMPLEMENTATO oppure NOT A

Nella figura che segue è mostrata la tabella della verità con le due possibili combinazioni
di A ed il simbolo logico corrispondente. Nella colonna Y è indicato il valore della variabile di uscita Y che soddisfa la definizione della porta logica NOT.

Porte logiche derivate

Sono le porte logiche ottenute partendo da una o più porte logiche fondamentali però poiché sono estremamente importanti per l’elettronica sono rappresentate con un simbolo unico.

Somma logica negata NOR

L’operazione può essere effettuata su due o più variabili di ingresso. Se chiamiamo con A e B le variabili di ingresso e con Y la variabile di uscita, la somma logica negata assumerà lo stato logico 1 solo se tutte le variabili di ingresso sono allo stato 1, in tutti gli altri casi l’uscita assumerà il valore 1.

La somma logica negata corrrisponde al collegamento di una OR seguita da una porta NOT.

si legge A NOR B

Nella figura che segue è mostrata la tabella della verità con le quattro possibili combinazioni delle variabili di ingresso A e B è il simbolo logico corrispondente. Nella colonna Y sono indicati i valori dalla variabile di uscita Y che soddisfa la definizione della porta logica NOR.

Prodotto logico negato NAND

L’operazione può essere effettuata su due o più variabili di ingresso. Il prodotto logico negato NAND assumerà il valore 1 se tutte le variabili di ingresso assumeranno il valore 0, in tutti gli altri casi l’uscita assumerà il valore 1.

Il prodotto logico negato corrrisponde al collegamento di una AND seguita da una porta NOT.

si legge A NAND B

Nella figura che segue è mostrata la tabella della verità con le quattro possibili combinazioni delle variabili di ingresso A e B è il simbolo logico corrispondente. Nella colonna Y sono indicati i valori dalla variabile di uscita Y che soddisfa la definizione della porta logica NAND.

OR esclusivo – XOR

L’operazione può essere effettuata su due o più variabili di ingresso. l’OR esclusivo assumerà il valore 1 e solo se vi è almeno un ingresso che differisce dagli altri, mentre varrà 0 se tutti gli ingressi assumono lo stesso valore.

Nel caso di due variabili di ingresso A e B, l’OR esclusivo assumerà il valore 1 se gli ingressi assumeranno valori diversi e varrà 0 se gli ingressi assumono lo stesso valore.

si legge A OR ESCLUSIVO B oppure A DIVERSO B

Nella seguente figura si mostra la tabella della verità con le quattro possibili combinazioni tra A e B ed il simbolo logico relativo ad una porta XOR. Nella colonna Y si sono posti i valori assunti dall’uscita Y che soddisfa la definizione della porta XOR.

L’OR ESCLUSIVO può essere espresso anche dalla seguente formula:

formula da ricordare quando dovrete implementare il codice C per Arduino che realizza questa funzione.

NOR esclusivo XNOR

L’operazione può essere effettuata su due o più variabili di ingresso. Il NOR esclusivo assumerà il valore 1 se e solo se tutti gli ingressi hanno il medesimo valore logico, è equivalente alla negazione della porta XOR.

Nel caso di due variabili di ingresso A e B, l’XNOR assumerà il valore 1 se gli ingressi assumeranno valori uguali e varrà 0 se gli ingressi assumono valore diverso.

e si legge A NOR ESCLUSIVO B oppure A COINCIDENTE CON B.

Nella seguente figura si mostra la tabella della verità con le quattro possibili combinazioni tra A e B ed il simbolo logico relativo ad una porta XNOR. Nella colonna Y si sono posti i valori assunti dall’uscita Y che soddisfa la definizione della porta XNOR.

Il NOR ESCLUSIVO può essere espresso anche dalla seguente formula:

formula da ricordare quando dovrete implementare il codice C per Arduino che realizza questa funzione.

Porte logiche con Arduino

Partiamo ora con la realizzazione delle porte logiche descritte sompra utilizzando Arduino.

In un precedente post ho descritto quali sono gli operatori logici disponibili all’interno di Arduino

Se gli ingressi A e B li indichiamo con le varibili:

  • pinInA
  • pinInB

e l’uscita Y la indichiamo con

  • pinOutY

usando la notazione in C che ritrovate nel link indicato sopra si otterrà:

OR

Y = A || B

AND

Y = A && B

NOT

Y = !A

NOR

Y = !(A || B)

NAND

Y = !(A && B)

XOR

Y = A ⊕ B = (A && !B) || (!A && B)

XNOR

Y = !(A ⊕ B) = !((A && !B) || (!A && B))

Premesso ciò la scrittura dello sketch è estremamente semplice.

Realizziamo i due ingressi A e B mediante due pulsanti connessi rispettivamente ai pin 8 e 7, mentre l’uscita sarà connessa al pin 9.

/* Michele Maffucci
   08.09.18

   Versione 1 - sostituzione del codice per verificare
   la tabella di verità degli operatori logici
*/

int pinOutY = 9;
int pinInA = 8;
int pinInB = 7;

void setup()
{
  pinMode(pinOutY, OUTPUT); 
  pinMode(pinInA, INPUT);
  pinMode(pinInB, INPUT);
}
void loop()
{
  boolean statoInA = digitalRead(pinInA);
  boolean statoInB = digitalRead(pinInB);
  boolean statoOut;

  // --- sostituire l'operatore logico indicata nel commento in fondo --- 

  // funzione logica OR
  statoOut = statoInA || statoInB;
  digitalWrite(pinOutY, statoOut);
}

/*
Sostituisci all'interno del loop nella posizione indicata

  // operatore logico OR
  statoOut = statoInA || statoInB;
  
  // operatore logico AND
  statoOut = statoInA && statoInB;

  // operatore logico NOT
  statoOut = !statoInA;
  
  // operatore logico NOR
  statoOut = !(statoInA || statoInB);

  // operatore logico NAND
  statoOut = !(statoInA && statoInB);

  // operatore logico XOR
  statoOut = (statoInA && !statoInB) || (!statoInA && statoInB);
  
  // operatore logico XNOR
  statoOut = !((statoInA && !statoInB) || (!statoInA && statoInB));
*/

Vediamo ora come realizzare uno sketch che permette dalla Serial Monitor di selezionare mediante menù il tipo di porta logica che si intende simulare; quando la selezione viene effettuata da menù la pressione dei pulsanti deve realizzare la tabella di verità della funzione logica selezionata.

Un pulsante aggiuntivo, che chiameremo: “AVVIA MENU'” verrà utilizzato per riavviare il menù di scelta.

Le azioni quindi saranno:

  1. primo avvio – selezione funzione (da 1 a 7) inserendo da tastiera il numero sulla Serial Monitor
  2. verifica della tabella di verità premendo i pulsanti A e B controllando che il LED, che identifica la Y, sarà acceso per un livello logico 1 e sarà spento per un livello logico 0
  3. Cambio funzione logica:
    1. premere il pulsante: “AVVIA MENU'”
    2. seleziono la funzione logica da verificare e ripetere nuovamente dal passo 1

/* Michele Maffucci
   08.09.18

   Versione 2 - scelta dell'operatore logico da menù stampato sulla Serial Monitor.

 All'avvio compare un menù di selezione dell'operatore logico
  1: OR
  2: AND
  3: NOT
  4: NOR
  5: NAND
  6: XOR
  7: XNOR

  Scrivere sulla Serial Monitor il numero corrispondente e con
  i pulsanti A e B verificare la tabella di verità

  La selezione di un altro operatore avviene premendo il pulsante: AVVIA MENU
  che mostrerà nuovamente sulla Serial Monitor il menù di selezione operatore
*/

int pinOutY = 9;
int pinInA = 8;
int pinInB = 7;

int pinChiave = 10;

boolean statoInA;
boolean statoInB;
boolean statoOut;

// array da utilizzare come chiave di stampa
// per le tabelle di verità delle singole funzioni logiche

int chiavi[] = {1, 1, 1, 1, 1, 1, 1};

void setup()
{
  Serial.begin(9600);
  pinMode(pinOutY, OUTPUT);
  pinMode(pinInA, INPUT);
  pinMode(pinInB, INPUT);

  pinMode(pinInB, INPUT);

  // funzione per la stampa sulla Serial Monitor
  // del menù di scelta dell'operatore logico
  stampaMenu();
}

void stampaMenu() {
  Serial.println("---- Seleziona l'operatore logico ----");
  Serial.println("1: OR");
  Serial.println("2: AND");
  Serial.println("3: NOT");
  Serial.println("4: NOR");
  Serial.println("5: NAND");
  Serial.println("6: XOR");
  Serial.println("7: XNOR");
  Serial.println("--------------------------------------");
  Serial.println("");
  Serial.println("");
}

// La funzione di stampa tabella di verità pone a 0 il valore della
// chiave, chiavi[n] = 0, dove n identifica la tabella di verità
// e il valore associato, 1 o 0, indica il fatto di poterla stampare oppure no.
// chiavi[n] = 0 permette di non stampare ciclicamente una stessa tabella
// di verità all'interno dei singoli cicli while presenti nel loop in cui avviene
// il controllo di quale selezione è stata fatta.

void stampaOr() {
  Serial.println("Hai selezionato l'operatore OR");
  Serial.println("La tabella di verità è:");
  Serial.println(" A  |  B  |  Y  ");
  Serial.println("----|-----|-----");
  Serial.println(" 0  |  0  |  0  ");
  Serial.println(" 0  |  1  |  1  ");
  Serial.println(" 1  |  0  |  1  ");
  Serial.println(" 1  |  1  |  1  ");
  Serial.println("");
  Serial.println("----------------------------------------------------------------");
  Serial.println("Per selezionare un altro operatore premi il pulsante AVVIA MENU'");
  Serial.println("----------------------------------------------------------------");
  Serial.println("");

  mettiUnoChiavi();
  chiavi[0] = 0;
}

void stampaAnd() {
  Serial.println("Hai selezionato l'operatore AND");
  Serial.println("La tabella di verità è:");
  Serial.println(" A  |  B  |  Y  ");
  Serial.println("----|-----|-----");
  Serial.println(" 0  |  0  |  0  ");
  Serial.println(" 0  |  1  |  0  ");
  Serial.println(" 1  |  0  |  0  ");
  Serial.println(" 1  |  1  |  1  ");
  Serial.println("");
  Serial.println("----------------------------------------------------------------");
  Serial.println("Per selezionare un altro operatore premi il pulsante AVVIA MENU'");
  Serial.println("----------------------------------------------------------------");
  Serial.println("");

  mettiUnoChiavi();
  chiavi[1] = 0;
}

void stampaNot() {
  Serial.println("Hai selezionato l'operatore NOT");
  Serial.println("La tabella di verità è:");
  Serial.println(" A  |  Y  ");
  Serial.println("----|-----");
  Serial.println(" 0  |  1  ");
  Serial.println(" 1  |  0  ");
  Serial.println("");
  Serial.println("----------------------------------------------------------------");
  Serial.println("Per selezionare un altro operatore premi il pulsante AVVIA MENU'");
  Serial.println("----------------------------------------------------------------");
  Serial.println("");

  mettiUnoChiavi();
  chiavi[2] = 0;
}

void stampaNor() {
  Serial.println("Hai selezionato l'operatore NOR");
  Serial.println("La tabella di verità è:");
  Serial.println(" A  |  B  |  Y  ");
  Serial.println("----|-----|-----");
  Serial.println(" 0  |  0  |  1  ");
  Serial.println(" 0  |  1  |  0  ");
  Serial.println(" 1  |  0  |  0  ");
  Serial.println(" 1  |  1  |  0  ");
  Serial.println("");
  Serial.println("----------------------------------------------------------------");
  Serial.println("Per selezionare un altro operatore premi il pulsante AVVIA MENU'");
  Serial.println("----------------------------------------------------------------");
  Serial.println("");

  mettiUnoChiavi();
  chiavi[3] = 0;
}

void stampaNand() {
  Serial.println("Hai selezionato l'operatore NAND");
  Serial.println("La tabella di verità è:");
  Serial.println(" A  |  B  |  Y  ");
  Serial.println("----|-----|-----");
  Serial.println(" 0  |  0  |  1  ");
  Serial.println(" 0  |  1  |  1  ");
  Serial.println(" 1  |  0  |  1  ");
  Serial.println(" 1  |  1  |  0  ");
  Serial.println("");
  Serial.println("----------------------------------------------------------------");
  Serial.println("Per selezionare un altro operatore premi il pulsante AVVIA MENU'");
  Serial.println("----------------------------------------------------------------");
  Serial.println("");

  mettiUnoChiavi();
  chiavi[4] = 0;
}

void stampaXor() {
  Serial.println("Hai selezionato l'operatore XOR");
  Serial.println("La tabella di verità è:");
  Serial.println(" A  |  B  |  Y  ");
  Serial.println("----|-----|-----");
  Serial.println(" 0  |  0  |  0  ");
  Serial.println(" 0  |  1  |  1  ");
  Serial.println(" 1  |  0  |  1  ");
  Serial.println(" 1  |  1  |  0  ");
  Serial.println("");
  Serial.println("----------------------------------------------------------------");
  Serial.println("Per selezionare un altro operatore premi il pulsante AVVIA MENU'");
  Serial.println("----------------------------------------------------------------");
  Serial.println("");

  mettiUnoChiavi();
  chiavi[5] = 0;
}

void stampaXnor() {
  Serial.println("Hai selezionato l'operatore XNOR");
  Serial.println("La tabella di verità è:");
  Serial.println(" A  |  B  |  Y  ");
  Serial.println("----|-----|-----");
  Serial.println(" 0  |  0  |  1  ");
  Serial.println(" 0  |  1  |  0  ");
  Serial.println(" 1  |  0  |  0  ");
  Serial.println(" 1  |  1  |  1  ");
  Serial.println("");
  Serial.println("----------------------------------------------------------------");
  Serial.println("Per selezionare un altro operatore premi il pulsante AVVIA MENU'");
  Serial.println("----------------------------------------------------------------");
  Serial.println("");

  mettiUnoChiavi();
  chiavi[6] = 0;
}

void mettiUnoChiavi() {
  for (int indice = 0; indice < 7; indice++) {
    chiavi[indice] = 1;
  }
}

void loop()
{

  if (Serial.available())
  {
    byte selezione = Serial.read();

    while (selezione == '1')
    {
      if (chiavi[0] == 1)
      {
        stampaOr();
      }

      statoInA = digitalRead(pinInA);
      statoInB = digitalRead(pinInB);

      // funzione logica OR
      statoOut = statoInA || statoInB;
      digitalWrite(pinOutY, statoOut);

      if (digitalRead(pinChiave) == HIGH) {
        mettiUnoChiavi();
        stampaMenu();
        break;
      }
    }

    while (selezione == '2')
    {
      if (chiavi[1] == 1) {
        stampaAnd();
      }
      statoInA = digitalRead(pinInA);
      statoInB = digitalRead(pinInB);

      // funzione logica AND
      statoOut = statoInA && statoInB;
      digitalWrite(pinOutY, statoOut);
      //selezione = Serial.read();

      if (digitalRead(pinChiave) == HIGH) {
        mettiUnoChiavi();
        stampaMenu();
        break;
      }
    }

    while (selezione == '3')
    {
      if (chiavi[2] == 1) {
        stampaNot();
      }

      statoInA = digitalRead(pinInA);
      statoInB = digitalRead(pinInB);

      // funzione logica NOT
      statoOut = !statoInA;
      digitalWrite(pinOutY, statoOut);

      if (digitalRead(pinChiave) == HIGH) {
        mettiUnoChiavi();
        stampaMenu();
        break;
      }
    }

    while (selezione == '4')
    {
      if (chiavi[3] == 1) {
        stampaNor();
      }

      statoInA = digitalRead(pinInA);
      statoInB = digitalRead(pinInB);

      //funzione logica NOR
      statoOut = !(statoInA || statoInB);
      digitalWrite(pinOutY, statoOut);

      if (digitalRead(pinChiave) == HIGH) {
        mettiUnoChiavi();
        stampaMenu();
        break;
      }
    }

    while (selezione == '5')
    {
      if (chiavi[4] == 1) {
        stampaNand();
      }

      statoInA = digitalRead(pinInA);
      statoInB = digitalRead(pinInB);

      //funzione logica NAND
      statoOut = !(statoInA && statoInB);
      digitalWrite(pinOutY, statoOut);

      if (digitalRead(pinChiave) == HIGH) {
        mettiUnoChiavi();
        stampaMenu();
        break;
      }
    }

    while (selezione == '6')
    {
      if (chiavi[5] == 1) {
        stampaXor();
      }

      statoInA = digitalRead(pinInA);
      statoInB = digitalRead(pinInB);

      // funzione logica XOR
      
      statoOut = (statoInA && !statoInB) || (!statoInA && statoInB);
      digitalWrite(pinOutY, statoOut);

      if (digitalRead(pinChiave) == HIGH) {
        mettiUnoChiavi();
        stampaMenu();
        break;
      }
    }

    while (selezione == '7') {
      if (chiavi[6] == 1) {
        stampaXnor();
      }

      statoInA = digitalRead(pinInA);
      statoInB = digitalRead(pinInB);

      // funzione logica XNOR
      statoOut = !((statoInA && !statoInB) || (!statoInA && statoInB));
      digitalWrite(pinOutY, statoOut);

      if (digitalRead(pinChiave) == HIGH) {
        mettiUnoChiavi();
        stampaMenu();
        break;
      }
    }
  }
}

 

Propongo come esercizio cinque varianti all’esempio precedente:

  1. Gli ingressi selezionati con pulsanti devono essere visualizzati con LED (1 acceso, 0 spento), create inoltre una libreria dedicata per l’implementazione delle funzioni logiche (inserite il codice in un file .h). Per sapere come creare una libreria seguire il link, al fondo del tutorial troverete la spiegazione.
  2. La selezione della funzione logica non avviene più tramite la serial monitor, ma attraverso 7 pulsanti di selezione funzione, la cui selezione deve essere visualizzata con un LED (Acceso = selezionato; Spento= non selezionato). Deve essere presente un pulsante che effettua il reset dell’operazione (non deve essere il reset della scheda Arduino).
  3. Realizzare le stesse funzionalità del punto 2 ma in questo caso non si devono utilizzare LED ma un display 16×2 del tipo Hitachi HD44780, se desiderate potete utilizzare un’interfaccia I2C HD44780 per utilizzare meno pin.
  4. Aggiungere il codice per effettuare il debounce (anti-rimbalzo) sui pulsanti.
  5. Per gli studenti di 4′ superiore realizzare un debounce usando l’elettronica a componenti discreti.

Per chi volesse esplorare nuove frontiere:

  • Realizzate tutto ciò che è stato esposto prima mediante BBC micro:bit e programmate tutto in microPython.
  • Realizzate tutto ciò che è stato esposto prima mediante Raspberry Pi e programmate tutto in Python.

Buon Coding (e studio) a tutti 🙂

Livelli logici TTL e CMOS – cosa si nasconde dietro un HIGH o LOW di una digitalWrite di Arduino?

ttl-cmos-atmega328-bannerSpesso, durante i miei corsi su Arduino ricorrono le domande sui livelli logici in elettronica digitale, il tutto nasce quando è indispensabile introdurre la differenza tra segnali analogici e digitali o quando si parla di livelli logici 1 o 0. Mi preme quindi fare un’estensione agli appunti che utilizzo durante le lezioni con un breve post, nulla di complicato, spiegato per chi si avvicina per la prima volta all’uso di Arduino. Vediamo di capire cosa si nasconde, dal punto di vista elettronico dietro le parole HIGH e LOW che si utilizzano sempre nei nostri sketch Arduino.

Viviamo in un mondo pieno di segnali analogici, si pensi ad esempio alla temperatura, alla pressione atmosferica al suono emessa da un flauto e mille di questi esempi si potrebbero fare. Come ben sapete in elettronica digitale vengono utilizzati due stati logici ON e OFF, ACCESO e SPENTO, o in altro modo 1 e 0. Con questi due livelli logici potete codificare e trasportare una grande quantità di informazioni e far colloquiare tra loro dispositivi elettronici.
Per fare un esempio per chi ha incominciato con il classico “Blink”, provate a pensare cosa si può fare con sequenze ACCESO e SPENTO di LED, potete comunicare ad esempio informazione con il codice morse.

Cos’è un livello logico

In estrema sintesi possiamo dire che un livello logico è una tensione specifica o uno stato in cui un segnale può esistere.
Spesso, come detto nell’introduzione di questo post, ci riferiamo a due stati logici ON e OFF dove traduciamo ON con il numero binario 1 e OFF si traduce in uno 0 binario. In Arduino noi identifichiamo lo 0 binario in LOW e 1 binario in HIGH.
I livelli di tensione elettrica che definiscono l’1 binario o lo 0 binario possono differire in funzione della tecnologia utilizzata per la costruzione dei dispositivi elettronici digitali.

Attivo alto e attivo basso

Dal punto di vista elettrico a cosa corrisponde un HIGH o un LOW?
Se avete incominciato a lavorare con circuiti integrati e microcontrollori è molto probabile che troverete pin che sono attivi bassi o attivi alti, ciò indica quando il pin è attivo. Spesso capita che un circuito integrato per poter funzionare o per abilitare alcune sue funzionalità necessita che su un pin prestabilito vi sia un livello logico basso o alto si dice quindi quindi che quel particolare pin è attivo basso o alto ovvero deve esserci un livello logico 1 (tensione di 5V o 3,3V) o un livello logico 0 (tensione di 0V o poco più).

Per fare un esempio è come se paragonassimo la vostra automobile ad un circuito integrato (vedi ad esempio il microcotrollore su Arduino) e la chiave di accensione ad un pin dell’integrato, allora diciamo che: “l’automobile si avvia se giriamo la chiave, il circuito integrato fa quello che desideriamo se su un particolare pin abbiamo un livello logico stabilito (alto o basso)”.

Ad esempio, per chi ha già fatto qualche sperimentazione con gli shift register avrà notato che questo integrato possiede un pin Enable identificato con CE. Se sul data datasheet notate che sulla lettere CE è presente una linea, vuol dire che il pin è attivo basso ovvero lo shift register può funzionare se su CE è presente una tensione di 0V, in altro modo il pin deve essere collegato a GND. Se invece sulle lettere CE non è presente una linea allora vuol dire che il pin CE è attivo alto e quindi l’integrato potrà funzionare solo se il pin è portato ad un livello logico 1 (5V o 3,3V).

I dispositivi elettronici digitali vengono suddivisi in famiglie logiche le quali differiscono le una dalle altre per la tecnologia usata per costruirli e per la tipologia di circuiti di base (componenti elettronici usati) su cui si basa il loro funzionamento.
Inoltre capita che nell’ambito di una stessa famiglia logica vi siano alcune serie di componenti che hanno caratteristiche migliori, ad esempio temperature limite di funzionamento.

Le famiglie logiche più utilizzate sono la TTL (Transistor Transistor Logic) e la CMOS (Complementary MOS).
La prima prende il nome da TTL in quanto sono costituiti da transito sia nello stadio di ingresso che di uscita. La seconda detta CMOS perché usa dei MOSFET (Transistor ad Effetto di Campo MOS, Metallo Ossido Semiconduttore).

Per chi affronta per la prima volta questo argomento consideri questi due acronimi, TTL e CMOS, come il nome per specificare la tecnologia usata per realizzare il dispositivo elettronico digitale, per fare un’analogia e come dire: “l’automobile ci serve per spostarci, ma l’automobile può essere a GASOLIO (ad es. TTL) o a BENZINA (ad es. CMOS) entrambe comunque ci consentiranno di spostarci”.
Sappiamo però che le auto a gasolio hanno certe problematiche/pregi e quelle a benzina allo stesso modo hanno problematiche/pregi è in funzione delle performance che ci interessano: consumi, potenza, velocità, ecc… si deciderà quale automobile acquistare.

Esistono alcune convenzioni che sarà utile conoscere per meglio districarsi ad esempio nella lettura dei manuali tecnici dei componenti elettronici digitali.

Per convenzione, le correnti e le tensioni di qualsiasi terminale di un dispositivo logico hanno due suffissi come pedice:

  • il primo indica se il terminale è di ingresso (I=input) o di uscita (O=output)
  • il secondo indica lo stato logico alto (H=high) o basso (L=low)

Inoltre ogni terminale di ciascuna porta logica assorbe o eroga corrente.

Logica TTL

I circuiti elettronici digitali della famiglia TTL sono alimentati (funzionano con…) con una Vcc=+5V. Come già accennato sono basati su circuiti costituiti da transistor bipolari utilizzati per ottenere variazioni (commutazioni di livelli) e mantenere stati logici (memorizzare), possiamo definirli come dei microinterruttori. Sono caratterizzati da una serie (numeri identificativi) che ha come cifra iniziali, 74, serie commerciale che funziona tra 0°C e 70 °C e la serie 54 che costituisce la serie militare che funziona tra -55°C e +125°C.

Esistono  sottofamiglie della TTL:

  • L – serie a basso consumo (L indica Low power), ormai obsoleta
  • S – serie che utilizza i transito veloci Schottly
  • LS – serie che riunisce le caratteristiche delle due precedenti
  • AS – serie S con prestazioni avanzate
  • ALS – serie LS con prestazioni avanzate

Ad ogni famiglia logica (TTL e CMOS) è associata una serie di tensioni che identificano quando si ha 0 logico e quando l’1 logico.

I livelli di tensione da applicare in ingresso sono:

  • VIL – compreso tra 0 e VILmax =+0,8V per il riconoscimento del livello logico basso (LOW), quindi una tensione tra 0V e +0,8V sarà identificata come un LOW
  • VIH – tensione compresa tra VIHmin=+2V e +5V per il riconoscimento del livello logico alto (HIGH), quindi una tensione tra +2V e +5V sarà identificata come un HIGH

I livelli di tensione che si ottengono in uscita sono:

  • VOL – compreso tra 0 e VOLmax =+0,4V purchè la corrente entrante sia IOL<16mA
  • VOH – compreso tra VOHmin=2,7V e circa 4V purché la corrente di ingresso sia IOH<400uA

Il disegno chiarisce meglio questi intervalli di tensione:

ttl-small

I valori di tensione compresi tra +0,8V e +2V individuano una zona di indeterminazione che non è da utilizzare.

Consideriamo ad esempio una porta NOT, l’intervallo dei segnali di input e output accettabili per avere un livello alto o basso sono quelli indicati in figura:

livelli-in-out-ttl

Logica CMOS

Tutti i circuiti integrati della famiglia logica CMOS utilizzano al loro interno MOSFET di tipo P o di tipo N e caratteristica principale di un CMOS è la ridottissima potenza dissipata e ampio intervallo di tensioni di alimentazione: da +3V a +15V.
Come per la famiglia TTL anche la CMOS ha delle sottofomiglie:

  • CD4000 ormai obsoleta
  • 74C compatibile, nella piedinatura (posizione funzionalità) ai corrispondenti integrati della famiglia TTL
  • 74HC e 74HCT come la serie precedenti, ma con tempi di propagazione (del segnale) ridotti e valori di tensione compresi tra +2V e +6V (HC) e +5V (HCT)
  • 74 AHC simile alla HC ma con prestazioni avanzate

Se si usano dispositivi in tecnologia CMOS bisogna tener conto di alcune regole:

  • la tensione applicata in ingresso deve essere compresa tra 0V e Vcc
  • a differenza della famiglia TTL tutti i piedini di ingresso devono essere utilizzati collegandoli a livello alto o basso
  • il terminale di uscita può essere collegato a massa o all’alimentazione senza pericolo di avarie
  • le tensioni di commutazioni di soglia (le tensioni che identificano il passaggio dallo 0 a 1 o viceversa) sono circa la metà della tensione di alimentazione applicata al dispositivo

Considerando dispositivi CMOS alimentati a tensione di +3,3V i livelli di tensione da considerare sono quelli rappresentati nel disegno che segue:

cmos-small

Si noti come, al fine di garantire la compatibilità generale, che la maggior parte dei livelli di tensione sono uguali ai dispositivi alimentati a +5V. Un dispositivo alimentato a +3,3V potrà in generale, con alcune cautele, essere interfacciato ad un dispositivo a +5V senza componenti aggiuntivi. Ad esempio, un 1 logico (HIGH) per un dispositivo a +3,3 V sarà almeno +2,4 V e tale tensione sarà ancora interpretato come un 1 logico (HIGH) in un sistema a+5V perché è sopra la VIG di 2 V.

Quando bisogna interfacciare (in senso opposto) un dispositivo a 5V con uno a 3,3V bisogna verificare che il dispositivo a 3,3V possa funzionare a 5V, cioè che la tensione massima di ingresso sia di 5V. Su alcuni dispositivi 3,3 V, tensioni superiori a 3,6 V causeranno danni permanenti al componente. Sarà sufficiente utilizzare un semplice partitore di tensione (ad esempio una resistenze da 1kOhm ed una da 2kOhm) per abbattere segnali da 5V a 3,3V.

Livelli logici in Arduino

Guardando il datasheet dell’ATMega328, (microcontrollore presente nell’Arduino UNO) i livelli di tensione sono leggermente diversi da quelli indicati per le famiglie TTL e CMOS:

atmega328-small

La differenza più evidente è che la regione di interdizione (da non usare) di tensioni è tra 1,5 V e 3,0 V. Il margine di rumore è maggiore sul Arduino ed ha una soglia più elevata per l’identificazione di una tensione che indica un segnale basso (LOW) ed una soglia minima per l’identificazione di un segnale alto (HIGH) più elevata. In altro modo i livelli di tensione massima di identificazione dello 0 logico e di tensione minima di identificazione dell’1 logico rientrano all’interno degli intervalli di diverse tipologie di famiglie logiche, sia TTL che CMOS, in questo modo la costruzione di interfacce risulta più semplice.

Paragonado i grafici dei livelli di tensione per TTL, CMOS e ATMega328 abbiamo:

ttl-cmos-atmega328