Sida 1 av 3
Söker BIN2BCD rutin till AVR. "Nu BCD2BIN"
Postat: 12 mars 2008, 21:45:54
av Fagge
Som rubriken säger ,så är jag på jakt efter en BIN2BCD subrutin som funkar ihop med en MEGAxx...
Jag har en som jag hittade på nätet, men den funkar bara att köra en gång, sen flippar den ut & ger mig helt galna välden tillbaka.
Detta är förmig helt koko eftersom alla register nollställs innan rutinen går igång. Så det kan omöjligt finnas några rester kvar i rutinens register...
.DEF TEMP =r16
.DEF BCDL =r21
.DEF BCDM =r22
.DEF BCDH =r23
.DEF AL =r19
.DEF AH =r20
.DEF Temp2 =r17
.DEF BCDT =r18
;****************************BIN2BCD**************
; BIN2BCD
; Converts a 16 bit binary number in A (AH,AL) into packed
; bcd in BCDH, BCDM, BCDL. For example, if AH=high(12345),
; AL=low(12356), calling to the routine will produce
; BCDH=$12, BCDM=$34, BCDL=$45.
;
BIN2BCD:
clc
ldi TEMP,16
clr BCDL
clr BCDM
clr BCDH
clr Temp2
clr BCDT
bitloop:
rol AL
rol AH
rol BCDL
rol BCDM
rol BCDH
dec TEMP
brne bitadj
ret
bitadj:
mov TEMP2,BCDL
rcall adjbit
mov BCDL,TEMP2
mov TEMP2,BCDM
rcall adjbit
mov BCDM,TEMP2
mov TEMP2,BCDH
rcall adjbit
mov BCDH,TEMP2
rjmp bitloop
adjbit:
mov BCDT,TEMP2
subi BCDT, -3 ; add 3
sbrc BCDT,3
mov TEMP2,BCDT
mov BCDT,TEMP2
subi BCDT,-(0x30) ; add 0x30
sbrc BCDT,7
mov TEMP2,BCDT
ret
Postat: 12 mars 2008, 22:16:57
av Swech
Hur många bitar behöver du?
16 bits?
Postat: 12 mars 2008, 22:21:01
av sodjan
> ...For example, if AH=high(12345), ; AL=low(12356),...
Är inte det där felskrivet ?
Postat: 12 mars 2008, 22:27:13
av Fagge
Swech, Det duger fint med 8bitar till & börja med!. Men om någon har 16bitars varianten så tar jag gärna den med!.
Sodjan, det känns som det ja. Men som nybörjare, så har jag dålig koll på hur den där program snutten jobbar.
Postat: 12 mars 2008, 22:57:39
av Swech
16 bit till packed BCD.. rykande färsk
Kod: Markera allt
.DSEG
R_TEMP_BUFFER: .BYTE 5
R_RESULT_HI: .BYTE 1
R_RESULT_MID: .BYTE 1
R_RESULT_LO: .BYTE 1
; > R16,R17:=VALUE TO CONVERT R16 LOW, R17 HI
; < R_RES_HI,MID,LO BCD CONVERTED
.CSEG
CONV_BCD:
LDI ZL,LOW(DIV_LIST*2) ;SETUP PTR DIV LIST
LDI ZH,HIGH(DIV_LIST*2)
LDI XL,LOW(R_TEMP_BUFFER) ;SETUP TEMP BUFFER
LDI XH,HIGH(R_TEMP_BUFFER)
DIV_LOOP1:
LDI R20,0 ;SET RESULT = 0
LPM R18,Z+ ;GET DIV VALUE
LPM R19,Z+
CPI R18,-1 ;END OF DIV TABLE?
BREQ DIV_SK1 ; YES
DIV_LOOP2:
CP R16,R18 ;COMPARE IF VALUE < DIV
CPC R17,R19
BRLT DIV_NEXT ; NO CHECK NEXT
INC R20 ; YES INC DIV
SUB R16,R18 ;REMOVE DIV VALUE
SBC R17,R19
RJMP DIV_LOOP2 ;AGAIN
DIV_NEXT:
ST X+,R20 ;SAVE RESULT TEMP
RJMP DIV_LOOP1 ;AGAIN
;------ CONVERT DIV INTO PACKED BCD
DIV_SK1:
ST X+,R16 ;SAVE REMAINING DIV RESULT
LDI XL,LOW(R_TEMP_BUFFER) ;GET BUFFER
LDI XH,HIGH(R_TEMP_BUFFER)
LD R16,X+ ;GET FIRST TEMP 10.000
STS R_RESULT_HI,R16 ;SAVE AS HI BCD
LD R16,X+ ;GET 1.000
LD R17,X+ ;GET 100
SWAP R16 ;SWAP 1.000
OR R16,R17 ;PACK WITH 100
STS R_RESULT_MID,R16 ;SAVE RESULT MID
LD R16,X+ ;GET 10
LD R17,X+ ;GET 1
SWAP R16 ;SWAP 10
OR R16,R17 ;PACK WITH 1
STS R_RESULT_LO,R16 ;SAVE RESULT LOW
RET ;DONE
DIV_LIST:
.DW 10000
.DW 1000
.DW 100
.DW 10
.DW -1
Postat: 12 mars 2008, 23:16:17
av Fagge
Ooo, tackar.
Du har dock tagit med några saker som jag aldrig har sätt förut.
Skulle du vilja förklara hur följande 2 snuttar nedan funkar?.
.DSEG
R_TEMP_BUFFER: .BYTE 5
R_RESULT_HI: .BYTE 1
R_RESULT_MID: .BYTE 1
R_RESULT_LO: .BYTE 1
.CSEG
Jag antar att denna snutten ska placeras allra överst, där man t.e.x sätter namn på register mm. Rätt?
---------------------------------------
DIV_LIST:
.DW 10000
.DW 1000
.DW 100
.DW 10
.DW -1
Postat: 12 mars 2008, 23:33:24
av Swech
.DSEG
.
.
. Här deklarerar du alla bytes du vill ha.
. man referear alltså INTE till fasta adresser, låt assemblern fixa detta
. placeras helst i början av koden, men det är inget krav.
NAMN: .BYTE ANTAL BYTES
; Jag döper ALLTID alla ramceller till R_NAMN.... då kan man se
; direkt i koden om det är en ramcell man pillar med.
; finns säkert de som kommer att invända, men jag tycker att det
; blir mest lättläst så.
;
.CSEG
.
.
; Här kommer ditt program
lds R16,NAMN ; läser från ramcell NAMN
Sedan.... något som är AVR´s starkaste sida men som ingen riktigt
har förstått att utnyttja.
Man kan lägga data i programkoden, t.ex tabeller som man sedan
på ett EXTREMT lätt sätt kan läsa av via Z registren.
DIV_LIST:
.DW 10000
.DW 1000
.DW 100
.DW 10
.DW -1
skapar en tabell med värden 10.000,1000,100,10 och -1 i ditt programminne -1 är endast för att markera slut av tabell.
LDI ZL,LOW(DIV_LIST*2)
LDI ZH,HIGH(DIV_LIST*2)
ställer Z att peka på divisionslistan.
Nu är det också så att AVR instruktioner är 16 bitar -> 2 bytes.
Varje instruktion ligger alltså på en jämn 2 bits adress. (jämför MC68000)
Programräknaren behöver alltså inte räkna med bytes, den räknar word.
Assemblern räknar därför också alla adresser som word.
instruktionerna ligger på adress 0,1,2,3,4,5... men i flash minnet
blir det 0.2.4.6.8.10....
Så... när vi skall räkna ut BYTE adressen i FLASH måste man skriva
(DIV_LIST*2) annars hamnar man helt galet...
Här är ytterligare ett tips!
Skriv ALDRIG en loopräknare för att läsa tabeller.
Avsluta dem med t.ex. -1
Lägger man till / tar bort något ur tabellen behöver man INTE justera
någon loopräknare.
Divisionstabellen i mitt program används helt enkelt för att dividera
med 10.000 1.000 100 och 10
Swech
Postat: 13 mars 2008, 07:30:13
av Mindmapper
Atmel har en ap.note avr204 som behandlar ämnet. Borde väl även fungera med Mega.
Postat: 13 mars 2008, 20:17:53
av exile
En till.... ganska snabb. men det exemplet du visar bör fungera, är du säker att du inte förstör "in argumenten" då de förstörs av rutinen? (då de används)
Kod: Markera allt
// BIN to BCD
sh2bcd:
//argument in AH:AL
clr BCDL
clr BCDM
clr BCDH
ldi TEMP, 16
rjmp sh2bcd_jmp
sh2bcd_loop:
subi BCDL, -(0x33)
sbrs BCDL,3
subi BCDL, 3
sbrs BCDL,7
subi BCDL, 0x30
subi BCDM, -(0x33)
sbrs BCDM,3
subi BCDM, 3
sbrs BCDM,7
subi BCDM, 0x30
sh2bcd_jmp:
lsl AL
rol AH
rol BCDL
rol BCDM
rol BCDH
dec TEMP
brne sh2bcd_loop
ret
Postat: 13 mars 2008, 23:32:21
av Fagge
Hur som helst så funkar
Swech rutin helt perfekt

Postat: 16 september 2008, 23:55:53
av Fagge
Nu sitter jag här & måste dela ett binärt 16bitars tal med 10, & jag kommer inte fram till annat än att först omvandla talet till BCD för att sen skippa sista siffran & sen omvandla talet till binär form igen för vidare bearbetning.
Eller kan man göra detta på ett enklare sätt?.
Om inte, är det någon här som har en BCD2BIN rutin liggandes som vill dela med sig?.
Det går ju tyvärr inte att skriva BIN2BCD rutinen upp & ner & baklänges

Postat: 17 september 2008, 00:06:52
av sodjan
Jag körde en kodgenerator för PIC och fick följande algoritm :
; ACC = ACC * 0.1
; Temp = TEMP
; ACC size = 16 bits
; Error = 0.5 %
; Bytes order = little endian
; Round = no
; ALGORITHM:
; Clear accumulator
; Add input / 16 to accumulator
; Add input / 32 to accumulator
; Add input / 256 to accumulator
; Add input / 512 to accumulator
; Move accumulator to result
;
; Approximated constant: 0.0996094, Error: 0.390625 %
Divisionerna med 16,32,256 och 512 är ju enkla shiff/rotate
och för övrigt är det enbart ADD...
Man får även en komplett ASM kod, men den är ju för PIC...
Postat: 17 september 2008, 11:06:27
av Fagge
16bitars talet, ska delas med decimal talet 10, eller (hex talet A) som jag borde ha sagt från början.
Jag blev ju väldans förvånad när jag inte hittade DIV instruktionen.
Varför i hela värden har dom inte lagt till den för?
T.o.m. min gamla trotjänare till tegelsten 68HC11 hade både DIV & FDIV i sin instruktions uppsättning.
Postat: 17 september 2008, 11:38:48
av sodjan
> 16bitars talet, ska delas med decimal talet 10, eller (hex talet A ) som jag borde ha sagt från början
Det gjorde du och det gör den "kod" jag postade...
Eller vad menar du ?
Postat: 17 september 2008, 12:23:54
av ahlsten
Fagge: 68HC11an kör CISC, AVR kör RISC