Multiplikation med en decimalkonstant i 8bit-assembler

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
bos
Inlägg: 2312
Blev medlem: 24 februari 2007, 23:29:15
Kontakt:

Multiplikation med en decimalkonstant i 8bit-assembler

Inlägg av bos »

Jag har en PIC12F510 som har en 8-bit ADC som jag ska använda. Matningsspänningen är 5V, och då det jag ska mäta är upp till och med 12V så behöver jag skala ner mätvärdet med 2.4 (12/5) och detta gör jag med externa komponenter, så ADC-ingången får spänning inom rätt område (iallafall i teorin).

Efter att ha ritat ut problemet på papper så kom jag fram till att varje bit från ADC:n motsvarar ca. 0.02mV (5/256), men eftersom detta värde är nerskalat med konstanten 2.4 måste detta kompenseras. Ekvationen blir därför (5*24) / (256*10), vilket blir 3/64.

Koll: Mätvärde 6.42V blir 2.67V nerskalat. 2.67 / (5/256) = 136.7 = 137, vilket är det värde ADC:n kommer att avge. 137 * (3/64) = 6.42, så min tes stämmer.

MCU:n har add, sub, shift left och shift right, men ingen divisionsoperator, och det är här mitt problem dyker upp:

Kod: Markera allt

137 = 00000000 10001001
 x 3 = 00000001 10011011 (add tre gånger)
/64 = 00000000 00000110 (64 = 2^6, alltså skift höger 6 ggr)
Det blir alltså rätt volttal med hjälp av min konstant, men jag vet inte hur jag ska plocka ut decimalen. Jag nöjer mig med en decimals nogrannhet, så 6.4 räcker gott och väl.

Tips?
victor_passe
Inlägg: 2436
Blev medlem: 28 januari 2007, 18:45:40
Ort: Kungsbacka

Re: Multiplikation med en decimalkonstant i 8bit-assembler

Inlägg av victor_passe »

gångra 137 med 10 och gör sedan samma sak.
Användarvisningsbild
Swech
EF Sponsor
Inlägg: 4750
Blev medlem: 6 november 2006, 21:43:35
Ort: Munkedal, Sverige (Sweden)
Kontakt:

Re: Multiplikation med en decimalkonstant i 8bit-assembler

Inlägg av Swech »

Du kan ju gångra med 30 istället för 3
Då får du 64.2 som resultat.
Sen är det "bara" att sortera ut 1 tal och 10-100 tal

Swech
Användarvisningsbild
tecno
Inlägg: 27254
Blev medlem: 6 september 2004, 17:34:45
Skype: tecnobs
Ort: Sparreholm, Södermanland N 59° 4.134', E 16° 49.743'
Kontakt:

Re: Multiplikation med en decimalkonstant i 8bit-assembler

Inlägg av tecno »

gångra ?

ett nytt ord eller?
bearing
Inlägg: 11676
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Re: Multiplikation med en decimalkonstant i 8bit-assembler

Inlägg av bearing »

Gör en "8bit x 8 bit = 16 bit"-multiplikation med 120. Den höga byten kommer få resultatet 0-120, vilket kommer motsvara 0-12V med en decimal. Då slipper du skiftningen. Ifall din spänningsdelare inte stämmer riktigt, så att max på skalan egentligen motsvarar t.ex. 12,1V, är det bara att kompensera för det genom att istället multiplicera med 121.

EDIT: om din spänningsdelare är justerbar m.h.a. t.ex. trimpotentiometer skulle du ju kunna ordna så att max invärde är 12,8V, och på så sätt få resultatet i mjukvaran genom att bara skifta ADC-resultatet ett steg höger.
Senast redigerad av bearing 27 februari 2011, 22:17:54, redigerad totalt 1 gång.
bos
Inlägg: 2312
Blev medlem: 24 februari 2007, 23:29:15
Kontakt:

Re: Multiplikation med en decimalkonstant i 8bit-assembler

Inlägg av bos »

Intressanta tips!

Jag kör på *30-varianten till att börja med då den känns mest handgriplig just nu, men sen ska jag prova 8bit x 8bit.
victor_passe
Inlägg: 2436
Blev medlem: 28 januari 2007, 18:45:40
Ort: Kungsbacka

Re: Multiplikation med en decimalkonstant i 8bit-assembler

Inlägg av victor_passe »

Nej, att multiplicera heter gångra och dra roten ur heter rotera...
Användarvisningsbild
Swech
EF Sponsor
Inlägg: 4750
Blev medlem: 6 november 2006, 21:43:35
Ort: Munkedal, Sverige (Sweden)
Kontakt:

Re: Multiplikation med en decimalkonstant i 8bit-assembler

Inlägg av Swech »

Gångra = att ta ett tal gånger ett annat 2*3 gångra 2 med 3...

http://sprakmy.wordpress.com/2007/09/10/gangra/
" Ett ord som jag lagt märke till de senaste veckorna är verbet gångra istället för multiplicera. Intressant och praktiskt ord, mindre krångligt än multiplicera, men mer vardagligt förstås. Ordet har faktiskt kommit med i SAOL, dock med beteckningen vardagligt. Så, ett lite kul vardagsord, men inget som passar i akademiska uppsatser precis."

Men ok då... multiplicera :D

Hmmm att ta minus.... blir det "subba" då?

Swech
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 46989
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Multiplikation med en decimalkonstant i 8bit-assembler

Inlägg av TomasL »

Ni har fel alla, det heter ju plutteficera.
Användarvisningsbild
4kTRB
Inlägg: 20783
Blev medlem: 16 augusti 2009, 19:04:48

Re: Multiplikation med en decimalkonstant i 8bit-assembler

Inlägg av 4kTRB »

Kan den här sidan vara till hjälp?
http://avtanski.net/projects/math/
bos
Inlägg: 2312
Blev medlem: 24 februari 2007, 23:29:15
Kontakt:

Re: Multiplikation med en decimalkonstant i 8bit-assembler

Inlägg av bos »

Assemblerkunskaperna var lite ringrostiga, men efter en stunds mnemonic-läsande blev jag varm i kläderna igen och svängde hop följande:

Kod: Markera allt

        ; The external voltage is scaled down by a 2.4 factor using a
        ; voltage divider, and then fed into the 8-bit ADC-port. After
        ; converting, the value has to be scaled up again.
        ;
        ; 8-bit ADC voltage: 5V => each bit is 5/256 (~0.02mV).
        ;
        ; Scaling up 24/10 (= 2.4), the equation becomes:
        ;     (24*5)/(10*256) = (3/64)
        ;
        ; Also, one decimal is wanted so we'll multiply the constant
        ; with 10. Thus, the resulting constant is:
        ;     (30/64) = (15/32)
        ;
        ; Test case: ADC-returns value 137. We will multiply this value
        ; with 15 and then divide it by 32. Multiply by 15 is done by
        ; shifting left 4 times (= *16) and then substracting the value
        ; once.

        ; Clear the 16-bit place holder
        clrf    BYTE_H
        clrf    BYTE_L

        ; Load the test case
        movlw   .137
        movwf   ADC_VALUE
        movwf   BYTE_L

        ; Multiply by 16
        ; The nice thing here is that if MSB of BYTE_L is set, it will
        ; go into carry, and then rotated into BYTE_H.
        rlf     BYTE_L
        rlf     BYTE_H
        rlf     BYTE_L
        rlf     BYTE_H
        rlf     BYTE_L
        rlf     BYTE_H
        rlf     BYTE_L
        rlf     BYTE_H

        ; Subtract the value itself
        movfw   ADC_VALUE
        subwf   BYTE_L

        ; If BYTE_L was smaller, then this subtraction will set
        ; the Borrow bit (inverse of Carry).
        btfss   STATUS, C
         decf  BYTE_H

        ; Multiply by 15 is done. Only thing left is shifting it right
        ; five times (= dividing by 32).
        rrf     BYTE_H
        rrf     BYTE_L
        rrf     BYTE_H
        rrf     BYTE_L
        rrf     BYTE_H
        rrf     BYTE_L
        rrf     BYTE_H
        rrf     BYTE_L
        rrf     BYTE_H
        rrf     BYTE_L

        ; BYTE_L now contains a single integer in the range 00..99, where
        ; the value is the actual measured voltage (with one decimal).
        
        ; We will now split the integer into 'tens' and 'ones'. This is
        ; achieved by subtracting 10 from the value until a Borrow occurs.
        movlw   .10
        clrf    TENS
sub10  
        subwf   BYTE_L                              ; Remove 10
        incf    TENS
        btfsc   STATUS, C                           ; Borrow?
         goto    sub10                              ; No, continue

        addwf   BYTE_L                              ; Yes, correct the values.
        decf    TENS

        ; Copy the remainder, and we're done.
        movfw   BYTE_L
        movwf   ONES
bearing
Inlägg: 11676
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Re: Multiplikation med en decimalkonstant i 8bit-assembler

Inlägg av bearing »

Ifall syftet med det här är att få variablerna TENS (som innehåller hela volt?) och ONES (innehåller decimaldelen?) verkar det som en stor omväg att först räkna ut ett ett tal som är tio gånger så stort som spänningen, och sedan i en loop dividera talet med 10 för att få fram TENS (kvoten), och till sist ONES (resten).

Multiplicera t.ex. ADC-värdet med 12 så blir höga delen TENS. Nollställ sedan höga delen och multiplicera med 10, så blir höga delen ONES.
bos
Inlägg: 2312
Blev medlem: 24 februari 2007, 23:29:15
Kontakt:

Re: Multiplikation med en decimalkonstant i 8bit-assembler

Inlägg av bos »

Intressant, och helt klart mycket snabbare än min variant (inte för att hastighet spelar någon roll i det här fallet, men likväl).

Kan du utveckla genvägen? Exempelvis hur du kom fram till den.
bearing
Inlägg: 11676
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Re: Multiplikation med en decimalkonstant i 8bit-assembler

Inlägg av bearing »

Jag betraktar ADC-resultatet som en bråkdel utav 1, eller hur man ska säga. Jag tänker att 0-256 motsvarar 0,00-1,00. Så om ADC-resultatet multipliceras med 120, kommer det ge ett tal (i höga byten) som varierar mellan 0 och 120 (eller, 0-119 egentligen, eftersom att bara 255 får plats i en byte), precis som när ett tal som varierar mellan 0 och 1 multipliceras med 120 ger ett tal som variera mellan 0 och 120.

Att först multiplicera med 12, och sedan multiplicera med 10 är ju som att multiplicera med 120, fast i två steg. När höga byten nollas efter första steget finns bara "decimaldelen" kvar, och den första av dom decimalerna tas fram genom att multiplicera med 10.

Tycker att förklaring blev lite rörig, jag hoppas att det gick fram hur jag resonerar.

Jag har även använt samma princip med AD-omvandlingar som ger fler än 8 bitar. Då har jag först sett till att AD-resultatet är 16 bitar (t.ex. med 10-bit AD genom addera 64 stycken omvandlingar för att få ett medelvärde på 64 omvandlingar), och sedan skalat om genom att multiplicera med lämplig faktor. På en microcontroller med inbyggd multiplikator är det här speciellt fiffigt, eftersom att man snabbt kan multiplicera med valfritt tal, och inte behöver hårdkoda en faktor som går att utveckla genom skiftningar för att det ska gå snabbt.
bos
Inlägg: 2312
Blev medlem: 24 februari 2007, 23:29:15
Kontakt:

Re: Multiplikation med en decimalkonstant i 8bit-assembler

Inlägg av bos »

Jag förstår vad du menar men jag är inte riktigt med på hur magin bakom "gångra med 120" fungerar. Därför kan jag heller inte felsöka riktigt, vilket hade varit behövligt för nu när jag kört 5 olika testfall så får jag decimalfel på samtliga med din metod:

ADC 111: 5.2 min, 5.1 din (min 22 cykler slöare)
ADC 47: 2.2 min, 2.1 din (min 7 cykler slöare)
ADC 209: 9.7 min, 9.6 din (min 42 cykler slöare)
ADC 148: 6.9 min, 6.7 din (min 27 cykler slöare)
ADC 14: 0.6 min, 0.5 din (min 3 cykler snabbare)

Så "tyvärr" behåller jag nog min egen variant :)

Men det är ändå trevligt att se hur olika man löser saker och ting. Jag själv har generellt svårt att se och utföra genvägar, utan jag måste oftast gå den raka linjen i en top-down-algoritm utan avvikelser. Det brukar resultera i större mängd kod som - precis som du såg - kan behöva optimeras ordentligt.

(Hastigheten är för övrigt inte en relevant faktor i detta sammanhang, jag tog bara med den för skojs skull för att se hur stora skillnaderna blev)
Skriv svar