SONDA DI LIVELLO AD ULTRASUONI

Schema con Microcontrollore che grazie a un Sensore di Distanza ad Ultrasuoni, permette di Misurare il Livello di una Cisterna o simili…

 

 

 



 

INTRO

In commercio esistono molti moduli che permettono di misurare grazie agli ultrasuoni la distanza del sensore da un oggetto. Il principio di funzionamento è molto semplice, vi è un altoparlante piezoelettrico che emette un segnale nell’ultrasuono, ovvero 40KHz, e vi è un microfono utile a ricevere il suono riflesso. Infatti il segnale emesso dall’altoparlante, non udibile da persone o animali, rimbalza contro l’oggetto e il suono ci mette un certo tempo a tornare indietro. Il tempo che ci mette a raggiungere il bersaglio e tornare al modulo sarà dato dal doppio della distanza diviso la velocità del suono  (34300 cm al secondo) t=((distanza*2)/34300.

Quando un fascio di ultrasuoni colpisce un liquido esso in parte viene riflesso e in parte penetra nel liquido e si attenua. Considerando che l’impedenza acustica dell’acqua è circa 1.5 [g/cm^2/s] mentre quella dell’aria è 0.0004, viene facile comprendere che per gli ultrasuoni (soprattutto alla frequenza di funzionamento del sensore) l’acqua è un ostacolo.

Bisogna però considerare la forma della cisterna che potrebbe creare riflessioni e falsare la misura, per questo bisogna eseguire una piccola modifica al sensore ad ultrasuoni. Questo metodo di misura permette di non avere sensori all’interno del liquido che si possono ossidare o non si possono proprio usare per liquidi infiammabili e inoltre è il metodo più semplice e che restituisce valori teoricamente continui di livello (nel caso si usi un sensore di livello resistivo si hanno n livelli dove n è il numero di contatti). Per questo progetto è stato usato il modulo US-015 disponibile su Ebay a pochi euro.

Per quanto riguarda il serbatoio bisogna definire 3 parametri, il primo D1 è la distanza del sensore dal livello massimo dell’acqua, D2 è il livello minimo, ovvero quando il serbatoio è vuoto mentre LitriMax è la capacità massima del serbatoio. Con questi valori e la distanza misurata dal sensore posso calcolare i litri come segue:

L’equazione funziona se il serbatoio è cilindrico o rettangolare, se non è irregolare (in alcuni punti è più stretto in alcuni è più largo) non dà il valore esatto, però in ogni caso può distinguere senza errore gli estremi.

 



 
CODICE

Il codice è il seguente:

#define Hi(param) ((char *)&param)[1]       //definizione funzione Hi
#define Lo(param) ((char *)&param)[0]       //definizione funzione Lo

sbit LCD_RS at RB7_bit;                     //Definizione libreria LCD
sbit LCD_EN at RB6_bit;
sbit LCD_D4 at RB5_bit;
sbit LCD_D5 at RB4_bit;
sbit LCD_D6 at RB3_bit;
sbit LCD_D7 at RB2_bit;                     //Fine definizione libreria LCD

sbit LCD_RS_Direction at TRISB7_bit;        //Configurazione I/O per LCD
sbit LCD_EN_Direction at TRISB6_bit;
sbit LCD_D4_Direction at TRISB5_bit;
sbit LCD_D5_Direction at TRISB4_bit;
sbit LCD_D6_Direction at TRISB3_bit;
sbit LCD_D7_Direction at TRISB2_bit;        //Fine configurazione I/O per LCD

int Misura, Litri;                         //Variabile distanza e litri
int D1=30;                                 //distanza sensore limite acqua
int D2=130;                                //distanza sensore -livello serbatoio vuoto
int LitriMax=100;                          //Capienza massima serbatoio
char TXT[7];                                //Testo litri
unsigned short int trigger = 0;             //Variabile trigger

void EXT_INT() iv 0x0008 ics ICS_AUTO {     //Interrupt fronte di salita di echo
  TMR1L = 0;                                //Resetto timer
  TMR1H = 0;                                //Resetto timer
  PIR1.CCP1IF = 0;                          //Resetto flag modulo ccp1
  INTCON3.INT1IF = 0;                       //Resetto flag interrupt
}

void main() {                               //Programma principale

  CCP1CON = 0b00000100;                     //Capture ogni fronte di discesa

  TRISB = 0b00000010;                       //Portb uscite tranne int1
  TRISC.B1 = 0;                             //Uscita trigger
  TRISC.B2 = 1;
  TRISC.B0 = 0;                             //Ingresso modulo CCP1

  Lcd_init();                               //Inizializza lcd
  Lcd_cmd(_LCD_CLEAR);                      //Pulisci lcd
  Lcd_cmd(_LCD_CURSOR_OFF);                 //Disattiva cursore lcd

  T1CON = 0b10000001;                       //tmr1 on, clock int, prescaler 1

RCON.IPEN = 0;                              //disabilito la priorità degli interrupt
INTCON.GIE = 1;                             //attivo gli interrupt

INTCON2.INTEDG1 = 1;                        //interrupt sul fronte di salita
INTCON3.INT1IE = 1;                         //Abilita interrupt

Delay_ms(500);                              //Delay 0.5S

while(1){                                   //Ciclo infinito
 if(trigger == 0){                          //Se non si è gia inviato un trigger...
  PORTC.B1 = 1;                             //Impulso di trigger
  Delay_us(10);                             //Durata 10uS
  PORTC.B1 = 0;                             //Spegni impulso
  trigger = 1;                              //Segnala che si è inviato il trigger
  }

    if (PIR1.CCP1IF == 1){                  //Se fronte di discesa echo (capture)...
      Lo(Misura) = ccpr1l;                //Unisci ccpr1l alla variabile
      Hi(Misura) = ccpr1h;                //Unisci ccpr1h alla variabile
      Misura = Misura / 58;                //Converti in centrimetri fattore che dipende da f (Fclk=8->116)
      if(Misura < D1-5){                   //Se la Misura è molto minore di D1 c'è overflow
        PORTC.B0=1;                        //accendi il relè o il led
        Lcd_Cmd(_LCD_CLEAR);               //Pulisci LCD
        Lcd_Out(1, 1, "Overflow");          //mostra overflow
        PIR1.CCP1IF = 0;                      //Resetta flag capture
        Delay_ms(500);                        //0.5S prima di una nuova misura
        trigger = 0;                          //Prepara nuovo invio trigger
      }
        else{
          if(Misura < D1) Misura = D1;       //Controlla i valori massimi e minimi
          if(Misura > D2) Misura = D2;
          else PORTC.B0=0;                    //spegni relè o led
          Litri = ((-1)*Litrimax*(Misura-D1)/(D2-D1))+LitriMax; //calcola Litri (formula retta per due punti con (d1, Lmax) e (d2, 0)

          IntToStr(Litri, TxT);              //Converti in stringa la distanza
          Lcd_Cmd(_LCD_CLEAR);                  //Pulisci LCD
          Lcd_Out(1, 1, TxT);                   //Stringa distanza su seconda riga
          Lcd_Out_Cp(" Litri");                  //Stringa prima riga
          PIR1.CCP1IF = 0;                      //Resetta flag capture
          Delay_ms(500);                        //0.5S prima di una nuova misura
          trigger = 0;                          //Prepara nuovo invio trigger
        }
    }
 }//fine ciclo infinito
}//fine

Nelle prime due righe si definisce la funzione di concatenazione di due numeri a 8 bit, poi si dichiara l’interfaccia dell’LCD e le variabili per la misura, per i litri, per il testo e infine la variabile  per la conferma dell’invio del trigger.

Inoltre vanno inizializzate le variabili che descrivono la forma del serbatoio, ovvero la distanza tra il livello massimo di acqua e il sensore, tra il livello minimo dell’acqua e il sensore e inoltre la capienza totale, Questi valori servono per calcolare i litri.

Dopo di che viene inizializzata la subroutine di interrupt nella quale si resetta il timer counter e i flag. Questa routine parte sul fronte di salita del segnale echo.

Nel programma principale, prima di tutto si inizializza il modulo capture e gli ingressi uscite; l’ingresso del modulo CCP1 è settato ingresso e anche quello per l’interrupt INT1 mentre il pin dove collegare il trigger è un uscita. Dopo di che si inizializza LCD e timer counter 1 con lunghezza a 16bit, prescaler 1:1 e si accende.

Dopo di che si attivano gli interrupt senza priorità e INT1 sul fronte di salita. Per quanto riguarda il timer counter, si è pensato di utilizzare un quarzo da 4MHz, quindi il counter conta ogni 1us visto che si ha un prescaler 1:1. Quindi se il timer counter conta 1500 vuol dire che sono trascorsi 1500us in realtà.

Nel programma principale si ha un ciclo infinito nel quale prima di tutto si va a controllare se si è inviato un impulso di trigger o no, se non si è inviato si invia ovvero si pone a 1 l’uscita, poi si aspettano 10us e poi si pone a 0 l’uscita e si informa che si è già inviato il trigger.

Dopo di che si avrà il fronte di salita del segnale di echo e il ciclo di interrupt resetta il timer counter, dopo un certo istante si avrà il fronte di discesa del segnale echo, quindi il modulo capture setta il flag CCPIF uguale a 1 e conserva il valore del timer counter. Questo valore è dato da due numeri a 8 bit che vengono uniti e una volta uniti si va a dividere per 58, 58 per convertire da tempo a distanza.

Se la distanza è 5cm minore di D1 avrò overflow, accendo il LED o il relè per un eventuale controllo del riempimento, mentre se non vi è overflow vado a calcolare il valore dei litri nel serbatoio e lo mostro a display.Successivamente si aspetta mezzo secondo e si resettano i flag in modo tale che l’operazione di misura possa ricominciare.

La configurazione dei fuses è la seguente:

È tutto disabilitato tranne il brownout reset e l’oscillatore è in configurazione HS con quarzo a 4MHz.
 

SCHEMA

Lo schema è il seguente:

Il cristallo X1 è un quarzo da 4MHz e con i due condensatori da 22pF formano il circuito di oscillazione. La resistenza R4 serve per evitare che il PIC18F252 si resetti, mentre ai pin 20 si hanno 5V e ai pin 19 e 8 la massa dell’alimentazione.

Il modulo US-015 è alimentato anch’esso a 5V e anche il display LCD, mentre si fornisce una tensione per regolare il contrasto dell’LCD grazie alla resistenza RV1.

Quando vi è un evento di overflow il pin PORTC.B0 assume un valore alto, a questo pin può essere collegato un buzzer per avvisare del possibile trabocco del serbatoio. Nel mio caso ho collegato un led con resistore di protezione da 470 Ohm con catodo connesso a massa e l’anodo connesso alla resistenza che a sua volta è connesso al pin di uscita. Si può collegare un relè che ad esempio attiva una elettrovalvola. Il relè si collega come nel caso presente al seguente link(https://www.ne555.it/rele-monostabile-ne555/) utilizzando solo R6, Q2, D2 e il relè.

Al pin 13 e al pin 22 è connesso il segnale di echo dal modulo mentre al pin 12 il trigger.

Il circuito montato su breadboard ha il seguente aspetto:

 

DOWNLOAD

È possibile scaricare la simulazione con proteus, il file .hex e il file .c scritto con MIKROC al seguente LINK!!!

 



 

[Voti Totali: 0 Media Voti: 0]
Segui la Nostra Pagina Facebook: Facebook

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *