Sida 2 av 4

Re: Räkna ut skillnad på två kompass-kurser? Assembler AVR

Postat: 8 februari 2012, 21:19:01
av Glattnos
Okej, nä egentligen har jag inget interrupt i koden ännu. Tänkte bara IFALL man skulle ha det. Men naturligtvis måste man ha koll på helheten för att veta vad som är bäst.

I princip så tänkte jag ha ett timer-avbrott som inträffar med kanske 1 sek intervall. Så gör jag beräkningarna där och justerar kursen. Jag kan även spara kursen varje sekund och lägga i en buffer, då finns det historik om hur kursen har ändrat sig senaste tiden tex 10 st kurser i buffern visar kursens förändring de senaste 10 sekunderna. Det är nog ett måste för att beräkna hur mycket styrning som behövs åt ena eller andra hållet.
Men det visar sig nog vad som blir bäst :D

Re: Räkna ut skillnad på två kompass-kurser? Assembler AVR

Postat: 9 februari 2012, 00:47:05
av jesse
Menar du att 'skillnad' är en 16-bitars variabel som du t.ex. ska läsa av i ett interrupt, och är då rädd att bara ena byten har blivit ändrad när interruptet sker? Sådant kan ju hända, och i AVR C skriver man i så fall så här:

Kod: Markera allt

cli(); // avaktivera tillfälligt alla interrupt
Skillnad = Kurs_bor - Kurs_ar;

if (Skillnad < -180) {
   Skillnad += 360;
} else if (Skillnad > 180) {
   Skillnad -= 360;
}
sei(); // aktivera interrupt
Om du inte vill låta interrupten vara avstängd så länge (under hela beräkningen) kan man göra så här:

Kod: Markera allt

temp = Kurs_bor - Kurs_ar;

if (temp < -180) {
   temp += 360;
} else if (temp > 180) {
   temp -= 360;
}
cli(); // avaktivera tillfälligt alla interrupt
skillnad = temp;
sei(); // aktivera interrupt
Variabeln 'skillnad' måste dessutom ha attributet 'volatile' för att man ska vara säker på att värdet verkligen lagras. Och då är det smartare att använda en lokal variabel 'temp' för beräkningarna och i slutet flytta över värdet till 'skillnad'. Annars blir det mycket onödigt skyfflande av data mellan processor-registren och SRAM.

(Jag hade ett liknande fall i en ATTiny som skulle läsa in värden från en ADC. Den fick ett interrupt när mastern (en annan AVR) ville ha värdet skickat via SPI. För att det skulle fungera garanterat riktigt var jag tvungen att göra som ovan.)

Re: Räkna ut skillnad på två kompass-kurser? Assembler AVR

Postat: 9 februari 2012, 06:29:55
av Swech
I princip så tänkte jag ha ett timer-avbrott som inträffar med kanske 1 sek intervall. Så gör jag beräkningarna där och justerar kursen.
Det är bättre att timerabrottet endast sätter en flagga att 1 sek har passerat.
Beräkningen utför du därefter i huvudloopen då flaggan är satt, samt avslutar
med att nollställa flaggan

Huvudprincipen är att interrupt skall utföra så lite som möjligt

Swech

Re: Räkna ut skillnad på två kompass-kurser? Assembler AVR

Postat: 9 februari 2012, 07:45:45
av labmaster
Swech har en poäng med sin synpunkt men är det inte tidkritiskt kan du implementera kursändringen i timerinterrupt. En sekund intervaller låter väldigt litet men du kanske har en båt som gör 40 - 50 knop.

Re: Räkna ut skillnad på två kompass-kurser? Assembler AVR

Postat: 9 februari 2012, 09:39:29
av Icecap
Alltså... jag förstår att det är data från en GPS och att de kommer med 1 sekunds intervall.

Dessa dekodas till verkliga värden och när den del är klart ligger dessa värden i minnet.
När de är konverterat passar du på att sätt en flagga! Denna flagga betyder "nu ska det räknas på detta" och uträkningen utförs. När den uträkning är avklarat nollar rutinen flaggan.

Då behövs ingen interrupt eller liknande, det hela kan ligga i main-loop och bara uppdatera just när nya värden är läst in.

Om din main-loop kan ta upp till 1 sekund eller mer att göra är det en del andra fel i den och hela strukturen ska göras om från grunden!

Själva styrdelen kan fint göras medelst interrupt men basen är att man inte kan vara säker på att GPS-data och interrupten passar överens! In interrupten kan man även ha en time-out som varna om det inte finns nya data, det kan vara så enkelt som att man för varje omvandling av GPS-data ställer en räknare till 2. I 1-sek-ISR'n räknar man sedan denna räknare ner ett steg om den är större än noll. Om den då når noll är det ingen nya GPS-data och lämplig uppgift ska då utföras (larm?).

Det låter som att hela programstrukturen är ganska osäker än så länge, det behöver inte vara ett stort problem om det är för att testa underrutiner och dylikt men är det såhär det ska fungera i verkliga livet får du problem!

Ursprungsfrågan om hur du får rätt värde när du jämför två kurser har inget med asm eller annat att göra, det är en ren matte-fråga. Kan du lösa den för alla värden på papper kan du göra det samma i ASM eller C eller vilket programmeringsspråk som helst egentligen.

Problemet med ASM i detta fall är att det snabbt blir ganska svårt att överskåda och med en kass struktur i programmet blir det rent av en katastrof.

Mycket ofta/alltid består ett µC-program av:
* Startkod. Kan vara att nollställa minnet eller liknande. I C är denna biten oftast innan själva C-programmet börjar.
* Sedan börjar själva programmet, i C med "void main(void)".
* Initiering av hårdvara utförs.
* Initiering av värden utförs.
* Sedan kommer den eviga loopen som oftast kallas "main-loop". I den kan de ske mycket eller lite, helt beroende på strukturen.

I många fall använder man interruptrutiner (ISR = Interrupt Service Routine) för att ta hand om saker, det kan vara timerinterrupt, serieportshändelser, AD-omvandlingar som blir klara osv. Alla dessa ISR ska vara snabba och effektiva! Inget väntande!

Main-loop bör vara snabb och effektiv! Om det väntas på ett villkor för att en sak ska hända kan man likaväl bara hoppa förbi den bit om saken inte är klar, sedan kan den ta hand om resten under tiden.

Jag brukar mäta main-loop tiden i mina projekt om jag kan och den har väl i ett extremfall varit upp i 200ms...

Vill man att en sak ska ske efter en viss tid och exakt timing inte är det viktigaste (tänk en display med "Välkommen, nu startar programmet" som ska visas i 5 sekunder -eller en lampa som ska blinka i sakta mak) kan man ha en timer-ISR som sker med lagom mellanrum. Jag har ofta 10Hz men andra värden kan användas.

Om man då har en variabel ("Delay") som timer-ISR'n använder på följande sätt:
if(Delay) Delay--; // På icke-C: om Delay>0 räkna ner Delay med en.

Då kan man annorstädes i koden skriva:
Show_Sign_On = true; // Placeras innan main-loop t.ex.

I main-loop kan man då ha en snudd:

Kod: Markera allt

if(Show_Sign_On)
  {
  Show_Sign_On = false;
  Do_The_Show_Sign_On(); // Skriv ut vad som ska skrivas till displayen som välkomsthälsning
  Delay = 50; // Ger 5 sekunders delay vid 10Hz
  }
Detta ger ju bara en start av visningen men resten ska ju inte stå still för det:

Kod: Markera allt

if(!Delay) // Betyder "om Delay inte är > 0"
  {
  <Skriv den normala uppdatering på displayen>
  }
Då kan hela main-loop köra full fart utan problem och är inte vissa saker klar kan man hoppa vidare fram till de är det. Såklart gäller detta många andra saker, det kan vara att man bara behöver reagera på en ändring an en ingång eller liknande, är den inte ändrat går programmet bara vidare.

Re: Räkna ut skillnad på två kompass-kurser? Assembler AVR

Postat: 9 februari 2012, 10:54:04
av Glattnos
jesse: Ja, det var det jag hade tänkt, att "Skillnad" är en global variabel. Då vill man ju inte att något annat ska hända förrän den verkligen är uppdaterad. Tack för tipsen! :D

Swech: Jo, det går ju att göra beräkningarna i huvudloopen och bara sätta flagga i interrupet. Det är nog smartare kanske :)

labmaster: Nja, båten är ganska långsam men det var det som var iden med en "historik-buffer", att man kan kolla om hur kursen har ändrats de senaste 10 sek(eller längre, får ju testa lite). Kanske är helt fel väg att gå men jag kan ju inte fråga om allt så ofta improviserar jag och ser hur det fungerar :D eller inte fungerar :shock:

Icecap: Tack för det informativa inlägget! Kursen kommer från en GY-26 kompass-sensor som jag för övrigt inte fått ännu, beställde på ebay så det kan nog dröjja ett tag. Det är vad jag har förstått en magnetfält-sensor så den uppdaterar nog värdet i alla fall några gånger i sekunden.

Kan verkligen en main-loop på detta bli över 1 sek lång? Är det i så fall för att man använder C, jag har märkt att det går åt mycket mer minne än när jag använder assembler så det kanske är viktigare att tänka tidskritiskt i C än i assembler.
I assembler känns det som att jag måste skriva 4 000 000 rader innan det tar en sekund :D

Re: Räkna ut skillnad på två kompass-kurser? Assembler AVR

Postat: 9 februari 2012, 11:07:52
av Icecap
Hur lång tid en main-loop beror på hur man programmerar den! Läser du lite runt i detta forum ser du att en del verkligen gillar att använda funktioner som Delay_ms() och liknande och då kan det bli hur trögt som helst.

Jag har aldrig haft någon som har tagit mer än 200ms och det projekt var ett extremfall, vanligen är runt 50ms eller snabbare.

Man använder bara mer minne i C om man t.ex. använder de färdiga printf-funktioner eller liknande. Det finns en del guider som kan ge minimala printf-funktioner och i vissa projekt där minnet är ett problem kan man kapa en hel del vid att skippa dessa rutiner och fixa själv.

Att just printf tar en del beror på att den per default kan skriva ut flyttal också och den del slukar en del programminne.

Så C är inte mer slösaktig än ASM - men programmören kan vara det!

Jag roade mig för ett tag sedan, någon skrev om att just använda en serieport-funktion i MikroC. Den var så praktisk då man kunde välja vilken pinnar som helst som UART RX & TX, varför den funktion inte fanns i ASM? Det visade sig snabbt att det var en soft-UART som helt skippade den inbyggda hårdvaran...

Dessa färdiga funktioner kan vara bra - men de kan även vara minnesslukande och dåligt optimerade och ibland rent av usla.

Re: Räkna ut skillnad på två kompass-kurser? Assembler AVR

Postat: 9 februari 2012, 11:48:04
av Nerre
Men mainloopen kanske ska köra nån form av användarinterface? Just den process som håller kursen vill man väl mer eller mindre köra som en bakgrundsprocess som alltid körs och då måste det ju vara vettigast att köra den med interrupt?

Re: Räkna ut skillnad på två kompass-kurser? Assembler AVR

Postat: 9 februari 2012, 12:03:08
av Icecap
Själva styrdelen bör definitivt köra i interrupten! I main-loop kör man okritiska dela.

Re: Räkna ut skillnad på två kompass-kurser? Assembler AVR

Postat: 9 februari 2012, 15:07:02
av jesse
Glattnos skrev:Kan verkligen en main-loop på detta bli över 1 sek lång? Är det i så fall för att man använder C, jag har märkt att det går åt mycket mer minne än när jag använder assembler så det kanske är viktigare att tänka tidskritiskt i C än i assembler.
I assembler känns det som att jag måste skriva 4 000 000 rader innan det tar en sekund :D
Nej, nej... det är omöjligt att det kommer i närheten av en sekund. Om det ens tar en millisekund, så har du skrivit en ofantligt klumpig kod! Kanske handlar det om några hundra klockcykler på sin höjd.

Så det var nog en missuppfattning.

Det som tar tid är ofta kommunikation (antal bytes per sekund) och väntan på att få svar utifrån etc... inte själva koden, om du inte håller på med astronomiska beräkningar... Om din GPS t.ex. sänder data en gång per sekund så måste du ju vänta...

Re: Räkna ut skillnad på två kompass-kurser? Assembler AVR

Postat: 9 februari 2012, 15:16:56
av sodjan
> I assembler känns det som att jag måste skriva 4 000 000 rader innan det tar en sekund

Det är oerhört ovanligt att varje kodrad bara körs en gång.

Re: Räkna ut skillnad på två kompass-kurser? Assembler AVR

Postat: 9 februari 2012, 15:26:16
av Glattnos
Icecap: Ja jag kollar funktionerna innan jag använder dom, fattar inte riktigt allt eftersom jag inte kan C så bra men just Delay_ms() blev jag livrädd för och använder nog bara i nödfall eller tillfälligt vid något test. Att funktionerna hanterar flyttal gör såklart att mycket minne äts upp, det gör det ju även i assembler om man börjar räkna med lite större tal.

Men ska man få till ett "stort" program i assembler så måste man ha tålamod och skriva mycket och/eller dålig kod(vet jag av erfarenhet :D ). Är nog lika i C fast man har fler minnes-slukande funktioner som måste hanteras varsamt.

jesse: Ja, jag tänkte väll att det var ungefär så :)

sodjan: Det "känns" så skrev jag :D Sen kan man ju köra på 20 Mhz och har man 4 000 000 rader så är det kanske troligt att några rader hoppas över varje varv också, eller nått :wink: Men jag säger inte emot, du har nog liiite mer erfarenhet än mig i detta :D

Re: Räkna ut skillnad på två kompass-kurser? Assembler AVR

Postat: 9 februari 2012, 15:29:53
av sodjan
Det jag menar är att det är vanligare att t.ex 40 rader körs 100.000 gånger.
Inte att man har 4.000.000 rader kod...

Re: Räkna ut skillnad på två kompass-kurser? Assembler AVR

Postat: 9 februari 2012, 15:42:31
av Glattnos
Ja, helt rätt! :D

Re: Räkna ut skillnad på två kompass-kurser? Assembler AVR

Postat: 11 februari 2012, 20:43:38
av NULL
Icecap skrev:Alltså... jag förstår att det är data från en GPS och att de kommer med 1 sekunds intervall.

Dessa dekodas till verkliga värden och när den del är klart ligger dessa värden i minnet.
När de är konverterat passar du på att sätt en flagga! Denna flagga betyder "nu ska det räknas på detta" och uträkningen utförs. När den uträkning är avklarat nollar rutinen flaggan.

Då behövs ingen interrupt eller liknande, det hela kan ligga i main-loop och bara uppdatera just när nya värden är läst in.

Om din main-loop kan ta upp till 1 sekund eller mer att göra är det en del andra fel i den och hela strukturen ska göras om från grunden!

Själva styrdelen kan fint göras medelst interrupt men basen är att man inte kan vara säker på att GPS-data och interrupten passar överens! In interrupten kan man även ha en time-out som varna om det inte finns nya data, det kan vara så enkelt som att man för varje omvandling av GPS-data ställer en räknare till 2. I 1-sek-ISR'n räknar man sedan denna räknare ner ett steg om den är större än noll. Om den då når noll är det ingen nya GPS-data och lämplig uppgift ska då utföras (larm?).

Det låter som att hela programstrukturen är ganska osäker än så länge, det behöver inte vara ett stort problem om det är för att testa underrutiner och dylikt men är det såhär det ska fungera i verkliga livet får du problem!

Ursprungsfrågan om hur du får rätt värde när du jämför två kurser har inget med asm eller annat att göra, det är en ren matte-fråga. Kan du lösa den för alla värden på papper kan du göra det samma i ASM eller C eller vilket programmeringsspråk som helst egentligen.

Problemet med ASM i detta fall är att det snabbt blir ganska svårt att överskåda och med en kass struktur i programmet blir det rent av en katastrof.

Mycket ofta/alltid består ett µC-program av:
* Startkod. Kan vara att nollställa minnet eller liknande. I C är denna biten oftast innan själva C-programmet börjar.
* Sedan börjar själva programmet, i C med "void main(void)".
* Initiering av hårdvara utförs.
* Initiering av värden utförs.
* Sedan kommer den eviga loopen som oftast kallas "main-loop". I den kan de ske mycket eller lite, helt beroende på strukturen.

I många fall använder man interruptrutiner (ISR = Interrupt Service Routine) för att ta hand om saker, det kan vara timerinterrupt, serieportshändelser, AD-omvandlingar som blir klara osv. Alla dessa ISR ska vara snabba och effektiva! Inget väntande!

Main-loop bör vara snabb och effektiv! Om det väntas på ett villkor för att en sak ska hända kan man likaväl bara hoppa förbi den bit om saken inte är klar, sedan kan den ta hand om resten under tiden.

Jag brukar mäta main-loop tiden i mina projekt om jag kan och den har väl i ett extremfall varit upp i 200ms...

Vill man att en sak ska ske efter en viss tid och exakt timing inte är det viktigaste (tänk en display med "Välkommen, nu startar programmet" som ska visas i 5 sekunder -eller en lampa som ska blinka i sakta mak) kan man ha en timer-ISR som sker med lagom mellanrum. Jag har ofta 10Hz men andra värden kan användas.

Om man då har en variabel ("Delay") som timer-ISR'n använder på följande sätt:
if(Delay) Delay--; // På icke-C: om Delay>0 räkna ner Delay med en.

Då kan man annorstädes i koden skriva:
Show_Sign_On = true; // Placeras innan main-loop t.ex.

I main-loop kan man då ha en snudd:

Kod: Markera allt

if(Show_Sign_On)
  {
  Show_Sign_On = false;
  Do_The_Show_Sign_On(); // Skriv ut vad som ska skrivas till displayen som välkomsthälsning
  Delay = 50; // Ger 5 sekunders delay vid 10Hz
  }
Detta ger ju bara en start av visningen men resten ska ju inte stå still för det:

Kod: Markera allt

if(!Delay) // Betyder "om Delay inte är > 0"
  {
  <Skriv den normala uppdatering på displayen>
  }
Då kan hela main-loop köra full fart utan problem och är inte vissa saker klar kan man hoppa vidare fram till de är det. Såklart gäller detta många andra saker, det kan vara att man bara behöver reagera på en ändring an en ingång eller liknande, är den inte ändrat går programmet bara vidare.
Det måste vara det bäst skrivna förklaringen jag har sett! :tumupp: :tumupp: :tumupp: