Creative Commons BY-NC-ND 2.5Questo sito e tutto il suo contenuto sono distribuiti sotto la licenza Creative Commons Attribuzione - Non Commerciale - Non opere derivate 2.5 Italia e con le condizioni d'uso definite nel disclaimer: siete pregati di leggere entrambi questi documenti prima di usufruire dei contenuti di questo sito. Per alcuni contenuti è necessaria una registrazione gratuita: non è necessario pagare e non è necessario accumulare punteggi per accedere agli articoli e scaricare i sorgenti. Basta solo essere onesti. Se volete che questo sito continui a rimanere attivo, a contribuire ogni giorno alla diffusione della cultura libera, non copiate il materiale per ripubblicarlo in altri luoghi : chi fa questo è solo un miserabile e un perdente. Se volete partecipare su settorezero e rendere le vostre idee, i vostri progetti, fruibili da tutti senza limitazioni, come dovrebbe essere in un paese civile e acculturato, potete farlo tranquillamente.

Corso programmazione PICMicro in C – Lezione 4 – Cosa sono gli interrupt, concetti di base per sistemi operativi MultiTasking su PICMicro

Autore: Giovanni Bernardo | Data pubblicazione: 16 settembre 2009
Categorie: PICmicro 10/12/16

interrupt_picmicroAlla fine di questa lezione e della successiva, con il nostro programma che andremo a caricare nel picmicro, saremo in grado di eseguire ben 2 compiti contemporaneamente: faremo lampeggiare un led mentre un cicalino suona una nota fissa. Tale cosa non è realizzabile normalmente senza ricorrere a particolari ed elaborati artifizi: un programma viene difatti eseguito in maniera sequenziale: un’istruzione dopo l’altra, e realizzare due compiti in contemporanea, con tale sistema, non è assolutamente possibile. Chi sa programmare sul pc con i linguaggi ad alto livello, sa benissimo che per eseguire due o più compiti in contemporanea è necessario ricorrere alla creazione di thread separati: nella nostra fantasia possiamo immaginare che il nostro programma principale crei uno spazio in memoria,  al quale affida un compito: trovandosi in uno spazio separato, la funzione può lavorare contemporaneamente al programma principale senza influire sulla sua normale esecuzione.

In un picmicro non è possibile la creazione di thread nel senso stretto, e si ricorre ad un vecchio metodo sempre molto valido (che può comunque essere utilizzato anche nei linguaggi di programmazione ad alto livello): si sfruttano i timer per scandire a tempo le operazioni. Facendo un esempio pratico: voglio che ogni 2 secondi venga eseguita la funzione A e ogni mezzo secondo la funzione B: un timer principale effettuerà il conteggio del tempo  (mentre il resto del programma continua ad essere eseguito), incrementando a sua volta due diversi contatori: uno che terrà il tempo per la funzione A e uno che terrà il tempo per la funzione B, ad ogni evento particolare associato al timer principale (vedremo poi), entrambi i contatori saranno incrementati: quando il B arriverà a mezzo secondo, ecco che sarà quindi azzerato/ricaricato e verrà eseguita la funzione B associata ad esso, stessa cosa per il contatore A arrivato a 2 secondi.

Ovviamente qui ci sarebbe da obiettare non poco, dal momento che tali funzioni effettivamente non vengono eseguite in contemporanea, ma a scadenze predeterminate, certo: avete perfettamente ragione. Immaginiamo però che i tempi non siano così “palpabili”: definiamo scadenze bassissime, di cui non possiamo renderci conto: parliamo di millesimi di secondo, e mettiamo caso che le nostre funzioni vengano eseguite così velocemente da non prendere più di un millisecondo di tempo… Vedete? Le cose stanno già cambiando: con tale velocità di esecuzione, anche se i vari task (i vari compiti) sono schedulati, al nostro occhio sembrerà che tutto avvenga in contemporanea.

Gli interrupt

La chiave per ottenere tutto questo tramite i timer è l’utilizzo degli interrupt. Il Timer0 dei picmicro può generare un interrupt. Ma procediamo per gradi, cosa è un interrupt? Su Wikipedia viene definito come:

un segnale asincrono che indica il bisogno di attenzione oppure un evento sincrono che consente l’interruzione di un processo qualora si verifichino determinate condizioni

Questa spiegazione è molto valida e rende bene il concetto (asincrono: non segue il normale ritmo del flusso principale), ma ovviamente un esempio può chiarire meglio come agisce un interrupt, per tale motivo ricorderò qui il famoso esempio del telefono di Sergio Tanzilli, al quale devo molto dal momento che è grazie a lui che sono entrato nel mondo dei picmicro.

Siamo a casa e abbiamo un telefono: per vedere se qualcuno ci sta chiamando è necessario che ogni tanto alziamo la cornetta? No, che fesseria direte voi! Il telefono squilla avvisandoci che qualcuno ci sta chiamando, quindi alzeremo la cornetta solo quando sta squillando (vi sembra una banalità eh? Non lo è affatto).

Immaginate: siamo a casa ad effettuare le nostre faccende, non abbiamo bisogno di alzare ogni tanto la cornetta, perchè il telefono ci avviserà con uno squillo, consentendoci di interrompere ciò che stiamo facendo e dedicarci quindi alla telefonata.

Ecco il concetto chiave: lo squillo del telefono è il nostro interrupt: ci consente di interrompere momentaneamente ciò che stavamo facendo per dedicarci alla situazione che ha generato l’interrupt fino a quando non decidiamo di  riprendere le nostre faccende nel punto in cui le avevamo rimaste.

Un interrupt periodico (come è quindi quello scatenato da un timer), legato a vari contatori, ci aiuta a creare sistemi multitasking che operano in tempo reale: ovvero più funzioni eseguite in contemporanea, indipendentemente dal programma principale!

Ma continuiamo a leggere per addentrarci di più in questi concetti e capire come possiamo mettere in pratica tutto questo.

Le sorgenti di interrupt dei PICMicro

L’esempio del telefono era qualcosa di molto banale, certo, ma rende chiaramente il concetto che sta alla base di un interrupt. In un picmicro c’è più di un “campanello” che ci permette di fermare momentaneamente il nostro programma in un punto, dedicarci a una determinata funzione, e quindi riprendere nel punto esatto in cui ci eravamo fermati (senza la necessità di andare a controllare di continuo questo o quel particolare evento).

Le varie sorgenti di interrupt, in particolare nel PIC16F877, sono:

Interrupt su overflow del Timer0 (indicato come T0IF o TMR0IF): il Timer0 è un contatore ad 8 bit che si autoincrementa (indipendentemente dall’esecuzione del programma principale) ogni tot di tempo. Possiamo stabilire noi ogni quanto tempo deve essere incrementato tramite appositi settaggi (che vedremo nella prossima lezione). Essendo un contatore ad 8 bit, potrà avere valori che vanno da 0 a 255. Quando è arrivato a 255 e arriva il momento dell’incremento, passa a zero sollevando un interrupt, che possiamo intercettare e sfruttare. E’ proprio questa funzione che utilizzeremo (è la funzione più sfruttata in generale) per incrementare altri contatori con i quali attivare o disattivare determinate funzioni nei tempi che vogliamo, vedremo in seguito come.

Interrupt esterno sul pin RB0 (INTF). Come potete vedere dalla designazione dei pin del 16F877, il pin N°33 è indicato come RB0/INT. Sappiamo che quando c’è una sbarra che separa più nomi, vuol dire che quel pin può avere più di una funzione. Difatti quel pin funziona sicuramente come I/O digitale, ma se lo settiamo come Ingresso e se abilitiamo la gestione dell’interrupt su RB0, ecco che funziona anche come INT: si avrà la generazione di un interrupt ogniqualvolta tale pin viene settato a livello logico alto dall’esterno. Difatti questo viene anche definito Interrupt Esterno dal momento che non è generato dalla circuiteria interna al picmicro ma da un evento esterno che pilota tale pin.

Interrupt su cambio di stato dei pin RB4-RB7 (RBIF). Si può verificare un interrupt anche al cambio di stato di uno di tali 4 pin: ovviamente se abilitiamo tale sorgente di interrupt, basta che cambi lo stato di almeno uno dei 4 per sollevare l’interrupt.

Interrupt di periferica: in questa categoria rientrano gli interrupt generati da periferiche come l’eeprom interna ( la fine delle operazioni di scrittura sulla memoria eeprom interna: se abilitata questa sorgente di interrupt, quando andremo a scrivere sull’eeprom e la scrittura sarà terminata, si verificherà un interrupt), l’overflow sul timer1, la ricezione di un byte nel buffer dicomunicazione seriale ecc. Ovviamente alcuni di questi interrupt sono disponibili unicamente sui pic che hanno a disposizione la periferica che lo genera (ad esempio l’USART, che permette la comunicazione seriale, o il modulo MSSP, che permette la comunicazione seriale I2C o SPI, non sono disponibili su tutti i pic).

Quando si verifica un interrupt, si scatenano i seguenti eventi (spiegati in maniera molto banale):

  • Il programma principale viene interrotto.
  • Viene salvato il valore numerico del PC (Program Counter, che è un contatore interno del PICMicro che tiene conto di quale riga di programma sta eseguendo) nello Stack (che è un registro apposito per memorizzare tale valore).
  • Il PICMicro salta ad una determinata locazione di memoria (denominata Interrupt Vector) nella quale vengono appunto memorizzate le istruzioni da eseguire in caso di interrupt.
  • Il PICMicro esegue le istruzioni contenute nell’interrupt vector.
  • Finito di eseguire tali istruzioni, il picmicro preleva dallo Stack la riga del programma principale in cui si era fermato.
  • Il programma principale riprende dal punto in cui era stato interrotto dall’interrupt.

Da ciò si capisce che le istruzioni da eseguire durante un interrupt, vengono memorizzate in una locazione di memoria apposita: in assembler dovremo difatti specificare nel nostro programma la locazione di memoria adatta (che varia in base al picmicro) in cui scrivere le istruzioni da eseguire in caso di interrupt.

In C questo non è necessario: si utilizzerà difatti una notazione particolare di funzione: basterà anteporre la parola  “interrupt” davanti al nome della funzione che vogliamo eseguire (questo è valido per l’Hitec-C, altri compilatori utilizzano una notazione differente per definire le istruzioni dell’interrupt vector), sarà quindi il compilatore a posizionare tali istruzioni nella locazione di memoria adatta in base al picmicro che abbiamo scelto per lo sviluppo.

In pratica si scriverà una cosa del genere:

void interrupt NomeDellaFunzione(void)
{
}

Quindi: ogni qualvolta si verificherà un interrupt, il picmicro eseguirà in automatico quella funzione, fermandosi momentaneamente nell’esecuzione del main. In tale funzione, inoltre, dovremo specificare le istruzioni da eseguire in base all’interrupt verificatosi, generalmente questo si fa eseguendo una serie di IF sul flag settato dall’interrupt. Difatti se si verifica un interrupt causato dal Timer0, in automatico il suo flag (T0IF) viene settato su VERO, e quindi possiamo capire CHI ha causato l’interrupt e agire di conseguenza. Finito di eseguire le istruzioni dovrà essere nostro compito reimpostare manualmente a FALSO tale flag, altrimenti non saremo più in grado di reintercettare l’interrupt:

void interrupt ISR(void)
{
if (T0IF)
   {
   // qui vanno le istruzioni da eseguire se l'interrupt è stato causato dal Timer0
   T0IF=0; // devo resettare il flag altrimenti non mi sarà più possibile reintercettare tale interrupt
   }
}

NOTE:
Personalmente chiamo sempre le funzioni di interrupt come ISR (Interrupt Service Routine), perchè questo è il nome che comunemente viene dato alla serie di istruzioni eseguite durante un interrupt.

Tutte le funzioni da eseguire dovranno necessariamente essere scritte nell’ISR: l’ISR non può difatti richiamare altre funzioni come è invece possibile fare nel main, questo perchè come detto prima, l’ISR si trova in una zona di memoria differente. In caso ci sia la necessità di eseguire funzioni esterne il trucco consiste nel definire dei flag, abilitarli nell’ISR e creare delle routine nel main che controllano tali flag: se verificano che un determinato flag (chiamato generalmente semaforo) è stato abilitato, allora lo resettano e richiamano la funzione desiderata, questa è una maniera indiretta di richiamare funzioni dall’ISR: l’utilizzo dei “semafori di stato”.

Ovviamente il fatto che abbiamo scritto una funzione apposita nell’interrupt vector non è sufficiente a gestire l’interrupt: bisogna abilitarli. Questo si fa tramite appositi flag nel registro di configurazione degli interrupt, denominato come INTCON (pag.24 del datasheet):

registro_intcon_interrupt_pic16f877come vedete si sono dei bit contrassegnati come Flag e altri contrassegnati come Enable. Quelli contrassegnati come Enable sono in pratica gli “interruttori” che permettono di attivare o disattivare un determinato evento di interrupt, i Flag invece sono le “spie” che ci avvisano se si è verificato questo o quel determinato tipo di interrupt. e vengono settati (cioè impostati a 1) dal picmicro e devono essere resettati (cioè impostati a zero) da noi via software.

L’impostazione degli interrupt può essere fatta o settando in un solo colpo il registro INTCON, dandogli un valore numerico, oppure settando uno ad uno i vari bit, (vediamo più giù come), dal momento che anche i singoli bit godono di un proprio nome mnemonico. Spieghiamo il significato dei vari bit:

Bit di abilitazione (Enable Bits)

GIE (Global Interrupt Enable) = è l’ “interruttore” generale, impostato su zero disattiva tutti i tipi di interrupt

PEIE (Peripheral Interrupt Enable) = abilita/disabilita gli interrupt di periferica (eeprom, timer1, usart ecc ecc)

TMR0IE (Timer0 Interrupt Enable) = abilita/disabilita l’interrupt sull’overflow del Timer0.

INTE (INT Enable) = abilita/disabilita l’interrupt sull’ingresso contrassegnato come INT (RB0)

RBIE (RB Interrupt Enable) = abilita/disabilita l’interrupt sul cambio di stato dei pin RB4-RB7

“Spie che indicano l’avvenuto interrupt (Flag bits)

TMR0IF (Timer0 Interrupt Flag) = viene settato in automatico quando il Timer0 ha finito il suo conteggio ed è ripartito daccapo (overflow). Viene anche indicato anche come T0IF

INTF (INT Flag) = viene settato in automatico se il pin RB0/INT viene portato a livello logico alto dall’esterno

RBIF (RB Interrupt Flag) = viene settato in automatico se una delle porte RB4:RB7 cambia stato

Tutti i Flag devono essere resettati via software (ovvero riportati a zero) se vogliamo continuare a monitorare l’interrupt.

Come vedete tra questi flag non ci sono quelli relativi alle periferiche che si trovano invece in altri registri che poi vedremo mano a mano.

Volendo quindi abilitare l’interrupt sull’overflow del Timer0 possiamo procedere quindi in due modi: impostando in un unico colpo il registro INTCON:

// Impostazione Interrupt
INTCON=0b10100000;
// bit 0 -> RBIF - Flag interrupt su porte B
// bit 1 -> INTF - Flag interrupt su RB0/INT
// bit 2 -> T0IF - Flag interrupt su Timer0
// bit 3 -> RBIE, Interrupt su porte B disattivato
// bit 4 -> INTE, Interrupt su porta RB0/INT disattivato
// bit 5 -> TMR0IE, Interrupt su Timer0 attivato
// bit 6 -> PEIE, Interrupt di periferica disattivato
// bit 7 -> GIE, Gestione Interrupt attiva

Stessa cosa sarebbe stato scrivere:

INTCON=0xA0; // l'ho scritto in esadecimale

Oppure:

INTCON=160; // l'ho scritto in decimale

Oppure ancora possiamo settare uno ad uno i singoli bit se ciò ci riesce più facile da ricordare:

TMR0IE=1; // abilito la gestione dell'interrupt sull'overflow del Timer0
GIE=1; // abilito la gestione generale degli interrupt

Nel software andremo poi a verificare quale interrupt ha causato il salto all’interrupt vector, ovviamente quando abilitiamo unicamente una sola sorgente di interrupt, fare questa operazione non è necessario, ma per me è buona norma farlo lo stesso per abituarsi ed entrare in una certa mentalità di programmazione che aiuta ad essere ordinati.

Nella prossima lezione vedremo come si imposta il Timer0 per fare in modo che l’interrupt si verifichi nei tempi che desideriamo e come sfruttarlo per fare in modo che il PIC esegua due cose in contemporanea: faremo lampeggiare un led mentre un altro pin emette un’onda quadra che pilota un cicalino.

Un’approfondimento del funzionamento degli interrupt e una comparazione della gestione degli interrupt tra le varie fasce di pic è presente in questo articolo

» Vai all’ indice delle lezioni e delle risorse del corso di programmazione

L'articolo ti è piaciuto o ti è stato utile per risolvere un problema? SettoreZero è realizzato soltanto contenuti originali: tutti gli articoli sono curati con passione dagli autori e nulla viene copiato da altri siti. Supporta e mantieni in vita SettoreZero con una donazione: basta soltanto un caffè o una birra. Puoi supportare SettoreZero anche con uno dei progetti elencati nella sezione servizi o partecipare anche tu con un tuo articolo/progetto personale.

Se desiderate che SettoreZero continui a rimanere gratuito e fruibile da tutti, non copiate il nostro materiale e segnalateci se qualcuno lo fa.

Puoi andare alla fine dell'articolo e lasciare un commento. I trackback e i ping non sono attualmente consentiti.

  1. #1 da Cristianoscr il 23 giugno 2010

    Il mio pic ha scritto PIC16F877-04/P è riprogrammabile? cioè va bene?

    • #2 da Giovanni Bernardo il 23 giugno 2010

      Si, ma è un tipo vecchio. Puoi usare massimo un quarzo da 4MHz e ora non ricordo se hai a disposizione tutte le periferiche. Controllati il datasheet sul sito della microchip, devi cercare PIC16F877 (senza la A).

  2. #3 da Fidus il 11 settembre 2010

    Ciao è possibile creare un orologio con un pic? Ho un 16F876A con quarzo da 20 Mhz.
    Qual’e il modo più veloce di fare un orologio?
    Ho letto tutto l’articolo ma non mi si accendono lampadine..

    • #4 da Giovanni Bernardo il 12 settembre 2010

      Basta usare un RTCC, ovvero un circuito integrato che funziona da orologio/calendario. Come il DS1307 che va pilotato in I2C. Questa è sicuramente la soluzione migliore in quanto gestisce da solo pure gli anni bisestili, i giorni della settimana ecc.

  3. #5 da odessos il 23 settembre 2010

    Domandone…
    Se abilito contemporaneamente l’interrupt sul TIME0 e l’interrupt sull’ingresso B0 con INTCON=……..
    devo scrivere due funzioni void interrupt(void) o con solo una posso gestire più interrupt.
    E se volessi chiamare l’attenzione dell’interrupt con qualsiasi altro ingresso, invece che degli ingressi RB?

    • #6 da Giovanni Bernardo il 23 settembre 2010

      Sui pic16 viene scritta una sola funzione di interrupt: qualsiasi interrupt richiama tale funzione. Nella funzione poi ti fai una serie di if per controllare quale interrupt è scattato… Ma scusa non l’ho scritto nell’articolo?! RB0 ha la funzione di interrupt, ma anche le porte RB4-RB7 se parliamo del 16F877A, il cui interrupt si abilita con RBIF e si controlla con RBIE, però un cambio di stato su qualsiasi delle porte RB4-RB7 ti fa sempre scattare l’interrupt per cui ti devi inventare tu un sistema, mediante flag globali, per capire su quale delle 4 porte è scattato.

    • #7 da Giovanni Bernardo il 23 settembre 2010

      I pic18 invece hanno due livelli di interrupt e si puo scegliere quale interrupt deve avere livello basso o livello alto e quindi avrai due routine di interrupt: interrupt di alto livello e di basso livello, oppure puoi averne una sola se scegli la modalità di interrupt compatibile coi pic16. I pic a 16 bit invece hanno 8 livelli di interrupt e una funzione di interrupt distinta per ogni evento e i pin che scatenano l’interrupt non si contano.

  4. #8 da Gela il 14 novembre 2010

    Ciao, ho un problema col timer1, non riesco a farlo partire. Uso il pic16F690 con quarzo 8MHz e il controller per il TMR1 è così settato (prescaler e preload servono per un interrupt ogni 50ms, li ho trovati con pictimer calculator):
    //T1CON
    // bit 0 -> TMR1ON: Timer1 On bit
    // 1 = Enables Timer1
    // 0 = Stops Timer1
    // bit 1 -> TMR1CS: Timer1 Clock Source Select bit
    // 1 = External clock from T1CKI pin (on the rising edge)
    // 0 = Internal clock (FOSC/4)
    // bit 2 -> T1SYNC: Timer1 External Clock Input Synchronization Control bit
    // TMR1CS = 1:
    // 1 = Do not synchronize external clock input
    // 0 = Synchronize external clock input
    // TMR1CS = 0:
    // This bit is ignored. Timer1 uses the internal clock
    // bit 3 -> T1OSCEN: LP Oscillator Enable Control bit
    // If INTOSC without CLKOUT oscillator is active:
    // 1 = LP oscillator is enabled for Timer1 clock
    // 0 = LP oscillator is off
    // bit5-4-> T1CKPS: Timer1 Input Clock Prescale Select bits
    // 11 = 1:8 Prescale Value
    // 10 = 1:4 Prescale Value
    // 01 = 1:2 Prescale Value
    // 00 = 1:1 Prescale Value
    // bit 6 -> TMR1GE Timer 1 Gate Enable bit
    // If TMR1ON = 0:
    // This bit is ignored
    // If TMR1ON = 1:
    // 1 = Timer1 counting is controlled by the Timer1 Gate function
    // 0 = Timer1 is always counting
    // bit 7 -> T1GINV: Timer1 Gate Invert bit
    // 1 = Timer1 gate is active high (Timer1 counts when Timer1 gate signal is high)
    // 0 = Timer1 gate is active low (Timer1 counts when gate is low)

    T1CON=0b00110010; //TMR1 off
    //preload del timer 1 -> 15536 serve per un interrupt ogni 50ms
    TMR1H=0b00111100;
    TMR1L=0b10110000;

    // INTCON
    // bit 0 -> RBIF PortB Interrupt Flag
    // bit 1 -> INTF RB0/INT Interrupt Flag
    // bit 2 -> T0IF Timer0 Interrupt Flag
    // bit 3 -> RABIE PortAB Interrupt Enable (off)
    // bit 4 -> INTE RA2/INT External Interrupt Enable bit (off)
    // bit 5 -> TMR0IE Timer0 Interrupt Enable (off)
    // bit 6 -> PEIE PEripheral Interrupt Enable (on)
    // bit 7 -> GIE Global Interrupt Enable (on)
    INTCON=0b11000000;

    //PIE1
    // bit 0 -> TMR1IE Timer 1 Interrupt Enable (on)
    // bit 1 -> TMR2IE Timer 2 Interrupt Enable (off)
    // bit 2 -> CCP1E CCP1E Interrupt Enable (off)
    // bit 3 -> SSPIE Sinconous Serial Port Interrupt Enable (off)
    // bit 4 -> TXIE EUSART Transmit Interrupt Enable (off)
    // bit 5 -> RCIE EUSART Receive Interrupt Enable (off)
    // bit 6 -> ADIE A/D converter Interrupt Enable(on)
    // bit 7 -> Unimplemented
    PIE1=0b00000001;

    //avvio Timer1
    TMR1ON=1;

    Poi nella ISR ho scritto: if (TMR1IF) { GIE=0; …; GIE=1}

    Quando invece uso il Timer0 con il suo settaggio tutto va, ma mi serve il Timer1 per poterlo mandare il sleep.
    Sai dirmi dove sbaglio?

    • #9 da Giovanni Bernardo il 14 novembre 2010

      Come hai impostato T1CON, Timer1 vuole il clock dall’esterno (bit1 -> TMR1CS, l’hai messo a 1). Stai usando una sorgente di clock esterna?

      • #10 da Gela il 14 novembre 2010

        si, uso un quarzo da 8MHz come clock, è attaccato ai pin 2 e 3, rispettivamente
        pin2 -> RA5/OSC1/T1CKI/CLKIN
        pin3 -> RA4/OSC2/T1G’/CLKOUT
        è qui che sbaglio?

        • #11 da Giovanni Bernardo il 14 novembre 2010

          Il clock del pic è una cosa e il clock da usare come sorgente per il timer ne è un’altra. Arriva a capire il senso di:

          Timer1 Clock Source Select bit
          1 = External clock from T1CKI pin (on the rising edge)
          0 = Internal clock (FOSC/4)

          Tu lo hai messo a 1, quindi il timer1 si aspetta un clock sul pin T1CKI, hai un clock esterno che alimenta il timer1 o hai solo il quarzo che fornisce il clock al pic?

          • #12 da Gela il 15 novembre 2010

            Ho solo il quarzo che da il clock al pic, nel datasheet non ho trovato alcuna specifica su quale sorgente usare, pensavo bastasse quella che usa normalmente il pic e che si arrangiasse lui a farla funzionare.

          • #13 da Giovanni Bernardo il 15 novembre 2010

            Allora devi mettere quel bit a zero e non a uno come hai fatto, in maniera tale che il timer1 utilizzi come sorgente di clock il clock primario.

          • #14 da Giovanni Bernardo il 15 novembre 2010

            Voglio solo farti capire il senso di quel bit:

            Timer1 Clock Source Select bit
            1 = External clock from T1CKI pin (on the rising edge)
            0 = Internal clock (FOSC/4)

            Cosa deve dire di più il datasheet? Tradotto in italiano quello significa:

            Bit di selezione per sorgente di Clock del timer1
            1 = Clock esterno sul pin T1CKI (sul fronte di salita)
            0 = clock interno (FOSC/4)

            Quindi come puoi vedere il timer1, il quale ha ovviamente bisogno di una sorgente di clock per incrementare il suo conteggio, ha la possibilità di sfruttare o il clock interno (mettendo quel bit a zero) ovvero il quarzo che normalmente usi per far funzionare il pic, oppure un clock esterno (un’onda quadra fornita dall’esterno) da applicare sul pin del pic che ha il contrassegno T1CKI.

            Tu hai messo l’impostazione per usare il clock esterno (hai messo quel bit a 1), per cui non avendo tu messo una sorgente di clock esterna sul pin T1CKI ecco che il timer1 non incrementa mai e quindi non ti funziona nulla.

            Se invece metti quel bit a zero allora il timer1 incrementerà sfruttando il ciclo di clock interno.

            Spero di essere stato abbastanza chiaro.

  5. #15 da Gela il 18 novembre 2010

    Scusa ma sono stato un po’ impegnato in questi giorni. Comunque SI, sei stato molto chiaro come in tutte le tue spiegazioni :)
    Avevo interpretato male il datasheet, pensavo che con “clock esterno” intendesse l’oscillatore al quarzo, visto fisicamente sta fuori dal pic.
    Ora il programma funziona.
    Grazie per avermi aiutato

  6. #16 da Claudio il 6 dicembre 2010

    Ciao Giovanni. Ho voluto provare l’interrupt non sul timer0 ma sull ingresso RB0/INT…però non funziona..eppure non sembra di aver sbagliato…ho dato un’occhiata al datasheet ed ho settato sia l OPTION e che INTCON…cosa sbaglio? Vorrei fare una donazione con la postapay…però mi chiede l indirizzo, il cap, numero di telefono…ma è normale? Grazie.

    #include
    #include “delay.c”
    __CONFIG(WDTDIS & XT & UNPROTECT & LVPDIS);
    #define XTAL 4000000 // crystal frequency – 4MHz

    void main()
    {
    TRISB = 0b11000001;
    //CMCON = 0b00000111;
    INTCON = 0b10001000;
    OPTION = 0b11000000;
    PORTB = 0b00001100;
    }

    void interrupt ISR (void)
    {
    if (INTF)
    {
    PORTB=0b00111110;
    }
    }

    • #17 da Giovanni Bernardo il 6 dicembre 2010

      L’interrupt su RB0/INT va attivato (INTIE=1) e va intercettato nell’ISR (if INTF=1), dopo che hai eseguito tutte le operazioni va resettato. L’interrupt si verifica al cambio di stato (da 0 a 1 e viceversa). Per la donazione, se non sei iscritto a paypal penso sia normale

  7. #18 da Claudio il 6 dicembre 2010

    Ciao Giovanni, ho provato a modificare come dici tu, ma non sò perchè non funziona. Il datasheet indica INTE e non INTIE quindi l’ho modificato…un’altra cosa non ho resettato il flag per vedere come si “comportava”…grazie

    #include
    #include “delay.c”
    __CONFIG(WDTDIS & XT & UNPROTECT & LVPDIS);
    #define XTAL 4000000 // crystal frequency – 4MHz

    void main()
    {
    TRISB=0b00000001;
    INTE=1;
    PORTB=0b00111000;
    }

    void interrupt ISR (void)
    {
    if (INTF=1)
    {
    PORTB=0b00000011;
    }
    }

  8. #19 da damiano il 6 gennaio 2011

    ciao giovanni
    ho dei problemi con l’interupt, dato che esso mi deve richiamare delle funzioni esterne a seconda della causa.
    tu suggerisci si settare dei flag per poi controllare questi sul main………ma come faccio a fare dei flag se l’interrupt è una funzione void interrupt (void), la quale non mi da ne risultati, ne posso inserire dati???

    seconda cosa……se faccio uso di puntatori, nel main e nelle funzioni..hitec-c, li riconosce…….da problemi???

    • #20 da Giovanni Bernardo il 6 gennaio 2011

      Nell’interrupt i flag li setti all’interno della funzione, ad esempio:
      void interrupt isr(void)
      {if (t0if) flag=1;}
      dove flag l’hai dichiarato al di fuori del main.
      I puntatori vengono ovviamente gestiti.

      • #21 da damiano il 6 gennaio 2011

        ancora non ho capito.
        mi puoi spiegare meglio come devo dichiarare il flag al di fuori del main
        e come poi lo intercetto???

        • #22 da Giovanni Bernardo il 7 gennaio 2011

          prima del main metti la variabile che vuoi usare come flag, così viene dichiarata globale, nel senso che puo essere vista anche dalla isr:

          bit flag;
          ….
          ….
          void main(void)
          {
          flag=0; // lo inizializzi a zero qui nel main, prima di entrare nel while(1)
          … ecc ecc

          il tipo bit non lo puoi inizializzare nella dichiarazione come le altre variabili, anzi ringraziamo che l’hitec-c ha questo tipo di variabile perchè il c18 non ce l’ha.

          quindi nel while(1) del main ti intercetti il settaggio del flag avvenuto ad opera dell’isr:

          void main(void)
          {
          flag=0;



          while(1)
          {…
          if (flag)
          {
          // eseguo le istruzioni che avrei eseguito nell’isr
          // alla fine devo rimettere il flag a zero
          flag=0;
          } // chiudo if
          }// chiudo while(1)
          }// chiudo main

          quindi nell’isr mi setto il flag in base all’interrupt:
          void isr(void)
          {
          if (t0if) // interrupt su timer0? Ovviamente qua intercetti l’interrupt che serve a te
          {
          flag=1; // setto il flag, questo viene intercettato nel main dove ho messo if (flag=1)


          t0if=0; // azzero il flag di avvenuto interrupt
          } // chiudo if t0if
          } // chiudo isr

          più chiaro di così non riesco

          • #23 da damiano il 7 gennaio 2011

            infatti è perfetto!
            ti ringrazio……..quindi usando l’hitec-c posso dichiarare tranquillamente variabili bit??
            QUANDO DICHIARO UN BIT…….BASTA SCRIVERE : bit flag;……non devo aggiungere altro???
            come se fosse un altra variabile!

          • #24 da Giovanni Bernardo il 7 gennaio 2011

            Si. L’unica differenza è che non puoi fare l’inizializzazione nel momento della dichiarazione. Cioè non puoi fare:

            bit flag=1;

            come per le normali variabili:

            char variabile=20;

  9. #25 da maurytheking il 10 maggio 2011

    Ciao giovanni.
    Ho dei problemi con l’interrupt, vorrei realizzare un contagiri e quindi utilizzo 2 interrupt uno è il timer che mi resetta il contatore ogni 100 ms e l’altro è RB0/INT che mi incrementa il contatore ogni volta che la porta sale a livello alto.
    il problema è che nel simulatore non va ovvero la variabile contatore viene incrementata ma mai resettata perchè sembra che l’interrupt del timer non venga eseguito.
    Io utilizzo il compilatore della MikroC quindi il sorgente è leggermente diverso da quello che hai :

    unsigned int Timer;
    unsigned int Count;
    unsigned int OldCount;
    unsigned int FondSc;
    bit Pr;

    void interrupt(void)
    {
    if (T0IF) // L’interrupt è stato causato da un overflow del timer0 ?
    {
    TMR0 = 225; // Reimposto Timer0
    Timer++; // Incremento il Timer per il lampeggio del led
    if (Timer >= 100) // Se il tempo è passato
    {
    OldCount = Count;
    OldCount = OldCount*600;
    Pr = 0;
    Count = 0;
    Timer=0; // Ricarico il timer del led per ricominciare daccapo
    }
    INTCON.T0IF=0; // Resetto il flag interrupt su timer 0,
    } // fine che interrupt verificatosi su timer0
    if(INTF){
    Count++;
    PORTA.F7 = ~PORTA.F7;
    INTCON.INTF = 0;
    }
    } // fine interrupt ser

    void main(){
    TRISA = 0;
    TRISB = 0×01;
    PORTA = 0×00;
    PORTB = 0×00;
    OPTION_REG = 0b11000100;
    INTCON=0b10110000;
    TMR0=225; // Imposto Timer0 a 100
    Count = 0;
    Pr = 1;
    FondSc = 19800;
    while(1){
    if(Pr==0){//Accensione dei led
    if(OldCount>=100){
    PORTA = 0×00;
    PORTB = 0×02;
    }
    if(OldCount>=(FondSc/10)){
    PORTA = 0×00;
    PORTB = 0×06;
    }
    if(OldCount>=((FondSc/10)*2)){
    PORTA = 0×00;
    PORTB = 0x0E;
    }
    if(OldCount>=((FondSc/10)*3)){
    PORTA = 0×00;
    PORTB = 0x1E;
    }
    if(OldCount>=((FondSc/10)*4)){
    PORTA = 0×00;
    PORTB = 0x3E;
    }
    if(OldCount>=((FondSc/10)*5)){
    PORTA = 0×00;
    PORTB = 0x7E;
    }
    if(OldCount>=((FondSc/10)*6)){
    PORTA = 0×00;
    PORTB = 0xFE;
    }
    if(OldCount>=((FondSc/10)*7)){
    PORTA = 0×01;
    PORTB = 0xFE;
    }
    if(OldCount>=((FondSc/10)*8)){
    PORTA = 0×03;
    PORTB = 0xFE;
    }
    if(OldCount>=((FondSc/10)*9)){
    PORTA = 0×07;
    PORTB = 0xFE;
    }
    Pr = 1;
    }
    }
    }

  10. #26 da Paolo2BB il 13 dicembre 2011

    Ciao Giovanni,
    Non è un discorso perfettamente attinente, ma non trovo dove postarlo! Adesso inizio qui, poi ridirigimi dove preferisci!
    Ho scelto un PIC 16F873A ed ho scritto un semplice firmware per accendere un led con una certa frequenza utilizzando il TIMER0.
    Una volta collegato l’oscillatore esterno da 4MHz sui pin 9 e 10, e configurato il timer
    setup_timer_0(RTCC_DIV_256|RTCC_EXT_H_TO_L);
    SET_TIMER0(100); e fatto partire da 100 per ottenere un interrupt ogni 10ms.

    #int_TIMER0
    TIMER0_isr()
    {
    {
    SET_TIMER0(TIMER_RELOAD); // reset TIMERO
    output_bit(PIN_C3, 1);
    output_bit(PIN_C3, 0);
    ms10++;
    if(ms10=100){

    ms10=0;
    secondi++;
    .
    .
    .

    Collegato il led sul pin14, ossia RC3 (pilotato dall’interno dell’interrupt TIMER0), purtroppo non funziona! Ovviamente ho abilitato anche gli interrupt:

    enable_interrupts(INT_TIMER0);
    enable_interrupts(GLOBAL);

    cerco di misurare con un oscilloscopio la frequenza di oscillazione sui pin 9 e 10 ma non leggo niente!!
    Suggerimenti?

  11. #27 da MAIELLO il 8 agosto 2012

    //*************************************************
    // PROVA INTERRUPT
    //
    // modulo: main.c
    // autore:
    // data: 07/08/2012
    // descrizione: lampeggia un led su RB0 tramite interrupt e suona un cicalino su RE1
    // picmicro: PIC16F886
    // clock: 20MHz
    //
    //*************************************************

    #define XTAL_FREQ 20MHZ // questo è utilizzato dalle routine di ritardo contenute in Delay.C
    #include // contiene i nomi mnemonici di registri e porte

    // Fuses di configurazione
    __CONFIG (HS & WDTDIS & PWRTEN & BORDIS & LVPDIS & DUNPROTECT & WDTEN & DEBUGDIS & UNPROTECT);

    #include “delay.h” // routine per ritardi
    #include “settings.h” // settaggi del picmicro

    // funzione principale, eseguita all’avvio del picmicro
    void main(void)
    {

    settings(); // eseguo la funzione settings contenuta nel file header settings.h, così imposto le porte e i registri
    int flag0=0;
    int flag1=0;
    int flag2=0;

    LED1=0; // all’avvio tutti i led devono essere spenti
    LED2=0;
    LED3=0;
    LED4=0;

    while(1) // eseguo un ciclo finito
    {
    {
    if ((!BTN1)&& (flag0==0))
    {
    DelayMs (100);
    if (!BTN1)
    {
    LED1=1;
    flag0=1;
    }
    if(!BTN2)
    {
    LED1=0;
    flag0=0;
    }
    }
    }

    DelayMs(200);
    LED4=1;
    DelayMs(200);
    LED4=0;

    }// Fine ciclo continuo

    } // Fine main

    void interrupt ISR (void)
    {
    if (T0IF) // L’interrupt è stato causato da un overflow del timer0
    {
    TMR0 = 100; // Reimposto Timer0
    TimerLed++; // Incremento il Timer per il lampeggio del led
    if (TimerLed >= TEMPOLED) // Se il tempo è passato
    {
    LED2=LED2^1; // Inverto lo stato del led per farlo lampeggiare
    TimerLed=0; // Ricarico il timer del led per ricominciare daccapo
    }
    T0IF=0; // Resetto il flag interrupt su timer 0,
    } // fine che interrupt verificatosi su timer0

    } // fine interrupt service routine

  12. #30 da zavvy il 4 marzo 2013

    Ciao Giovanni.
    Ho un forte dubbio.
    Ricevo sulla uart di un pic16f887 tramite un modulo xbee un dato molto velocemente.
    Ad ogni ricezione attivo un interrupt.
    Puo’ succedere che se l’interrupt si attiva troppo di frequente, tra una ricezione e l’altra il pic non ha il tempo necessario per eseguire almeno 1 volta tutto il main?
    Forse sono solo mie paranoie.
    Spero di essere stato chiaro.

    In pratica trasmetto un byte e subito dopo il suo complementare
    il ricevitore li confronta e se tutto è ok scrivo le funzioni del main se no butto via il dato.
    una sorta di CRC fatto in casa… anzi… in garage.

    Chiunque abbia idee migliori sono ben accette.

    Ciao
    grazie in anticipo.

Devi essere collegato per lasciare un commento.

settorezero.com e il logo Zroid™ ©2007÷2013 Giovanni Bernardo - E' vietata la copia e la distribuzione anche parziale dei contenuti di questo sito web senza l'esplicito consenso dell'autore.
I contenuti di settorezero.com sono distribuiti sotto una licenza Creative Commons Attribuzione-Non Commerciale-Non Opere derivate 2.5 Italia a cui vanno aggiunte le condizioni d'uso definite nel disclaimer.
settorezero.com e tutti i suoi contenuti sono tutelati dalla legge sul diritto d'autore per cui i trasgressori sono perseguibili a norma di legge.
Creative Commons BY-NC-ND 2.5
Il tema di questo sito è basato sul tema Fusion per wordpress, realizzato originariamente da digitalnature e fa uso del plugin Wassup per il computo delle statistiche. Per contattare l'autore siete pregati di utilizzare la sezione contatti.
Per essere aggiornato con tutte le novità di settorezero.com seguici anche anche su Facebook Twitter Tumblr Blogspot Youtube.