Räkna division med int utan förluster

Avdelningen för programmering, nätverk samt alternativa OS.
Post Reply
User avatar
tkh
Hedersbit
Posts: 3901
Joined: 2002-03-08 0:41:22
Location: Landskrona (Umeå)
Contact:

Räkna division med int utan förluster

Post by tkh »

Sitter och arbetar med samma hårdvara som tidigare (Atmel AVR), så alla superfunktioner i vanliga C finns inte.

Temperatur fås från en digital tempgivare, den ger ut en puls med frekvensen 1-4 MHz (varierar med temperatur efter okänd formel). D.C. (Duty Cycle) är den procentuella tid som pulsen är hög - vilken stiger med tempen. Det jag behöver gör är att räkna ut temperaturen i följande formel:

Code: Select all

    D.C. - 0,32
T = -----------
      0,0047

Där D.C. fås av:
          Th
D.C. = ---------
        Th + Tl
Den kod som jag skrivit just nu ger en lite för hög felmarginal enligt mig, till exempel så hoppar den nästan två grader åt gången.

Problemet ligger i att jag inte kan (vet hur man gör för att) räkna med decimaler utan att få overflow. Att gå över till long gör jag ogärna eftersom de tar äckligt mycket plats.

Som den är skriven nu är det bara fuljobb med att höja känsligheten genom att multiplicera med lämplig tiopotens där det ryms, men jag får ju inte kliva över 65536 såklart. Talen jag har ligger inte så högt, men tyvärr vet jag inte exakt. Kan försöka ta reda på detta tills senare inlägg.

Skulle kunna sitta en vecka med det här själv, men eftersom det fungerar något sånär och det finns nog att göra ändå så ber jag er om hjälp så jag kanske hinner med något annat också.
"Just because you are unique does not mean you are useful."
User avatar
IcePic
Hedersbit
Posts: 6061
Joined: 2002-03-08 16:09:38

Post by IcePic »

Generella klassikern är ju att shifta värdena ett par steg uppåt (dvs *2, *4, *8 osv per shift)
och sen räkna på det, och precis innan du presenterar datat så shiftar du ner
det igen lika mycket, så får du fixed.point-decimaler.
Oh give me a clone, my very own clone,
with the Y chromosome changed to X!
And since she's my own, of my own flesh and bone,
she'll be thinking of nothing but sex!
Pimp
Avstängd
Posts: 1
Joined: 2003-11-04 19:45:54

Post by Pimp »

Haha. Fixedpoint på en mikrokontroller. Sure.

Din beskrivning av problemdomänet är väldigt bristfällig och jag råder dig att uppdatera den med utförlig information som undviker att sätta dig i position där en potentiell lösning enbart kan leveras av någon som har direkt erfarenhet av mikrokontrollern du nämner.

Nu till ditt problem.

1. Om du studerar dina ekvationer lite närmare ser du nästan alltihop kan beräknas i förväg. Om jag har förstått det rätt så läser du direkt från en ADC, eller hur? Om så är fallet bör du åtminstone ha två steg i implementation:

(a) Avgör om sensorn är tillförlitig
(b) Beräkna ett medelvärde från ADC så att vi får ett stabiliserat värde

2. Ställ upp en array med n-element där n anger hur pass nogrann din "temperaturtabell" skall vara. Om jag vidare antar att detta är enkel labb som utförs inomhus kan du begränsa definitionsområdet till 15-30 grader. Nu måste du (utan min hjälp) beräkna i förväg vilket värde elementen i array motsvarar när du använder (ett stabiliserat) ADC värde som index till denna array. Alltså shifta ADC-värde ett par steg etc...

Den bästa optimeringen är att undvika behöva optimera.
User avatar
fisk
Posts: 7485
Joined: 2003-03-18 21:09:30
Location: Örebro / Sverige
Contact:

Post by fisk »

Bobby - är det inte ganska lamt att skapa en ny användare då man redan vet att man är portad? Din sorti var full av "jag behöver inte vara här"-ynk, så det här blir en smula patetiskt.

Är det inte Bobby får jag väl be så mycket om ursäkt - men det är så fånigt likt så det är osannolikt.
Aotearoa - tino rangatiratanga
User avatar
IcePic
Hedersbit
Posts: 6061
Joined: 2002-03-08 16:09:38

Post by IcePic »

Pimp wrote:Haha. Fixedpoint på en mikrokontroller. Sure.
Så vad var problemet med att shifta värdena ett par steg på en mikrokontroller då?
Oh give me a clone, my very own clone,
with the Y chromosome changed to X!
And since she's my own, of my own flesh and bone,
she'll be thinking of nothing but sex!
User avatar
tkh
Hedersbit
Posts: 3901
Joined: 2002-03-08 0:41:22
Location: Landskrona (Umeå)
Contact:

Post by tkh »

Pimp wrote:Din beskrivning av problemdomänet är väldigt bristfällig och jag råder dig att uppdatera den med utförlig information som undviker att sätta dig i position där en potentiell lösning enbart kan leveras av någon som har direkt erfarenhet av mikrokontrollern du nämner.

Nu till ditt problem.

1. Om du studerar dina ekvationer lite närmare ser du nästan alltihop kan beräknas i förväg. Om jag har förstått det rätt så läser du direkt från en ADC, eller hur? Om så är fallet bör du åtminstone ha två steg i implementation:

(a) Avgör om sensorn är tillförlitig
(b) Beräkna ett medelvärde från ADC så att vi får ett stabiliserat värde

2. Ställ upp en array med n-element där n anger hur pass nogrann din "temperaturtabell" skall vara. Om jag vidare antar att detta är enkel labb som utförs inomhus kan du begränsa definitionsområdet till 15-30 grader. Nu måste du (utan min hjälp) beräkna i förväg vilket värde elementen i array motsvarar när du använder (ett stabiliserat) ADC värde som index till denna array. Alltså shifta ADC-värde ett par steg etc...

Den bästa optimeringen är att undvika behöva optimera.
Bristfällig? Vissa små detaljer hade jag inte med, men i det stora hela vill jag nog påstå att jag inte har missat speciellt mycket. Men om så önskas kan jag beskriva hela systemet.

Temperaturgivaren har tre pinnar, Vcc, Vss och Output. Givaren ger ut en fyrkantvåg mellan 1 och 4 kHz, men detta är inte speciellt intressant egentligen, eftersom det är pulskvoten som är viktig. Den går ut på att desto högre temperatur det är, desto längre tid är perioden hög. Notera att både Th och Tl ändras, därför måste jag räkna hur länge den är hög samt låg.

Mikrokontrollern (uc) har en pinne/funktion som heter Input Capture (IC), den används för att bland annat räkna ut frekvenser och pulskvoter. Den fungerar på så sätt att den genererar ett avbrott vid flankskifte. Alltså när insignalen går från hög till låg och vice versa. Jag bestämmer själv vilken av dem som uc:n ska söka efter.

Samtidigt i bakgrunden använder jag TIMER1, en räknare som varje klockcykel räknar upp ett 16-bits register. Vid overflow genereras ett avbrott och registret ställs på lämpligt tal igen. Detta tal väljs utifrån hur ofta jag vill ha interrupt såklart. Man använder den klocka som är kopplad till processorn (8MHz) och skalar ned den till lämpligt tal. Mitt önskemål är 200ms, men eftersom TIMER1 även används till IC får den inte vara för långsam då den blir lite okänslig - den hinner inte räkna så långt alltså. Jag är tvungen att ställa avbrottet på var 50:e ms (för då kan min prescaler vara 8 istället för 64. har jag den på 8 och väljer 200ms hinner den räkna runt i förväg), och sedan slå igång temperaturinhämtningen var fjärde gång.

När IC får interrupt kopieras automatisk värdet av TIMER1 till ett annat register, vilket jag använder mig av när jag ska räkna sedan. Detta register läser jag av och sparar sedan undan värdet. Eftersom det är 16-bitars register jag använder mig av så får jag ett tal mellan 0 och 64k. Min algoritm ser ut på följande sätt:
  • Starta IC-avbrottsfunktionen
    Vänta på att kunna börja räkna (väntar in en låg flank för att kunna känna av en hög)
    Spara undan startvärdet (när avbrottet för IC kommer)
    Ställ om och vänta på låg
    Spara undan mellanvärdet (när avbrottet kommer)
    Ställ om och vänta på hög
    Spara undan slutvärdet (när avbrottet kommer)
    Stäng av IC-avbrottsfunktionen
Nu har jag fått tre värden, start, växling och slut. Dags att börja räkna. Jag har fått fram värden för rumstemperatur (19 grader C just nu). Th = 63 (motsvarar 136 us), Ttot (Th+Tl) = 152 (motsvarar 196 us). Dessa värden växlar med någon siffra hit och dit, men är relativt stabila.

Det är alltså ingen ADC som jag läser av, utan en inkommande puls enbart. Eftersom insignalen är så pass stabil behöver jag inte räkna ut ett medelvärde. Jag vill bara använda de värden jag har och räkna ut min temperatur. Temperaturen kommer att variera mellan 5 och 50 grader gissar jag på. Men jag har inte kunnat testa vid denna temperatur och få fram värden att räkna på.

Grejen är nu så pass enkel att jag har två värden och en formel som jag vill räkna på. Jag vill helst inte byta datatyp till något större då de tar så mycket plats. Nu borde jag ha beskrivit problemet mer än nödvändigt...
Generella klassikern är ju att shifta värdena ett par steg uppåt (dvs *2, *4, *8 osv per shift)
och sen räkna på det, och precis innan du presenterar datat så shiftar du ner
det igen lika mycket, så får du fixed.point-decimaler.
Shifta vet jag hur man gör, men hur menar du att jag ska göra exakt...? Jag vet inte om jag hänger med riktigt. Principen låter som att jag flyttar värdesiffrorna, men blir inte det väldigt okänsligt och desktruktivt (liknande division med heltal)?

Förklara gärna. Jag går på lunch under tiden ;)
"Just because you are unique does not mean you are useful."
User avatar
IcePic
Hedersbit
Posts: 6061
Joined: 2002-03-08 16:09:38

Post by IcePic »

tkh wrote:Shifta vet jag hur man gör, men hur menar du att jag ska göra exakt...? Jag vet inte om jag hänger med riktigt. Principen låter som att jag flyttar värdesiffrorna, men blir inte det väldigt okänsligt och desktruktivt (liknande division med heltal)?
Förklara gärna. Jag går på lunch under tiden ;)
Antag att man har invärden av typen 1-100. Sen vill man köra:

Code: Select all

x=invärde * 2.5
    ---------------
           10
men man vill inte ta till float, och 2.5 är typiskt svårt att representera som int,
och 2.5/10 som konstant är .25, ännu tristare att ha som int.
Alltså shiftar man upp alla tal med 8 steg (* 256 alltså) så att man får en formel
som blir:

Code: Select all

x * 256 = invärde * 256 * 2.5
               ----------------------
                         10
Om nu x är en short (dvs u_int16_t eller liknande) så kommer invärdet som
är från 0-100 inte flöda över den, och beräkningen går lättare att genomföra
eftersom (256 * 2.5)/10 = 64. (som en extra bonus, i mitt idiotexempel, så
är en division med 64 samma sak som att shifta ner 6 steg. Snabbt och fint. =)

Så, du får ett x som går från 0*256 till 100*256, sedan kan du använda det
på övriga ställen i andra formlet så länge du vet om att den är 256 ggr för stor. När du slutligen
ska presentera talet så delar du med 256 (eventuellt adderar du 2^7 innan
för avrundningens skull) och visar det.
Då har x (som ett 16-bitars tal) 8 bitar "heltal" och 8 bitar decimaler, som
den kommer behålla hela tiden ända tills du presenterar på displayen.

Oftast är 8 binära decimaler (dvs noggrannhet på 1/256) nog för de flesta
simplare uppgifter, men du kan välja 10 bitars heltal+6 decimaler eller 4+12 decimaler
hur du vill beroende på vad du vet om indatat. Så länge som indatat ryms
inom din heltalsdel av din fixedpoint-variabel så är allt lugnt. Och oftast går
shiftar väldigt fort på enklare cpu:er, så det är inte så dyrt att göra det som
divisioner och annat annars blir.
Oh give me a clone, my very own clone,
with the Y chromosome changed to X!
And since she's my own, of my own flesh and bone,
she'll be thinking of nothing but sex!
User avatar
IcePic
Hedersbit
Posts: 6061
Joined: 2002-03-08 16:09:38

Post by IcePic »

Det är alltså lite som en gammal vinylspelare, den höjer diskanten för det är
där som bruset har mest energi, sen sänker din förstärkare diskanten i samma
grad efteråt så minimerar man den mängd brus som uppstått av induktion i kabeln
osv.

Så länge som båda sidor är med på det och man inte slår i nåt tak så är det lugnt.
Oh give me a clone, my very own clone,
with the Y chromosome changed to X!
And since she's my own, of my own flesh and bone,
she'll be thinking of nothing but sex!
User avatar
tkh
Hedersbit
Posts: 3901
Joined: 2002-03-08 0:41:22
Location: Landskrona (Umeå)
Contact:

Post by tkh »

Tror jag förstår hur du menar nu, och det ska nog inte vara några större problem att realisera det där. Men det blir om ett tag, återkommer om jag får problem isf. Just nu har jag UARTen att tänka på, samt att jag ska göra klart EEPROM-drivarna.

:)
"Just because you are unique does not mean you are useful."
User avatar
Ancalagon
Posts: 550
Joined: 2002-03-08 14:16:41
Location: Stockholm

Post by Ancalagon »

tkh wrote:Tror jag förstår hur du menar nu, och det ska nog inte vara några större problem att realisera det där. Men det blir om ett tag, återkommer om jag får problem isf. Just nu har jag UARTen att tänka på, samt att jag ska göra klart EEPROM-drivarna.

:)
Vad är det du kodar egentligen ?
User avatar
tkh
Hedersbit
Posts: 3901
Joined: 2002-03-08 0:41:22
Location: Landskrona (Umeå)
Contact:

Post by tkh »

Ancalagon wrote:Vad är det du kodar egentligen ?
Hela mitt projekt menar du?

Isf är det mitt examensarbete, som är en beställning från ett dragracingteam via ett företag. Vi ska göra en temperaturmätning/logger till deras bil. En sensor (termoelement) placeras i varje grenrör (8st), vi ska göra en mätning på var och en fem gånger i sekunden (40 mätningar i sekunden alltså). Värden ska sparas i två minuter ungefär. Sedan ska allt kunna tankas över till en dator så man ska kunna se grafer på temperaturen.

Det är ju en rätt kort beskrivning, eftersom arbetet innefattar allt från grunden. Vi beställer alla komponenter, programmerar mikrokontrollern så den fungerar med eeprom och alla andra delar av systemet. Instrumentförstärkare->MUX->ADC->bearbeta med fyra olika polynom för att göra en linjeanpassning till termoelementets polynom, det blir 3 hystereser alltså->lagra i eeprom->tanka över till dator->visa.

Ungefär :)
"Just because you are unique does not mean you are useful."
Post Reply