Newsgroup: it.hobby.elettronica.digitale
Titolo: Timers software,qual'è il modo giusto?

Salve
 
supponiamo di avere una CPU che,in modo non RTOS, deve svolgere alcuni 
compiti immediatamente in caso di alcuni eventi,e altri con priorità 
molto più bassa..supponiamo fare lampeggiare in modo indipendente 10 LEDs.
Riservando per altri compiti i Timers HW,che ormai, almeno in alcuni uC 
,ti tirano dietro(l'STM32F103RB che sto usando ne ha 15) supponiamo di 
usare ,per il lampeggio dei 10 LEDs dei contatori software.
 
Vorrei però evitare di usarli nel modo peggiore.
 
La cosa più alla buona ,più buttata lì, che potrei fare al volo sarebbe 
dichiarare i contatori come variabili globali,visibili sia nel file 
interrupt che in quello di utilizzo e utilizzerei il systick_timer
utilizzerei il systick_timer
//NB da prendere come pseudo codice ..scrivo di fretta per fare un 
//esempio,chiedo scusa per eventuali errori
 
*****in interrupts.c******
 
uint32_t counter_led0,counter_led1,counter_led1..counter_led9;
void SysTick_Handler(void)
{
	if(counter_led0)counter_led0--;
	if(counter_led1)counter_led1--;
	if(counter_led2)counter_led2--;
	.....
	if(counter_led9)counter_led9--;
}
 
***** in funzioni_varie.c  *****
extern uint32_t counter_led0,counter_led1,counter_led1..counter_led9;
.....
void LampeggioLeds(void)
{
	if(counter_led0==0)
         {
		if(LED0==OFF)
		{
			LED0=ON;
			counter_led0=TIME_ON_LED0;
		}
		else
		{
			LED0=OFF;
			counter_led0=TIME_OFF_LED0;
		}
 
	}
 
	if(counter_led0==0)
         {
	....//ecc ecc
}
 
in questo modo nel systick handler dovrei fare ogni volta 10 if,10 
decrementi ecc.Probabilmente un Cartex M3 che gira fino a 72 M_Hz 
potrebbe fare il tutto in meno di 1 us,ma in linea di principio non 
sembra bello.Alternative?Mi viene in mente..
 
 
uint32_t tick_per_tutti;
 
void SysTick_Handler(void)
{
	tick_per_tutti++;
}
 
void LampeggioLeds(void)
{
   statick uint32_t  tick_old;
 
   if(	tick_per_tutti!=tick_old)
   {
	if(counter_led0)counter_led0--;
	if(counter_led1)counter_led1--;
	if(counter_led2)counter_led2--;
 
   }
 
//e qui valuto e uso i counters
 
 
 
}
 
critiche o altre idee?Grazie
 
 
 
 

 
old_millis=0
Quando nel loop di arduino fai
if ((millis()-old_millis) > periodo ){
 
//codice da eseguire trascorso dato tempo
//Okkio dopo 49 gg
old_millis=millis();
}
 
 
 
  sostanzialmente fai cio', tu hai reinventato la funzione millis!!
 
 
Giulia
 
 
 
 

 critiche o altre idee?Grazie
 
Ma ogni quanto tempo scatta l'interrupt del systick? Perché se pensi ad 
1ms, come spesso si fa, quell'if lo devi eseguire troppo spesso.
 
A me piace un'altra tecnica, che però prevede un timer hw/sw a 64 bit. 
Perché a 64 bit? Perché anche contando ogni us, avresti un contatore che 
ti dura sicuramente più della durata tra un riavvio e l'altro.
Non ti devi preoccupare di wrap-around del contatore.
 
Quindi il tuo tick, unico per tutti, conta i us dall'accensione ed è un 
valore *assoluto* del tempo. Quindi puoi fare:
 
uint64_t tmr_led0;
tmr_led0 = ticks64() + MS_TO_TICKS(100); // set di un timer
if (ticks64() >= tmr_led0) // timer spirato
 
Il problema è che spesso non hai un contatore hw a 64 bit, ma puoi 
costruirlo facilmente con una variabile. In pratica fai correre un 
contatore hw a 32 bit ad 1us nell'ISR dell'evento di overflow, 
incrementi una variabile sw a 32 bit.
Il tuo tick a 64 bit sarà l'insieme del contatore sw + contatore hw.
 
volatile static uint32_t ticks_high = 0;
 
void TmrOverflow_Handler(void)
{
   ticks_high++;
}
 
void ticks64(void) {
   uint32_t ticks_low = TMR0_COUNTER();
   return ((uint64_t)ticks_high << 32) + ticks_low;
}
 
Chiaramente qui si nascondo una grossa insidia se l'interrupt scatta 
durante l'esecuzione di ticks64(). La funzione potrebbe tornare il nuovo 
ticks_high (incrementato di 1) accoppiato ad un vecchio ticks_low. 
Questo potrebbe portare i ticks ad avere una discontinuità molto pericolosa.
 
Tipicamente si risolve disattivando gli interrupt nella ticks64(), ma 
sono allergico a disattivare gli interrupt (oltre che al preemptive). 
Un'altra soluzione magica è:
 
void ticks64(void) {
   uint32_t h1 = ticks_high;
   uint32_t l1 = TMR0_COUNTER();
   uint32_t h2 = ticks_high;
   if (h1 == h2) return ((uint64_t)h2 << 32) + l1;
   else          return ((uint64_t)h2 << 32) + 0;
}
 
h1 e h2 sono diversi proprio se capita l'interrupt in mezzo. In questo 
caso è sicuro che c'è stato un overflow (wrap-around) del contatore hw 
proprio nella ticks64(). In questo caso usiamo 0 come parte bassa, in 
quanto molto probabilmente sarà ancora 0 (o magari 1, ma che importa?).

 //Okkio dopo 49 gg
 
La sottrazione intera unsigned millis()-old_millis non da nessun 
problema con i tempi a cavallo del ritorno a zero del contatore.
 
L'unico problema è che usando il solo contatore di millis non si puo` 
contare un tempo superiore a 2 alla 32 millisecondi (il che comunque non 
avrebbe molto senso visto l'errore di almeno cinque secondi all'ora di 
un risuonatore ceramico).

 quanto molto probabilmente sarà ancora 0 (o magari 1, ma che importa?).
 
sembra risolvere,grazie delle idee

  Quelli ad alta priorità da interrupt,hardware (es capture di impulsi)
 
forse mi sono espresso male
non so nemmeno se sia giusto chiamarli task,(si può dire anche in ambiente  
non RTOS?)
Diciamo che se ho un sensore on off che chiude di questo si deve prendere  
atto subito ed agire
senza passare attraverso timers software,e così per due impulsi"capturati"  
oltre un certo valore,o un valore ADC oltre il limite.
Mentre per i LED, per un certo tempo passo oltre,e quando è il momento  
agisco a riguardo,ma solo se altre cose a priorità più alta non me lo  
fanno rimandare,ma poichè queste ultime dovranno essere servite in fretta  
il lampeggio dei LED all'occhio apparirà regolare