Sida 1 av 2

Fix-punkts multiplikation i assembler

Postat: 6 februari 2009, 09:48:36
av Johel572
Jag tänkte jag skulle skriva ut lite värden från en 16-bits A/D till en display och skulle behöva lite hjälp med att få ut reella värden. Det jag vill göra är att multiplicera ett 16-bitars tal med en faktor (alltid mindre än noll) och få ut en produkt med lämplig upplösning. Om jag har förstått det rätt vore en fix-punkts multiplikation kunna vara lämplig i detta fall. Först undrar jag vad som skulle kunna vara ett lämpligt format? Ett typiskt exempel vore att beräkna 3500x0,0571=?, dvs 16-bits heltal multiplicerat med 16-bits fraktions tal och resultatet borde väl kunna representeras som 16+16 bitar? Heltalet är antalet bitar från A/Dn men faktorn kommer alltid att vara samma.

Sen skulle jag behöva lite hjälp med hur man ska tänka vid multiplikation av binärtal. Jag har tittat på kodexempel men jag fattar inte riktigt vad det är de gör. Hur ska man tänka? Finns det några bra trick att ta till? Koden är tänkt att köras på en pic18 men jag vet inte om det gör någon större skillnad.

Re: Fix-punkts multiplikation i assembler

Postat: 6 februari 2009, 09:58:48
av vfr
Vid multiplikation så gör man på samma sätt som vid manuell multiplikation "på papper". Finns massor av förslag på nätet. Om man inte har en färdig "mul"-instruktion eller ett funktionsbiliotek att tillgå.

Jag skulle valt att multiplicera med ett bråk där nämnaren är t.ex 65536. Då kan divisionsdelen reduceras till en skiftning med 16 bitar åt höger (droppa två lägsta byten av resultatet) och kvar blir bara multiplikationen. Vad täljaren skall vara beror på din faktor.

Re: Fix-punkts multiplikation i assembler

Postat: 6 februari 2009, 10:10:17
av Icecap
Multiplicerar i ASM:
Tal1: 13 (1101)
Tal2: 7 (0111)

Resultat
0000000 0111 Start värde
Då LSB är'1' ^ ska Tal1 adderas till resultatet (LSB = Least Significant Bit), om den är noll lägger man INTE till Tal1 till Resultatet.
0001101 (Resultat)
Sedan shiftas Tal1 upp ett steg (till 11010) och Tal2 ner ett steg (till 0011). Då Tal2 är icke-noll upprepas ovenstående funktion:
0001101
+0011010 (Tal1 efter shift)
======
0100111 (summa)

Tal1 & Tal1 shiftas som förut och funktionen upprepas om Tal2 är icke-noll:

0100111
+0110100
=======
1011011 (summa)

Efter ytterligare ett shift är Tal2 noll, alltså avslutas hela klabbet.
Och 1011011 är 91 vilket 13*7 också är, häftigt eller hur?

Re: Fix-punkts multiplikation i assembler

Postat: 6 februari 2009, 11:27:33
av Johel572
Tack för förklaringen Icecap, det behövdes. Nu tror jag greppar principen.

Vfr, det där med att multiplicera med en faktor av 2, typ 256 för att sedan dividera med samma faktor har jag läst om men förstod inte riktgt hur det gick till. Det är väl ett sätt att optimera koden antar jag? Tror jag måste ta det från början först.

Nu ska jag försöka få i hop en funktion för 16x16bitar. Dock hade jag tänkt att de ena 16bitarna skulle representera ett tal mindre än noll. Dvs 2^-1 till 2^-8 för 8msb och 2^-9 till 2^-16 för 8lsb. Om jag har förstått det hela rätt så är det bara att räkna som vanligt (som Icecaps exempel) och sen att man måste göra rätt när man konvertera till decimaltal (om man vill visa på en display eller något).

Re: Fix-punkts multiplikation i assembler

Postat: 6 februari 2009, 11:36:56
av sodjan
I sådana här fall så är det alltid vädefullt att titta på hela kedjan.
T.ex så kan en nivåjustering av det analoga värdet in till ADC'n kanske
medföra att du antingen kan skippa justeringen (multiplikationen) helt
eller i alla fall få den att bli en potens av 2 (d.v.s ett antal shift).

Om ett tal är större eller mindre än 0 är helt ointressant, beräkningen
blir exakt densamma det är bara att fixa teknet på resultatet efteråt.
Samma sak med multiplikation av 0.125 1.25, 12.5 eller 125, det är helt
ointressant för själva multiplikationen var kommat sitter, det får du
hålla redan på separat.

Båda dessa saker missar många i början.

Men, det viktigaste är som sagt att först kolla om du inte antingen
kan undvika beräkningen helt eller i alla fall förrenkla den till att vara
med en potens av 2.

För att få kod för multiplikationer med fasta konstanter, så finns det en
kodgenerator på http://www.piclist.com :
http://www.piclist.com/techref/piclist/ ... divmul.htm

Re: Fix-punkts multiplikation i assembler

Postat: 6 februari 2009, 12:26:48
av vfr
Vfr, det där med att multiplicera med en faktor av 2, typ 256 för att sedan dividera med samma faktor har jag läst om men förstod inte riktgt hur det gick till. Det är väl ett sätt att optimera koden antar jag? Tror jag måste ta det från början först.

Så tror jag inte riktigt jag sa...

Multiplicera med en faktor oberoende av två, sedan dividera med en konstant som är en potens av två. Gärna potens av 256.

Den stora fördelen med denna lösning är om man har en hårdvarumultiplikation men inte motsvarande division. Då ger den avsevärt snabbare exekvering. Men även utan hårdvara för multiplikation så kanske multiplikationen blir något enklare/snabbare. Iallafall känns det så, då divisionen upplevs som lite bökigare att göra "pappersvis".

Re: Fix-punkts multiplikation i assembler

Postat: 6 februari 2009, 13:04:51
av Gimbal
Det är därför jag numera kör i C. Det kan vara en sport att grotta på bitnivå men attan vilken tid det tar. Normalt busenkla detaljer stoppar upp hela projekt.

Eh, rant over, som de säger över there.

Re: Fix-punkts multiplikation i assembler

Postat: 6 februari 2009, 13:08:30
av sodjan
Alltså, om prestanda är ointressant så kan man naturligstvis köra brute-force matte
oavsett språk. På samma sätt kan man optimera det hela (om det behövs) så som
har beskrivts i tråden, oavsett språk. Att skriva det i C gör det inte lättare för
processorn på något sätt, däremot så kan man uppleva det så som programmerare...

Re: Fix-punkts multiplikation i assembler

Postat: 6 februari 2009, 13:14:55
av Icecap
Visst är det lättare att skriva i C men processorn ska göra samma jobb med att flytta bits och allt.

Re: Fix-punkts multiplikation i assembler

Postat: 6 februari 2009, 14:42:52
av Johel572
Nu har jag varit inne på piclist och hittat en rutin för 16x16bit och testat lite. Sen har jag nu efter lunch suttit och räknat för hand på 16x16bit för att förstå pricipen och det är lite klurit men faktisk ganska kul. Det blev lite lättare när jag insåg att produkten skall innehålla 32bitar :D

Så nu återstår det bara att konvertera de 16lsb från svaret till decimal tal. I huvudet så flyttar jag ju bara decimalpunkten (heter det så när man har binära tal?) till mellan 16msb och 16lsb och saken är biff. Men hur ska man bete sig i assembler? Att konvertera heltal från binärt till decimalt går ju men när man har decimaler blir jag lite osäker på hur jag skall representera dem.

Alltså jag får ut de 16lsb (fraktionsdelen) av mitt 32bits resultat till
hög byte = 11010000 (208D)
låg byte = 00000000 (0D)
Detta vill jag sen representera decimalt som 0.8125 på tex en display. Här kan jag ju inte separera heltal på samma sätt som när jag konverterar binärt till decimalt.

Hm, det kanske var lite luddigt uttryckt.

Re: Fix-punkts multiplikation i assembler

Postat: 6 februari 2009, 14:48:41
av sodjan
Vilket spänningsområde är det som mäts ?
Vad är "full-scale" på de 16 bitarna från ADC'n ?
Och är 0V = h'0000' från ADC'n ?

Re: Fix-punkts multiplikation i assembler

Postat: 6 februari 2009, 15:29:03
av Icecap
Vid att köra med "fixed decimal point" kan du besluta att din upplösning är kanske 1/1000. Detta betyder att du konverterar tal som vanligt innan utskrift men att du ser till att kommat kommer in på rätt plats, när detta sker visar uträkningen kanske 8125 st 1/1000-delar men i en utläsning med rätt placerat komma blir det 0,8125, så enkelt är det.

Man räknar alltså ut hur talet ser ut decimalt, petar in ett komma på plats 5 bakifrån och ser i övrigt till att det finns minst 5 siffror i dekodningen även om de främsta ska vara noll:
08125, peta in ett komma -> 0,8125 -> skriv ut.

Re: Fix-punkts multiplikation i assembler

Postat: 6 februari 2009, 16:11:42
av Rohan
Gimbal skrev:Det är därför jag numera kör i C. Det kan vara en sport att grotta på bitnivå men attan vilken tid det tar. Normalt busenkla detaljer stoppar upp hela projekt.

Eh, rant over, som de säger över there.
Visst har du en poäng men det är förhoppningsvis så att du gjort det några gånger och därför vet hur det funkar. Tycker det finns en mycket stor poäng i att implementera multiplikation och division 'för hand' så man förstår vad det är som händer. När man gjort det så blir man förhoppningsvis en bättre programmerare, oavsett språk. I mikroprocessorer har man väldigt begränsade resurser (för det mesta) och om man själv gjort kod för multiplikation så förstår man varför man inte hinner med 10000 32-bitars multiplikationer på en halv sekund, så man låter bli att försöka och sparar sig lite tid... någon annan gång.

(Sen kan man ju spara sin kod och återanvända den, så behöver man bara göra det en gång.)

Re: Fix-punkts multiplikation i assembler

Postat: 6 februari 2009, 17:33:19
av Gimbal
Icecap skrev:Visst är det lättare att skriva i C men processorn ska göra samma jobb med att flytta bits och allt.
Jo, det är ju liksom det som är poängen.
Nu hänger det lite på vad man gör för prylar, men oftast (för min del) så är det inte särskilt bråttom i själva huvudloopen. Interrupt plockar ingångarna och så räknar man i huvudloopen.

Nu senast så skrev jag en enkel reglerloop för PWM av en liten motor. Första versionen helt i integer, men varför inte göra det lätt för sig, flyttal vore trevligt. Ändra long till float och kör på. Kraft och minne finns så det räcker i den lilla 8 pinnars attiny45'an. Himla trevligt.

Re: Fix-punkts multiplikation i assembler

Postat: 6 februari 2009, 19:58:03
av AndersG
Jag har kört med 32-bitars aritmetik och Peter Helmsleys rutiner från PICList i min batterimätare och det funkar bra. Ett tips är att skala förnuftigt, skall du mäta tex upp till 16V så kan du låta fullskateutslag på en 10bitars AD vara 20,48V tex. Då kan du ta värdet direkt och rotera ett snäpp höger så är det klart,

1024 << 1 = 2048 = 20,48V

Ett annat bra tips är att se över hur du räknar saker, om formeln är a/b * c, kan det löna sig att i stället ta a * c /b, förutsatt att talen är sådana att du ej får owerflow.