Domanda:
`Millis ()` è influenzato da ISR lunghi?
Anonymous Penguin
2014-09-29 07:49:44 UTC
view on stackexchange narkive permalink

Ho un progetto che utilizza spesso timer e interruzioni. Viene speso molto tempo della CPU per la gestione degli ISR ​​per un periodo di tempo prolungato.

Ciò influirebbe sul codice all'interno del ciclo principale che si basa sulla funzione millis () poiché una quantità significativa di tempo viene impiegata per elaborare gli ISR ?

Inoltre, qualsiasi cosa basata su millis () fallirà? Penso che la libreria servo (nuova basata su software) e forse anche il software seriale (?) Possano anche avere problemi di temporizzazione? Anche se non sono sicuro che siano entrambi basati su questo.
Tre risposte:
Anonymous Penguin
2014-09-29 07:49:44 UTC
view on stackexchange narkive permalink

Dopo aver scavato un po 'nel core, sembra che Arduino aggiorni millis () con un timer a 8 bit: usa overflow con un valore di prescaler di 64. In termini più semplici, lo ha impostato in modo che un determinato pezzo di codice (l'ISR) venga eseguito circa una volta al millisecondo su un sistema a 16 MHz (e proporzionalmente meno frequentemente su sistemi con clock più lento).

Quando viene eseguito quell'ISR, incrementa una variabile che viene utilizzato dalla funzione millis () .

Quindi, anche se del codice non interrompibile viene eseguito per una frazione di millisecondo, non avrà importanza dal momento che il bit perché quel vettore di interrupt rimarrà ancora impostato.

Tuttavia, c'è un problema quando l'ISR viene eseguito meno spesso di quanto dovrebbe essere attivato. Questo può essere il risultato dell'utilizzo di noInterrupts () per un periodo di tempo prolungato o quando vengono eseguiti troppi altri interrupt e non c'è abbastanza CPU per tutte le attività. Se molti altri ISR ​​hanno priorità di interrupt più alte, è possibile che il codice per millis () non venga mai eseguito. In questo caso, avresti problemi più seri di questo, ma è qualcosa da tenere a mente quando scrivi il codice.

La regola pratica generale degli ISR ​​si applica ancora: mantienila breve !!!

Nota: se hai bisogno di estrema precisione, la funzione incorporata non è la scelta migliore. Un RTC è una buona idea per mantenere il tempo a lungo termine.

Penso che tu possa avere interruzioni, interrompendo altre routine di interruzioni (chiamando `sei ()` all'interno di `ISR`). Ma è un pendio scivoloso. Per esempio. gli overflow di memoria possono verificarsi molto più velocemente.
`micros ()` sembra avere lo stesso problema, e nel mio test ha dato lo stesso momento sbagliato di `millis ()`.
La questione non si limita agli ISR. La [funzione write ()] (https://github.com/adafruit/Adafruit_TLC59711/blob/master/Adafruit_TLC59711.cpp#L63) nella libreria TLC59711 di Adafruit disabilita gli interrupt per forse molti millisecondi, causando il mio tentativo iniziale di cronometrare questa funzione utilizzando `micros ()` fallire.
@UlrichStern, Tehee, adafruit ti fa pensare che la loro libreria sia super veloce, mettendo in pausa il tuo "benchmark temporale"; D
@Paul, durante il cronometraggio della libreria di Adafruit, il valore di ritorno di `micros ()` aumentava ancora abbastanza che il runtime non era ovviamente sbagliato e sono stato ingannato per un po '. :) (Adafruit non ha disabilitato gli interrupt per ingannarmi, credo, vedere il [Wiki] della mia libreria (https://github.com/ulrichstern/Tlc59711/wiki#how-does-the-tlc59711-know-when-a- trasferimento-è-fatto).)
Nick Gammon
2016-01-18 11:40:32 UTC
view on stackexchange narkive permalink

Ciò influirebbe sul codice all'interno del ciclo principale che si basa sulla funzione millis () poiché viene spesa una quantità significativa di tempo per elaborare gli ISR?

Per fare una cifra it for you ...


Frequenza del timer 0 overflow ISR che viene chiamato

Viene utilizzato il codice chiamato dal (predefinito) Timer 0 overflow interrupt vector (TIM0_OVF_vect) di millis e micros per restituire i risultati. Il suo scopo è contare gli overflow del Timer 0.

Per ottenere risultati accurati, questo ISR non deve perdere un overflow. Il timer è configurato per spuntare ogni 4 µs su un sistema a 16 MHz (a causa del prescaler di 64: 64 * 62,5 ns = 4000 ns ) e overflow ogni 1.024 ms (1024 µs) - perché va in overflow dopo 256 tick ( 4 µs * 256 = 1024 µs ).

Poiché c'è un solo flag di overflow, se l'ISR perde un overflow, quindi sia millisecondi che micro usciranno di 1.024 ms (o più, se manca più overflow).

Per essere certi di catturare tale overflow, l'ISR deve quindi essere chiamato all'interno 1.024 ms (probabilmente leggermente inferiore a causa del tempo impiegato per inserire l'ISR, quindi diciamo: 1 ms).


Priorità interrupt

Su Atmega328P (come usato in Arduino Uno) queste sono le priorità del vettore di interrupt:

  1 Reset 2 Richiesta di interruzione esterna 0 (pin D2) (INT0_vect) 3 Richiesta di interruzione esterna 1 (pin D3) (INT1_vect) 4 Cambio pin Richiesta di interrupt 0 (pin da D8 a D13) (PCINT0_vect) 5 Pin Change Inte richiesta interrotta 1 (pin da A0 ad A5) (PCINT1_vect) 6 Pin Change Interrupt Request 2 (pin da D0 a D7) (PCINT2_vect) 7 Watchdog Time-out Interrupt (WDT_vect) 8 Timer / Counter2 Compare Match A (TIMER2_COMPA_vect) 9 Timer / Counter2 Confronta partita B (TIMER2_COMPB_vect) 10 Overflow timer / contatore2 (TIMER2_OVF_vect)
11 Evento acquisizione timer / contatore1 (TIMER1_CAPT_vect) 12 Confronto timer / contatore1 Match A (TIMER1_COMPA_vect) 13 Confronto timer / contatore1 Match B (TIMER1_COMPB_vect) 14 Overflow timer / contatore1 (TIMER1_OVF_vect) 15 Confronto timer / contatore0 Match A (TIMER0_COMPA_vect) Counter0 Compare Match B (TIMER0_COMPB_vect) 17 Timer / Counter0 Overflow (TIMER0_OVF_vect) 18 SPI Serial Transfer Complete (SPI_STC_vect) 19 USART Rx Complete (USART_RX_vect) 20 USART, Data Register vuoto (USART_UDRE_vect) 21 USART, Tx Complete_vect (USART 22TX Conversion) Completo (ADC_vect) 23 EEPROM pronto (EE_READY_vect) 24 Comparatore analogico (ANALOG_COMP_vect) 25 Interfaccia seriale a 2 fili (I2C) (TWI_vect) 26 Store Program Memory Ready (SPM_READY_vect)  

Puoi vedere da quell'elenco che TIMER0_OVF_vect è il numero 17 in quell'elenco, quindi qualsiasi interrupt di priorità precedente avrebbe la precedenza, ad esempio interrupt esterni , interrompe il cambio pin, gli altri timer (ma non SPI / Serial / ADC / I2C).

Se un overflow fosse appena avvenuto , avresti praticamente 2 ms di grazia ( perché hai 1 ms prima del successivo e poi un altro 1 ms prima di doverlo notare). Tuttavia, se l'overflow sta per verificarsi , hai solo il periodo di grazia di 1 ms.

Lo dico perché se hai un evento di interrupt esterno 0 (INT0_vect) e l'ISR richiede 500 µs, quindi l'interrupt esterno 1 evento (INT1_vect) durante quel periodo (quindi verrà servito un altro ISR), quindi l'interrupt del timer potrebbe essere bloccato per un po '.

Ecco perché tutti gli ISR ​​dovrebbero essere brevi. Non è abbastanza buono che alcuni di loro lo siano.


Riattivazione degli interrupt

Raccomando vivamente di contro questo. Le librerie non sono progettate per essere rientranti e una volta che inizi ad abilitare gli interrupt in un ISR potresti scoprire che essa stessa viene richiamata di nuovo quando è a metà della chiamata per la prima volta. Si può anche plausibilmente interrompere una funzione di libreria (es. Memcpy) che non è stata progettata per essa.

E, naturalmente, se stai riattivando gli interrupt all'interno di un ISR perché l'ISR richiede molto tempo: beh, questa è la situazione esatta in cui potresti attivare questo re -entrancy.


Ulteriori informazioni:

mpflaga
2014-09-29 18:39:58 UTC
view on stackexchange narkive permalink

ASSOLUTAMENTE

qualsiasi ISR ​​lungo impedirà a millis () di aumentare. Può essere dubbiamente inclinazione lenta di questo orologio.

La funzione ISR inizia sempre con interrupt di blocco con il comando cli () all'inizio e finisce abilitandoli con il comando sei (). Bloccare altri ISR ​​insieme all'interruzione OverFlow di Timer0 che viene utilizzata per aumentare il contatore dei millisecondi.

Qualsiasi uso esteso di noInterrupts () o cli () o l'uso implicito con gli ISR ​​dovrebbe essere attentamente di breve durata. Non vi è alcun danno generale nell'esecuzione di interrupts () o sei () con nell'ISR, in quanto consentire nuovamente gli interrupt.

Esempio:

nella libreria VS1053 I have Data Richiesta su un pin INT. Quale ISR può essere letto dalla SdCard, il che può richiedere molto tempo. Quindi riattivo gli interrupt () prima di leggere la SdCard, ma ancora nell'ISR.

Nel tuo esempio il ciclo principale non viene mai ripreso durante quella lunga routine di interruzione, giusto? Quindi questo è uno svantaggio. Buono a sapersi abilitare gli interrupt all'interno di altri interrupt non è così "pericoloso" come pensavo.
Penso che solo un ISR (o una catena di ISR) più lungo di un millisecondo impedirà che si interrompa. Puoi bloccarlo per 0,5 ms e si aggiornerà come la prossima cosa da fare (interruzione).


Questa domanda e risposta è stata tradotta automaticamente dalla lingua inglese. Il contenuto originale è disponibile su stackexchange, che ringraziamo per la licenza cc by-sa 3.0 con cui è distribuito.
Loading...