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 *)¶m)[1] //definizione funzione Hi
#define Lo(param) ((char *)¶m)[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!!!