|
|||||||
| Arcade | Registrazione | Blogs | Regolamento | Feedback | FAQ | Lista Utenti | Calendario | Segna come Letti |
| Ultimi 5 blog pubblicati su PcTuner Blog | ||
|
||
![]() |
|
|
Strumenti Discussione | Modalità Visualizzazione |
|
|
#1 |
|
Super Moderator
|
Firmware di controllo WaterStation
Immagino che molti di voi abbiano letto o dato uno sguardo all'articolo che ho scritto qualche tempo fa sulla mia waterstation.
__________________Visto che la neonata sezione PIC ha bisogno di una spintarella ho pensato di rispolverarlo per trasformarlo in argomento di discussione. Il motivo è che il cuore della waterstation è un glorioso 16F84 che si occupa di fare un po' di cose interessanti, quindi, se anche buona parte del programma è finalizzato al progetto specifico, con un po' di pratica se ne possono estrapolare parti utili a tutti e soprattutto toccare con mano la programmazione del microcontrollare. Spero che questo serva a "rompere il ghiaccio" per chi ogni volta, armato di buona volontà, arriva alla pagina bianca del MPLab e si ferma esclamando "e mo da dove comincio?!?". Vorrei quindi iniziare un commento "riga per riga" del programma con tutte le spiegazioni del perchè e per come le cose siano state fatte in un certo modo anche da un punto di vista delle scelte circuitali effettuate. Spero solo di avere un minimo di feedback in questo topic visto che non sarebbe bello impantanarmi in un soliloquio, quindi per adesso inizio ad esporre solo la prima parte del programma per vedere se la cosa suscita un minimo di interesse. Codice:
;________________________________________________________
;
;
; FluidStation
;
; ver 1.0 (senza standby)
;________________________________________________________
PROCESSOR 16F84a
RADIX DEC
INCLUDE "P16F84a.INC"
ERRORLEVEL -302, -305
;PIC configuration flags
__CONFIG 0x3FF1 ;XT oscillator
;Disable watch dog timer
;Enable power up timer
;Disable code protect
cod_1 equ 11H ;\
cod_2 equ 6BH ; |
cod_3 equ 5BH ; |
cod_4 equ 1DH ; |
cod_5 equ 5EH ; |
cod_6 equ 7EH ; >Codici per le 10 cifre + Error +Low + High
cod_7 equ 13H ; |
cod_8 equ 7FH ; |
cod_9 equ 5FH ; |
cod_0 equ 77H ; |
cod_E equ 6EH ; |
cod_L equ 64H ; |
cod_H equ 3DH ;/
Successivamente ho modificato il programma per risolvere il problema dell'avviamento sbagliato delle L25. La soluzione non è definitiva ma funziona nel 99% degli avviamenti. Ma di questo parleremo eventualmente più avanti. Le prime righe del codice sono direttive per il compilatore (mi riferisco all'MPLab scaricabile dal sito della microchip). Si dichiara di utilizzare il 16F84A e si dice al compilatore che i numeri presenti nel codice saranno da interpretare come numeri decimali. Si include un file di intestazione tipico del dispositivo che andremo ad usare; in tale file sono definiti dei nomi simbolici per identificare i registri e le porte del PIC. Si disattivano i messaggi 302 e 305 che il compilatore mostra ad ogni compilazione (provate ad eliminare questa riga per scoprire quali siano... sono solo una noia, tanto vale disattivarli). Si definisce il valore del CONFIGURATION REGISTER. Si tratta di un valore a 14 bit che viene scritto in fase di programmazione del PIC e il significato di ciascun bit è documentato sul datasheet. Nel nostro caso dobbiamo: settare a 1 i bit dal 14 al 4: in questo modo evitiamo di attivare la protezione del codice settare a 1 il bit 3: in questo modo attiviamo il Power Up timer settare a 0 il bit 2: in questo modo disattiviamo il Watch Dog Timer settare a 10 i bit 1 e 0: in questo modo attiviamo la modalità High Speed per l'oscillatore esterno. Traducendo in esadecimale la parola binaria così costruita si ottiene il valore 3FF1 che è quello che andremo ad impostare. To be continued... ![]() ![]() ![]() Tu non sai niente, Jon Snow! Ultima Modifica di FluidGuitar : 18-08-2006 21.53.32. |
|
|
|
|
|
#3 |
|
Super Moderator
|
Dopo la configurazione del dispositivo ci sono ancora delle direttive per il compilatore: le "equ".
__________________Codice:
cod_1 equ 11H ;\ cod_2 equ 6BH ; | cod_3 equ 5BH ; | cod_4 equ 1DH ; | cod_5 equ 5EH ; | cod_6 equ 7EH ; >Codici per le 10 cifre + Error +Low + High cod_7 equ 13H ; | cod_8 equ 7FH ; | cod_9 equ 5FH ; | cod_0 equ 77H ; | cod_E equ 6EH ; | cod_L equ 64H ; | cod_H equ 3DH ;/ Non si tratta di variabili ma di costanti; in fase di compilazione saranno sostituite dal rispettivo valore esadecimale. Per inciso avrete notato che, avendo definito la radice numerica decimale, per esprimere un numero in esadecimale è necessario e sufficiente posporre una H alla fine del numero. Per i valori binari basta aggiungere una B. In questa parte del programma definisco dei valori che mi serviranno per la visualizzazione sul display. Credo che questa sia l'aspetto più "custom" del progetto in quanto si basa sull'utilizzo di un integrato molto particolare il "M5480" della ST. Non ho indagato sulla reperibilità di questo integrato; io l'ho recuperato da un vecchio decoder di tele+ e l'ho trovato decisamente comodo. E' in grado di pilotare direttamente display a 7 segmenti ad anodo comune per un totale di 23 segmenti. Il controllo avviene trasmettendo una parola di 35 bit più un bit di start. Quindi l'occupazione in termini di porte del pic si riduce ad una porta per il dato e una per il clock. I codici di cui sopra sono stati costruiti assegnando un bit ad ogni segmento in modo abbastanza arbitrario secondo come sono stati collegati i singoli segmenti dei display al controller. Faccio notare che nello stabilire questi collegamenti non ho minimamente ottimizzato la facilità di collegamento quanto lafacilità di scrittura del codice. A posteriori mi sono reso conto che sarebbe stato più furbo fare il contrario, ma quando l'ho fatto ero alle prime armi con i PIC e in seguito non ho più avuto voglia di riscrivere il codice. ![]() Il codice prosegue così: Codice:
ORG 00H ;Reset Vector - Punto di inizio del programma al reset della CPU goto start ;_______Interrupt Vector - Punto d'inizio delle routines di interrupt handling_______________ ; Routine che serve a generare un'onda quadra sul pin RA0 alla frequenza di circa 400Hz ORG 04H goto interrupt_handler ORG 0CH start Si tratta di una informazione data al compilatore su dove piazzare il codice nella memoria del PIC. Al momento del reset il program counter è posizionato sull'indirizzo 00H e li metteremo la prima istruzione che vogliamo sia eseguita all'accensione o al reset del dispositivo. In questo caso si verifica subito un salto incondizionato ("goto") che ci porta in un'altra posizione. All'indirizzo 04H si trova l'interrupt vector: il punto in cui salta il program counter quando si verifica un interrupt. To be continued.... ![]() ![]() ![]() Tu non sai niente, Jon Snow! Ultima Modifica di FluidGuitar : 18-08-2006 21.56.12. |
|
|
|
|
|
#5 |
|
Registered User
|
questa è manna dal cielo fluid.. grazie tante mi serve molto visto che mi stanno arrivando un paio di sample per iniziare a giochicchiare con i pic
__________________![]()
i'm proud to be a yadayada lammah
|
|
|
|
|
|
#6 |
|
Super Moderator
|
Continuiamo ad analizzare il codice.
__________________Ovviamente ogni commento/critica è gradito sia per correggere eventuali errori sia per avere occasioni di approfondimento; questo è un invito sia a chi ne vuole sapere di più, sia a chi ne sa di più dal momento che la mia esperienza non è che sia tantissima. Il programma continua così: Codice:
ORG 0CH
start
;____________________Configurazione Generale____________________________________
Display_u res 1 ;8 bit per unità
Display_d res 1 ;8 bit per decine
Display_c res 1 ;8 bit per le centinaia anche se non servono
Delay res 1 ;Delay per il mezzo clock
LoopDelay res 1 ;Delay per il mezzo clock ausiliaria
BitCount res 1 ;Contatore bit trasmessi
uni res 1 ;unità
dec res 1 ;decine
cen res 1 ;centinaia
loops res 1
loops2 res 1
KEY res 1
count res 1
ds1820 res 1
temp1 res 1 ;byte alto della temperatura letto dalla sonda
temp2 res 1 ;byte basso della temperatura letto dalla sonda
d1 res 1
d2 res 1
d3 res 1
errore res 1 ;variabile da usare come flag
temp3 res 1 ;variabile temporanea per conversione temperatura
Pintera res 1
Pdecimale res 1
;___________ Sezione di configurazione delle porte e del prescaler ____________________
bsf STATUS,RP0 ;Commuta sul secondo banco dei registri
;Configurazione del Timer
;Assegna il PRESCALER a TMR0 e lo configura a 1:4
movlw 01000001B ;T0CS=0, PSA=0, PS2-PS1-PS0=100 ->1/4 --- INTEDG = 1
movwf OPTION_REG ;250000 / 258 = 976.6 Hz ->onda quadra a 488.3 Hz
;Definizione delle linee di I/O (0=Uscita, 1=Ingresso)
movlw 00001010B
movwf TRISA
;RA0->USCITA PWM RB0->INGRESSO LINEA DAL PC
;RA1->INGRSSO SENS LIVELLO RB1->USCITA LINEA VERSO IL PC
;RA2->ACCENSIONE DISPLAY RB2->RELE' VENTOLA
;RA3->USCITA SENSORE TEMP RB3->RELE' POMPE
;RA4->INGRESSO SENSORE TEMP RB4->SEGNALE ALLARME
; RB5->SWITCH VENTOLA
; RB6->DISPLAY CLOCK
; RB7->DISPLAY DATO
movlw 00100001B
movwf TRISB
clrf INTCON
bcf INTCON,T0IE ;Abilitazione del Timer Overflow
bcf INTCON,RBIE ;Abilitazione interrupt su RB4-RB7
bcf STATUS,RP0 ;Commuta sul primo banco dei registri
bcf INTCON,GIE ;Global Interrupt Enable
Un'etichetta è ancora una volta una istruzione per il compilatore il quale prende nota della locazione di memoria in cui è situata per effettuare dei salti in quel punto. In questo caso l'etichetta si trova subito dopo la direttiva ORG quindi "goto start" equivale a "goto 0CH"! Dalla locazione 0CH inizia una serie di direttive che definiscono le variabili utilizzate nel programma. Si utilizza la direttiva "res" la quale dice al compilatore di "riservare" un numero di byte specificato e di riferirsi a qualla locazione con l'etichetta specificata. Quindi Display_u res 1 significa che dalla locazione corrente, cioè 0CH, per la lunghezza di un byte (quindi di fatto solo la locazione 0CH) non sarà utilizzata dal compilatore per metterci delle righe di codice eseguibile ma verrà laasciata libera e etichettata col nome "Display_u". E così via per tutte le altre variabili. Tale cella di memoria si trova in una zona di memoria detta GPR da General Purpose Register. Si tratta di un insieme di registri di memoria RAM in cui si possono memorizzare dei valori anche a Run-Time e quindi di fatto ogni etichetta assegnata a tali locazioni è utilizzabile a tutti gli effetti come una variabile. Bisogna fare solo un poco di attenzioe a non confondere l'assembly con un linguaggio ad alto livello. Tali variabili sono SEMPRE e comunque delle parole a 8 bit che il processore tratta come interi in complemento a 2 e corrispondono a registri fisici della RAM: lo scope è a livello di programma, non di routine! Quindi, nel prosieguo, quando anche si faranno delle chiamate a subroutine, non bisogna dimenticarsi che le variabili che si usano sono sempre globali ed evitare di utilizzarle in modo spregiudicato da una routine all'altra. Sembra una precisazione ovvia ma essendo abituati ad utilizzare un linguaggio ad alto livello in cui tendenzialmente le varibili sono locali si rischia di prendere delle cantonate e di metterci un bel po' prima di accorgersene. Non sto qui a commentare la funzione di ciascuna variabile perchè non se ne capirebbe subito l'utilità; meglio rimandare a quando saranno utilizzate nel programma. Vale la pena di commentare invece la parte di configurazione e dire qualcosa sullo STATUS REGISTER. Ma questo lo vedremo più avanti ![]() Continua... ![]() ![]() ![]() Tu non sai niente, Jon Snow! Ultima Modifica di FluidGuitar : 18-08-2006 21.58.22. |
|
|
|
|
|
#7 | |
|
Amministratore
|
Quote:
![]() |
|
|
|
|
|
|
#8 |
|
Super Moderator
|
Spendiamo adesso due parole per descrivere lo STATUS REGISTER.
__________________Il registro di stato è una locazione di memoria che fa parte degli SFRs, cioè degli Special Function Registers. Si tratta di un insieme di registri che assolvono a varie funzioni e di cui parleremo man mano che li incontriamo. Vorrei solo ricordare che tutte le informazioni sull'hardware e sulla costituzione interna del PIC sono estratte pari pari dal datasheet del componente sul quale con un po' di pazienza si reperiscono tutte le informazioni necessarie ad utilizzare le varie funzionalità del processore. Lo status register è un registro ad 8 bit_ bit 7- IRP: selezione del banco per indirect addressing i PIC permettono di indirizzare la memoria in modo indiretto mediante una specie di puntatore; il bit 7 dello status register serve a specificare a quale banco di memoria si riferisce il puntatore. Nel 16F84A tale bit non è implementato e la microchip consiglia di lasciarlo a 0. bit 6/5 - RP1/RP0: selezione del banco di memoria la memoria del PIC è organizzata in banchi da 128 byte. Nel 16F84A ci sono 2 banchi, il banco 0 e il banco 1 ed è lo status register che decide qual'è il banco attivo istante per istante sulla base del valore del bit 5 (RP0). Se il bit 5 è 0 è attivo il banco 0, se il bit 5 è 1 è attivo il banco 1. Il bit 6 serve ad indirizzare altri banchi di memoria nei pic che li possiedono (es. il 16F877). Nel nostro caso il valore di RP1 non ha importanza ma la microchip consiglia di lasciarlo a 0. Il passaggio dal banco 0 al banco 1 può essere fatto a Run-Time ed è indispensabile per poter leggere o scrivere gli SFR che vi sono contenuti. Gli Special Function Registers occupano le prime 12 locazioni di ciascun banco e le restanti rappresentano della RAM liberamente utilizzabile nei programmi. Per l'uso di questa ram però è indifferente la selezione del banco in quanto gli indirizzi del banco 1 sono mappati sullo 0. Questo significa che se punto alla locazione 8C che si trova nel banco 1 effettivamente leggo/scrivo nel registro 0C che si trova nel banco 0. La gestione dei banchi può sembrare macchinosa ma, almeno nel 16F84A, si riduce a qualche piccola accortezza di tanto in tanto in quanto di fatto ho necessità di attivare il banco 1 solo sporadicamente quando devo usare i registri speciali che vi sono contenuti. bit 4 - TO: bit (negato) di time out è un bit che è normalmente posto a 1 e diviene 0 quando si verifica un time out del Watch Dog Timer. Il WDT non è utilizzato nel progetto in esame quindi per chi volesse saperne di più rimando al datasheet. bit 3 - PD: bit (negato) di Power Down è un bit che è normalmente a 1 e diviene 0 quando il pic riceve una istruzione di sleep. bit 2 - Z: zero bit insieme al bit 0 è uno dei più importanti: diventa 1 quando il risultato di una operazione aritmetica o logica è zero. bit 1 - CD: digit carry bit diventa 1 quando una operazione matematica produce un overflow oltre il 4 bit bit 0 - C: carry bit è il bit di riporto; diventa 1 quando si produce un overflow oltre l'ottavo bit. I bit 0 e 1 hanno anche la funzione di borrow (neg) nelle sottrazioni ricordando che le sottrazioni sono effettuate in complemento a 2. Continua... ![]() ![]() ![]() Tu non sai niente, Jon Snow! |
|
|
|
|
|
#9 |
|
Guaranteed Entropy Mod
|
scrib scrib... sottoscritto il 3d
__________________![]() ~ ~ ![]() ~ Memento Audere Semper. ~ Ingegneria Italiana anni '80 ~ La moderazione è una cosa fatale. Nulla ha più successo dell'eccesso. [Oscar Wilde] ~ È la realtà, spesso, ad essere inesatta. [Douglas Adams] |
|
|
|
|
|
#10 |
|
Super Moderator
|
Prima di andare avanti con l'esame del codice è necessario parlare di un altro registro molto importante: lo OPTION REGISTER.
__________________L'option register si trova nel banco 1, quindi per modificarne il contenuto è necessario prima attivare il banco 1 attraverso lo status register. Vediamo la funzione dei singoli bit dell'option register: bit 7 - RBPU (negato): abilita le resistenze di pull-up della porta B; il pic ha due porte di input/output denominate A e B. Quando un pin della porta B è configurato come input ed è lasciato scollegato, attivando le resistenze interne di pull-up è possibile stabilizarne ad 1 lo stato. bit 6 - INTEDG: fronte di riferimento per l'interrupt; è possibile attivare un interrupt sul pin 0 della porta B. Se questo bit è posto a 1 l'interrupt avviene sul fronte di salita del segnale sul piedino 0. bit 5 - T0CS: sorgente per il Timer 0; il pic ha un timer interno che può avere alternativamente segnali di clock: se il bit 5 è posto a 1 viene preso un segnale presente sul pin 4 della porta A, altrimenti si utilizza il clock interno. bit 4 - T0SE: fronte di riferimento del clock per il timer; nel caso in cui sia stato selezionato un clock esterno con questo bit si controlla se il timer debba essere incrementato quando il segnale sul pin 4 si abbassa (T0SE=1) o si alza (T0SE=0). bit 3 - PSA: assegnazione del prescaler; fra la sorgente del clock e il contatore del timer è possibile interporre un divisore detto prescaler. Dal momento che il pic oltre al timer 0 ha anche il watch dog timer con questo bit si può decidere di assegnare il prescaler all'uno o all'altro (PSA=1->prescaler assegnato al WDT). bit 2:0 - PS2:PS0: valore del prescaler; decide il rapporto di divisione per il prescaler secondo la seguente tabellina: valore - TMR0 - WDT 000 1/2 1/1 001 1/4 1/2 010 1/8 1/4 011 1/16 1/8 101 1/32 1/16 101 1/64 1/32 110 1/128 1/64 111 1/256 1/128 Adesso abbiamo gli strumenti per capire il significato delle successive righe di codice. Continua... ![]() ![]() ![]() Tu non sai niente, Jon Snow! |
|
|
|
|
|
#11 |
|
Super Moderator
|
Riprendiamo questa parte di codice:
__________________Codice:
bsf STATUS,RP0 ;Commuta sul secondo banco dei registri
;Configurazione del Timer
;Assegna il PRESCALER a TMR0 e lo configura a 1:4
movlw 01000001B ;T0CS=0, PSA=0, PS2-PS1-PS0=100 ->1/4 --- INTEDG = 1
movwf OPTION_REG ;250000 / 258 = 976.6 Hz ->onda quadra a 488.3 Hz
;Definizione delle linee di I/O (0=Uscita, 1=Ingresso)
movlw 00001010B
movwf TRISA
;RA0->USCITA PWM RB0->INGRESSO LINEA DAL PC
;RA1->INGRSSO SENS LIVELLO RB1->USCITA LINEA VERSO IL PC
;RA2->ACCENSIONE DISPLAY RB2->RELE' VENTOLA
;RA3->USCITA SENSORE TEMP RB3->RELE' POMPE
;RA4->INGRESSO SENSORE TEMP RB4->SEGNALE ALLARME
; RB5->SWITCH VENTOLA
; RB6->DISPLAY CLOCK
; RB7->DISPLAY DATO
movlw 00100001B
movwf TRISB
Si tratta di una operazione che opera sul singolo bit del registro specificato e accetta come argomento il numero del bit da impostare a 1. Quindi: bsf STATUS,RP0 non fa altro che impostare a 1 il bit RP0 del registro STATUS di cui abbiamo parlato prima. Faccio notare che se avessi scritto: bsf 03H,5 avrei ottenuto lo stesso risultato in quanto il registro di Status si trova alla locazione esadecimale 03 e il bit RP0 è il quinto. Come detto poco sopra mettere a 1 il bit RP0 significa commutare sul banco 1 l'accesso alla memoria; questo è necessario in quanto vogliamo impostare il valore dell'option register che si trova appunto nel banco 1. Incontriamo altre 2 istruzioni del PIC: movlw (MOVe Literal W): mette il valore "letterale" nell'accumulatore movwf (MOV W to F): mette il valore dell'accumulatore nel registro f. Il PIC essendo un processore risc con parola da 14 bit non è in grado di spostare direttamente il contenuto di una cella di memoria ad un'altra. Tutti gli spostamenti avvengono passando da uno speciale registro detto "accumulatore" e indicato con W. Anche le operazioni aritmetiche e booleane hanno come uno dei due argomenti sempre l'accumulatore. In questo caso eseguiamo le seguenti operazioni: movlw 01000001B cioè mettiamo nell'accumulatore il valore "letterale" 01000001 e in seguito: movwf OPTION_REG spostiamo il contenuto dell'accumulatore nell' option register. Questo ci risparmia di impostare uno ad uno i bit del registro in oggetto. Il significato di ciascuno dei bit è chiaro per quanto detto poco sopra sull'option register. Avevo bisogno di generare un'onda quadra permanente su uno dei piedini e, per far questo in modo trasparente al resto del programma, ho deciso di sfruttare il timer interrupt di cui parleremo fra poco. In questa sede sto assegnando il prescaler al timer0 e lo sto configurando in modo da avere gli interrupt ad una frequenza di circa 976Hz. L'operazione di impostare un valore a 8 bit per un intero registro viene eseguita subito dopo con due altri importanti registri del banco 1: TRISA e TRISB. Il contenuto dei registri TRISA e TRISB decide rispettivamente per la porta A e la porta B il funzionamento di ciascun PIN. La porta A ha 5 PIN che corrispondono ai primi 5 bit del registro TRISA e la porta B ha tutti e 8 i PIN mappati sul TRISB. Se nel registro TRIS un bit è impostato a 1 quel piedino si comporta come un ingresso, altrimenti come un'uscita. Nota Bene: all'accensione e al reset del dispositivo i registri TRIS sono impostati a 1 per tutti i pin, cioè per default i piedini del pic sono degli ingressi. Nel codice è chiaramente commentata la funzione di ciascun piedino. Continua... ![]() ![]() ![]() Tu non sai niente, Jon Snow! Ultima Modifica di FluidGuitar : 18-08-2006 22.01.28. |
|
|
|
|
|
#12 |
|
Super Moderator
|
Prima di continuare con l'esame del codice è necessario studiare la struttura di un altro importante registro: lo INTCON (INTerrupt CONfiguration).
__________________Il significato dei singoli bit è il seguente: bit 7 - GIE: abilitazione generale degli interrupt se messo a 1 gli interrupt sono abilitati. bit 6 - EEIE: fine scrittura sulla eprom mettendo a 1 questo bit si verificherà un interrup alla fine di una operazione di scrittura sulla eprom interna del pic. bit 5 - T0IE: interrupt sul timer overflow abilita l'interrupt quando il contatore del timer raggiunge il massimo valore (255). Come già detto la velocità con cui si incrementa tale contatore dipende dall'impostazione del prescaler. bit 4 - INTE: interrupt sul piedino RB0 abilita l'interrupt quando viene rilevato un impulso sul piedino 0 della porta B. Come già detto il registro di configurazione controlla se l'impulso viene rilevato sul fronte di salita o sul fronte di discesa del segnale. bit 3 - RBIE: interrupt sui piedini 4:7 della porta B una variazione di stato su uno di questi piedini (se configurati come ingresso ovviamente) genera un interrupt. bit 2 - T0IF: flag per il timer interrupt viene messo a 1 dal verificarsi dell'interrupt sul timer 0 bit 1 - INTF: flag per l'interrupt su RB0 viene messo a 1 dal verificarsi dell'interrupt sul piedino RB0 bit 0 - RBIF: flag per l'interrupt su RB4:RB7 viene messo a 1 dall'interrupt sul cambiamento della porta B. Il verificarsi di uno qualsiasi degli interrupt abilitati provoca l'interruzione del programma principale e il salto alla locazione 04H. Per discriminare quale interrupt si sia verificato è necessario controllare lo stato dei bit 0, 1, e 2 e ricordarsi di rimetterli eventualmente a 0 per permettere all'interrupt di verificarsi nuovamente. Continua... ![]() ![]() ![]() Tu non sai niente, Jon Snow! |
|
|
|
|
|
#13 |
|
Super Moderator
|
Adesso che sappiamo come interpretare il registro di configurazione degli interrupt hanno un senso le seguenti righe del codice:
__________________Codice:
clrf INTCON
bcf INTCON,T0IE ;Abilitazione del Timer Overflow
bcf INTCON,RBIE ;Abilitazione interrupt su RB4-RB7
bcf INTCON,GIE ;Global Interrupt Enable
bcf STATUS,RP0 ;Commuta sul primo banco dei registri
![]() Subito dopo vi sono altre istruzioni di inizializzazione: Codice:
;______Inizializzazione delle variabili___________________ clrf Display_u clrf Display_d clrf Display_c clrf errore bcf PORTA,0 bsf PORTA,4 bcf PORTA,2 bcf PORTB,1 bcf PORTB,2 bcf PORTB,3 ;spegne le pompe bcf PORTB,6 bcf PORTB,7 bcf PORTB,4 ;________________________________________________________ Sono state usate due nuove istruzioni: bcf= Bit Clear F che serve a porre a 0 il bit specificato analogamente a come la bsf lo metteva a 1 clrf= Clear F che serve ad azzerare un intero registro. Con la prossima istruzione vedremo come realizzare delle subroutine in asm. Continua... ![]() ![]() ![]() Tu non sai niente, Jon Snow! Ultima Modifica di FluidGuitar : 18-08-2006 22.03.54. |
|
|
|
|
|
#14 |
|
Super Moderator
|
Dopo queste fasi preliminari di configurazione e inizializzazione inizia il programma vero e proprio.
__________________Codice:
call test_allarme Codice:
;________________Routines controllate dall'interrupt_________________________________________ test_allarme bcf INTCON,GIE ;disabilito interrupt bcf PORTB,4 call delay_1s bsf PORTB,4 call delay_15ms call delay_15ms bcf PORTB,4 call delay_1s bsf PORTB,4 call delay_15ms call delay_15ms bcf PORTB,4 call delay_1s bsf PORTB,4 bcf errore,1 bsf INTCON,GIE ;riabilito interrupt return Su un filo viaggia il segnale che segnala alla waterstation che il computer è acceso; si tratta di un filo che prende i 5 volt direttamente da molex con una resistenza di sicurezza in serie. Il secondo filo invece serve alla waterstation per segnalare al computer che è tutto a posto. In realtà questo filo serve solo a mantenere spento una schedina di allarme che viene alimentata direttamente dal pc. Se la waterstation è spenta o scollegata l'allarme suona. Il filo che mantiene spento l'allarme è collegato al piedino 4 della porta B e, all'avviamento della waterstation viene chiamata questa routine che non fa altro che mettere a 0 per 3 volte il piedino di allarme col preciso scopo di farlo suonare! Si tratta cioè di un check per verificare che il circuito di allarme funzioni correttamente. In realtà quell'allarme è talmente fastidioso che in seguito ho modificato il codice per fargli fare solo un beep anzichè tre, ma qui volevo commentare il codice originale dell'articolo In questa porzione di codice troviamo l'istruzione "call". Si tratta dell'istruzione che permette di fare il salto ad una subroutine specificata da una label lungo il listato quindi se scorriamo il codice fin verso la fine troviamo la porzione che ho postato poche righe sopra. Notiamo che la subroutine termina con l'istruzione "return". Spendiamo due parole sulla coppia "call" - "return". Nell'assembler del PIC l'uso delle subroutine, se anche permette di compattare il codice (cosa buona visto che la memoria non è tantissima), non è comunque da prendere alla leggera. Infatti il PIC possiede uno stack a soli 8 livelli. Questo significa che non possiamo nidificare più di 8 chiamate succesive a subroutine. Infatti la call non fa altro che eseguire un "push" dell'indirizzo della successiva istruzione nello stack ed esegue un salto alla locazione indicata dalla label. Successivamente la return eseguirà il "pop" di tale valore e lo rimetterà nel program counter. Se la subroutine contiene al suo interno altre sub il valore del nostro intirizzo verrà spostato più in profondità nello stack e così via. Siccome lo stack contiene al massimo 8 indirizzi, se eseguiamo 8 call senza nessuna return lo stack sarà pieno e una ulteriore call causerà la perdita del primo indirizzo memorizzato. Lo stack funziona secondo una logica LIFO: Last Input First Output - l'ultimo valore immesso è il primo ad uscire. La routine "test_allarme" fa le seguenti cose: -disabilita la gestione degli interrupt -azzera il piedino 4 della porta B: in questo modo l'allarme inizierà a suonare -aspetta un secondo: l'allarme continua a suonare -alza il piedino 4: l'allarme si spegne -aspetta 15ms -aspetta 15ms -ripete l'operazione altre due volte -alza il piedino 4 -mette a 1 il bit 1 della variabile errore: si tratta di una variabile della quale utilizo il bit 0 e il bit 1 come flag di controllo; in questo caso il bit 1 serve a capire se la waterstation è operativa o meno -riabilito la gestione degli interrupt -ritorna al punto in cui si è verificata la chiamata (andando a pescare questa informazione proprio nello stack). Avrete notato che le operazioni "aspetta 1 s" e "aspetta 15ms" corrispondono ad altrettante chiamate a subroutine delle quali ci occuperemo nel prossimo post. Continua... ![]() ![]() ![]() Tu non sai niente, Jon Snow! Ultima Modifica di FluidGuitar : 18-08-2006 22.06.19. |
|
|
|
|
|
#16 |
|
Super Moderator
|
Grazie Hunty, ma soprattutto in bocca al lupo!
__________________Intanto qui si va avanti col codice visto che è ancora lunghetto Codice:
delay_15ms movlw 182 ; Aspetta 15 ms
movwf d1
movlw 12
movwf d2
delay_15ms_loop decfsz d1, f
goto $+2
decfsz d2, f
goto delay_15ms_loop
goto $+1
nop
return
Il ritardo in un PIC si realizza ovviamente facendo fare al programma dei cicli a vuoto in numero sufficiente. Dal momento che fra una istruzione e l'altra passano 4 cicli di clock e il clock è, nel caso in esame, a 4 MHz, le istruzioni vengono eseguite alla frequenza di 1MHz. Cioè fra una istruzione e l'altra passa un tempo di 1us. Per fare 15ms dobbiamo eseguire 15000 istruzioni da 1 ciclo di clock. Vediamo come: delay_15ms (label per identificare l'inizio della routine) movlw 182 (mette il valore numerico 182 nell'accumulatore) movwf d1 (mette il valore dell'accumulatore nella variabile d1 definita a suo tempo al'inizio del programma) movlw 12 movwf d2 (come sopra per la variabile d2) delay_15ms_loop (label funzionale alla routine) decfsz d1, f Una nuova istruzione: "DECFSZ" questa istruzione decrementa di uno il valore contenuto nel registro indicato (in questo caso d1) e mette il valore decrementato ancora in d1. Se il risultato del decremento è zero l'istruzione immediatamente successiva viene saltata (Skip if Zero) N.B. Se il valore del decremento è zero diventa 1 il bit di zero dello status register (ricordate?) e se è uno il bit di zero l'istruzione decfsz eseguirà il salto. goto $+2 Altra nuova istruzione: "GOTO" è l'istruzione per il salto incondizionato. Ovviamente ha come argomento l'indirizzo di destinazione che può essere rappresentato da una label. In questo caso l'argomento della goto è calcolato dal compilatore in quanto si fa uso del simbolo $ che è un meta-simbolo che indica la posizione corrente del Program Counter. Cioè, durante la compilazione del programma la nostra istruzione goto occuperò un indirizzo ben preciso (che non conosco a priori e che può cambiare man mano che il programma viene scritto) e, con simbolo $ non faccio altro che identificare la locazione che contiene la mia goto. A questo punto posso far puntare la goto in un punto qualsiasi rispetto a dove essa si trova , semplicemente aggiungendo dei valori al suo indirizzo. Nel nostro caso la goto mi porterà più avanti di 2 istruzioni rispetto a dove si trova. Rispetto alle altre istruzioni che richidono 1 ciclo di clock la goto ne richiede due mentre la decfsz ne richiede 1 se fa solo il decremento e 2 se oltre al decremento fa anche lo skip. Studiando il flusso di queste poche righe di programma scopriremo che il ciclo non fa altro che decrementare d1 da 182 a zero eseguendo 5 istruzioni per fare questo. Dopodichè decrementa d2 e ricomincia a decrementare d1 che essendo zero ricomincerà da 255. Questo decremento da 255 a 0 avviene quindi per 12 volte. Abbiamo quindi: 4 istruzioni di inizializzazione 182*5 istruzioni del primo decremento di d1 (totale 910) 5 istruzioni per decrementare d2 che vengono eseguite 12 volte (totale 60) 12*255*5 istruzioni per eseguire i 12 loop (totale 15300) Più i due cicli di return (anche la return richiede due cicli di clock. Raggiungiamo un totale di 16274 che è un po' di più di 15ms. Non so perchè abbiano scelto questi valori perchè queste routine sono copia/incollate da non ricordo più quale sito, ma siccome funzionava non mi è sembrato che valesse la pena modificarla Continua... [EDIT] Errata Corrige. Ho rifatto meglio il calcolo visto che ieri sera ero un po' assonnato. Le istruzioni da eseguire in loop sono 11*255*5 dal momento che d2 viene decrementata subito prima di iniziare i cicli da 255. Quindi il totale è 14999..... decisamente meglio ![]() ![]() ![]() ![]() Tu non sai niente, Jon Snow! Ultima Modifica di FluidGuitar : 18-08-2006 22.08.02. |
|
|
|
|
|
#17 |
|
man of constant sorrow
|
intanto lo segno e me lo leggo con calma =]
__________________ |
|
|
|
|
|
#18 |