Supponiamo di avere il circuito riportato nella figura qui sotto. E' un semplice risponditore telefonico che quando viene chiamato invia un tono in linea per un certo tempo. Sulla sinistra c'è l'interfaccia di linea, non ci interessa come è fatta, quello che è importante è conoscere il significato dei tre fili che escono da essa. L è il filo di impegno, una tensione positiva su questo filo fa "prendere la linea". F è il filo su cui va inviata la frequenza da mandare in linea. R è il filo che segnala l'arrivo di una chiamata. E'normalmente a +5V (o 1 logico), si porta a zero quando arriva la chiamata.
Il funzionamento è il seguente: appena arriva un impulso di chiamata il filo R si porta a zero, il flip flop di tipo set-reset si attiva e la sua uscita Q si porta a 1. La rete RC del TIMER1 fa si che per 15 secondi il circuito non risponda. Trascorso questo tempo l'uscita del TIMER1 si porta a 1 comandando l'impegno sul filo L, e abilitando la porta AND ad inviare in linea la frequenza prodotta dall'oscillatore. Contemporanemante questo livello 1 comanda il TIMER2, che dopo 10 secondi invia un 1 (negato da una porta NOT) verso il pin reset del flip flop, a questo punto il circuito si azzera liberando la linea e rimettendosi in attesa.La logica di funzionamento incorporata nel circuito di controllo composto dal flip flop, dai timer ecc... si può quindi riassumere nei seguenti passi:
Un programma per microprocessore che esegua questi passi può realizzare un processo di controllo del tutto equivalente al circuito hardware mostrato sopra. Una differenza sostanziale è che il funzionamento può essere modificato o migliorato senza intervenire su alcun componente hardware.
- attendere una chiamata (filo R a 0)
- attendere 15 secondi
- prendere la linea (risposta)
- inviare un tono per 10 secondi
- liberare la linea e tornare al punto 1
Per questo esempio supponiamo di usare un PIC16F84, settando la porta A come ingresso e la porta B come uscita.
Bisogna innanzitutto stabilire come si vogliono usare i singoli bit di I/O delle porte, in sostanza si deve decidere su quale ingresso collegare il filo R e su quali uscite i fili L e F, scegliamoli ad esempio nel seguente modo:
Il programma deve eseguire esattamente i passi visti prima, prima di tutto però deve settare le porte ed impostare in uscita i corretti valori di riposo (nel nostro caso 0).A questo punto dobbiamo leggere il filo R per vedere se è in arrivo una chiamata. Se R è a 1 continuiamo a leggerlo finchè lo troviamo a zero, questo ciclo di letture realizza il passo "attendere una chiamata":
CLRF PORTB BSF STATUS,RP0 CLRF TRISB BSF STATUS,RP0L'istruzione CLRF PORTB azzera i latch di uscita della porta ancora prima che questa venga settata come uscita, in questo modo appena viene configurata le uscite assumono già il valore logico 0. Se invece il filo R è a zero, cioè è in arrivo un impulso di chiamata, si esce dal ciclo (non si salta più a ATTESA1) e si prosegue con il passo 2: "attendi 15 secondi".
ATTESA1 BTFSC PORTA,0 GOTO ATTESA1Con l'istruzione BTFSC PORTA,0 leggiamo la porta controllando il valore del bit 0. Se questo bit vale 0 viene saltata la GOTO e il programma prosegue, se invece non lo è si risalta ad ATTESA1 e ricominciamo la lettura. Un PIC a 4MHz impiega 3 microsecondi ad eseguire queste due istruzioni, e quindi effettuerà circa 333 mila letture al secondo. Adesso dobbiamo prendere la linea, e questo si fa portando a 1 il bit RB1 della porta di uscita:
MOVLW 58 MOVWF DH MOVLW 152 MOVWF DL CALL DELAYQui chiamiamo semplicemente una subroutine di ritardo di cui ci occuperemo dopo, quello che importa è sapere che causerà un ritardo di circa N millisecondi, dove N è un valore a 16 bit compreso tra 1 e 65535 scritto nei registri DH e DL. In particolare DH contiene la "parte alta" del nostro valore, e DL quella bassa. Impostiamo quindi 58*256 + 152 = 15000. Ora (mantenendo fisso a 1 il pin RB1 per tenere impegnata la linea) si deve commutare ciclicamente a 1 e a 0 il pin RB0 per inviare sul filo F una frequenza, e il tutto deve continuare per 10 secondi. Inoltre la commutazione del pin RB0 deve essere rallentata con appositi cicli di ritardo, altrimenti verrebbe generata una frequenza ultrasonica non udibile di oltre 100kHz.
BSF PORTB,1Considerando che con un clock di 4 Mhz l'esecuzione di una DECFSZ e della successiva GOTO dura 3 microsecondi, l'esecuzione del ciclo completo di 200 iterazioni dura 599 microsecondi. Se commutiamo il pin di uscita RB0 ogni 599 microsecondi otteniamo una frequenza di circa 835Hz.
BSF PORTB,0 MOVLW 200 MOVWF CL RIT1 DECFSZ CL,F GOTO RIT1 BSF PORTB,0 MOVWF CL RIT2 DECFSZ CL,F GOTO RIT2Si setta il pin RB0 a 1. Poi si carica il registro CL con il valore 200 e si esegue un'istruzione DECFSZ. Questa istruzione decrementa il valore di CL e se è arrivato a zero salta il GOTO. Se CL non è arrivato a 0 viene eseguita l'istruzione GOTO RIT1, per cui si effettua un nuovo decremento e così via. In pratica vengono eseguiti 200 decrementi, dopo di che il programma prosegue. Si azzera il pin RB0 e si attende un'altra pausa di 200 iterazioni. Noi dobbiamo emettere questa frequenza per 10 secondi, e visto che in 10 secondi ci sono circa 8350 periodi dobbiamo inserire le istruzioni viste sopra in un ciclo di 8350 iterazioni:
A questo punto liberiamo la linea riportando a 0 RB1 e torniamo all'inizio del programma:
MOVLW 32 MOVWF DH MOVLW 158 MOVWF DL TONO BSF PORTB,0 MOVLW 200 MOVWF CL RIT1 DECFSZ CL,F GOTO RIT1 BSF PORTB,0 MOVWF CL RIT2 DECFSZ CL,F GOTO RIT2 DECF DL,F COMF DL,W BTFSC STATUS,Z DECF DH,F MOVF DH,W IORWF DL,W BTFSS STATUS,Z GOTO TONOSi usa la coppia di registri DH e DL come contatore a 16 bit, lo si inizializza a 8350 (32*256+158=8350) e, dopo un periodo della frequenza, lo si decrementa controllando se entrambi i byte che lo compongono sono a zero. Questo controllo si effettua con una operazione di OR, se anche uno solo dei bit di DH o DL è a 1 si salta a TONO per emettere un altro periodo. Rimane ancora una cosa da scrivere, la subroutine DELAY per il ritardo dei 15 secondi. E'evidente che sfrutterà lo stesso principio dell'esecuzione ciclica di qualche istruzione per far trascorrere del tempo. Per comodità creiamo un'ulteriore subroutine per il ritardo di 1 millisecondo da richiamare 15000 volte (chiamata MS1).
BCF PORTB,1 GOTO ATTESA1
DELAY CALL MS1 DECF DL,F COMF DL,W BTFSC STATUS,Z DECF DH,F MOVF DH,W IORWF DL,W BTFSS STATUS,Z GOTO DELAY RETURN MS1 MOVLW 142 MOVWF CL MSR1 GOTO $+1 GOTO $+1 DECFSZ CL,F GOTO MSR1 NOP RETURNQui si vede bene come ogni subroutine chiamata con CALL deve terminare con l'istruzione RETURN, che fa riprendere l'esecuzione dall'istruzione successiva alla CALL.
La coppia di registri DH:DL viene usata nello stesso modo del ciclo di emissione della frequenza.
La subroutine MS1 dura esattamente 1 millisecondo (compresa la CALL che la chiama). Le due istruzioni GOTO $+1 servono per far trascorrere 2 microsecondi (a 4MHz) e occupano una sola locazione di memoria programma.Le istruzioni NOP non producono alcun effetto se non un po' di ritardo, 1 microsecondo ciascuna (con clock a 4 Mhz). Calcolando la somma complessiva di tutte le istruzioni eseguite si può determinare che la subroutine delay dura esattamente 15,135001 secondi.
Il programma completo
PROCESSOR 16F84 RADIX DEC INCLUDE "P16F84.INC" __CONFIG 11111111110001B ORG 12 ;INDIRIZZO INIZIO RAM DATI CL RES 1 DL RES 1 DH RES 1 ORG 0 ;INDIRIZZO INIZIALE PROGRAMMA ;------------------------------------------------------------ CLRF PORTB BSF STATUS,RP0 CLRF TRISB BSF STATUS,RP0 ATTESA1 BTFSC PORTA,0 ;LETTURA FILO R GOTO ATTESA1 MOVLW 58 ;IMPOSTA RITARDO 15 SECONDI MOVWF DH MOVLW 152 MOVWF DL CALL DELAY BSF PORTB,1 ;IMPEGNA LA LINEA MOVLW 32 ;IMPOSTA 8350 PERIODI MOVWF DH MOVLW 158 MOVWF DL TONO BSF PORTB,0 ;ALZA PIN RB0 MOVLW 200 MOVWF CL RIT1 DECFSZ CL,F GOTO RIT1 ;ATTESA 599 MICROSEC. BSF PORTB,0 ;ABBASSA PIN RB0 MOVWF CL RIT2 DECFSZ CL,F GOTO RIT2 ;ATTESA 599 MICROSEC. DECF DL,F ;DECREMENTA CONTEGGIO PERIODI COMF DL,W BTFSC STATUS,Z DECF DH,F MOVF DH,W IORWF DL,W BTFSS STATUS,Z GOTO TONO ;SE NON SONO FINITI TORNA A TONO BCF PORTB,1 ;LIBERA LA LINEA GOTO ATTESA1 ;TORNA AD ATTESA CHIAMATA ;------------------------------------------------------------ DELAY CALL MS1 ;CHIAMA RITARDO 1 MILLISEC. DECF DL,F ;DECREMENTA CONTEGGIO MILLISEC COMF DL,W BTFSC STATUS,Z DECF DH,F MOVF DH,W IORWF DL,W BTFSS STATUS,Z GOTO DELAY ;SE NON SONO FINITI TORNA A DELAY RETURN ;------------------------------------------------------------ MS1 MOVLW 142 MOVWF CL MSR1 GOTO $+1 GOTO $+1 DECFSZ CL,F GOTO MSR1 NOP RETURN ;------------------------------------------------------------ ENDQuesto è un programma piuttosto semplice, usa solo 3 byte di memoria per contenere i valori di lavoro, non usa gli interrupt, però mostra come si può realizzare via software un processo dal funzionamento del tutto identico a quello di una rete hardware.
Il funzionamento potrebbe essere reso più affidabile per esempio controllando l'effettiva temporizzazione degli impulsi di chiamata, in modo da distinguere una chiamata vera da un impulso spurio o da un disturbo.
O ancora al posto di una frequenza fissa potrebbero essere emessi dei treni di impulsi, magari a frequenze diverse. Tutte queste modifiche sarebbero molto pesanti da realizzare in hardware, al contrario via software sono relativamente semplici.
Inoltre quelle che in hardware sarebbero le eventuali regolazioni dei trimmer dei temporizzatori, qui diventano semplici regolazioni del valore di un registro, per esempio si potrebbe calibrare il valore scritto nei registri DH:DL per ottenere 15 secondi con maggior precisione, o altri valori che fossero necessari.
Pagina e disegni realizzati da Claudio Fin