Smartare delay i C
Smartare delay i C
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
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.
Re: Smartare delay i C
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 *".
Re: Smartare delay i C
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.
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.
Re: Smartare delay i C
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!
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!
Re: Smartare delay i C
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.
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.
Re: Smartare delay i C
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
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
Re: Smartare delay i C
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.
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.
Re: Smartare delay i C
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.
Om vi nu antar att ticket är 1 mS, så kan man skriva så här:
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.
Så gör man en liten ändring i :
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++;
}
Kod: Markera allt
twait = tick + 10; // vänta 10 ms
while (tick < twait) {
gör_nåt_vettigt_ffs();
}
Kod: Markera allt
#define TIMER_PERIOD 0.475 // period i ms per timer overflow (interrupt)
#define TICKS_PER_MS(x) (x)/TIMER_PERIOD
Kod: Markera allt
twait = tick + TICKS_PER_MS(45); // vänta 45 ms
while (tick < twait) {
gör_nåt_vettigt_ffs();
}
Re: Smartare delay i C
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.
Re: Smartare delay i C
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.
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.
Re: Smartare delay i C
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.
Re: Smartare delay i C
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å.
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å.
Re: Smartare delay i C
@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.
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.
Re: Smartare delay i C
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.
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
}
Re: Smartare delay i C
Använd LCD:ns busyflagga istället för en delayfunktion.kimachren skrev:andra uppgifter när man t.ex. väntar på en lcd?