Microcontrollori PIC
Transcript
Microcontrollori PIC
Microcontrollori PIC Teoria, Esperimenti, Esercizi HARDWARE DEL PIC SOFTWARE DEL PIC MPLAB MEMORIA PORTE-IO TIMER DISPLAY AUDIO ADC INTERRUPT EEPROM HARDWARE DEL PIC Il PIC16F684 (PIC=Peripheral Interface Controller=Controllore di interfaccia periferica) è un microcontrollore RISC (Reduced Instruction Set Computer = Computer con Set di Istruzioni Ridotto) con un set di sole 35 istruzioni. La sua memoria programma è separata dalla memoria dati, che ha estensione 8 bit. Questa separazione permette l’utilizzo di una memoria programma con word a 14 bit. Grazie a ciò tutte le istruzioni sono codificate con una sola word e quindi vengono eseguite in un solo ciclo. Di conseguenza le dimensioni dei programmi risultano ridotte e la velocità esaltata rispetto ad altre famiglie di microchip. Il contenitore ha 14 pin: VDD 1 14 VSS RA5/T1CKI/OSC1/CLKIN 2 13 RA0/AN0/C1IN+/ICSPDAT/ULPWU RA4/AN3/T1G/OSC2/CLKOUT 3 12 RA1/AN1/C1IN-/VREF/ICSPCLK RA3/MCLR/VPP 4 11 RA2/AN2/T0CKI/INT/C1OUT RC5/CCP1/P1A RC4/C2OUT/P1B 5 10 RC0/AN4/C2IN+ 6 9 RC1/AN5/C2IN- RC3/AN7/P1C 7 8 RC2/AN6/P1D Il significato dei pin è descritto nella seguente tabella: NOME FUNZIONE DESCRIZIONE NOME VDD VDD RA5/T1CKI/ OSC1/CLKIN RA5 Alimentazione Porta I/O con pull-up programmabile e interrupt per transizione clock Timer 1 oscillatore al quarzo ingresso clock esterno, connessione oscillatore RC Porta I/O con pull-up programmabile e interrupt per transizione VSS RA0/AN0/C1IN+/I CSPDAT /ULPWU T1CKI OSC1 CLKIN RA4/AN3/T1G/ OSC2/CLKOUT RA4 FUNZIONE VSS C1IN+ massa Porta I/O con pull-up programmabile e interrupt per transizione input canale 0 A/D ingresso comparatore 1 ICSPDAT I/O dati programmazione seriale ULPWU input potenza ultra-bassa wakeup RA0 AN0 RA1/AN1/C1IN/VREF/ICSPCLK RA1 AN3 input canale 3 A/D T1G OSC2 porta Timer 1 oscillatore al quarzo AN1 C1IN- CLKOUT uscita fOSC/4 VREF RA3 input PortA con interrupt per transizione ICSPCLK MCLR clear principale con pull-up interno VPP RC5 CCP1 P1A tensione di programmazione I/O PORTC ingresso cattura/uscita confronto uscita PWM RC4/C2OUT/ P1B RC4 I/O PORTC RC3/AN7/P1C C2OUT P1B RC3 AN7 P1C uscita comparatore 2 uscita PWM I/O PORTC input canale 7 A/D uscita PWM RA3/MCLR/VPP RC5/CCP1/P1A RA2/AN2/T0CKI/I NT/C1OUT RC0/AN4/C2IN+ RC1/AN5/C2IN- RC2/AN6/P1D DESCRIZIONE Porta I/O con pull-up programmabile e interrupt per transizione input canale 1 A/D input comparatore 1 tensione di riderimento estena A/D clock programmazione seriale AN2 T0CKI INT C1OUT Porta I/O con pull-up programmabile e interrupt per transizione input canale 2 A/D input clock Timer 0 interrupt esterno output comparatore 1 RC0 I/O PORTC AN4 C2IN+ RC1 AN5 C2INRC2 input canale 4 A/D input comparatore 2 I/O PORTC input canale 5 A/D input comparatore 2 I/O PORTC input canale 6 A/D uscita PWM RA2 AN6 P1D Si riporta di seguito lo schema a blocchi, con indicazione dei pin. La CPU (Central Processing Unit = Unità Centrale di Elaborazione) è il cervello del microcontrollore, riceve le istruzioni dalla memoria flash, attraverso il Bus Programma. Le istruzioni sono selezionate dal Program Counter PC. La CPU decodifica l’istruzione, ovvero interpreta il suo significato e impartisce i comandi per eseguirla. In questo compito è supportata dal registro Accumulatore WREG, centro nevralgico per la circolazione dei dati e dalla ALU (Arithmetic and Logic Unit = Unità Aritmetico Logica), centralina di calcolo matematico. 1 La RAM contiene i File Registers (Registri Archivio), che conservano l’”impronta” del sistema. Se la CPU è il cervello del sistema, i fili register ne sono il cuore. I file register di tipo “core” contengono informazioni che disciplinano il comportamento del sistema nel suo insieme. I file register di tipo “periferica” mantengono memorizzate le informazioni di configurazione delle periferiche. Questi registri sono volatili, quindi vengono programmati di volta in volta. I dati di configurazione delle periferiche sono trasmessi attraverso il Bus Dati, così come i dati utili manipolati. I dati da conservare possono essere salvati sulla EEPROM. Non esiste un vero e proprio Bus Indirizzi. La RAM riceve l’indirizzo di registro dalla CPU e la Flash dal PC. La EEPROM riceve l’indirizzo dal bus dati e lo salva entro un registro dedicato, prima di ricevere il dato. Come tradizione la CPU gestisce l’istruzione di programma in due fasi: la fase di Fetch e quella di Execute. Nella fase di fetch avviene il prelievo dell’istruzione dalla memoria (fetch=ricerca), in quella di execute ha luogo l’esecuzione vera e propria del comando impartito dall’istruzione. Nel PIC è possibile una parziale sovrapposizione delle due fasi (Pipeline), con risparmio di tempo e conseguente aumento della velocità. Grazie alla separazione tra Bus Programma e Bus Dati, mentre è ancora in corso l’esecuzione, la quale coinvolge il bus dati, la CPU può iniziare la lettura dell’istruzione successiva, che coinvolge il bus programma. 14 OSC1/CLKIN OSC2/CLKOUT Bus Programma CPU 13 PC flash 2K14 memoria programma INT RAM 128 bytes file registers T0CKI T1CKI T1G CCP1/P1A P1B P1C P1D C1INC1IN+ C1OUT C2INC2IN+ C2OUT Timer 0 Timer 1 Timer 2 ECCP EEPROM 256 bytes dati B U S PORTA D A T I PORTC 2 comparatori analogici e riferimento Cat / Comp / PWM Progr Seriale IC 8 Convertitore Analogico Digitale RA0 RA1 RA2 RA3 RA4 RA5 RC0 RC1 RC2 RC3 RC4 RC5 AN0 AN1 AN2 AN3 AN4 AN5 AN6 AN7 VREF Il chip incorpora le seguenti periferiche: • Due porte di I/O per un totale di 12 pin con controllo individuale della direzione del dato. Le porte assicurano connessioni fisiche tra microprocessore e dispositivi esterni ad esso. Entrambe le porte dispongono di 6 terminali digitali, nella PORTC sono tutti bidirezionali, nella PORTA il terminale RA3 è solo di input. • Timer0, Timer1 e Timer2 per la generazione di intervalli temporali. Il timer è un dispositivo basato su un registro contatore. Ad ogni quarto di periodo del clock esso incrementa il suo valore. La misurazione del contenuto del registro riflette allora una misutazione temporale utile in molte applicazioni. • Convertitore Analogico Digitale A/D di risoluzione 10 bit a 8 canali di input per il dato analogico. Consente di tradurre segnali presenti sugli input analogici in formato digitale, adeguato all’elaborazione matematica. • Modulo con comparatori analogici. Contiene due comparatori analogici a due ingressi e una tensione nota di riferimento. Un comparatore stabilisce un confronto tra due segnali di tipo analogico, fornendo in uscita livello alto o basso, come risultato del confronto. • Modulo Cattura, Compara, PWM Fornisce su quattro uscite impulsi digitali con periodo e duty cycle stabiliti programmaticamente (PWM = Pulse Width Modulation = Modulazione della Durata di Impulso). • Programmazione seriale In-Circuit per la scrittura del programma con il chip nel circuito finale 2 Il PIC16F684 dispone di tre memorie: • • • memoria di Programma memoria Register File memoria EEPROM MEMORIA EEPROM byte 0 byte 1 byte 2 00h 01h 10h byte 254 byte 255 FEh FFh MEMORIA DI PROGRAMMA PC<12:0> MEMORIA REGISTER FILE Livello 1 Stack Livello 2 Stack . . Livello 8 Stack Vettore di Reset 0000h indirizz indiretto TMR0 PCL STATUS FSR PORTA PORTC Vettore di Interrupt 0004h 0005h Memoria programma 07FFh 0800h 1FFF • Organizzazione memoria di programma La memoria di programma è di tipo flash, una memoria non volatile programmabile e cancellabile elettricamente fino a 100000 volte. Nella memoria di programma viene riversato, al momento della programmazione, il codice macchina a 14 bit. Il PIC dispone di un Program Counter PC<12:0> di 13bit, in grado di scandire una istruzione dopo l’altra, puntando di volta in volta alla istruzione da eseguire. Il PC è virtualmente in grado di indirizzare 13 2 =4098byte=4k, dall’indirizzo 0000h all’indirizzo 1FFFh. Tuttavia, di questi, solo i primi 2k, da 0000h a 07FFh sono implementati. All’indirizzo 0000h è collocato il vettore di reset. A questa locazione si dispone il PC al momento del reset; da qui inizia l’esecuzione del programma. L’indirizzo 0004h è riservato ai vettori di interrupt (vedi capitolo). E’ necessario mantenere vuoto questo spazio e collocare la prima istruzione del programma all’indirizzo 005h. PCLATH INTCON PIR1 TMR1L TMR1H T1CON TMR2 T2CON CCPR1L CCPR1H CCP1CON PWM1CON ECCPAS WDTCON CMCON0 CMCON1 ADRESH ADCON0 00h 01h 02h 03h 04h 05h 06h 07h 08h 09h 0Ah 0Bh 0Ch 0Dh 0Eh 0Fh 10h 11h 12h 13h 14h 15h 16h 17h 18h 19h 1Ah 1Bh 1Ch 1Dh 1Eh 1Fh 20h indirizz indiretto OPTION_REG PCL STATUS FSR TRISA TRISC PCLATH INTCON PIE1 PCON OSCCON OSCTUNE ANSEL PR2 WPUA IOCA VRCON EEDAT EEADR EECON1 EECON2 ADRESL ADCON1 General Purpose Registers 32 Bytes 80h 81h 82h 83h 84h 85h 86h 87h 88h 89h 8Ah 8Bh 8Ch 8Dh 8Eh 8Fh 90h 91h 92h 93h 94h 95h 96h 97h 98h 99h 9Ah 9Bh 9Ch 9Dh 9Eh 9Fh A0h BFh Registri uso Generale 96 Bytes 7Fh BANCO 0 ACCESSO 70h-7Fh F0h FFh BANCO 1 Per questo si deve far precedere le istruzioni del programma dalla direttiva per il compilatore: org 0x005 e collocare all’indirizzo 0x000 l’istruzione di salto all’indirizzo 0x005: org 0x000 goto 0x005 La direttiva org ha il significato di ORiGin (origine) per il programma. Sono presenti infine 8 livelli di stack (catasta) entro i quali il programma salva (push) l’indirizzo di ritorno da sottoprogramma e lo ripristina (pop) all’uscita dal sottoprogramma. 3 • Organizzazione memoria Register File Register File significa Archivio Registri. E’ una memoria di 128 bytes 2 banchi di tipo RAM, quindi veloce ma volatile, necessaria per registrare, nel corso del programma, i dati di gestione dei dispositivi del microcontrollore e i dati manipolati dal programma. E’ divisa in due banchi: banco 0: spazio indirizzi 00h – 7Fh banco 1: spazio indirizzi 80h – FFh Entrambi sono selezionati dagli indirizzi base a 7 bit 00 – 7F. L’indirizzo reale si ottiene completando l’indirizzo base con il contenuto dei bit RP1, RP0 del registro STATUS secondo le combinazioni: RP1RP0 = 00 selezione banco 0 RP1RP0 = 01 selezione banco 1 I primi 32 byte dei banchi sono Special Function Registers (Registri Funzione Speciale) SFR. I rimanenti 96 bytes del banco 0 e solo 32 bytes del banco 1 sono General Purpose Registers (Registri di Impiego Generale) GPR. Ogni File Register (Registro Archivio) dello spazio SFR controlla il comportamento di uno o più dispostivi del microcontrollore. Ci sono registri per la gestione del Timer, delle porte di I/O ecc. Questi saranno approfonditi ognuno in un proprio capitolo. Ogni registro dello spazio GPR può essere utilizzato liberamente per la registrazione di dati manipolati dal programma. Qui si possono memorizzare quelle che anche nei linguaggi ad alto livello sono note come “variabili”. • Organizzazione memoria EEPROM La sigla EEPROM è l’acronimo di Electrically Erasable Programmable Read Only Memory (Memoria di sola Lettura Cancellabile e Programmabile Elettricamente). E’ una memoria di tipo non volatile, impiegata per la conservazione di dati importanti per il processo gestito dal microcontrollore. Il suo contenuto permane anche a microcontrollore spento, pertanto può essere programmata una sola volta. Se necessario comunque è possibile cancellarla e riprogrammarla con opportune istruzioni, che agiscono elettricamente nei suoi circuiti interni, senza la necessità, come nelle EPROM, di esposizione a raggi ultravioletti per la cancellazione. La memoria è riprogrammabile 1000000 di volte. Il tempo di conservazione del dato oltrepassa 40 anni. 4 SOFTWARE DEL PIC • Set di istruzioni E’ composto dalle 35 istruzioni qui riportate: Bit registro STATUS influenzati Codice operativo di 14 bit Mnemonico, operandi Descrizione Cicli MSB LSB ISTRUZIONI SU FILE REGISTER ORIENTATE AL BYTE f, d f, d f f, d f, d f, d f, d f, d f, d f, d f f, d f, d f, d f, d f, d ADDWF ANDWF CLRF CLRW COMF DECF DECFSZ INCF INCFSZ IORWF MOVF MOVWF NOP RLF RRF SUBWF SWAPF XORWF Add W and f AND W with f Clear f Clear W Complement f Decrement f Decrement f, Skip if 0 Increment f Increment f, Skip if 0 Inclusive OR W with f Move f Move W to f No Operazione Rotate Left f through Carry Rotate Right f through Carry Subtract W from f Swap nibbles in f Exclusive OR W with f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1 1 1 1 1 1 1(2) 1 1(2) 1 1 1 1 1 1 1 1 1 0111 0101 0001 0001 1001 0011 1011 1010 1111 0100 1000 0000 0000 1101 1100 0010 1110 0110 dfff dfff lfff 0xxx dfff dfff dfff dfff dfff dfff dfff lfff 0xx0 dfff dfff dfff dfff dfff ffff ffff ffff xxxx ffff ffff ffff ffff ffff ffff ffff ffff 0000 ffff ffff ffff ffff ffff C, DC, Z Z Z Z Z Z Z Z Z C C C, DC, Z Z ISTRUZIONI SU FILE REGISTER ORIENTATE AL BIT f, b f, b f, b f, b BCF BSF BTFSC BTFSS Bit Clear f Bit Set f Bit Test f, Skip if Clear Bit Test f, Skip if Set 01 01 01 01 1 1 1 (2) 1 (2) 00bb 01bb 10bb 11bb bfff bfff bfff bfff ffff ffff ffff ffff ISTRUZIONI CON LETTERALI E OPERAZIONI DI CONTROLLO ADDLW ANDLW CALL CLRWDT GOTO IORLW MOVLW RETFIE RETLW RETURN SLEEP SUBLW XORLW • k k k k k k k k k Add literal and W AND literal with W Call subroutine Clear Watchdog Timer Go to address Inclusive OR literal with W Move literal to W Return from interrupt Return with literal in W Return from Subroutine Go into Standby mode Subtract W from literal Exclusive OR literal with W 1 1 2 1 2 1 1 2 2 2 1 1 1 11 11 10 00 10 11 11 00 11 00 00 11 11 111x 1001 0kkk 0000 1kkk 1000 00xx 0000 01xx 0000 0000 110x 1010 kkkk kkkk kkkk 0110 kkkk kkkk kkkk 0000 kkkk 0000 0110 kkkk kkkk kkkk kkkk kkkk 0100 kkkk kkkk kkkk 1001 kkkk 1000 0011 kkkk kkkk C, DC, Z Z TO, PD Z TO, PD C, DC, Z Z Campi codice operativo Campo Descrizione f indirizzo numerico o simbolico di un registro del register file (da 0x00 a 0x7F) W registro accumulatore b disposizione del bit entro un file register di 8 bit k campo letterale, dato costante o etichetta d selezione destinazione risultato; d=0 destinazione=W; d=1 destinazione= il file register f specificato nell’istruzione 5 Il set di istruzioni del PIC16F684 è suddiviso nelle seguenti categorie: • istruzioni orientate al byte • istruzioni orientate al bit • istruzioni letterali e di controllo Ogni istruzione, codificata con 14 bit, è ripartita in un campo codice operativo, che specifica il tipo di istruzione e un campo operando, che specifica ulteriormente il/i dati sui quali l’istruzione opera. Istruzioni orientate al byte • Per le istruzioni orientate al byte, ‘f’ designa il nome di un operando di tipo file register, ‘d’ specifica se la destinazione del risultato è W (d=0) oppure il file register stesso (d=1). esempio movwf f STATUS 83h significa “copia il contenuto del registro W entro il registro f ”. FSR 84h WREG Nella specifica istruzione, al posto dell’identificatore TRISA 85h 3F generico f, ci sarà il nome di un qualsiasi file register. 86h Ad esempio, ipotizzando WREG=0x3F, l’istruzione: TRISC 87h movwf TRISA copia il valore esadecimale 3F entro il registro TRISA. Si noti una importante caratteristica comune a tutte le istruzioni: gli operandi coinvolti formano parte dell’istruzione, il sorgente a sinistra e la destinazione a destra. esempio incf f,d Il contenuto del registro ‘f’ viene incrementato. Se ‘d’ è 0 il +1 ADCON0 1Fh risultato viene posto nel registro W. Se ’d’ è 1 il risultato 0 viene posto in ‘f’. 20h Ad esempio, ipotizzando la variabile UNITA inizializzata a 0, General 21h Pur pose l’istruzione: 22h Registers incf UNITA,1 incrementa di 1 il valore di UNITA. Una variabile è un registro utente dello spazio di memoria General Purpose Register. Per accedere a questo spazio mediante simboli anziché mediante indicazione dell’indirizzo, si deve dichiarare un blocco di variabile all’inizio del programma. I nomi di variabile vengono associati, nell’ordine di dichiarazione, con i registri a partire dall’indirizzo indicato. Ad esempio il seguente segmento: cblock 0x20 UNITA DECINE endc associa la variabile UNITA al registro 20h e la variabile DECINE all’indirizzo 21h. esempio xorwf f,d Il contenuto del registro W è posto in OR esclusivo con il registro ‘f’. Se ‘d’ è 0 il risultato viene posto nel registro W. Se ’d’ è 1 il risultato viene posto in ‘f’. L’utilità dell’istruzione risiede nella possibilità di verificare l’uguaglianza di due registri, testando il valore del flag di zero del registro STATUS. L’operazione infatti influenza il flag di zero in questo modo: risultato operazione=0 Flag di zero settato Z=1 1 risultato operazione=1 Flag di zero resettato Z=0 Ad esempio, ipotizzando WREG=10, UNITA=10, l’istruzione: xorwf UNITA,0 IRP RP1 RP0 TO PD Z DC C imposta Z=1. Per completezza si riportano di seguito le specifiche del registro STATUS. STATUS - REGISTRO DI STATO (INDIRIZZO: 03h O 83h) riservato riservato 5 4 3 2 1 0 IRP RP1 RP0 TO PD Z DC C bit 7-6 sono riservati e devono essere mantenuti a ‘0’ bit 5 RP0: Register bank select bit (Bit di selezione del banco registri) 1 = Banco 1 (80h – FFh) 0 = Banco 0 (00h – 7Fh) bit 4 TO: Time Out bit (Bit di Time-out) è posto a 1 dopo l’accensione o un’istruzione CLRWDT o SLEEP è posto a 0 quando il Watchdog Timer va in Time-out 6 bit 3 PD: Power Down bit (Bit di Power Down) è posto a 1 dopo un’istruzione CLRWDT è posto a 0 all’esecuzione dell’istruzione SLEEP bit 2 Z: Zero bi (Flag di Zero) è posto a 1 quando il risultato di una operazione logica o aritmetica è zero è posto a 0 quando il risultato di una operazione logica o aritmetica è diverso da zero bit 1 DC: Digital Carry/borrow bit (Bit di Riporto/prestito di una cifra) è posto a 1 se è presente un riporto dal quarto bit della cifra meno significativa del risultato è posto a 0 se non è presente un riporto dal quarto bit della cifra meno significativa del risultato bit 0 C: Carry/borrow bi (Bit di Riporto/prestito) è posto a 1 se è presente un riporto dal bit più significativo del risultato è posto a 0 se non è presente un riporto dal bit più significativo del risultato • Istruzioni orientate al bit Per le istruzioni orientate al bit, ‘b’ designa il numero del bit influenzato dall’operazione, mentre ‘f’ rappresenta il file register nel quale il bit è collocato. esempio btfss f,b Se il bit ‘b’ del registro ‘f’ è 0 viene eseguita l’istruzione successiva. Se il bit ‘b’ del registro ‘f’ è 1 l’istruzione successiva viene annullata e al suo posto viene eseguita una istruzione NOP. In altri termini in questo ultimo caso l’istruzione successiva viene saltata. Questo è appunto il significato dell’acronimo: btfss = Bit Test File Register Skip Set = Esegui il test del bit del file register e salta se è settato Ad esempio l’istruzione: btfss STATUS,Z testa il flag di zero e salta l’istruzione successiva se è settato. Si tratta di un cosiddetto salto condizionato, ovvero l’inibizione del normale avanzamento all’istruzione successiva di esecuzione sostituita dal salto all’istruzione susseguente. Questo tipo di istruzione è fondamentale per effettuare test sul valore delle variabili. Sostituisce in pratica il costrutto if…else dei linguaggi di programmazione ad alto livello, come evidenziato a fianco. Nel seguente segmento di programma ad esempio, viene effettuato l’XOR tra WREG e UNITA. Se sono uguali il flag di zero risulta settato e l’istruzione btfss rinvia al corpo delle istruzione relative all’if. Se il flag di zero è resettato invece vengono eseguite le istruzioni relative all’else. Si noti la presenza dell’istruzione goto finetest, senza la quale verrebbe eseguito sia il corpo dell’if che quello dell’else. xorwf UNITA,0 btfss STATUS,Z goto else ; corpo istruzioni if ………………… ………………… goto finetest else ; corpo istruzioni else ………………… ………………… finetest ………………… • SI NO WREG=UNITA? if else corpo istruzioni if corpo istruzioni else ……….. Istruzioni letterali e di controllo Nelle istruzioni letterali e di controllo ‘k’ rappresenta un dato immediato come un numero o una lettera, oppure una etichetta di salto o una costante dichiarata. Le costanti vengono dichiarate all’inizio del programma con la clausola #define. Ad esempio; #define PortA_input B'00111111' dichiara una costante di nome PortA_input alla quale viene assegnato il valore binario 00111111. Per dichiarare un valore in formato esadecimale si deve utilizzare il modificatore 0x e in decimale il punto. Ad esempio le due seguenti dichiarazioni sono equivalenti alla precedente: #define PortA_input 0x3F #define PortA_input .63 Il vantaggio nell’uso delle costanti è dovuto alla possibilità di utilizzare simboli al posto di sequenze complesse di numeri. esempio movlw k 7 Il letterale ‘k’ viene copiato nel registro W. Ad esempio: movlw .10 copia in WREG il numero 10 movlw PortA_input copia in WREG il numero rappresentato dalla costante PortA_input movlw ‘a’ copia in WREG la lettera ‘a’, memorizzata come carattere ASCII numero ….61H. Tra le istruzioni di controllo si annoverano le istruzioni di salto e di chiamata a sottoprogramma. esempio goto k Il GOTO è un salto incondizionato. ‘k’ viene posto in modo immediato nei primi undici bit <10:0> della parte bassa del Program Counter PCL. I bit alti vengono copiati dal PCLATH<4:3> nella parte alta PCH. Il Program Counter, ovvero contatore di programma, è un registro di 13 bit che conserva l’indirizzo dell’istruzione in corso di esecuzione. Mantenere PC memorizzata questa informazione è fondamentale per PC+1 tenere traccia delle istruzioni eseguite e poter avanzare regolarmente nella sequenza di esecuzione. Il PC “punta” dunque all’istruzione corrente; dopo la sua esecuzione, il PC viene automaticamente incrementato per puntare all’istruzione successiva, come descritto a fianco. Spesso serve saltare ad una locazione del programma PC diversa da quella successiva, per questo è necessario introdurre un salto incondizionato con una istruzione goto. Incondizionato significa che il salto viene fatto in tutti i casi, indipendentemente dallo stato del microcontrollore. Quando incontra una istruzione goto k il sistema sostituisce k al posto dei primi 11 bit del PC, alterando il suo valore. Come conseguenza diretta si ha l’esecuzione al passo successivo dell’istruzione di indirizzo k, come descritto a fianco. 000017 000018 000019 00001A 00001B 281C 3000 0A0 3000 0A1 00001C 008 GOTO 0x1c MOVLW 0 MOVWF 0x20 MOVLW 0 MOVWF 0x21 goto movlw movwf movlw movwf memoria programma 0005 0005h 0006h 0007h 0008h 0006 0009h memoria programma 0005 0005h 0006h 0007h 0008h 0009h 000Ah 000Bh 0006B 000Ch fine .0 UNITA .0 DECINE fine RETURN return Nel segmento di programma sopra riportato, nel quale è presente a sinistra la versione disassemblata, cioè con i veri codici di memoria al posto dei simboli, si vede che l’istruzione goto fine, codificata come GOTO 0x1c, determina il salto all’indirizzo 001C. esempio call k Chiamata di sottoprogramma. L’indirizzo di ritorno (PC+1) viene inserito nello stack. Il valore di k viene posto in modo immediato negli undici bit <10:0> del PC. I bit alti del PC vengono caricati dal PCLATH. La chiamata a sottoprogramma determina il salto incondizionato alla locazione della prima istruzione del sottoprogramma. Un sottoprogramma è una sezione di programma separata dal programma principale. Lo scopo di questa divisione è di razionalizzare la scrittura dei programmi, affidando compiti diversi a sezioni distinte di programma, richiamabili anche ripetutamente. programma principale PC=5 ……… 5 call sottoprogramma1 …………….. …………….. …………….. sottoprogramma1 100 call sottoprogramma2 …………….. …………….. …………….. return sottoprogramma2 200 …………….. …………….. …………….. return 8 Come si vede nella figura sopra, a differenza dell’istruzione goto, che prevede solo il salto iniziale, l’istruzione call deve assicurare il ritorno al programma principale e la prosecuzione all’istruzione successiva alla call stessa. Per questo è necessario che il sistema, all’atto della chiamata di sottoprogramma, registri il valore corrente del PC, in quanto il PC viene caricato con l’indirizzo della prima istruzione del sottoprogramma. A conclusione del sottoprogramma il sistema recupera il vecchio valore di PC e lo incrementa per puntare alla successiva istruzione. Il salvataggio degli indirizzi di ritorno viene fatto nello stack. Nel PIC lo stack è una catasta di 8 parole di 13 bit (13=numero di bit del PC). Come descritto dalla figura seguente, gli indirizzi, man mano che vengono salvati, risultano impilati uno sopra l’altro, con una organizzazione di tipo LIFO Last In First Out, ovvero l’ultimo a entrare è anche il primo a uscire. Ad ogni salto a sottoprogramma, anche nel caso di sottoprogrammi che richiamano altri sottoprogrammi, l’indirizzo di ritorno viene salvato sopra la pila. Per percorrere a ritroso il ritorno al programma principale, gli indirizzi di ritorno vengono prelevati uno ad uno fino a svuotare la catasta. PC=PC+1 PC=5 PC=PC+1 PC=100 PC=100 PC=5 100 5 prima di call sottoprogramma1 call sottoprogramma1 5 call sottoprogramma2 return return Ad ogni nuovo salvataggio un registro stack pointer viene incrementato, per puntare alla nuova locazione vuota disponibile per il salvataggio. 9 • Descrizione delle istruzioni ADDLW Add Literal and W Sintassi: [label] ADDLW k Operandi: 0 ≤ k ≤ 255 Operazione: (W) + k → (W) Bit STATUS influenzati: C, DC, Z Descrizione: Il contenuto del registro W è sommato al letterale ‘k’ e il risultato è posto in W BCF Bit Clear f Sintassi: [label] BCF f,b Operandi: 0 ≤ f ≤ 127 0≤b≤7 Operazione: 0 → (f<b>) Bit STATUS influenzati: None Descrizione: Il bit 'b' del registro 'f' viene impostato a 0. ADDWF Add W and f Sintassi: [label] ADDWF f,d Operandi: 0 ≤ f ≤ 127 d ∈ [0,1] Operazione: (W) + (f) → (Destinazione) Bit STATUS influenzati: C, DC, Z Descrizione: Il contenuto del registro W viene sommato al file register ‘f’. Se ‘d’ è 0 il risultato viene posto nel registro W. Se’d’ è 1 il risultato viene posto in ‘f’. BSF Bit Set f Sintassi: [label] BSF f,b Operandi: 0 ≤ f ≤ 127 0≤b≤7 Operazione: 1 → (f<b>) Bit STATUS influenzati: None Descrizione: Il bit 'b' del registro 'f' viene impostato a 1. ANDLW AND Literal with W Sintassi: [label] ANDLW k Operandi: 0 ≤ k ≤ 255 Operazione: (W) .AND. (k) → (W) Bit STATUS influenzati: Z Descrizione: Il contenuto del registro W è posto in AND con il letterale ‘k’. Il risultato è posto in W. BTFSS Bit Test f, Skip if Set Sintassi: [label] BTFSS f,b Operandi: 0 ≤ f ≤ 127 0≤b<7 Operazione: skip if (f<b>) = 1 Bit STATUS influenzati: None Descrizione: Se il bit ‘b’ del registro ‘f’ è 0 viene eseguita l’istruzione successiva. Se il bit ‘b’ del registro ‘f’ è 1 l’istruzione successiva viene annullata e al suo posto viene eseguita una istruzione NOP. ANDWF AND W with f Sintassi: [label] ANDWF f,d Operandi: 0 ≤ f ≤ 127 d [0,1] Operazione: (W) .AND. (f) → (Destinazione) Bit STATUS influenzati: Z Descrizione: Il contenuto del registro W è posto in AND con il file register ‘f’. Se ‘d’ è 0 il risultato viene posto nel registro W. Se ’d’’ è 1 il risultato viene posto in ‘f’. BTFSC Bit Test, Skip if Clear Sintassi: [label] BTFSC f,b Operandi: 0 ≤ f ≤ 127 0≤b≤7 Operazione: skip if (f<b>) = 0 Bit STATUS influenzati: None Descrizione: Se il bit ‘b’ del registro ‘f’ è 1 viene eseguita l’istruzione successiva. Se il bit ‘b’ del registro ‘f’ è 0 l’istruzione successiva viene annullata e al suo posto viene eseguita una istruzione NOP. CALL Call Subroutine Sintassi: [ label ] CALL k Operandi: 0 ≤ k ≤ 2047 Operazione: (PC)+ 1→ TOS, k → PC<10:0>, (PCLATH<4:3>) → PC<12:11> Bit STATUS influenzati: None Descrizione: Chiamata di sottoprogramma. L’indirizzo di ritorno (PC+1) viene inserito nello stack. Il valore di k viene posto in modo immediato negli undici bit <10:0> del PC. I bit alti del PC vengono caricati dal PCLATH. CLRWDT Clear Watchdog Timer Sintassi: [ label ] CLRWDT Operandi: None Operazione: 00h → WDT 0 → WDT prescaler, 1 → TO 1 → PD Bit STATUS influenzati: TO, PD Descrizione: Resetta il Watchdog Timer. Il prescaler del WDT e il bit TO del registro STATUS vengono impostati a 1. CLRF Clear f Sintassi: [label] CLRF f Operandi: 0 ≤ f ≤ 127 Operazione: 00h → (f) 1→Z Bit STATUS influenzati: Z Descrizione: Il contenuto del registro ‘f’ viene cancellato e il bit Z viene posto a 1. COMF Complement f Sintassi: [ label ] COMF f,d Operandi: 0 ≤ f ≤ 127 d ∈ [0,1] Operazione: (f) → (Destinazione) Bit STATUS influenzati: Z Descrizione: Il contenuto del registro ‘f’ viene complementato. Il risultato viene posto nel registro W. Se ’d’ è 1 il risultato viene posto in ‘f’. CLRW Clear W Sintassi: [ label ] CLRW Operandi: None Operazione: 00h → (W) 1→Z Bit STATUS influenzati: Z Descrizione: Il contenuto del registro W viene cancellato e il bit Z viene posto a 1. DECF Decrement f Sintassi: [label] DECF f,d Operandi: 0 ≤ f ≤ 127 d ∈ [0,1] Operazione: (f) - 1 → (Destinazione) Bit STATUS influenzati: Z Descrizione: Il contenuto del registro ‘f’ viene decrementato. Se ‘d’ è 0 il risultato viene posto nel registro W. Se ’d’ è 1 il risultato viene posto in ‘f’. DECFSZ Decrement f, Skip if 0 Sintassi: [ label ] DECFSZ f,d Operandi: 0 ≤ f ≤ 127 d ∈ [0,1] Operazione: (f) - 1 → (Destinazione); skip if result = 0 Bit STATUS influenzati: None Descrizione: Il contenuto del registro ‘f’ viene decrementato. Se ‘d’ è 0 il risultato viene posto nel registro W,. Se ’d’ è 1 il risultato viene posto in ‘f’. Se il risultato è 1 viene eseguita l’istruzione successiva. Se il risultato è 0 viene eseguita l’istruzione NOP. INCFSZ Increment f, Skip if 0 Sintassi: [ label ] INCFSZ f,d Operandi: 0 ≤ f ≤ 127 d ∈ [0,1] Operazione: (f) + 1 → (Destinazione), skip if result = 0 Bit STATUS influenzati: None Descrizione: Il contenuto del registro ‘f’ viene incrementato. Se ‘d’ è 0 il risultato viene posto nel registro W,. Se ’d’ è 1 il risultato viene posto in ‘f’. Se il risultato è 1 viene eseguita l’istruzione successiva. Se il risultato è 0 viene eseguita l’istruzione NOP. GOTO Unconditional Branch Sintassi: [ label ] GOTO k Operandi: 0 ≤ k ≤ 2047 Operazione: k → PC<10:0> PCLATH<4:3> → PC<12:11> Bit STATUS influenzati: None Descrizione: Il GOTO è un salto incondizionato. ‘k’ viene posto in modo immediato nei primi undici bit <10:0> del PC. I bit alti vengono copiati dal PCLATH<4:3>. IORLW Inclusive OR Literal with W Sintassi: [ label ] IORLW k Operandi: 0 ≤ k ≤ 255 Operazione: (W) .OR. k → (W) Bit STATUS influenzati: Z Descrizione: Il contenuto del registro W è posto in OR con il letterale ‘k’. Il risultato viene posto nel registro W. 10 INCF Increment f Sintassi: [ label ] INCF f,d Operandi: 0 ≤ f ≤ 127 d ∈ [0,1] Operazione: (f) + 1 → (Destinazione) Bit STATUS influenzati: Z Descrizione: Il contenuto del registro ‘f’ viene incrementato. Se ‘d’ è 0 il risultato viene posto nel registro W. Se ’d’ è 1 il risultato viene posto in ‘f’. IORWF Inclusive OR W with f Sintassi: [ label ] IORWF f,d Operandi: 0 ≤ f ≤ 127 d ∈ [0,1] Operazione: (W) .OR. (f) → (Destinazione) Bit STATUS influenzati: Z Descrizione: Il contenuto del registro W è posto in OR con il registro ‘f’. Se ‘d’ è 0 il risultato viene posto nel registro W. Se ’d’ è 1 il risultato viene posto in ‘f’. MOVF Move f Sintassi: [ label ] MOVF f,d Operandi: 0 ≤ f ≤ 127 d ∈ [0,1] Operazione: (f) → (Destinazione) Bit STATUS influenzati: Z Il contenuto del registro ‘f’ viene copiato entro un registro di destinazione. Se ‘d’ è 0 la destinazione è il registro W. Se ’d’ è 1 la destinazione è ‘f’. Il caso d=1 è utile per testare un file register, poiché viene influenzato il flag Z. NOP No Operation Sintassi: [ label ] NOP Operandi: None Operazione: No Operazione Bit STATUS influenzati: None Descrizione: Nessuna operazione. MOVLW Move Literal to W Sintassi: [ label ] MOVLW k Operandi: 0 ≤ k ≤ 255 Operazione: k → (W) Bit STATUS influenzati: None Descrizione: Il letterale ‘k’ viene copiato nel registro W. RETFIE Return from Interrupt Sintassi: [ label ] RETFIE Operandi: None Operazione: TOS → PC, 1 → GIE Bit STATUS influenzati: Nessuno MOVWF Move W to f Sintassi: [ label ] MOVWF f Operandi: 0 ≤ f ≤ 127 Operazione: (W) → (f) Bit STATUS influenzati: None Descrizione: Copia il contenuto del registro W nel registro ‘f’. RETLW Return with Literal in W Sintassi: [ label ] RETLW k Operandi: 0 ≤ k ≤ 255 Operazione: k → (W); TOS → PC Bit STATUS influenzati: None Descrizione: Il registro W viene caricato con il letterale ‘k’. Il program counter viene aggiornato con il byte alto dello stack (indirizzo di ritorno). E’ una istruzione a due cicli. RLF Rotate Left f through Carry Sintassi: [ label ] RLF f,d Operandi: 0 ≤ f ≤ 127 d ∈ [0,1] Operazione: Vedi descrizione sotto Bit STATUS influenzati: C Descrizione: Il contenuto del registro ‘f’ viene fatto ruotare di un bit a sinistra attraverso il Carry. Se ‘d’ è 0 il risultato viene posto nel registro W. Se ’d’ è 1 il risultato viene posto in ‘f’. SLEEP Sintassi: [ label ] SLEEP Operandi: None Operazione: 00h → WDT, 0 → WDT prescaler, 1 → TO, 0 → PD Bit STATUS influenzati: TO, PD Descrizione: Il bit PD di power-down del registro STATUS, il Watchdog Timer e il prescaler vengono cancellati. Il bit di timeout TO del registro STATUS viene settato. Il processore viene posto in stato di SLEEP con l’oscillatore fermo. RETURN Return from Subroutine Sintassi: [ label ] RETURN Operandi: None Operazione: TOS → PC Bit STATUS influenzati: None Descrizione: Ritorno da sottoprogramma. Il byte in cima allo stack viene estratto e caricato nel program counter. SUBLW Subtract W from Literal Sintassi: [ label ] SUBLW k Operandi: 0 ≤ k ≤ 255 Operazione: k - (W) → (W) Bit STATUS influenzati: C, DC, Z Descrizione: Il contenuto del registro W viene sottratto dal letterale ‘k’ (metodo complemento a due). Il risultato viene posto nel registro W. RRF Rotate Right f through Carry Sintassi: [ label ] RRF f,d Operandi: 0 ≤ f ≤ 127 d ∈ [0,1] Operazione: Vedi descrizione sotto Bit STATUS influenzati: C Descrizione: Il contenuto del registro ‘f’ viene fatto ruotare di un bit a destra attraverso il Carry. Se ‘d’ è 0 il risultato viene posto nel registro W. Se ’d’ è 1 il risultato viene posto in ‘f’. SUBWF Subtract W from f Sintassi: [ label ] SUBWF f,d Operandi: 0 ≤ f ≤ 127 d ∈ [0,1] Operazione: (f) - (W) → (Destinazione) Bit STATUS influenzati: C, DC, Z Descrizione: Il contenuto del registro W viene sottratto a ‘f’ (metodo complemento a due). Se ‘d’ è 0 il risultato viene posto nel registro W. Se ’d’ è 1 il risultato viene posto in ‘f’. SWAPF Swap Nibbles in f Sintassi: [ label ] SWAPF f,d Operandi: 0 ≤ f ≤ 127 d ∈ [0,1] Operazione: (f<3:0>) → (Destinazione<7:4>), (f<7:4>) → (Destinazione<3:0>) Bit STATUS influenzati: None Descrizione: Vengono scambiati il nibble basso e alto del registro ‘f’. Se ‘d’ è 0 il risultato viene posto nel registro W. Se ’d’ è 1 il risultato viene posto in ‘f’. XORWF Exclusive OR W with f Sintassi: [label] XORWF f,d Operandi: 0 ≤ f ≤ 127 d ∈ [0,1] Operazione: (W) .XOR. (f) → (Destinazione) Bit STATUS influenzati: Z Descrizione: Il contenuto del registro W è posto in OR esclusivo con il registro ‘f’. Se ‘d’ è 0 il risultato viene posto nel registro W. Se ’d’ è 1 il risultato viene posto in ‘f’. XORLW Exclusive OR Literal with W Sintassi: [label] XORLW k Operandi: 0 ≤ k ≤ 255 Operazione: (W) .XOR. k → (W) Bit STATUS influenzati: Z Descrizione: Il contenuto del registro W è posto in OR con il letterale ‘k’. il risultato viene posto in W. 11 IDE MPLAB MPLAB (MicrochiP LABoratory) è un ambiente di sviluppo integrato, o IDE (Integrated Development Environment), che incorpora tutti gli strumenti utili per la programmazione dei microcontrollori PIC: • editor: per la scrittura del programma; automaticamente colora il codice sorgente per assicurarne la correttezza • compilatore: per convertire il codice simbolico assembler in codice macchina ‘1’, ‘0’ • debugger: per la simulazione del programma: consente, osservando registri e simboli, di identificare i “bug”, cioè i quasi inevitabili errori • programmatore: per trasferire il programma sviluppato “on board” nel microcontrollore, scrivendolo (burn) nella sua memoria di programma. 1) Inserire il CD Blu/Rosso, attendere l’autocaricamento e cliccare sul punto indicato per avviare l’installazione 2) Dopo l’installazione fare doppio click sull’icona nel desktop. Compare la schermata del MPLAB IDE. 3) Selezionare Configure Select Device. Selezionare il PIC16F684. I led verdi indicano i componenti supportati dall’MPLAB IDE. 4) Iniziare a creare un nuovo progetto. Predisporre una cartella con il nome del progetto e entro essa un file con estensione asm di un precedente lavoro, per disporre di una base di partenza dalla quale iniziare il lavoro. In questo capitolo è utilizzato il file esempio.asm della cartella Applicazioni\MPLAB\esempio. Si consiglia di utilizzare Esplora Risorse e lavorare per trascinamento. 5) Selezionare nel menu Project Project Wizard Nella finestra che si apre selezionare AVANTI 6) Selezionare il dispositivo 12 7) Selezionare il linguaggio 8) immettere il nome del progetto e selezionare, con l’ausilio del pulsante browse, la cartella predisposta. Cliccare su AVANTI. 9) Aggiungere al progetto il file con estensione .asm premento ADD>> Cliccare su AVANTI. 10) Nella finestra che si apre selezionare Fine 11) Fare doppio click sul nome del file sotto la voce Source Files, per aprire la finestra con il listato del programma. if SI UNITA =10 ? NO UNITA=0 INCREMENTA DECINE if SI DECINE =6 ? NO Il programma di esempio è un contatore da 0 a 59. Esso risponde al diagramma di flusso riportato a fianco. Viene confrontato il valore delle unità con 10: se risulta uguale vuole dire che il 9 è stato superato, quindi UNITA deve essere azzerato e si deve far progredire la cifra delle DECINE. Un controllo simile viene eseguito sulle decine: se risulta DECINE=6 vuol dire che il 5 è stato superato, pertanto si deve forzare DECINE=0. Ad ogni ciclo l’istruzione call reset? chiama il sottoprogramma reset?. Questo controlla se il bit 3 della porta A è settato; in caso affermativo resetta sia UNITA che DECINE. Si riporta di seguito il listato completo. DECINE=0 13 goto Inizio ;DIRETTIVE list p=16f684 ; direttiva list per definizione del processore #include <p16f684.inc> ; definizione variabili specifiche processore errorlevel -302 ; ;bit di configurazione che specificano modalità operative del PIC: Code protect off, Watchdog ;timer off, Power-up timer on ………… __CONFIG _CP_OFF & _CPD_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT ;DEFINIZIONE COSTANTI E VARIABILI #define PortA_input B'00111111' cblock 0x20 UNITA DECINE endc ;INIZIALIZZAZIONE org 0x000 ; vettore di reset del processore goto Initialize org 0x005 ; vettore di inizio del programma Initialize bsf STATUS, RP0 ; selezione banco 1 movlw PortA_input movwf TRISA ; imposta alti tutti bit porta A bcf STATUS, RP0 ; selezione banco 0 ;PROGRAMMA Inizio call reset? incf UNITA,1 ; incrementa numero movlw .10 xorwf UNITA,0 ; se UNITA=10 riprende il conteggio da 0 btfss STATUS,Z ; salta se Z=0 goto controllodecine clrf UNITA ; azzera UNITA incf DECINE,1 ; incrementa DECINE controllodecine movlw .6 ; se DECINE=6 pone DECINE=0 xorwf DECINE,0 btfsc STATUS,Z clrf DECINE goto Inizio ;SOTTOPROGRAMMA reset? btfss PORTA,3 goto fine movlw .0 movwf UNITA movlw .0 movwf DECINE fine return end ; testa se Switch premuto ; azzera unità e decine Ora si passa alla fase di analisi/debugging del programma. Analizzeremo una ad una le istruzioni più significative, osservandone gli effetti sui registri e le variabili. Le istruzioni analizzate sono le stesse all’uopo enucleate e spiegate nel capitolo precedente. 12) Per avviare lo strumento debugger selezionare Debugger Select Tool MPLAB SIM. Di seguito si riportano le opzioni di debugging con le relative spiegazioni. Animate Run Halt Step Into Step Over Reset Step Out AZIONE Run Halt DESCRIZIONE Il programma viene eseguito in tempo reale, in pratica alla massima velocità consentita dal PC Il programma viene arrestato Animate Step Into Step Out Il programma viene eseguito passo passo con avanzamento automatico Il programma viene eseguito passo passo con avanzamento manuale; se sono presenti sottoprogrammi, anche questi vengono percorsi passo passo Il programma viene eseguito passo passo con avanzamento manuale; se sono presenti sottoprogrammi, non vengono mostrate passo passo le istruzioni interne Il sottoprogramma corrente viene abbandonato Resest L’esecuzione viene riportata all’inizio del programma; i registri vengono reinizializzati Step Over 14 13) Compilare il programma selezionando Project Build All; chiudere la finestra Output dei risultati di compilazione. 14) Impostare la velocità di animazione selezionando Debugger Settings… In modalità Animate il programma viene eseguito Digitare 50 nel campo Animate step time della scheda Animation/Realtime Updates. Questo è il automaticamente come nella modalità Run, ma in modo rallentato. Questo permette di osservare la progressione tempo di scansione automatica delle istruzioni. dei dati coinvolti nell’algoritmo. 15) Ridurre la finestra del programma premendo sull’icona di ripristino (due quadrati sovrapposti); invocare la finestra Watch selezionando View Watch e accostarla a fianco della finestra di programma, come sotto mostrato. Per visualizzare i numeri d’ordine delle istruzioni cliccare con il tasto destro sulla banda grigia a sinistra e selezionare Properties… Nella scheda “Editor” spuntare la casella “Line Numbers”. La finestra Watch permette di osservare in tempo reale i valori degli elementi coinvolti nel programma. Un menu a sinistra permette di selezionare un file register; premendo Add SFR esso viene aggiunto all’elenco di visualizzazione. Nel menu a destra sono selezionabili i singoli bit dei registri o simboli vari, come le variabili dichiarate, premendo Add Symbol essi vengono aggiunti all’elenco di visualizzazione. 16) Selezionare le due variabili UNITA e DECINE e aggiungerle alla visualizzazione. 17) Eseguire il programma in modalità Animate. Si può osservare il conteggio effettuato sulla variabile unità e l’incremento delle decine. Il programma effettua un conteggio da 0 a 59 e riprende da zero. Di seguito eseguiremo istruzioni singole passo passo, per verificare quanto descritto nel capitolo precedente, valutando di volta in volta l’effetto dell’istruzione su registri e simboli. 18) Aggiungere alla finestra Watch il registro TRISA. Portarsi in modalità Step Over all’istruzione numero 19 del programma ed eseguirla: 19 movwf TRISA Il dato 0x3F presente in W viene copiato nel file register TRISA 15 0X3F 19) Portarsi in modalità Step Over all’istruzione numero 24 del programma: 24 incf UNITA,1 Il dato presente nella variabile UNITA viene incrementato di 1; il risultato viene riversato nella variabile stessa. 0X01 20) Aggiungere alla finestra Watch il registro STATUS. Eseguire continuamente il programma in modalità Step Over per far avanzare il valore di UNITA. 0X1F Quando si verifica l’uguaglianza UNITA=0x0A, la coppia di istruzioni: xorwf UNITA,0; btfss STATUS,Z; cambia STATUS da: STATUS=0x18=00011000 a: STATUS=0x1C=00001100 ovvero setta il flag di zero. 21) Aggiungere alla finestra Watch il simbolo WREG. Resettare il programma e portarsi in modalità Step Over all’istruzione numero 18 del programma: movlw . PortA_input Il valore della costante PortA_input, prefissato a B’00111111’=0x3F viene copiato nell’accumulatore WREG. 0X3F 22) Questo programma è in grado di interagire con un input, il pin3 della porta A. Per impostare un dato, o “stimulus”, su una linea di input, si deve abbinare ad essa un pulsante virtuale. Per questo selezionare Debugger Stimulus Controller New Scenario; compare la seguente finestra: Nella colonna Pin selezionare il pin RA3. La colonna Action offre quattro modalità di azione del pulsante: Set High: sempre premuto Set Low: sempre non premuto Pulse High: premuto per una istruzione Pulse Low: non premuto per una istruzione Toggle: inversione stato pulsante ad ogni istruzione. Selezionare Set High e premere Fire per innescare l’azione. Eseguendo il programma in modalità Step Into si può osservare che il conteggio viene resettato. Se invece si imposta Set Low il conteggio procede regolarmente. 16 23) Per osservare gli elementi di interesse nel caso di istruzioni di salto conviene invocare la finestra del listato disassemblato, selezionando View Disassembling Listing. Disassemblare significa convertire il listato assembler simbolico nei codici macchina di codifica delle istruzione, con indicazione degli indirizzi delle word dove le istruzioni sono collocate. Disporre le finestre in modo tale da poter osservare il programma, il listato disassemblato e la finestra Watch. Portarsi all’istruzione 28 goto controllodecine, che come si può vedere nella finestra del disassemblato, è codificata all’indirizzo 0x000E. Aggiungere il registro PCL alla finestra Watch. Il PCL segna giustamente 0x0E. Avanzare di un passo: viene eseguito il salto all’istruzione movlw .6, che è collocata all’indirizzo 0x0011. La finestra Watch mostra il valore PCL=0X11. 0X11 24) Per osservare il comportamento dello stack nel caso di una istruzione call visualizzare la finestra Hardware Stack selezionando View Hardware Stack. Portarsi all’istruzione numero 23 call reset?. Come si può vedere dal disassemblato essa è codificata all’indirizzo 0x0009; la successiva al 0x000A. Proseguire in modalità Step Into: in questo modo si entra nel sottoprogramma. Il valore 0x0A dell’indirizzo di ritorno, cioè dell’istruzione successiva a call reset?, viene subito salvato nello stack, come si può osservare dalla finestra Hardware Stack. All’esecuzione dell’istruzione return, lo stack viene svuotato, il PCL riacquista l’indirizzo di ritorno e il programma riprende dall’istruzione successiva alla call. 25) Il programma di esempio è un contatore da 0 a 59, con due variabili UNITA e DECINE. Per osservare l’incremento delle decine passo passo è necessario ogni volta scandire tutte le unità da 0 a 9. Questo comporta uno spreco di tempo e richiede una grande dose di pazienza. Si ricorre in questo e in numerosi altri casi simili e più complessi al Breakpoint. Il Breakpoint (punto di arresto) introduce, come suggerisce il termine inglese, un punto di arresto nella sequenza delle istruzioni. In questo caso conviene introdurlo in coincidenza con l’istruzione movlw .6. Per questo si fa doppio click in un punto qualsiasi dell’istruzione, appare di conseguenza una B circondata da un cerchio rosso. Si può ora agire sul tasto Run anziché Step Over, in quanto il programma comunque si arresta al Breakpoint. Se c’è aperta la finestra Watch con l’indicazione di UNITA e DECINE, si potrà osservare l’avanzamento delle UNITA’ ad ogni passaggio per il breakpoint. 17 Esercizi es1 Scrivere un programma che effettui, con una variabile UNITA, il conteggio da 0 a 9. Confrontare UNITA con 10, quando sono uguali forzare UNITA=0. Per il test sul valore di UNITA utilizzare l'istruzione btfsc anzichè btfss. es2 Scrivere un programma che effettui, con una variabile UNITA, il conteggio da un valore MIN compreso a un valore MAX escluso. Dichiarare MIN e MAX come costanti e assegnarvi il valore desiderato. Confrontare UNITA con MAX, quando sono uguali forzare UNITA=MIN. es3 Scrivere un programma che effettui, con una variabile UNITA, il conteggio da 0 a 9 in avanti oppure all'indietro da 9 a 0. La direzione del conteggio viene imposta dal valore della costante DIREZIONE dichiarata nel programma. Se DIREZIONE=1 conteggio avanti, se DIREZIONE=0 conteggio indietro. Per controllare il valore di DIREZIONE immettere .1 in WREG con l'istruzione movlw .1 e far seguire l'istruzione xorlw DIREZIONE, la quale confronta WREG con il letterale DIREZIONE. Nel conteggio avanti confrontare UNITA con 10, quando sono uguali forzare UNITA=0. Nel conteggio indietro confrontare UNITA con 255, quando sono uguali forzare UNITA=9. es4 Scrivere un programma che effettui, con una variabile UNITA, il conteggio da 0 a 9 in avanti oppure all'indietro da 9 a 0. La direzione del conteggio viene imposta dal livello RA3 del bit3 della Porta A. Se RA3=1 conteggio avanti, se RA3=0 conteggio indietro. Nel conteggio avanti confrontare UNITA con 10, quando sono uguali forzare UNITA=0. Nel conteggio indietro confrontare UNITA con 255, quando sono uguali forzare UNITA=9. Provare il programma con un pulsante virtuale. 18 MEMORIA Tra le principali attività di un microprocessore ci sono le operazioni di lettura e scrittura entro la memoria dati. Nel PIC sono presenti tre diverse sezioni di memoria: • memoria di PROGRAMMA – dove viene scritto il programma che il microcontrollore deve eseguire • memoria REGISTER FILE – dove sono presenti i registri Special Function Register SFR e General Pur pose Register GPR. • memoria EEPROM – dove possono essere salvati i dati permanentemente. Nei registri SFR sono presenti i dati di configurazione dei vari dispositivi che corredano il PIC. Nei registri GPR sono presenti i dati utente, ovvero i dati che il programma manipola. Un gruppo di registri GPR può contenere i numeri di un calcolo matematico, oppure le lettere di un brano testuale. Ancora: può registrare i byte di configurazione dei pixel di una immagine o i campioni di un brano musicale. Il capitolo ha lo scopo di descrivere come questi dati possano essere gestiti, con una panoramica su algoritmi base di gestione della memoria. Nel PIC lo spazio GPR del banco 0 è compreso tra gli indirizzi 20h e 7Fh. Le modalità di accesso ai 96 bytes di questo spazio sono due: • indirizzamento diretto • indirizzamento indiretto. • Indirizzamento Diretto Nelle istruzioni con indirizzamento diretto l’indirizzo del byte GPR da manipolare è specificato direttamente nell’istruzione. WREG Ad esempio nell’istruzione movwf 0x0020 è specificato in esadecimale l’indirizzo 20h. Il significato dell’istruzione è: “copia il valore dell’accumulatore ‘w’ nel byte di indirizzo 20h”. 5 5 20h L’istruzione duale movfw 0x0020 copia invece il valore del byte di indirizzo 20h nell’accumulatore. Esperimento …\Memoria\diretto Scrivere un programma che inizializzi il blocco dei registri GPR dall’indirizzo 20h all’indirizzo 24h con i numeri 0, 1, 2, 3, 4. ;DIRETTIVE list p=16f684 ; direttiva list per definizione del processore #include <p16f684.inc> ; definizione variabili specifiche processore errorlevel -302 ;INIZIALIZZAZIONE org 0x000 ; vettore di reset del processore goto Initialize org 0x005 ; vettore di inizio del programma Initialize bcf STATUS, RP0 ; selezione banco 0 movlw .0 ; carica 0 in w movwf 0x0020 ; copia w nel File Register di indirizzo 20h movlw .1 movwf 0x0021 movlw .2 movwf 0x0022 movlw .3 movwf 0x0023 movlw .4 movwf 0x0024 end Ogni coppia di istruzioni mov è costituita da una prima istruzione di tipo letterale che carica un numero in w e una seconda istruzione che copia w nel File Register, di indirizzo specificato in modo diretto. Selezionare View File Registers per visualizzare la finestra File Registers. In essa è visualizzato tutto lo spazio Register File. Ogni riga visualizza 16 registri, dislocati in altrettante colonne. Si può vedere che all’indirizzo 0020di riga, nelle prime quattro colonne, ovvero agli indirizzi 20h, 21h, 22h, 23h, 24h il programma ha registrato i rispettivi numeri 0, 1, 2, 3, 4. 19 • Indirizzamento Indiretto Nelle istruzioni di implementazione dell’indirizzamento indiretto l’indirizzo del byte GPR da manipolare è specificato nel registro FSR (File Select Register = Registro di Selezione Archivio). La lettura e la scrittura del byte GPR prevede che l’indirizzo venga scritto nel registro FSR. Per indicare che l’operazione avviene in modo indiretto si utilizza poi la parola chiave INDF (Indirect File). Ad esempio nel seguente gruppo di istruzioni: movlw 0x0020 movwf FSR movfw INDF la prima carica in w l’indirizzo 20h, la seconda lo copia in FSR, la terza sposta nell’accumulatore il contenuto del registro, il cui indirizzo è presente in FSR. In questa ottica FSR assume il ruolo di un “puntatore”, ovvero di un registro che con il suo contenuto punta alla locazione da leggere. INDF complica un po’ il meccanismo, esso ha il ruolo di “alias” del registro FSR. WREG 5 FSR 20h 5 20h Esperimento …\Memoria\indiret Scrivere un programma che sposti un blocco di 5 byte dall'indirizzo iniziale 20h all'indirizzo 30h, mediante indirizzamento indiretto. 20h blocco sorgente 30h blocco destinazione Come descritto dalla figura a fianco, la prima istruzione copia il byte di indirizzo 20h in WREG e la seconda copia WREG in 30h. Si noti che la copia diretta da 20h a 30h senza WREG è impossibile, in quanto tutte le istruzioni sono implementate da WREG, che è il solo registro che ha “nervature” con tutto il resto dell’architettura. Lo spostamento di blocchi di memoria avviene byte per byte: si deve copiare il byte di indirizzo 20h all’indirizzo 30h, quello di indirizzo 21h all’indirizzo 31h etc. Per comprendere a pieno il concetto di indirizzamento indiretto studiamo prima la soluzione con indirizzamento diretto. Essa comporta l’utilizzo di una coppia di istruzioni, una per spostare il byte sorgente in WREG e una per spostare WREG nel byte destinazione. Per il primo byte ad esempio, si ha: movfw 0x0020 movwf 0x0030 byte sorgente WREG 20h byte destinazione 30h Questo è un evidente esempio dove l’utilizzo dell’indirizzamento indiretto è indispensabile: per la copia di un blocco ad esempio di 100 byte verrebbero impiegate 200 istruzioni; per la copia (ipotetica) di un blocco di 100000 bytes, il programmatore dovrebbe scrivere (!) 200000 istruzioni. In questo e in tutti i casi analoghi, si procede in modo iterativo con indirizzamento indiretto, collocando un ridotto numero di istruzioni con indirizzamento diretto entro un ciclo. PUNTATORE1 20h byte sorgente 20h APPOGGIO 0 1 2 3 4 PUNTATORE2 30h byte destinazione 30h 0 1 2 3 4 20 L’algoritmo utilizza 4 variabili: due puntatori PUNTATORE1 e PUNTATORE2 per puntare alla coppia di byte corrente, APPOGGIO per la copia temporanea di un byte e CICLI per il numero di iterazioni. I due puntatori vengono inizializzati all’indirizzo iniziale dei due blocchi, rispettivamente 20h e 30h. L’operazione di trasferimento del byte sorgente entro APPOGGIO è implementata dalle seguenti istruzioni: movfw movwf movfw movwf PUNTATORE1 FSR INDF APPOGGIO ; ; ; ; copia copia copia copia il contenuto di PUNTATORE1 in WREG WREG in FSR il byte puntato da FSR in WREG WREG in APPOGGIO Il contenuto di PUNTATORE1, che inizialmente è l’indirizzo 20h viene copiato in WREG e da qui in FSR. FSR “punta” quindi al primo byte. La terza istruzione copia il contenuto del primo byte in WREG e la quarta lo salva in APPOGGIO. La seconda quaterna di istruzioni agisce in modo speculare, copiando APPOGGIO nel primo byte del secondo blocco: movfw movwf movfw movwf PUNTATORE2 FSR APPOGGIO INDF ; ; ; ; copia copia copia copia il contenuto di PUNTATORE2 in WREG WREG in FSR APPOGGIO in WREG WREG nel byte puntato da FSR Successivamente i due puntatori vengono incrementati per puntare ai byte successivi dei blocchi e viene decrementato CICLI. Infine viene eseguito un test su CICLI. Se CICLI è diverso da zero l’algoritmo viene iterato, altrimenti il programma termina. Esperimento …\Memoria\indiret ;DIRETTIVE list p=16f684 ; direttiva list per definizione del processore #include <p16f684.inc> ; definizione variabili specifiche processore errorlevel -302 ;DEFINIZIONE COSTANTI E VARIABILI cblock 0x50 PUNTATORE1 PUNTATORE2 APPOGGIO CICLI endc ;INIZIALIZZAZIONE org 0x000 ; vettore di reset del processore goto Initialize org 0x005 ; vettore di inizio del programma Initialize bcf STATUS, RP0 ; selezione banco 0 movlw .0 ; carica 0 in w movwf 0x0020 ; copia w nel File Register di indirizzo 20h movlw .1 movwf 0x0021 movlw .2 movwf 0x0022 movlw .3 movwf 0x0023 movlw .4 movwf 0x0024 movlw 0x0020 ; inizializza puntatori movwf PUNTATORE1 movlw 0x0030 movwf PUNTATORE2 ;PROGRAMMA movlw .5 movwf CICLI Inizio movfw PUNTATORE1 ; copia il contenuto di PUNTATORE1 in WREG movwf FSR ; copia WREG in FSR movfw INDF ; copia il byte puntato da FSR in WREG movwf APPOGGIO ; copia WREG in APPOGGIO movfw movwf movfw movwf PUNTATORE2 FSR APPOGGIO INDF ; ; ; ; copia copia copia copia il contenuto di PUNTATORE2 in WREG WREG in FSR APPOGGIO in WREG WREG nel byte puntato da FSR incf incf decfsz goto end PUNTATORE1,1 PUNTATORE2,1 CICLI,1 Inizio ; punta alle successive locazioni ; se CICLI<>0 itera 21 Per analizzare l’algoritmo passo-passo conviene invocare in MPLAB le finestre File Registers e Watch. Nella Watch aggiungere i seguenti simboli e variabili: WREG, FSR, PUNTATORE1, PUNTATORE2, APPOGGIO, CICLI, Osservarne l’evoluzione in modalità di debugging. A fianco è rappresentata Watch in corrispondenza della quarta passata del ciclo. La tabella che segue riporta i valori assunti da simboli e variabili di interesse in corrispondenza di ciascuna istruzione. ISTRUZIONE movlw .0 movwf 0x0020 movlw .1 movwf 0x0021 movlw .2 movwf 0x0022 movlw .3 movwf 0x0023 movlw .4 movwf 0x0024 movlw 0x0020 movwf PUNTATORE1 movlw 0x0030 movwf PUNTATORE2 ;PROGRAMMA movlw .5 movwf CICLI Inizio movfw PUNTATORE1 movwf FSR movfw INDF movwf APPOGGIO movfw PUNTATORE2 movwf FSR movfw APPOGGIO ISTRUZIONE movwf INDF incf PUNTATORE1,1 incf PUNTATORE2,1 decfsz CICLI,1 goto Inizio W REG 0 FSR PUNTA TORE1 PUNTA TORE2 APPOG GIO CICLI 20h 21h 22h 23h 24h 0 1 1 2 2 3 3 4 4 20 20 30 30 5 5 20 20 0 0 30 30 0 W REG FSR PUNTA TORE1 PUNTA TORE2 APPOG GIO CICLI 30h 31h 32h 33h 34h 0 21 31 4 Esperimento …\Memoria\somma Scrivere un programma che sommi i bytes da 20h al 24h e depositi il risultato in 25h. byte0 + byte1 + byte2 + byte3 + byte4 20h 21h 22h 23h 24h 25h byte 0 byte 1 byte 2 byte 3 byte 4 Risultato 22 ;DIRETTIVE list p=16f684 ; direttiva list per definizione del processore #include <p16f684.inc> ; definizione variabili specifiche processore errorlevel -302 __CONFIG _CP_OFF & _CPD_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT ;DEFINIZIONE COSTANTI E VARIABILI cblock 0x50 CICLI PUNTATORE1 SOMMA endc ;INIZIALIZZAZIONE org 0x000 ; vettore di reset del processore goto Initialize org 0x005 ; vettore di inizio del programma Initialize bcf STATUS, RP0 ; selezione banco 0 movlw .0 ; carica 0 in w movwf 0x0020 ; copia w nel File Register di indirizzo 20 movlw .1 movwf 0x0021 movlw .2 movwf 0x0022 movlw .3 movwf 0x0023 movlw .4 movwf 0x0024 movlw 0x0020 ; inizializza puntatore movwf PUNTATORE1 ;PROGRAMMA movlw .5 movwf CICLI Inizio movfw PUNTATORE1 ; copia contenuto di PUNTATORE1 in WREG movwf FSR ; copia WREG in FSR movfw INDF ; copia il byte puntato da FSR in WREG addwf SOMMA,1 ; somma WREG alla variabile SOMMA ; il risultato viene depositato in somma incf PUNTATORE1,1 ; punta alla successiva locazione decfsz CICLI,1 goto Inizio ; se CICLI<>0 itera movfw movwf movfw movwf end ; ; ; ; PUNTATORE1 FSR SOMMA INDF copia copia copia copia contenuto di PUNTATORE1 in WREG WREG in FSR SOMMA in WREG SOMMA nel byte puntato da FSR (25h) Il grafico seguente descrive l’effetto delle istruzioni movfw INDF; addwf SOMMA,1 che sono le istruzioni chiave. La prima carica nell’accumulatore WREG il byte il cui indirizzo è contenuto nel registro INDF. In altre parole INDF “punta” al byte di memoria da spostare; l’indirizzo a cui puntare è contenuto in FSR. La seconda somma WREG al risultato preesistente contenuto in SOMMA; il risultato viene sovrascritto in SOMMA. La figura mostra l’ultimo ciclo, nel quale il valore 6 accumulato in SOMMA viene sommato al byte di valore 4 puntato da FSR. Il risultato 0Ah viene depositato in SOMMA. FSR 24h 20h 21h 22h 23h 24h 0 1 2 3 4 movfw INDF SOMMA WREG + 25h 6 0A L’ultima quaterna di istruzioni pone in FSR l’indirizzo del byte 25h e vi copia SOMMA. FSR 25h movwf INDF 25h 0A WREG 0A 23 Esperimento …\Memoria\selez Scrivere un programma che copi un byte dall'indirizzo 20h al 21h, solo se il byte è inferiore a 6. Per implementare confronti tra numeri si impiega l’istruzione sublw (oppure subwf), osservando il valore del flag di carry, che è il bit 0 del registro STATUS. Il flag C viene influenzato in questo modo: risultato sottrazione >0 risultato sottrazione <0 movfw APPOGGIO sublw .6 C=1 C=0 .Testando il C mediante una istruzione btfss è possibile percorrere uno di due rami, come descritto nel diagramma di flusso e di conseguenza eseguire le operazioni opportune. L’algoritmo, mediante l’istruzione movfw APPOGGIO carica in WREG il valore della variabile da confrontare con il numero. In questo caso questa variabile è contenuta in APPOGGIO e il numero è 6. L’istruzione sublw .6 ha effetto sul flag C, che viene successivamente testato dalla btfss STATUS,C. Se C=1 il valore della variabile APPOGGIO, mediante le istruzioni movfw APPOGGIO e movwf 0x0021, viene depositata all’indirizzo 21h. ;DIRETTIVE list p=16f684 #include <p16f684.inc> errorlevel -302 ;DEFINIZIONE COSTANTI E VARIABILI cblock 0x50 APPOGGIO endc ;INIZIALIZZAZIONE org 0x000 goto Initialize org 0x005 Initialize bcf STATUS, RP0 movlw .3 movwf 0x0020 ;PROGRAMMA movfw 0x0020 movwf APPOGGIO movfw sublw btfss goto movfw movwf APPOGGIO .6 STATUS,C none APPOGGIO 0x0021 SI NO C=1 ? if movfw movwf APPOGGIO 0x0021 ; direttiva list per definizione del processore ; definizione variabili specifiche processore ; vettore di reset del processore ; vettore di inizio del programma ; selezione banco 0 ; carica 3 in w ; copia w nel File Register di indirizzo 20h ; copia WREG in APPOGGIO ; flag C settato se 6-APPOGGIO>0 ; se APPOGGIO non è inferiore a 6 non fare nulla none end Esercizi es1 Scrivere un programma che copi in ordine inverso un blocco di 5 byte dall'indirizzo iniziale 20h all'indirizzo 30h. Il byte di indirizzo 20h deve essere collocato in 34h, il 21h in 33h ecc. 20h 22h 0 1 2 23h 24h 3 4 30h 32h 4 3 2 33h 34h 1 0 21h 31h es2 Scrivere un programma che sommi 5 coppie adiacenti di byte e depositi il risultato nella terza posizione. Il byte di indirizzo 20h viene sommato al 21h e il risultato viene depositato in 22h ecc. 20h 21h 22h 3 1 4 2Ch 2 4 6 2 2 4 5 4 9 7 2Dh 2Eh 2 9 23h 24h 25h 26h 27h 28h 29h 2Ah 2Bh 24 20h 3 1 2 4 2 21h 22h es3 Scrivere un programma che sommi, byte per byte, i blocchi di 5 bytes con indirizzo di partenza 20h e 30h e depositi i risultati a partire da 40h. Il byte di indirizzo 20h viene sommato al 30h e depositato in 40h ecc. 23h 24h 30h 31h 33h 34h 20h 22h 3 1 7 23h 24h 9 2 30h 3 1 2 21h 31h 32h 20h 21h es5 Scrivere un programma che confronti il byte 20h con il 30h e depositi in 40h il minore, poi confronti il 21h con il 31h e depositi il minore in 41h ecc. Iterare per 5 bytes. es6 Scrivere un programma che determini il numero minimo nel blocco di 5 bytes di indirizzo 20h e lo depositi in 25h. 22h 23h 24h 30h 3 1 2 4 2 32h 2 5 4 33h 34h 7 2 20h 22h 3 1 7 23h 24h 25h 9 2 1 31h 21h 42h 43h 44h 40h < 5 6 6 B 4 41h + 2 5 4 7 2 32h es4 Scrivere un programma che sposti solo i bytes <6 dal blocco di 5 byte collocato da 20h al blocco di 5 bytes collocato da 30h. 40h 42h 2 1 2 43h 44h 4 2 41h 25 PORTE-IO L’integrato PIC16F684 dispone di due porte di nome porta A e Porta C. Una porta, ad esempio la porta A, dispone di due registri: • registro dati PORTA • registro di configurazione TRISA Il termine “Porta” nel linguaggio dei microprocessori designa un gruppo di pin fisicamente esistenti all’esterno dell’integrato, collegati internamente a un registro. Ogni pin corrisponde a un bit del registro e può essere configurato come input o come output. Se è configurato come output, l’impostazione, mediante il software, di un valore ‘0’ o ‘1’, si riflette nella presenza o assenza di tensione sul corrispondente pin. 7 6 TRISA 5 4 3 2 1 0 7 6 5 PORTA 4 3 2 1 0 RA5 RA0 RA4 RA1 RA3 RA2 La figura a fianco descrive appunto il collegamento dei bit del registro PORTA con i terminali dell’integrato; si noti che solo 6 degli otto bit del registro sono collegati ai pin, denominati RA0, RA1, RA2, RA3, RA4, RA5. Se un bit di PORTA viene programmato come ‘1’ il corrispondente pin si trova a livello alto e viceversa. Il registro TRISA non è collegato ai pin bensì è diretto verso PORTA a scopo di configurazione. Se un suo bit si trova a livello alto il corrispondente pin è configurato come input e viceversa. Si riporta di seguito il dettaglio dei registri con la descrizione di ciascun bit. PORTA - REGISTRO PORTA A (INDIRIZZO: 05h) 7 6 5 4 3 2 1 0 ------ ------ RA5 RA4 RA3 RA2 RA1 RA0 bit 7-6 non implementati: vengono letti come ‘0’ bit 5-0 RA<5:0>: poRtA I/O pin bit (Pin corrispondenti ai bit della porta A) 1 = il pin è a livello alto 0 = il pin è a livello basso TRISA - REGISTRO DELLA PORTA A TRI-STATE (INDIRIZZO: 85h) 7 6 5 4 3 sola lett. 2 1 0 ------ ------ TRISA5 TRISA4 TRISA3 TRISA2 TRISA1 TRISA0 bit 7-6 non implementati: vengono letti come ‘0’ bit 5-0 TRISA<5:0>: port A TRI-State pin bit (Bit di controllo tri-state della porta A) 1 = il pin della porta A è configurato come input (tri-stated) 0 = il pin della porta A è configurato come output PORTC - REGISTRO PORTA C (INDIRIZZO: 07h) 7 ------ 6 5 4 3 2 1 0 ------ RC5 RC4 RC3 RC2 RC1 RC0 bit 7-6 non implementati: vengono letti come ‘0’ bit 5-0 RA<5:0>: poRtA I/O pin bit (Pin corrispondenti ai bit della porta C) 1 = il pin è a livello alto 0 = il pin è a livello basso TRISC - REGISTRO DELLA PORTA C TRI-STATE (INDIRIZZO: 87h) 7 6 5 4 3 2 1 0 ------ ------ TRISC5 TRISC4 TRISC3 TRISC2 TRISC1 TRISC0 bit 7-6 non implementati: vengono letti come ‘0’ bit 5-0 TRISC<5:0>: port A TRI-State pin bit (Bit di controllo tri-state della porta C) 1 = il pin della porta C è configurato come input (tri-stated) 0 = il pin della porta C è configurato come output 26 Si noti che i registri PORTA, PORTC sono inscritti al banco 0 e il TRISA, TRISC al banco 1. L’esempio seguente configura in output la porta A e scrive su essa il numero 7. bsf movlw movwf bcf movlw movwf STATUS,RP0 B'00000000' TRISA STATUS, RP0 B'00000111' PORTA ; ; ; ; ; ; banco 1 pone 0 i w pone w in TRISA banco 0 pone 7 in w pone w in PORTA R5 RA4 150 D0 RED D1 RED D4 RED D5 RED D6 RED D7 RED D2 RED D3 RED R6 RA5 150 R7 RA2 150 R8 RA1 150 Per la sperimentazione delle porte di I/O il PICKit1 dispone di 8 led designati come D0, D1, D2, D3, D4, D5, D6, D7. A essi fa capo la porta A. Poiché questa dispone solo di 6 uscite, delle quali RA3 abilitata solo per l’input, non è possibile associare un led a ogni pin. Il collegamento dei led è strutturato in modo che l’accensione individuale avvenga per mezzo di combinazioni di RA5, RA4, RA2, RA1 del registro PORTA e programmando in modo opportuno il registro TRISA. Ad esempio per causare l’accensione del diodo D0 è necessario portare RA4=1 e RA5=0 in PORTA. E’ necessario inoltre programmare RA2 e RA1 in TRISA come input tri-state, in modo che non si accendano altri led. Per accendere D1 si può mantenere la medesima programmazione di TRISA, mentre vanno invertiti i livelli di RA4 e RA5 in PORTA. Generalizzando il concetto per tutti i led, risultano le seguenti tabelle: • programmazione TRISA IMPOSTA OUT OUT_D0_D1 OUT_D2_D3 OUT_D4_D5 OUT_D6_D7 • ----------0 0 0 0 ---------0 0 0 0 TRISA5 0 1 0 1 TRISA4 0 0 1 1 TRISA3 1 1 1 1 TRISA2 1 0 0 0 TRISA1 1 1 1 0 TRISA0 1 1 1 1 programmazione PORTA ATTIVAZIONE D0_ON D1_ON D2_ON D3_ON D4_ON D5_ON D6_ON D7_ON ----0 0 0 0 0 0 0 0 ----0 0 0 0 0 0 0 0 RA5 0 1 0 0 1 0 0 0 RA4 1 0 1 0 0 0 0 0 RA3 0 0 0 0 0 0 0 0 RA2 0 0 0 1 0 1 1 0 RA1 0 0 0 0 0 0 0 1 RA0 0 0 0 0 0 0 0 0 Alle due tabelle corrispondono le seguenti definizioni di costante da inserire nel segmento di inizializzazione del programma. • programmazione TRISA #define TRIS_D0_D1 B'00001111' #define TRIS_D2_D3 B'00101011' #define TRIS_D4_D5 B'00011011' #define TRIS_D6_D7 B'00111001' • programmazione PORTA #define D0_ON B'00010000' #define D1_ON B'00100000' #define D2_ON B'00010000' #define D3_ON B'00000100' #define D4_ON B'00100000' #define D5_ON B'00000100' #define D6_ON B'00000100' #define D7_ON B'00000010' ; ; ; ; impostazione impostazione impostazione impostazione ; ; ; ; ; ; ; ; D0 D1 D2 D3 D4 D5 D6 D7 TRISIO TRISIO TRISIO TRISIO per per per per D0 D2 D4 D6 and and and and D1 D3 D5 D7 LED LED LED LED LED LED LED LED 27 Esperimento …\PorteIO\switch Scrivere un programma che accenda il led D0 se viene premuto lo switch SW1. ;DIRETTIVE list p=16f684 ; direttiva list per definizione del processore #include <p16f684.inc> ; definizione variabili specifiche processore errorlevel -302 __CONFIG _CP_OFF & _CPD_OFF & _MCLRE_OFF & _ WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT ;DEFINIZIONE COSTANTI E VARIABILI #define TRIS_D0 B'00001111' ; impostazione TRISA per D0 #define D0_ON B'00010000' ; output per D0 ON #define D0_OFF B'00000000' ; output per D0 OFF ;INIZIALIZZAZIONE org 0x000 ; vettore di reset del processore goto Initialize org 0x005 ; vettore di inizio del programma Initialize bsf STATUS,RP0 ; banco 1 movlw TRIS_D0 movwf TRISA ; programma porta IO per D0 ;PROGRAMMA bcf STATUS, RP0 Inizio btfsc PORTA,3 ; testa il bit 3 della porta A goto Switch_OFF movlw D0_ON ; switch premuto quindi accendi led goto Fine Switch_OFF movlw D0_OFF ; switch rilasciato quindi spegni led Fine movwf PORTA ; invia dato su PORTA goto Inizio end In questo esempio l’istruzione btfsc implementa un costrutto if – else. Se il bit 3 della porta A vale 0 (pulsante premuto mette a massa) viene saltata l’istruzione goto Switch_OFF quindi viene eseguita la movlw D0_ON che predispone il led all’accensione. In caso contrario viene eseguita la movlw D0_OFF, che predispone il led allo spegnimento. In entrambi i casi si confluisce all’istruzione movwf PORTA, che perfeziona la scrittura sulla porta. Il diagramma di flusso a fianco illustra il concetto. Esercizi es1 Scrivere un programma che accenda i Led D0 e D1. Data la struttura circuitale l'accensione non può essere contemporanea. Il programma prevede pertanto un ciclo continuo entro il quale si alterna continuativamente l'accensione di D0 a quella di D1. E' sufficiente una sola programmazione della porta TRISA, valida per entrambi i diodi. SI bit 3 PORTA =0 ? if NO else W=D0_ON W=D0_OFF WPORTA es2 Scrivere un programma che accenda i Led D0 e D1 se premuto lo switch. Seguire la traccia dell'esempio, integrando quanto visto nell'es.1 per ciò che attiene il comando di due diodi. es3 Scrivere un programma che accenda i Led D4 e D5 se premuto lo switch, altrimenti accenda i led D0 e D1. Il codice alterna la programmazione del registro TRISA per la coppia D0_D1 a quella per la coppia D4_D5. La programmazione del TRISA non avviene pertanto in un'unica soluzione all'inizio del programma, ma è presente nel corpo del ciclo. Data la presenza di accessi al TRISA seguiti da accessi al PORTA, viene effettuata la commutazione continua tra banco 0 e banco 1. es4 Scrivere un programma che commuti tra Led0 e Led1 ad ogni pressione del tasto. E' presente la dichiarazione di un blocco di memoria RAM con la variabile CONTATORE. Contatore funge da variabile di stato, mantenendo in memoria lo stato acceso o spento del diodo. Alla pressione del tasto se CONTATORE=0 il programma, tramite le istruzioni incf CONTATORE,1 e movlw D0_ON incrementa il contatore e accende D0. Se CONTATORE=1 esso viene decrementato e viene acceso D1. Il controllo del valore di CONTATORE viene attuato dalle istruzioni movlw .1 xorwf CONTATORE,0 btfsc STATUS,Z. L'istruzione xorwf esegue l'EX_OR tra W, che è posto a 1 nell'istruzione precedente e CONTATORE. Se CONTATORE è pari a 1 il flag di zero viene settato; questa condizione viene testata dall'istruzione btfsc. 28 TIMER Il timer è un dispositivo dedicato utilissimo per la temporizzazione delle funzioni del microcontrollore. Esso affianca il microprocessore e opera facendosi carico di gestire i ritardi temporali, senza appesantire il carico del microprocessore. Il PIC16F684 dispone di tre timer; Timer0, Timer1 e Timer2; nelle nostre applicazioni utilizzeremo quasi sempre il Timer0. L’unità base del Timer0 è il registro TMR0 (TiMeR0) a 8 bit. Esso ha funzione di contatore: viene infatti incrementato ad ogni periodo del clock interno (è possibile anche impostare un clock esterno), assumendo i valori 0, 1, 2, 3, …, fino a 255. Questo è il valore di saturazione per un registro a 8 bit, al clock successivo il registro effettua un overflow e riprende il suo conteggio da 0. T0IF 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 fOSC/4 INTCON TMR0 0, 1, 2, …, 255 0 prescaler 1/2 prescaler 1/4 prescaler 1/8 L’evento di overflow influisce su un bit del registro INTCON, il bit T0IF (Timer 0 Interrupt Flag) il quale subisce una transizione da 0 a 1. Il flag T0IF funge quindi da spia, segnalando (flag significa appunto bandiera, segnalatore) l’avvenuto overflow. Il programmatore, per avvalersi del timer, nota la frequenza del clock fosc/4, imposta il valore del registro TMR0 per un ritardo base. Farà poi incrementare una variabile di conteggio ad ogni overflow. Il prodotto di questa variabile per il tempo richiesto per ciascun overflow fornirà il tempo di ritardo totale. Il tempo di ritardo può essere eventualmente amplificato mediante il prescaler. Il prescaler è un divisore di frequenza, esso riceve in ingresso la frequenza fosc/4 e la restituisce divisa per 2, 4, 8…, fino a 256. Il fattore di divisione può essere programmato tramite la combinazione di tre bit PS2, PS1, PS0 del registro OPTION_REG. Si riporta di seguito questo registro con la specifica del significato dei bit e la tabella del fattore di divisione del prescaler. OPTION_REG - REGISTRO OPZIONI (INDIRIZZO: 81h) 7 RAPU 6 5 4 3 2 1 0 INTEGD TOCS TOSE PSA PS2 PS1 PS0 bit 7 RAPU: PORTA PUll-up enable bit (bit abilitazione pull-up porta A) 1 = PORTA pull-ups disabilitati 0 = PORTA pull-ups abilitati dai valori di latch individuali presenti nel registro WPUA bit 6 INTEDG: INTerrupt EDGe select bit (bit selezione fronte interrupt) 1 = Interrupt sul fronte di salita del pin RA2/INT 0 = Interrupt sul fronte di discesa del pin RA2/INT bit 5 T0CS: TMR0 Clock Source select bit (bit selezione sorgente del clock) 1 = Transizione sul pin RA2/TOCKI 0 = Clock del ciclo istruzioni interne bit 4 T0SE: TMR0 Source Edge select bit (bit selezione fronte sorgente TMR0) 1 = Incremento sul fronte di discesa del pin RA2/TOCKI 0 = Incremento sul fronte di salita del pin RA2/TOCKI bit 3 PSA: PreScaler Assignment bit (bit assegnamento prescaler) 1= Il prescaler è assegnato al WDT 0 = Il precaler è assegnato al modulo Timer0 bit 2-0 PS<2-0>: bits di selezione del fattore di divisione Valore Bit 000 001 010 011 100 101 110 111 Periodo TMR0 1:2 1:4 1:8 1 : 16 1 : 32 1 : 64 1 : 128 1 : 256 Compreso il funzionamento di principio del timer dedichiamoci ora ad approfondire l’aspetto matematico del calcolo dei tempi di ritardo. Il clock del PIC16F684 può essere programmato fino a un massimo di 8MHz, ma è impostato di default a 4MHz, risulta pertanto: 6 f osc / 4 = 4 ⋅ 10 = 1MHz 4 periodo base = 1 = 10 − 6 = 1µ sec 6 1 ⋅ 10 29 Il tempo impiegato per raggiungere l’overflow dipende dal valore di TMR0 impostato. Quanto maggiore è TMR0 tanto minore è il numero di cicli necessari per giungere a 255. Si ha dunque: tempo ritardo base = 1µsec ⋅ (256 - TMR0) Per TMR0=0, impostando il prescaler a 32, si verifica un ritardo massimo di: 1µsec⋅256 ⋅ 32 = 8,192msec Per ritardi maggiori si deve ripetere il conteggio più volte, impostando un ciclo mediante una variabile di tipo contatore. Ad esempio per il ritardo di 1 secondo deve essere: CONTATORE = 1sec = 122 8,192msec Si riporta ora il programma per il ritardo di 1 secondo. ritardo movlw movwf loopesterno clrf bcf loopinterno btfss goto decfsz goto return end .122 CONTATORE ; ritardo di 1 secondo TMR0 INTCON,T0IF ; resetta registro timer ; resetta flag timer INTCON,T0IF loopinterno CONTATORE,1 loopesterno ; attendi se flag timer non settato ; decrementa contatore ritardo Il programma inizializza CONTATORE a 122, cancella TMR0 e T0IF che potrebbe risultare settato da un precedente overflow e rimane in attesa entro un ciclo di nome loopinterno. L’attesa finisce quando giunge l’overflow, in questo caso infatti l’istruzione btfss (Bit Test File Skip Set) provoca il salto dell’istruzione goto loopinterno e l’esecuzione di decfsz (Decrement File Skip Zero). Questa decrementa la variabile contatore; se essa è maggiore di zero si verifica un salto indietro a loopesterno e si ripete un nuovo ciclo del timer, altrimenti si esce dalla subroutine di ritardo. Di seguito è riportato lo schema reale del timer. Bus Dati CLKOUT (=FOSC/4) 8 0 1 1 TOCKI pin 0 TOSE 1 0 SYNC 2 Cycles TMR0 Prescaler 8-bit Imposta a 1 il flag bit TOCS PSA 8 PSA dal WDT T0IF quando si verifica OVERFLOW dal WDT PS<2:0> Si nota la presenza di TMR0 e T0IF. Inoltre è presente il bit PSA, che come visto in precedenza appartiene al registro OPTION_REG. Il PSA deve essere impostato a 0 affinchè il prescaler venga assegnato al modulo Timer0 e non al modulo WDT Watchdog Timer (linee tratteggiate che vanno alla parte di schema mancante). Il termine PS<2:0> indica i tre bit PS2, PS1, PS0 di OPTION_REG, per la selezione di una tra le 8 uscite del prescaler. Infine compaiono i bit T0CS e T0SE del registro OPTION_REG. Il T0CS (TMR0 Clock Source select Bit) se programmato a 0 specifica che la sorgente del clock è interna, in tal caso si verifica il normale funzionamento da timer. Se invece il T0CS viene programmato a 1 il clock viene prelevato dal pin T0CKI (Timer 0 ClocK Input). In questo caso il dispositivo funge da contatore e può essere utile per registrare in TMR0 un certo numero di eventi esterni, come i fronti d’onda di una cellula fotoelettrica adibita al conteggio di pezzi. Il bit T0SE definisce se il conteggio debba avere luogo sul fronte di discesa o di salita di questa onda. 30 Esperimento …\Timer\Ledonoff Scrivere un programma che accenda il Led D0 ad intermittenza, ad intervalli di 1 secondo. ;DIRETTIVE list p=16f684 ; direttiva list per definizione del processore #include <p16f684.inc> ; definizione variabili specifiche processore errorlevel -302 __CONFIG _CP_OFF & _CPD_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT ;DEFINIZIONE COSTANTI E VARIABILI #define TRIS_D0 B'00001111' ; impostazione TRISA per D0 #define D0_ON B'00010000' ; output per D0 ON #define D0_OFF B'00000000' ; output per D0 OFF cblock 0x20 CONTATORE ; contatore per ritardo endc ;INIZIALIZZAZIONE org 0x000 ; vettore di reset del processore goto Initialize org 0x005 ; vettore di inizio del programma Initialize bsf STATUS,RP0 ; banco 1 movlw B'10000100' ; clock timer interno, prescaler 1/32 movwf OPTION_REG ; overflow in 8.2ms movlw TRIS_D0 movwf TRISA ; programma porta IO ;PROGRAMMA bcf STATUS, RP0 Inizio movlw D0_ON movwf PORTA ; accendi led call ritardo movlw D0_OFF movwf PORTA ; spegni led call ritardo goto Inizio ;SOTTOPROGRAMMA ritardo movlw .122 movwf CONTATORE loopesterno clrf TMR0 bcf INTCON,T0IF loopinterno btfss INTCON,T0IF goto loopinterno decfsz CONTATORE,1 goto loopesterno return end ; ritardo di 1 secondo ; resetta registro timer ; resetta flag timer ; attendi se flag timer non settato ; decrementa contatore ritardo Esercizi es1 Scrivere un programma che accenda i Led D0 e D1 a intermittenza, a intervalli di 1 secondo. Programmare TRISA una sola volta per entrambi i diodi. es2 Scrivere un programma che accenda i Led D0, D1, D2, D3, D4, D5, D6, D7 in sequenza, ad intervalli di 1 secondo. I diodi vanno programmati a coppie, ovvero D0_D1, D2_D3, D4_D5, D6_D7, mediante 4 istruzioni TRISA. A ciascuna di queste seguono due istruzioni su PORTA per l'accensione di un singolo led della coppia. es3 Scrivere un programma che accenda i Led D0, D5, D2, D7, D3, D6, D1, D4 in sequenza, ad intervalli di 1 secondo. Per ciascun led è necessario in questo caso programmare diversamente TRISA, in quanto le coppie di led adiacenti nella sequenza appartengono a sezioni diverse nel circuito. es4 Scrivere un programma che accenda in sequenza D4, D5, D6, D7 se lo Switch è premuto, altrimenti accenda in sequenza D0, D1, D2, D3. Impiegare l'istruzione btfsc PORTA,3 per l'implementazione dell'if. 31 DISPLAY Il Laboratorio ha in dotazione una scheda di espansione, da innestare nella schedina del PICKit1, comprendente due display a 7 segmenti a catodo comune e due integrati 9368 per il loro pilotaggio. Ogni integrato riceve sui suoi quattro ingressi DCBA la combinazione binaria del numero da visualizzare. La sezione delle unità riceve i bit dalla Porta C, quella delle decine dalla porta A. Tuttavia poichè l’uscita RA3 non può fungere da output, la sezione delle decine si collega solo a tre pin, il quarto più significativo, cioè D, è collegato permanentemente a massa. Il numero massimo configurabile sulle decine è pertanto 7 (111 binario). Si veda la tabella che segue per una indicazione esaustiva dei collegamenti. decine C B RA2 RA1 4 5 6 7 8 9 10 11 12 13 DECINE UNITA’ A RC0 RA5 RA4 RA3 RC5 RC4 RC3 RA0 +5 f g a b c d RA1 RA2 B C LT D A e Vcc f g a b c d B C LT D A e GND 3 unità C B RC2 RC1 RBO RBI 2 D RC3 GND 1 A RA0 RBO RBI D 0 RC0 RC1 +5 +5 RC2 +5 13 Esperimento …\Display\contat Scrivere un programma che effettui un conteggio da 0 a 7 sul display delle unità. Il programma dichiara una variabile NUMERO per la conservazione dello stato del conteggio. NUMERO viene incrementato ogni secondo e azzerato quando vale 8 mediante le istruzioni movlw .8; xorwf NUMERO,0; btfsc STATUS,Z e clrf NUMERO. L'istruzione xorwf confronta NUMERO con 0: se sono uguali pone il flag Z a zero. L'istruzione btfsc testa Z: se esso risulta settato viene cancellato NUMERO con l'istruzione clrf. DIRETTIVE list p=16f684 ; direttiva list per definizione del processore #include <p16f684.inc> ; definizione variabili specifiche processore errorlevel -302 __CONFIG _CP_OFF & _CPD_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT ;DEFINIZIONE COSTANTI E VARIABILI cblock 0x20 CONTATORE ; contatore per ritardo NUMERO ; variabile di conteggio endc ;INIZIALIZZAZIONE org 0x000 ; vettore di reset del processore goto Initialize org 0x005 ; vettore di inizio del programma Initialize bsf STATUS, RP0 ; banco 1 movlw B'10000100' ; clock timer interno, prescaler 1/32 movwf OPTION_REG ; overflow in 8.2ms clrf ANSEL ; I/O digitale movlw 0x00 movwf TRISC ; programma porta C per tutti i pin output bcf STATUS, RP0 ; banco 0 movlw .0 ; inizializza conteggio movwf NUMERO ;PROGRAMMA Inizio movfw NUMERO movwf PORTC ; numero in output su unità 32 call incf movlw xorwf btfsc clrf goto ritardo NUMERO,1 .8 NUMERO,0 STATUS,Z NUMERO Inizio ; incrementa numero ; se numero=8 riprende il conteggio da 0 ; salta se Z=0 ; azzera numero ;SOTTOPROGRAMMA ritardo movlw .122 movwf CONTATORE loopesterno clrf TMR0 bcf INTCON,T0IF loopinterno btfss INTCON,T0IF goto loopinterno incfsz CONTATORE,1 goto loopesterno return end ; ritardo di 1 secondo ; resetta registro timer ; resetta flag timer ; attendi se flag timer non settato ; incrementa contatore ritardo Esercizi es1 Scrivere un programma che effettui un conteggio da 0 a 7 contemporaneamente sul display delle unità e delle decine. Procedere come nell’esperimento. es2 Scrivere un programma che effettui un conteggio da 4 a 8 sul display delle unità. Per bloccare il conteggio a 8 verificare quando NUMERO vale 9 mediante l'istruzione xorwf NUMERO, che setta il flag Z nel caso di uguaglianza. Con l'istruzione btfss STATUS,Z effettuare il test; nel caso Z=1 si deve porre NUMERO=4 per riprendere il conteggio da capo. L’algoritmo risponde al seguente diagramma di flusso di tipo “if”. a fianco è riportato lo stralcio di programma riguardante il test su NUMERO. if SI NUMERO =9 ? NUMERO=4 NO movlw .9 xorwf NUMERO,0 btfss STATUS,Z goto notnove movlw .4 movwf NUMERO notnove goto Inizio ; confronta NUMERO con 9 ; salta se Z=0 goto Inizio es3 Scrivere un programma che effettui un conteggio da 0 a 59. Il programma testa la cifra delle unità; quando questa raggiunge dieci viene forzata a 0. Parimenti quando la cifra delle decine raggiunge sei viene forzata a zero. I test sui valori delle cifre vengono condotti con l'abbinamento tra l'istruzione xorwf e btfss. L’algoritmo risponde al diagramma di flusso a fianco. es4 Scrivere un programma che effettui il conteggio a singola cifra dei numeri pari alternati a quelli dispari. Per generare queste sequenze il programma incrementa due volte NUMERO. Vengono effettuati due test su NUMERO. Nel primo, durante il conteggio dispari, quando NUMERO raggiunge 11 viene forzato a zero. Nel secondo, durante il conteggio pari, se raggiunge 10 viene forzato a uno. if SI UNITA =9 ? NO UNITA=0 if SI DECINE =6 ? NO DECINE=0 goto Inizio 33 AUDIO L’onda audio è una variazione di pressione prodotta generalmente dall’oscillazione di una membrana che sposta all’unisono le particelle di aria che la circondano. Nel caso di un sistema digitale come il microprocessore è possibile generare onde quadre, ovvero onde costituite da una alternanza di ‘0’ e ‘1’. Il dispositivo più economico per la riproduzione, che non necessita di amplificazione è il buzzer. GND PIC RA0 BUZZER call ritardo bsf PORTA,0 Variando la frequenza dell’onda quadra varierà il tono del bcf PORTA,0 segnale audio prodotto. Verrà utilizzato come uscita il pin 0 della porta A, cioè RA0. Un programma per la generazione di onde quadre deve prevedere una sezione di base in grado di call ritardo generare un singolo periodo di onda quadra. E’ sufficiente per questo utilizzare due istruzioni, la bcf PORTA,0 per impostare RA0=0 e la bsf PORTA,0 per impostare RA=1 e interporre tra queste dei ritardi di durata opportuna, come descritto dalla figura a fianco. Il ritardo si ottiene con il Timer. Per il calcolo del tempo di ritardo si ricorre alla formula del Timer, che qui riportiamo: tempo ritardo base = 1µsec ⋅ (256 - TMR0) Studiamo il problema del calcolo con riferimento a due casi concreti: una frequenza f1=320Hz e una f2=480Hz Risulta: periodo del segnale di 320Hz = T1=1/320 = 3125 µsec T1/2=1562 µsec periodo del segnale di 480Hz = T2=1/480 = 2083 µsec T2/2=1041 µsec Impostando il prescaler a 1/8 il tempo di ritardo base viene amplificato di 8 volte; si può ora calcolare i valore da impostare in TMR0 nei due casi: 8 ⋅ ( 256 – TMR0 ) = 1562 8 ⋅ ( 256 – TMR0 ) = 1041 256 – TMR0 = 195 256 – TMR0 = 130 TMR0 = 61 TMR0 = 126 Inserendo la sezione del programma che genera un singolo periodo entro un ciclo, si produce un’onda continuativa. Esperimento …\Audio\tono Scrivere un programma che generi sul pin RA0 numero 0 di PORTA un tono fisso di 320Hz oppure di 480Hz. Per impostare la durata del ritardo (semiperiodo) si devono assegnare a TMR0 rispettivamente i valori 61 oppure 126. ;DIRETTIVE list p=16f684 ; direttiva list per definizione del processore #include <p16f684.inc> ; definizione variabili specifiche processore errorlevel -302 __CONFIG _CP_OFF & _CPD_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT ;INIZIALIZZAZIONE org 0x000 ; vettore di reset del processore goto Initialize org 0x005 ; vettore di inizio del programma Initialize bsf STATUS,RP0 ; banco 1 movlw B'10000010' ; clock timer interno, prescaler 1/8 movwf OPTION_REG movlw .0 movwf TRISA ; tutti i pin di output ;PROGRAMMA bcf STATUS, RP0 Inizio bcf PORTA,0 ; imposta RA0=0 call ritardo bsf PORTA,0 ; imposta RA0=1 call ritardo goto Inizio ;SOTTOPROGRAMMA ritardo movlw .126 movwf TMR0 bcf INTCON,T0IF ; imposta registro timer ; resetta flag timer 34 loopinterno btfss goto return end INTCON,T0IF loopinterno ; attendi se flag timer non settato Per generare suoni più ricchi di sfumature rispetto al semplice tono continuo di frequenza fissa è necessario intervallare onde di diversa frequenza, quindi utilizzare differenti tempi di ritardo. In questo casi si lavora con un singolo sottoprogramma di ritardo che può essere reso variabile modificando il o i suoi registri di conteggio. Questi vengono spostati dal corpo del sottoprogramma entro il programma principale, dove diventano parametri che possono essere impostati di volta in volta per programmare il ritardo desiderato. Questi tipi di sottoprogrammi si chiamano sottoprogrammi di ritardo parametrici. Il vantaggio del loro impiego è dato dalla possibilità di impostare tempi di ritardo diversi in diverse sezioni del programma principale, sfruttando un solo sottoprogramma di ritardo. Di seguito sono riportati due programmi: nel primo il registro TMR0 è impostato nel corpo del sottoprogramma, pertanto il ritardo è prefissato e non modificabile. Nel secondo TMR0 è impostato nel programma principale, pertanto il medesimo sottoprogramma produce ritardi diversi in funzione del valore di TMR0 impostato nel programma principale. In grassetto sono indicate le istruzioni modificate nei due casi. Programma principale che richiama un sottoprogramma di ritardo parametrico Programma principale che richiama un sottoprogramma di ritardo fisso …………………… …………………… call ritardo …………………… ritardo movlw. 125 movwf TMR0 bcf INTCON,T0IF ; resetta flag timer loopinterno btfss INTCON,T0IF ; attendi se flag timer non settato goto loopinterno return …………………… …………………… movlw. 125 movwf TMR0 call ritardo …………………… ritardo bcf INTCON,T0IF ; resetta flag timer loopinterno btfss INTCON,T0IF ; attendi se flag timer non settato goto loopinterno return Esercizi es1 L’onda del tono di chiamata telefonica è un’onda composita, di durata 1 secondo, seguita da due secondi di sospensione. Essa risponde quindi al seguente grafico: 1sec 2 sec silenzio Magnificando la parte composita, riportata di seguito, si possono evidenziare alcune caratteristiche importanti. T1 12 impulsi 25msec T2 8 impulsi 12 impulsi 8 impulsi 25msec 35 L’onda è composta da una successione di 20 treni di onde di durata 25msec, caratterizzate da due frequenze diverse: 320Hz e 480Hz. I periodi delle due onde li conosciamo già, ne riportiamo qui il calcolo: inizializza TMR0=126 CICLI=12 periodo del segnale di 480Hz = T1=1/480 = 2083 µsec T1/2=1041 µsec TMR0 = 126 periodo del segnale di 320Hz = T2=1/320 = 3125 µsec TMR0 = 61 T2/2=1562 µsec movlw movwf movlw movwf .126 TMR0 .12 CICLI bcf PORTA,0 RA0=0 Possiamo allora ricavare il numero di impulsi replicati in 25msec: n° impulsi onda 480Hz: n° impulsi onda 320Hz: call ritardo 25msec / 2083µsec = 12 25msec / 3125µsec = 8 bsf PORTA,0 RA0=1 Scrivere un programma che generi continuativamente sul pin RA0 12 cicli di 480Hz seguiti da 8 cicli di 320Hz. call ritardo A fianco come ausilio è riportata la struttura del programma; parallelamente al diagramma di flusso sono specificate le istruzioni di implementazione. Per impostare la durata del ritardo (semiperiodo) vengono assegnati a TMR0 rispettivamente i valori 126 oppure 61. Il programma utilizza una variabile CICLI per conservare il numero delle ripetizioni. Il controllo del numero di ripetizioni è affidato all'istruzione decfsz. TMR0 deve essere inizializzato prima di ogni chiamata a "ritardo", in quando questa subroutine lo altera. decrementa CICLI NO CICLI=0 ? decfsz CICLI goto periodoT1 ……………….. ……………….. es2 SI Estendere il programma di generazione del tono telefonico in modo che produca 1 secondo di segnale seguito da 2 secondi di silenzio. Il conteggio dei 2 secondi è effettuato dal Timer1, in quanto il Timer 0 è impegnato nella generazione delle onde sonore. Il programma, dopo aver prodotto un ciclo d'onda, la itera per 20 volte. Il numero di iterazioni è conservato nella variabile RIPETIZIONI. L'istruzione decfsz RIPETIZIONI seguita da goto ripetizioni provvede a rinviare il flusso all'inizio se RIPETIZIONI>0. Segue l'istruzione call ritardotimer2 che invoca il ritardo di 2 secondi. Il Timer1 dispone di un registro a 16 bit formato da una parte alta TMR1H e una bassa TMR0L. L'overflow viene raggiunto al valore 65536, pertanto la formula del Timer1, sulla base delle considerazioni fatte per Timer0 è: inizializza TMR0=61 CICLI=8 RA0=0 call ritardo RA0=1 1 microsec ( 65536 - TMR1H_TMR1L ) call ritardo Se decidiamo di impostare TMR1H_TMR1L=0 risulta un ritardo a ciclo di 65,325msec. Per un ritardo di 2 secondi sono quindi richiesti 2 / 65,326msec = 30 cicli. L'overflow viene segnalato dal bit TMR1IF (Timer 1 Interrupt Flag) del registro PIR1. Il programma lo testa con l'istruzione btfss PIR1,TMR1IF. Finchè TMR1IF non è settato effettua un salto indietro e riazzera TMR1H, TMR1L, altrimenti incrementa il contatore CONTATORE del ciclo esterno e itera tutte le volte che CONTATORE<30. es3 decrementa CICLI NO CICLI=0 ? SI La frequenza della nota LA della ottava centrale è convenzionalmente fissata a 440Hz. Noto che il rapporto tra le frequenze della stessa nota nella ottava superiore è 1:2, possiamo calcolare le frequenze di tutte le note. Le note base sono 7, questo è noto a tutti, ma se si considerano i diesis diventano 12, intervallate dallo stesso semitono. Considerata la divisione dell’ottava in 12 intervalli e il rapporto 1:2 si avrà che tra una qualsiasi nota e la sua successiva esiste la relazione: f ( nota sup) = f ( nota inf) ⋅ 2 1 12 = f ( nota inf) ⋅ 2 0 , 083 = 1,059 Ad esempio: rispetto al LA a 440 Hz, la frequenza del LA# (LA diesis) è: 440 ⋅ 1,059 = 466 Hz quella de Si è: 466 ⋅ 1,059 = 494 Hz Si riporta la tabella completa delle frequenze. 36 NOTA FREQ NOTA FREQ NOTA FREQ NOTA FREQ NOTA FREQ NOTA FREQ DO2 66 Hz DO3 131 Hz DO4 262 Hz DO5 523 Hz DO6 1046 Hz DO7 2093 Hz DO#2 70 Hz DO#3 139 Hz DO#4 277 Hz DO#5 554 Hz DO#6 1109 Hz DO#7 2217 Hz RE2 74 Hz RE3 147 Hz RE4 294 Hz RE5 587 Hz RE6 1175 Hz RE7 2349 Hz RE#2 78 Hz RE#3 156 Hz RE#4 311 Hz RE#5 622 Hz RE#6 1245 Hz RE#7 2489 Hz MI2 83 Hz MI3 165 Hz MI4 330 Hz MI5 659 Hz MI6 1319 Hz MI7 2637 Hz FA2 88 Hz FA3 175 Hz FA4 349 Hz FA5 698 Hz FA6 1397 Hz FA7 2794 Hz FA#2 93 Hz FA#3 185 Hz FA#4 370 Hz FA#5 740 Hz FA#6 1480 Hz FA#7 2960 Hz SOL2 98 Hz SOL3 196 Hz SOL4 392 Hz SOL5 784 Hz SOL6 1568 Hz SOL7 3136 Hz 3322 Hz SOL#2 104 Hz SOL#3 208 Hz SOL#4 415 Hz SOL#5 831 Hz SOL#6 1661 Hz SOL#7 LA2 110 Hz LA3 220 Hz LA4 440 Hz LA5 880 Hz LA6 1760 Hz LA7 3520 Hz LA#2 117 Hz LA#3 233 Hz LA#4 466 Hz LA#5 932 Hz LA#6 1865 Hz LA#7 3729 Hz SI2 124 Hz SI3 247 Hz SI4 494 Hz SI5 988 Hz SI6 1976 Hz SI7 3951 Hz DO8 4186 Hz Per far suonare il microprocessore si deve generare, per ogni nota, un insieme di periodi di onda quadra, avvalendosi di un tempo di ritardo di un semiperiodo. All’aumentare della frequenza dell’onda, aumenta il numero di cicli necessari per una nota di durata prefissata. Nell’ipotesi di una durata di 1 secondo, per il DO3 risulta: fDO3 = 131Hz TDO3 = 7634 µsec Cicli = 1 / 7634 µsec = 130 TDO3/2 = 3817 Con un prescaler di 1/16 si ha: 16 ⋅ ( 256 – TMR0 ) = 3817 NOTA FREQ DO3 DO#3 RE3 RE#3 MI3 FA3 FA#3 SOL3 SOL#3 LA3 LA#3 SI3 131 139 147 156 165 175 185 196 208 220 233 247 Hz Hz Hz Hz Hz Hz Hz Hz Hz Hz Hz Hz PERIODO T 7634 µsec 7194 µsec 6803 µsec 6410 µsec 6061 µsec 5714 µsec 5405 µsec 5102 µsec 4808 µsec 4545 µsec 4292 µsec 4049 µsec 256 – TMR0 = 239 CICLI 131 139 147 156 165 175 185 196 208 220 233 247 TMR0 = 17 T/2 3817 3597 3401 3205 3030 2857 2702 2551 2404 2272 2146 2024 TMR0 17 31 43 56 67 77 87 96 105 114 121 129 Scrivere un programma che generi una nota di 131Hz alternata a una di 247Hz, entrambe di durata 1 secondo. Il segmento di programma che genera una singola nota deve prevedere l'inizializzazione del numero di cicli e della durata dei semiperiodi. Questa viene impostata in TMR0, il quale va inizializzato prima di ogni istruzione call ritardo, in quanto il sottoprogramma ritardo ne altera il valore. I due segmenti per le due note sono strutturalmente uguali fra loro; il diagramma di flusso è lo stesso dell’esercizio 1. es4 Scrivere lo stesso programma dell’esercizio 3 utilizzando un solo ciclo di generazione del treno di onde per entrambe le frequenze. Si deve ricorrere all’utilizzo di sottoprogrammi, uno per la generazione del ritardo e uno per la generazione di un treno di onde. Quest’ultimo deve essere parametrico, cioè deve ricevere dall’esterno, in forma di parametri, gli elementi per la specificazione della frequenza e del numero di cicli, che sono rispettivamente TMR0 e CICLI. Si riporta di seguito il sottoprogramma: nota movfw movwf bcf call movfw movwf bsf call decfsz goto return MEMREG TMR0 PORTA,0 ritardo MEMREG TMR0 PORTA,0 ritardo CICLI,1 nota ;Ripristina TMR0 con il contenuto di MEMREG ; imposta RA0=0 ;Ripristina TMR0 con il contenuto di MEMREG ; imposta RA0=1 ;salta a nota se CICLI>0 37 In esso il registro TMR0 viene utilizzato due volte e la seconda volta risulta alterato. E’ necessario allora disporre il suo valore entro una variabile di nome MEMREG, che viene inizializzata nel programma principale e permette di ripristinare ogni volta il corretto valore di TMR0, mediante le istruzioni movfw MEMREG e movwf TMR0. es5 In base alla struttura dell’esercizio 5, scrivere un programma che sintetizzi una breve melodia. Si provi ad esempio con la seguente sequenza: MI MI MI FA FA MI SOL MI MI SOL FA MI RE MI MI DO FA MI RE RE MI MI RE Copiare le seguenti costanti nel programma, per sveltirne lo sviluppo: #define DO3_CICLI #define DO#3_CICLI #define RE3_CICLI #define RE#3_CICLI #define MI3_CICLI #define FA3_CICLI #define FA#3_CICLI #define SOL3_CICLI #define SOL#3_CICLI #define LA3_CICLI #define LA#3_CICLI #define SI3_CICLI #define DO3_TMR0 #define DO#3_TMR0 #define RE3_TMR0 #define RE#3_TMR0 #define MI3_TMR0 #define FA3_TMR0 #define FA#3_TMR0 #define SOL3_TMR0 #define SOL#3_TMR0 #define LA3_TMR0 #define LA#3_TMR0 #define SI3_TMR0 .131 .149 .167 .156 .195 .175 .185 .196 .208 .220 .233 .247 .17 .31 .43 .56 .67 .77 .87 .96 .105 .114 .121 .129 38 ADC Il convertitore Analogico Digitale A/D implementa la conversione di un segnale analogico nella sua rappresentazione binaria a 10 bit. La funzionalità del dispositivo è controllata dai tre registri ANSEL, ADCON0, ADCON1, riportati più avanti. Di seguito è riportato lo schema interno del dispositivo. VCFG=0 VDD VREF VCFG=1 RA0/AN0 RA1/AN1/VREF RA2/AN2 RA4/AN3 RC0/AN4 RC1/AN5 RC2/AN6 RC3/AN7 10 A/D GO/DONE ADFM ADON 10 VSS ADRESH ADRESL CHS<2:0> In esso si distinguono otto ingressi analogici, multiplexati con i pin di PORTA. Per abilitare un pin al funzionamento come input analogico si deve settare il corrispondente bit del registro ANSEL (ANalogic SELect=Selezione Analogica). Occorre inoltre impostare il pin come input, tramite il registro TRISA. La selezione dell’ingresso analogico avviene mediante i tre bit ADCON0<4:2>. Il bit VCFG (ADCON0<6>) permette di selezionare una tensione di riferimento esterna (input RA1/AN1/VREF ) o interna (VDD). Il bit ADON <ADCON0<0> abilita il funzionamento dell’AD. La conversione viene avviata portando alto il pin GO/DONE. Il ritorno di questo al livello basso segnala la fine della conversione. Il risultato della conversione impegna 10 bit dei due registri di 8 bit ADRESH e ADRESL. Sono disponibili due formati per il dato convertito: giustificato a sinistra e giustificato a destra, come descritto nelle rispettive figure. La selezione del formato è data dal bit ADFM (ADCON0<7>). ADRESL ADRESH LSB MSB bit 7 bit 7 bit 0 bit 0 risultato giustificato a sinistra non implementato: letto come ‘0’ ADRESL ADRESH LSB MSB bit 7 bit 7 bit 0 bit 0 risultato giustificato a destra non implementato: letto come ‘0’ ANSEL – REGISTRO SELEZIONE ANALOGICA (INDIRIZZO: 91h) riservato riservato 5 4 3 2 1 0 ANS7 ANS6 ANS5 ANS4 ANS3 ANS2 ANS1 ANS0 bit 7-0 ANS<7:0>: ANalog Select bits (Bit di selezione analogica) Selezione tra funzione analogica e digitale sui rispettivi pin AN<7:0> 1 = Input analogico. Il pin è assegnato all’input analogico 0 = I/O digitale. Il pin è assegnato alla porta digitale o a una funzione speciale ADCON0 – REGISTRO DI CONTROLLO A/D (INDIRIZZO: 1Fh) 7 6 5 4 3 2 1 0 ADFM VCFG ----- CHS2 CHS1 CHS0 GO/DONE ADON bit 7-0 ADFM: A/D result ForMed Select bit (Bit di selezione formato risultato A/D) 1 = giustificato a destra 0 = giustificato a sinistra bit 6 VCFG: Voltage Reference bit (bit tensione di riferimento) 1 = pin VREF 39 0 = VDD bit 5 Non implementato (letto come ‘0’) bit 4-2 CHS<2:0>: analog CHannel select bits (bits selezione canale analogico) 000 = Canale 00 (AN0) 001 = Canale 01 (AN1) 010 = Canale 02 (AN2) 011 = Canale 03 (AN3) 100 = Canale 04 (AN4) 101 = Canale 05 (AN5) 110 = Canale 06 (AN6) 111 = Canale 07 (AN7) bit 1 GO/DONE: A/D convertion status bit (bit di stato della conversione) 1 = Conversione A/D in corso. Portando a 1 questo bit ha inizio la conversione. Questo bit è automaticamente cancellato via hardware a completamento della conversione 0 = Conversione A/D non in corso/completata bit 0 ADON: bit di avvio della conversione 1 = modulo convertitore A/D in funzione 0 = modulo convertitore A/D spento senza assorbimento di corrente Esperimento …\ADC\convers Scrivere un programma che accenda i Led D0 e D1 ad intermittenza, a intervalli regolabili tramite il trimmer RP1. Il formato deve essere giustificato a sinistra per poter utilizzare solo gli 8 bit più significativi della conversione, presenti nel registro ADRESH. I due meno significativi di ADRESL possono essere ignorati senza alcun errore, con la sola conseguenza trascurabile di un minore grado di risoluzione. Il valore di ADRESH, memorizzato nella variabile RESULTHI, viene passato entro il sottoprogramma di ritardo a CONTATORE. Un alto valore del registro, conseguente a una ampia rotazione de trimmer, comporta un alto tempo di ritardo e viceversa. In fase di inizializzazione le istruzioni bsf TRISA,0 e bsf ANSEL,0 impostano AN0 come input analogico. Le istruzioni movlw B'00000001' e movwf ADCON0 programmano il formato giustificato a sinistra, l’abilitazione del modulo A/D e dell’ingresso AN0. L’avvio della conversione è dato dall’istruzione bsf ADCON0,GO. L’istruzione btfsc ADCON0,GO mantiene in ciclo fino al raggiungimento della fine conversione. A questo punto il risultato della conversione, presente in ADRESH, viene caricato entro RESULTHI. Il programma invia sulla PORTC il valore RESULTH, che può essere così monitorato. ;DIRETTIVE list p=16f684 ; direttiva list per definizione del processore #include <p16f684.inc> ; definizione variabili specifiche processore errorlevel -302 __CONFIG _CP_OFF & _CPD_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT ;DEFINIZIONE COSTANTI E VARIABILI #define TRIS_D0_D1 B'00001111' ; impostazione TRISA per D0 #define D0_ON B'00010000' ; output per D0 ON #define D1_ON B'00100000' ; output per D1 ON cblock 0x20 CONTATORE ; contatore per ritardo RESULTHI ; bit alto conversione endc ;INIZIALIZZAZIONE org 0x000 ; vettore di reset del processore goto Initialize org 0x005 ; vettore di inizio del programma Initialize bsf STATUS,RP0 ; banco 1 movlw B'10000100' ; clock timer interno, prescaler 1/32 movwf OPTION_REG ; overflow in 8.2ms movlw TRIS_D0_D1 movwf TRISA ; programma porta IO per D0_D1 bsf TRISA,0 ; programma RA0 come input bsf ANSEL,0 ; imposta RA0 analogico movlw 0x00 movwf TRISC ; programma porta C per tutti i pin output bcf STATUS, RP0 movlw B'00000001' movwf ADCON0 ; Vref=Vdd ADON ;PROGRAMMA Inizio conv movlw movwf call movlw movwf call D0_ON PORTA ritardo D1_ON PORTA ritardo bsf ADCON0,GO ; accendi led D0 ; accendi led D1 ; avvia conversione 40 btfsc goto movf movwf movf movwf ADCON0,GO conv ADRESH,0 RESULTHI ADRESH PORTC goto Inizio ;SOTTOPROGRAMMA ritardo movf RESULTHI,0 movwf CONTATORE loopesterno clrf TMR0 bcf INTCON,T0IF loopinterno btfss INTCON,T0IF goto loopinterno decfsz CONTATORE,1 goto loopesterno return end ; attesa fine conversione ; poni parte significativa in WREG ; numero in output su unità ; ritardo funzione di RESULTHI ; resetta registro timer ; resetta flag timer ; attendi se flag timer non settato ; incrementa contatore ritardo Esercizi es1 Scrivere un programma che accenda i Led in sequenza a intervalli regolabili tramite il trimmer RP1. Disporre le istruzioni di conversione entro un sottoprogramma. Questo deve essere richiamato per ogni led, per tenere sempre aggiornato il ritardo dosato con RP1. es2 Scrivere un programma che mostri sul display delle unità i numeri da 0 a F, proporzionatamente al grado di rotazione del trimmer RP1. Programmare il dispositivo ADC per il formato giustificato a sinistra, affinchè il risultato a 8 bit della conversione si trovi nel registro ADRESH. L'intervallo del risultato varia da 0 a 255, perciò prevede 256 scaglioni. Se lo si divide per 16 il numero di scaglioni si riduce a 16. La divisione per 16 viene implementata mediante quattro istruzioni rrf, che fanno ruotare i bit di 4 posizioni a destra. In tal modo il valore massimo 11111111 diventa 00001111, ovvero F, come richiesto dal problema. Tutti gli scaglioni si contraggono proporzionatamente. Inserire un piccolo ritardo prima della conversione per la stabilizzazione del convertitore. 41 INTERRUPT Un microcontrollore che ha in carico l’esecuzione di un programma, svolge in sequenza le istruzioni programmate, secondo un ordine predeterminato. Oltre ad assolvere a un flusso di lavoro “determinato”, il microprocessore deve anche rispondere a richieste che gli giungono in modo casuale dai dispositivi con i quali interagisce. Una periferica o un dispositivo facente parte del sistema coordinato dal microcontrollore possono lanciare a esso una richiesta di servizio. Ad esempio, in un sistema di controllo della temperatura, un dispositivo può comunicare una sopravvenuta condizione di allarme. A fronte di una richiesta di servizio casuale, il microprocessore deve abbandonare il regime corrente e avviare una procedura di gestione della richiesta di servizio. Due sono le modalità con le quali il microcontrollore si dispone ad accogliere le richieste di servizio: il Polling e l’Interrupt. Con il Polling, che significa interrogazione, il controllo delle richieste segue uno schema prefissato: è lo stesso programma in esecuzione sul microprocessore a prevedere una ricognizione periodica delle periferiche per intercettare quelle con richieste di servizio pendenti. Il Polling è quindi un metodo software, il controllo è tutto a carico del programma che periodicamente deve sospendere la sua normale esecuzione, per interrogare le periferiche, dilatando i tempi di esecuzione. è presente una richiesta di servizio ? Microcontrollore Periferica POLLING ???? Con l’Interrupt è il dispositivo a lanciare la richiesta di servizio. Questa viene ricevuta dall’hardware del microcontrollore; il software non ha il compito di sondare le richieste di servizio, come nel Polling. richiesta di servizio Microcontrollore Periferica INTERRUPT !!!! Descriviamo il processo di interrupt: 1) Un dispositivo lancia sulla linea di interrupt, mediante un opportuno segnale, una richiesta di servizio 2) Il microcontrollore sospende il programma corrente e passa a eseguire la cosiddetta routine di gestione dell’interrupt 3) Il microcontrollore ritorna a eseguire il programma corrente dal punto dove era stato interrotto INTERRUPT programma POLLING programma è presente una richiesta di servizio ?? SI routine di gestione dell’interrupt routine di gestione della richiesta NO Il programma di gestione dell’interrupt individua il dispositivo che ha lanciato la richiesta, leggendo opportuni registri che vengono alterati in caso di interrupt, e passa a soddisfare le richieste di servizio. Il programma di gestione dell’interrupt si configura come una specie di salto a sottoprogramma. In effetti il microcontrollore sospende un flusso di programmazione principale per passare a eseguire una routine straordinaria. Ma c’è una importante differenza: il salto a sottoprogramma è un passaggio predeterminato a livello software, collocato in una ben determinata sezione del programma. L’interrupt è invece un evento casuale, hardware, che può essere sollevato in qualsiasi sezione del programma in esecuzione. Per comprendere totalmente i concetti ora espressi conviene riferirsi a due esempi, uno “domestico” e uno tecnico. Supponiamo di attendere un amico e avere il citofono non funzionante. Dobbiamo periodicamente sospendere la nostra attività per andare a controllare il cancello di casa. Questo metodo è assimilabile al polling, in quanto è necessario un controllo costante che comporta uno spreco di tempo. Se invece contattiamo l’amico chiedendogli di segnalare il suo arrivo con una breve telefonata, siamo in regime di Interrupt. Lo squillo del telefono ci farà sospendere la nostra attività una sola volta per passare a gestire la richiesta di servizio, che in questo caso sarà l’apertura del cancello. Riferiamoci ora al computer: la gestione delle sue periferiche, tastiera, mouse ecc. è fatta con la tecnica dell’interrupt. Quando viene premuto un tasto sulla testiera, il microprocessore sospende il suo lavoro, passa a leggere il codice del tasto premuto, lo memorizza e infine ritorna sul lavoro interrotto. Sarebbe molto 42 dispendioso e irrazionale sondare di continuo la testiera per verificare la pressione di un tasto, considerate soprattutto le differenti tempistiche di microprocessore e tastiera. Il PIC16F684 ha 11 sorgenti di interrupt: Interrupt esterno RA2/INT; Interrupt overflow Timer0 TMR0; Interrut per transizione su porta A; Interrupt da due comparatori;Interrupt convertitore A/D; Interrupt overflow Timer1; Interrupt Timer 2; Interrupt per scrittura su EEPROM; Interrupt monitor controllo clock; Interrupt CCP avanzato. Il registro INTCON contiene bit di controllo e flag relativi a interrupt Timer0 e per transizione su porta A, che sono le modalità approfondite in questo capitolo. Contiene inoltre bit per l’interrupt esterno e bit di abilitazione globale degli interrupt. INTCON - INTERRUPT CONTROL REGISTER (INDIRIZZO: 0Bh o 8Bh) 7 6 5 4 3 2 1 0 GIE PEIE T0IE INTE RAIE T0IF INTF RAIF bit 7 GIE: Global Interrupt Enable bit (bit di abilitazione globale interrupt) 1 = Abilita tutti gli interrupt non mascherati 0 = Disabilita tutti gli interrupt bit 6 PEIE: Peripheral Interrupt Enable bit (bit di abilitazione periferiche) 1 = Abilita tutti gli interrupt periferica non mascherati 0 = Disabilita tutti gli interrupt periferica bit 5 T0IE: TMR0 overflow Interrupt Enable bit (bit abilitazione interrupt overflow TMR0) 1 = Abilita l’interrupt TMR0 0 = Disabilita interrupt TMR0 bit 4 INTE: RA2/INT external INTerrupt Enable bit (bit di abilitazione interrupt esterno RA2/INT) 1 = Abilita interrupt esterno RA2/INT 0 = Disbilita interrupt esterno RA2/INT bit 3 RAIE: poRtA change Interrupt Enable bit (bit abilitazione interrupt per transizione su porta A) 1 = Abilita interrupt per transizione porta A 0 = Disabilita interrupt per transizione porta A bit 2 T0IF: TMR0 overflow Interrupt Flag (bit flag interrupt overflow TMR0) 1 = E’ avvenuto l’overflow di TMR0 (deve essere cancellato in software) 0 = Non è avvenuto l’overflow di TMR0 bit 1 INTF: RA2/INT external INTerrupt Flag bit (flag interrupt esterno RA2/INT) 1 = E’ avvenuto interrupt esterno RA2/INT (deve essere cancellato in software) 0 = Non è avvenuto interrupt esterno RA2/INT bit 0 RAIF: poRtA change Interrupt Flag bit (flag interrupt per transizione su porta A) 1 = Almeno uno dei bit <5:0> di PORTA ha cambiato stato (deve essere cancellato in software) 0 = Nessuno dei bit <5:0> di PORTA ha cambiato stato All’occorrenza di un evento di interrupt il microcontrollore provvede a sospendere il programma in corso di esecuzione e esegue un salto a una locazione fissa, dove deve essere collocata la routine di gestione dell’interrupt. Questa locazione, detta Vettore di Interrupt, si trova all’indirizzo 0004h. Vettore di Reset 0000h Vettore di Interrupt Memoria Programma 0004h Dato che lo spazio è di una sola word, qui il programmatore deve collocare una istruzione di salto ad altra etichetta di programma, dove collocare l’effettiva routine di gestione, chiusa dall’istruzione RETFIE (Return From IntErrupt). Al ritorno il programma proseguirà dall’istruzione successiva, la 0005. Qui il programmatore disporrà un salto al punto più logicamente corretto di prosecuzione del programma. Nella routine di interrupt il programmatore deve disabilitare ulteriori interrupt, per evitare pericolose chiamate nidificate e cancellare eventuali flag influenzati. L’istruzione retfie riabilita automaticamente gli interrupt. Si noti che il programma deve essere collocato a partire da 0006h, in quanto 0005h è ora impegnata per il salto. Interrupt Timer0 Il Timer0 è dotato del registro contatore TMR0. Quando esso raggiunge l’overflow setta il flag T0IF del registro INTCON. Se l’interrupt del Timer0 è abilitato si verifica un evento di interrupt. Per l’abilitazione si devono abilitare gli interrupt globali ponendo a 1 i bit GIE e PEIE dell’INTCON e lo specifico interrupt del Timer0 ponendo a 1 il bit T0IE di INTCON. Un interrupt abilitato globalmente ma non specificamente dal proprio bit si dice “mascherato”. 43 Esperimento …\Interrupt\Timer Scrivere un programma che effettui un conteggio da 0 a 7 sul display delle unità utilizzando l'interrupt del Timer0. Il processo deve essere gestito interamente dalla routine di gestione dell'interrupt. Questa deve incrementare una variabile CONTATORE ad ogni overflow di TMR0. TMR0 non deve essere impostato, pertanto deve svolgere tutto il conteggio da 00h a FFh. Non appena si ha CONTATORE=122, ovvero dopo 1 secondo, la routine di gestione deve azzerare CONTATORE e incrementare NUMERO, che non deve superare 7. In fase di inizializzazione, tramite INTCON, si devono abilitare globalmente gli interrupt e l'interrupt del Timer0. Si noti nella routine di gestione dell’interrupt l’istruzione bcf INTCON,T0IF, che predispone TMR0, azzerandolo, a generare il successivo interrupt. ;DIRETTIVE list p=16f684 ; direttiva list per definizione del processore #include <p16f684.inc> ; definizione variabili specifiche processore errorlevel -302 __CONFIG _CP_OFF & _CPD_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT ;DEFINIZIONE COSTANTI E VARIABILI cblock 0x20 CONTATORE ; contatore per ritardo NUMERO ; numero da visualizzare su display unità endc ;INIZIALIZZAZIONE org 0x000 ; vettore di reset del processore goto Initialize org 0x004 goto gestore_int_timer goto Inizio ; locazione dove saltare al ritorno da routine interrupt org 0x006 ; vettore di inizio del programma Initialize bsf STATUS, RP0 ; banco 1 movlw B'10000100' ; clock timer interno, prescaler 1/32 movwf OPTION_REG ; overflow in 8.2ms clrf ANSEL ; I/O digitale movlw 0x00 movwf TRISC ; programma porta C per tutti i pin output bcf STATUS, RP0 ; banco 0 movlw B'11100000' movwf INTCON ; abilitazione interrupt timer0 e abilitazione globale interrupt movlw .0 movwf NUMERO ;PROGRAMMA Inizio goto Inizio ;ROUTINE DI GESTIONE DELL'INTERRUPT TIMER gestore_int_timer bcf INTCON,T0IF ; resetta flag overflow timer0 movlw .122 ; ritardo di 1 secondo xorwf CONTATORE,0 btfss STATUS,Z goto noazioni ; attendere il completamento del ritardo movlw .0 movwf CONTATORE ; reinizializza CONTATORE movfw NUMERO movwf PORTC ; numero in output su unità incf NUMERO,1 ; incrementa numero movlw .8 xorwf NUMERO,0 ; se numero=8 riprende il conteggio da 0 btfsc STATUS,Z ; salta se Z=0 clrf NUMERO ; azzera numero retfie noazioni incf CONTATORE,1 retfie end Questo programma ha scopo di studio, perciò è stato creato nell’ottica della massima semplicità. In esso il programma principale non svolge alcun compito, ma questo contrasta con lo scopo dell’interrupt, che è di liberare il programma principale dal controllo puntuale delle richieste di servizio, in questo caso lanciate dal Timer0. Il programma successivo è arricchito da una nuova funzione. Il Timer0 provvede ancora a visualizzare il conteggio in avanti da 0 a 7 su un display, il programma principale comanda su un altro display il conteggio da 7 a 0 all’indietro, a diversa velocità. I due processi sono completamente indipendenti e asincroni. Il programma principale gestisce la temporizzazione del conteggio all’indietro con il timer1. Il timer 0 comanda in autonomia le temporizzazioni per il conteggio in avanti. 44 Esperimento …\Interrupt\DueTim Scrivere un programma che effettui un conteggio da 0 a 7 in avanti sul display delle unità e all'indietro su quello delle decine, a velocità diverse desincronizzate. ;DIRETTIVE list p=16f684 ; direttiva list per definizione del processore #include <p16f684.inc> ; definizione variabili specifiche processore errorlevel -302 __CONFIG _CP_OFF & _CPD_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT ;DEFINIZIONE COSTANTI E VARIABILI cblock 0x20 CONTATORE ; contatore per ritardo CONTAT NUM_UNITA NUM_DECINE endc ;INIZIALIZZAZIONE org 0x000 ; vettore di reset del processore goto Initialize org 0x004 goto gestore_int_timer goto Inizio org 0x006 ; vettore di inizio del programma Initialize bsf STATUS, RP0 ; banco 1 movlw B'10000100' ; clock timer interno, prescaler 1/32 movwf OPTION_REG ; overflow in 8.2ms clrf ANSEL ; I/O digitale movlw 0x00 movwf TRISC ; programma porta C per tutti i pin output movlw 0x00 movwf TRISA ; programma porta A per tutti i pin output bcf STATUS, RP0 ; banco 0 movlw B'11100000' movwf INTCON ; abilitazione interrupt timer0 e abilitazione globale interrupt bsf T1CON,TMR1ON ; attiva Timer1 movlw .0 ; inizializza conteggio movwf NUM_UNITA movlw .0 movwf NUM_DECINE ;PROGRAMMA Inizio movfw NUM_DECINE movwf PORTA ; visualizza NUM_DECINE decf NUM_DECINE,1 ; decrementa NUM_DECINE call ritardotimer1 goto Inizio ;ROUTINE DI GESTIONE DELL'INTERRUPT TIMER gestore_int_timer bcf INTCON,T0IF movlw .122 ; ritardo di 1 secondo xorwf CONTATORE,0 btfss STATUS,Z goto noazioni ; attendere il completamento del ritardo movlw .0 movwf CONTATORE movfw NUM_UNITA movwf PORTC ; numero in output su unità incf NUM_UNITA,1 ; incrementa numero movlw .8 xorwf NUM_UNITA,0 ; se numero=8 riprende il conteggio da 0 btfsc STATUS,Z ; salta se Z=0 clrf NUM_UNITA ; azzera numero retfie noazioni incf CONTATORE,1 retfie ;SOTTOPROGRAMMA ritardotimer1 movlw .4 movwf CONTAT loopesterno clrf TMR1H clrf TMR1L bcf PIR1,TMR1IF loopinternoTMR1 btfss PIR1,TMR1IF goto loopinternoTMR1 decfsz CONTAT,1 goto loopesterno return end ; ritardo breve ; resetta registri timer ; resetta flag timer 1 ; attendi se flag timer non settato ; decrementa contatore ritardo 45 Interrupt per transizione su PORTA • Questo interrupt viene lanciato se almeno uno dei sei bit <5:0> della PORTA subisce una transizione, cioè un passaggio da basso a alto o da alto a basso. In questo modo il microcontrollore può rispondere a richieste di servizio causate da segnali esterni, come ad esempio il segnale prodotto da una cellula fotoelettrica che intercetta il passaggio di un oggetto sulla sua direttrice. Per l’abilitazione dell’interrupt si devono abilitare gli interrupt globali ponendo a 1 i bit GIE e PEIE dell’INTCON e lo specifico bit RAIE di abilitazione interrupt per transizione su porta A. Ogni pin della PORTA è inoltre configurabile singolarmente come pin di interrupt per transizione. Il registro IOCA permette di specificare i pin abilitati con i corrispondenti bit. IOCA - INTERRUPT-ON-CHANGE PORTA REGISTER (INDIRIZZO: 96h) 7 6 5 4 3 2 1 0 ------ ------- IOCA5 IOCA4 IOCA3 IOCA2 IOCA1 IOCA0 bit 7-6 Non implementati bit 5-0 IOCA<5:0> bit di controllo interrupt di transizione su PORTA 1 = Interrupt su transizione abilitato 0 = Interrupt su transizione non abilitato Nella routine di gestione dell’interrupt è necessario disabilitare ulteriori interrupt e azzerare il flag interrupt per transizione su porta A. E’ inoltre necessario attendere alcuni istanti prima di uscire dall’interrupt, richiamando un ritardo (può andare bene 1 secondo). In questo modo si attende che i rimbalzi meccanici cessino e si evita che possano causare interrupt spuri. Esperimento …\Interrupt\Change Scrivere un programma che effettui un conteggio da 0 a 7 sul display delle unità in avanti o all'indietro. La direzione di conteggio deve invertirsi ogni volta che si preme il pulsante SW1. La pressione del pulsante deve invocare un interrupt. La routine di gestione dell'interrupt interroga il valore di DIREZIONE. Se DIREZIONE=0 pone DIREZIONE =1 e viceversa. Il programma principale esegue il conteggio nella direzione indicata appunto da DIREZIONE. ;DIRETTIVE list p=16f684 ; direttiva list per definizione del processore #include <p16f684.inc> ; definizione variabili specifiche processore errorlevel -302 __CONFIG _CP_OFF & _CPD_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_OFF & _INTRC_OSC_NOCLKOUT ;DEFINIZIONE COSTANTI E VARIABILI cblock 0x20 CONTATORE ; contatore per ritardo NUMERO ; numero da visualizzare DIREZIONE ; direzione del conteggio endc ;INIZIALIZZAZIONE org 0x000 ; vettore di reset del processore goto Initialize org 0x004 call gestore goto Inizio org 0x006 ; vettore di inizio del programma Initialize bsf STATUS, RP0 ; banco 1 movlw B'10000100' ; clock timer interno, prescaler 1/32 movwf OPTION_REG ; overflow in 8.2ms movlw B'11001000' ; RAIE=1; movwf INTCON ; abilitazione interrupt cambiamento su Porta A ; e abilitazione globale interrupt clrf ANSEL ; I/O digitale movlw B'00000000' movwf TRISC bsf IOCA,3 ; abilitazione interrupt cambiamento su bit 3 porta A bcf INTCON,0 ; resetta flag overflow timer 0 bcf STATUS, RP0 ; banco 0 movlw .0 ; inizializza conteggio movwf NUMERO ;PROGRAMMA Inizio movfw NUMERO movwf PORTC ; numero in output su unità call ritardo movlw .1 xorwf DIREZIONE,0 btfsc STATUS,Z ; se DIREZIONE=1 il conteggio procede in avanti goto avanti decf NUMERO,1 goto Inizio avanti incf NUMERO,1 goto Inizio 46 ;ROUTINE DI GESTIONE DELL'INTERRUPT DOVUTO A CAMBIAMENTO SU PORTA gestore bcf INTCON,RAIE ; disabilita ulteriori interrupt cambiamento su porta A call ritardo ; attende cessazione impulsi di SW1 per rimbalzi meccanici bcf INTCON,RAIF ; resetta flag overflow timer0 movlw .1 xorwf DIREZIONE,0 btfss STATUS,Z ; se DIREZIONE=1 pone DIREZIONE=0 e viceversa goto setta movlw .0 movwf DIREZIONE goto fine setta movlw .1 movwf DIREZIONE fine bsf INTCON,RAIE ; riabilita interrupt cambiamento su porta A retfie ;SOTTOPROGRAMMA ritardo movlw .122 movwf CONTATORE loopesterno clrf TMR0 bcf INTCON,T0IF loopinterno btfss INTCON,T0IF goto loopinterno decfsz CONTATORE,1 goto loopesterno return end ; ritardo di 1 secondo ; resetta registro timer ; resetta flag timer ; attendi se flag timer non settato ; incrementa contatore ritardo Esercizi es1 Scrivere un programma che effettui un conteggio in avanti sul display delle unità. Il conteggio deve essere resettato ogni volta che si preme il pulsante SW1. La pressione del pulsante deve invocare un interrupt. La routine di gestione dell'interrupt provvede a forzare NUMERO a 0. L’istruzione bsf IOCA,3 provvede ad abilitare l’interrupt del pin 3 di PORTA, al quale è collegato il pulsante SW1, es2 Scrivere un programma che effettui un conteggio in avanti a due velocità sul display delle unità. La velocità deve essere commutata ogni volta che si preme il pulsante SW1. Il valore della velocità impostata viene conservato da una variabile RICARICA_VELOCITA. Questa deve essere caricata entro CONTATORE all'interno del sottoprogramma di ritardo. La pressione del pulsante deve invocare un interrupt. La routine di gestione dell'interrupt provvede a commutare RICARICA_VELOCITA. Se RICARICA_VELOCITA=122 (conteggio lento) pone RICARICA_VELOCITA=20 (conteggio veloce) e viceversa. es3 Scrivere un programma che effettui un conteggio in avanti sul display delle unità, a due velocità, commutate ogni 16 secondi. Ad ogni overflow di TMR0 Timer0 deve lanciare un interrupt. In risposta la routine di gestione provvede a incrementare di una quota fissa il registro CONTATORE. Con il prescaler a 1/16, con il test CONTATORE=244, il tempo di attesa diventa di 16 secondi. Trascorsi questi la routine provvede a commutare la velocità di conteggio. Per implementare la doppia velocità si devono impiegare due variabili. Una variabile CONTAT_TIMER1 funge da contatore per il ciclo esterno del Timer1. Una seconda variabile RICARICA_TIMER1 serve da ricarica per CONTAT_TIMER1 e viene impostata alternativamente a 1 e a 4 dalla routine di interrupt per dosare la velocità. 47 EEPROM La memoria EEPROM (Electrically Erasable Programmable Read Only Memory=Memoria di sola Lettura Programmabile e Cancellabile Elettricamente) è una memoria permanente, che mantiene il proprio contenuto anche ad alimentazione spenta. E’ possibile sia leggere che scrivere sulla EEPROM; la scrittura ha luogo mediante una cancellazione che avviene con modalità elettriche. I registri associati sono: EEDAT: in esso viene posto il dato a 8 bit da scrivere o leggere EEADR: in esso viene posto l’indirizzo di una delle 256 locazioni EECON1: contiene bit di controllo EECON2: non fisicamente implementato EEDAT - EEPROM DATA REGISTER (INDIRIZZO: 9Ah) 7 6 5 4 3 2 1 0 EEDAT7 EEDAT6 EEDAT5 EEDAT4 EEDAT3 EEDAT2 EEDAT1 EEDAT0 bit 7-0 EEDATn: byte value to write or read from DATa EEPROM (bits del byte da leggere o scrivere nella EEPROM) EEADR - EEPROM ADDRESS REGISTER (INDIRIZZO: 9Bh) 7 6 5 4 3 2 1 0 EEADR7 EEADR6 EEADR5 EEADR4 EEADR3 EEADR2 EEADR1 EEADR0 bit 7-0 EEADRn: specifies the ADdRess of one of 256 locations for EEPROM read/write operation bits (specifica una delle 256 locazioni per lettura/scrittura su EEPROM) EECON1 - EEPROM CONTROL REGISTER (INDIRIZZO: 9Ch) 7 6 5 4 3 2 1 0 ------ ------ ------ ------ WRERR WREN WR RD bit 7-4 non implementati bit 3 WRERR: EEPROM WRite ERRor flag bit (flag segnalazione errore EEPROM) 1=operazione di scrittura terminata prematuramente 0=operazione di scrittura completata bit 2 WREN: EEPROM WRite ENable bi (bit abilitazione scrittura su EEPROM) 1=consente cicli di scrittura 0=inibisce scrittura su EEPROM bit1 WR: WRite control bit (bit di controllo scrittura) 1=inizia un ciclo di scrittura della EEPROM (il bit è cancellato dall’hardware a completamento della scrittura. Il bit WR può solo essere settato, non cancellato, con il software) bit0 RD: ReaD control bit (bit di controllo lettura) 1=inizia un ciclo di lettura della EEPROM (La lettura richiede un ciclo. RD è cancellato in dall’hardware. Il bit WR può solo essere settato, non cancellato, dal software) 0=Non ha inizio la lettura della EEPROM La lettura della EEPROM prevede le seguenti fasi: 1) scrittura indirizzo in EEADR 2) comando lettura mediante settaggio bit RD di EECON1 3) lettura dato entro registro EEDAT La scrittura della EEPROM prevede le seguenti fasi: 1) scrittura indirizzo in EEADR 2) scrittura dato entro registro EEDAT 3) abilitazione scrittura mediante settaggio bit WREN di EECON1 4) disabilitazione interrupt bit GIE di INTCON1 5) sequenza obbligata: movlw 0x55; movwf EECON2; movlw 0xAA; movwf EECON2 6) comando scrittura mediante settaggio bit WR di EECON1 7) riabilitazione interrupt bit GIE di INTCON1 Il programma che segue implementa entrambe le operazioni di scrittura e lettura. 48 Esperimento …\EEPROM Scrivere un programma che incida nella locazione di indirizzo 0 della EEPROM il numero 5, poi lo legga e lo visualizzi su display. ;DIRETTIVE list p=16f684 ; direttiva list per definizione del processore #include <p16f684.inc> ; definizione variabili specifiche processore errorlevel -302 __CONFIG _CP_OFF & _CPD_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT ;DEFINIZIONE COSTANTI E VARIABILI cblock 0x20 CONTATORE ; contatore per ritardo endc ;INIZIALIZZAZIONE org 0x000 ; vettore di reset del processore goto Initialize org 0x005 ; vettore di inizio del programma Initialize bsf STATUS, RP0 ; banco 1 movlw B'10000100' ; clock timer interno, prescaler 1/32 movwf OPTION_REG ; overflow in 8.2ms clrf ANSEL ; I/O digitale movlw 0x00 movwf TRISC ; programma porta C per tutti i pin output ;PROGRAMMA scrittura movlw .10 movwf EEADR ; indirizzo movlw .5 movwf EEDAT ; dato bsf EECON1,WREN ; abilita scrittura bcf INTCON,GIE ; disabilita interrupt movlw 0x55 movwf EECON2 movlw 0xAA movwf EECON2 bsf EECON1,WR ; comando scrittura bsf INTCON,GIE ; riabilita interrupt call ritardo lettura movlw .10 movwf EEADR ; indirizzo bsf EECON1,RD ; comando lettura movf bcf movwf EEDAT,W STATUS, RP0 PORTC call goto ritardo loop ; copia dato in W ; banco 0 loop ;SOTTOPROGRAMMA ritardo movlw .122 movwf CONTATORE loopesterno clrf TMR0 bcf INTCON,T0IF loopinterno btfss INTCON,T0IF goto loopinterno incfsz CONTATORE,1 goto loopesterno return ; ritardo di 1 secondo ; resetta registro timer ; resetta flag timer ; attendi se flag timer non settato ; incrementa contatore ritardo end 49