Smartare delay i C

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
kimachren
Inlägg: 104
Blev medlem: 15 september 2010, 21:27:18
Ort: Vasa

Smartare delay i C

Inlägg av kimachren »

Hej!

Hur gör man om man inte vill använda _delay_ms() och liknande, utan istället nyttja tiden till andra uppgifter när man t.ex. väntar på en lcd? Kan man på något vis spara programräknaren vid början av en delay, hoppa till main() och köra annan kod och sedan hoppa tillbaka när en viss tid har gått?
Jag använder AVR och skriver i c.

Tack!

c->C //adm
Senast redigerad av blueint 22 september 2010, 21:09:47, redigerad totalt 1 gång.
blueint
Inlägg: 23238
Blev medlem: 4 juli 2006, 19:26:11
Kontakt:

Re: Smartare delay i C

Inlägg av blueint »

Du skulle kunna starta en timer och kolla med jämna mellanrum om "tiden är inne". Eller rakt av räkna ut hur många cyckler en deterministisk programsnutt tar. Ett annat sätt är att generera ett avbrott när det är "dags" precis som select() med satt "struct timeval *".
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: Smartare delay i C

Inlägg av sodjan »

Det generella sättet att hantera "timing" i mikrokontrollers är just via "timers".
Läs på hur de fungerar i din processor och börja använda dom.
Det framgår inte vilka behov du har, men normnalt sätter man upp
ett timer-interrupt där man gör det som ska göras regelbundet.
Användarvisningsbild
Icecap
Inlägg: 26658
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Smartare delay i C

Inlägg av Icecap »

När man skriver till LCD är delayen inte i ms-klassen utom vid "clear all" som är 1,42ms men som sodjan skriver:
Man startar en timer och utför jobben i interrupten.

Viktigt är att interrupt rutinen (ISR) är snabb och effektiv, inget väntande i den!
kimachren
Inlägg: 104
Blev medlem: 15 september 2010, 21:27:18
Ort: Vasa

Re: Smartare delay i C

Inlägg av kimachren »

Jag vet vad timer interrupts är och hur man använder dem.
Det jag är ute efter är ett sätt att slippa ha CPU:n att stå och tugga i onödan när den istället kunde göra annat, eller sova.
Skulle det fungera att, när man kommer till en "delay" i en funktion, spara ner programräknarens värde i en variabel, ställa en timer för avbrott, hoppa till main() alt. "sleep" och vänta på avbrottet? I ISR skulle man återställa programräknaren för att fortsätta i samma funktion som tidigare. Hur skulle det fungera med alla registren? Jag har inte riktigt hundra koll på hur de fungerar.
Användarvisningsbild
jesper
Inlägg: 722
Blev medlem: 12 juni 2006, 16:04:08
Ort: Laem Mae Phim, Thailand

Re: Smartare delay i C

Inlägg av jesper »

Vad du vill ha är i princip ett preemptivt OS. Det kan bli lite komplext.
Om du "bara" vill göra annat under just delayer, gör som blueint föreslår och använd timeouts istället.


set_timer (vänttid)

loop:
om (tiden_är_ute)
fortsätt_med_whatever
annars
gör_något_vettigt_under_tiden
Användarvisningsbild
Icecap
Inlägg: 26658
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Smartare delay i C

Inlägg av Icecap »

När jag gör program har jag nästan alltid en "delay"-grej.

Den fungerar så att jag har en Delay_Timer i lämplig storlek (8/16 bit efter behov) och i timer ISR'n skriver jag då:
if(Delay_Timer) Delay_Timer--; // Ofta med 10Hz eller så

Sedan kan jag skriva i main:
bla bla Delay_Timer = 100;
if(!Delay_Timer)
{
}

På detta vis kan man starta något som innebär ett delay och låta mainloop köra full spett fram till tiden har gått ut varefter programmet gör vad som ska göras.

Man måste ha klart för sig att själva delay-tiden är mellan 0-1 klick kortare än man skriver in, ska man ha exakta delay är detta inte sättet men för att visa t.ex. en sign-on i 3 sekunder duger sättet strålande.

I många projekt har jag fler av dessa "Delay_Timer" (med olika namn då) så att jag kan låta olika saker ligga o vänta till det är dags.
Användarvisningsbild
jesper
Inlägg: 722
Blev medlem: 12 juni 2006, 16:04:08
Ort: Laem Mae Phim, Thailand

Re: Smartare delay i C

Inlägg av jesper »

Jag har ofta gjort som du, Icecap, men problemet är att man får så många variablar som måste uppdateras i interruptfunktionen.
En bättre variant, tycker jag, är att ha en global 'ticker' som incrementeras med lämpligt step i timerinterrupt funktionen.

Kod: Markera allt

volatile uint32_t tick;

void timer_interrupt(void) {
  tick++;
}
Om vi nu antar att ticket är 1 mS, så kan man skriva så här:

Kod: Markera allt

  twait = tick + 10; // vänta 10 ms
  while (tick < twait) {
    gör_nåt_vettigt_ffs();
  }
Har man inte en "snygg" period per timeroverflow (vilket man ju oftast inte har), då kan man antigen korrigera incrementet i timerinterruptet, vilket inte alltid blir så snyggt, eller kompensera offsetvärdet vid anropet.

Kod: Markera allt

#define TIMER_PERIOD       0.475   // period i ms per timer overflow (interrupt)
#define TICKS_PER_MS(x)   (x)/TIMER_PERIOD
Så gör man en liten ändring i :

Kod: Markera allt

  twait = tick + TICKS_PER_MS(45); // vänta 45 ms
  while (tick < twait) {
    gör_nåt_vettigt_ffs();
  }
blueint
Inlägg: 23238
Blev medlem: 4 juli 2006, 19:26:11
Kontakt:

Re: Smartare delay i C

Inlägg av blueint »

Låter som det enklaste är en timerinterrupt som går "lagom ofta". Sedan slänger man in instruktionerna till LCD:n i en kö så kan interruptrutinen utföra dom efter behag.
bearing
Inlägg: 11676
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Re: Smartare delay i C

Inlägg av bearing »

Jag har gång gjort som blueint föreslår. Jag hade en array som användes som FIFO. Den fylldes med LCD-instruktioner av huvudprogrammet, och tömdes av ett interrupt.

Dock tyckte jag i efterhand att det i mitt fall inte var värt besväret. Huvudprogrammet gjorde inte något speciellt intensivt, så det kunde gått väntat på LCD-rutinerna. Resultatet av att använda FIFO var att displayen (2x16 tecken) hade möjlighet att uppdateras i rasande fart, men den farten hade ögat inte hunnit uppfatta.
kimachren
Inlägg: 104
Blev medlem: 15 september 2010, 21:27:18
Ort: Vasa

Re: Smartare delay i C

Inlägg av kimachren »

Om man gör som jesper skriver kunde man ju fast slänga in en pekare till en lista med funktioner som cpu:n kan köra under tiden, som en slags att-göra lista.
Användarvisningsbild
Icecap
Inlägg: 26658
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Smartare delay i C

Inlägg av Icecap »

jesper: Jag förstår ditt exempel och gillar det inte. "Ditt sätt" medger INTE att main-loop kan rulla på utan problem vilket är vad jag vill ha, det använder även en del ork på att jämföra 32 bit värden med varandra medan "mitt sätt" kan nöja sig med att ladda en variabel o kolla Z-flaggan.

Men samtidig är det 2 helt olika saker som dessa funktioner används till, alltså är varken den ena eller andra bäst eller fel, de används helt enkelt till olika saker.

Dock gillar jag inte att rutinen inte medger att main kan loopa på.
Användarvisningsbild
jesper
Inlägg: 722
Blev medlem: 12 juni 2006, 16:04:08
Ort: Laem Mae Phim, Thailand

Re: Smartare delay i C

Inlägg av jesper »

@kimachren:
Det är en sån jag normalt användar nuförtiden (om inte jag bara har helt simpla behov). Ska plocka fram och förtydliga koden lite och lägga in den här i tråden bara för dokumentationens skull.

@IceCap:
Varför skulle inte mainloop kunna rulla på? Det är ju i princip samma sak som du gör, fast variablen är lokal.
Du kan kolla med if lika bra som med while, det var bara ett exempel.
Att det är en 32-bits variabel tar visserligen lite mera tid, men åt andra sidan är det bara EN variabel som ska incrementerase i interruptet, inte kanske 10 stk (som kanske inte ens är aktiva) som måste testas och eventuellt incrementeras. De två eller tre assembler instruktioner mer för 32 vs 16 bit kommer nog inte att märkas i alla fall.

Däremot kan jag hålla med om att det kanske inte är optimalt i alla situationer, inte med någon av dessa lösningar.
I vissa fall får man ta till andra metoder.
Det är dock oerhört sällan, tycker jag, i så fall håller man på med ultra tidskritiska saker och borde kanske redan bytt till assembler.
Användarvisningsbild
jesper
Inlägg: 722
Blev medlem: 12 juni 2006, 16:04:08
Ort: Laem Mae Phim, Thailand

Re: Smartare delay i C

Inlägg av jesper »

Här är en lite städat version av koden för yamppPod'en där jag användar en lista med timeouts.
Jag tog bara med det essentiella.
I yamppPod'en finns dessutom ett event-hanteringssystem där dessa timeouts kan köa upp events.
Detta för att flytta ut hanteringen från interruptnivå. Har man småsaker som ska (och kan) göras på interruptnivå kan man använda call-back funktionerna istället.
Har man ingen event-hanterare kan man sätta flaggor istället och testa på dem, fast då försvinner lite av automagin.

Kod: Markera allt

/** Number of simultaneous timeouts allowed
*/
#define NUM_TIMEOUTS	10

/** Structure for timeout handling
*/
typedef struct timeouts_t
{
	uint32_t 	timeout;	//!< Timeout value
	uint32_t 	reload;		//!< Original timeout requested
	uint8_t 	inuse;		//!< Indicates if this slot is used
	timeout_e 	type;		//!< Type of timeout 
	PFUNC 		pfunc;		//!< Pointer to callback function
	event_e		event;		//!< Event to send on timeout 
} timeouts_t;

/** Array for storing timeout calls
*/
volatile timeouts_t timeouts[NUM_TIMEOUTS];


/** Global counter variable for timeouts.
*/
volatile uint32_t timeval;             /* Current Time Tick */

/** Timer Interrupt Handler.
 */
void  NAKEDFUNC system_int(void)         /* System Interrupt Handler */
{
	uint8_t i;

	timeval++; 
	
	// check slots for timeouts
	for (i=0;i<NUM_TIMEOUTS;i++)
	{
		if (timeouts[i].inuse && (timeouts[i].timeout == timeval))	// if we have a timeout
		{
			if (timeouts[i].pfunc)					// if the callback pointer is valid
				timeouts[i].pfunc();				// call the callback function

			if (timeouts[i].event != EV_IDLE)
				set_event(timeouts[i].event);		// queue the event

			if (timeouts[i].type == ONE_SHOT)		// if this was a one time operation		
				timeouts[i].inuse = 0;				// clear the in-use flag
			else
				timeouts[i].timeout = timeouts[i].reload + timeval;	// otherwise reload for a new round
		}
	}
}


/** Set a timeout.
	\todo need detailed description
	\param tmout Timeout value (in 1 milisecond steps) to set
	\param callback Pointer to callback function, with will be called when the timeout triggers.
	\param event The event code to queue on timeout.
	\param tmtype Type of callback, one-shot, repetitive, see timeout_e enumerated type.
	\return Timeout index value that can be used to cancel the timeout
*/
uint8_t timer_set_timeout(uint32_t tmout, PFUNC callback, event_e event, timeout_e tmtype)
{
	uint8_t i; 

	// find a free slot
	for (i=0;i<NUM_TIMEOUTS;i++)
	{
		if (timeouts[i].inuse == 0)					// if this is a free slot
		{
			timeouts[i].reload = tmout;				// store the timeout for reloads
			timeouts[i].timeout = tmout + timeval;	// setup current timeout
			timeouts[i].pfunc = callback;			// set pointer to callback
			timeouts[i].event = event;				// set event code
			timeouts[i].type = tmtype;				// store the type
			timeouts[i].inuse = 1;					// flag the slot as in-use
			return i+1;			  					// -bugfix- return the slot number 1..10
		}
	}
	return 0;	// no free slots, indicate failure.
}

/** Cancel a previously set timeout
	\param tmoutkey Value previously retuned from set_timeout
*/
void timer_cancel_timeout(uint8_t tmoutkey)
{
	if(tmoutkey)
		timeouts[tmoutkey-1].inuse = 0;		// clear the in-use flag
}
bos
Inlägg: 2314
Blev medlem: 24 februari 2007, 23:29:15
Kontakt:

Re: Smartare delay i C

Inlägg av bos »

kimachren skrev:andra uppgifter när man t.ex. väntar på en lcd?
Använd LCD:ns busyflagga istället för en delayfunktion.
Skriv svar