Söker BIN2BCD rutin till AVR. "Nu BCD2BIN"

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
Fagge
Inlägg: 3930
Blev medlem: 27 maj 2003, 13:59:51
Ort: Blekinge

Inlägg av Fagge »

Sodjan: Eller vad menar du ?.
Dessvärre är det så att jag inte begriper ett jota av den så kallade koden du skrev. :wink:

Jag är med på att …
1 Skift år höger, ger en division x/2
2 Skift ger x/4
3 Skift ger x/8
Osv…
Men denna principen funkar ju inte, eftersom jag skulle dela med talet A.
Å där slutar mina binära mattematiska kunskaper. :?

Såvida man inte först kör igenom talet genom Swech / exile BIN2BCD rutin,> skippa sista siffran, > & sen omvandla tillbaka talet till binärform med en BCD2BIN rutin, som jag inte har hittat änu.
thepirateboy
EF Sponsor
Inlägg: 2109
Blev medlem: 27 augusti 2005, 20:57:58
Ort: Borlänge

Inlägg av thepirateboy »

Sodjans kod är väl att man gör fyra additioner. "16 bit tal"/16 + "16 bit tal"/32 + "16 bit tal"/256 + "16 bit tal"/512 = resultat

Resultatet blir då att du gjort en division med ungefär 10.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Du har ett tal/värde i en 16-bitars variabel som ett rent binärt värde, eller hur ?
Och du vill ha detta 16-bitars binära värde dividerat med konstanten 10 (decimalt, eller 0A i HEX), not ?

Det är exakt det som det som jag postade gör !!

Det som man gör är att skriva om (faktorisera) "dela med 10" så att det
skrivs som en serieutveckling av additioner av delningar med jämna
potenser av 2 (vilket är enkelt att fixa med shift/rotate).

Ta t.ex (för tydlighets skull omskrivet decimalt, men det spelar inge roll).

Säg att du vill dela 12.850 med 10 (och få 1,285)

12850/16 = 803
12850/32 = 401
12850/256 = 50
12850/512 = 25

803 + 401 + 50 + 25 = 1279

Notera att det blev ett visst fel, det beror på att jag hade kört med
ett 0.5% acceptabalt fel. Om jag ändrar det till 0.05% så får jag istället :

; ALGORITHM:
; Clear accumulator
; Add input / 16 to accumulator
; Add input / 32 to accumulator
; Add input / 256 to accumulator
; Add input / 512 to accumulator
; Add input / 4096 to accumulator
; Add input / 8192 to accumulator
; Move accumulator to result

Och om vi kör med det så blir det :

12850/16 = 803
12850/32 = 401
12850/256 = 50
12850/512 = 25
12850/4096 = 3
12850/8192 = 1

803 + 401 + 50 + 25 + 3 + 1 = 1283

Om jag ändrar till 0.01% precission och även lägger till "round result" så får man:

; ALGORITHM:
; Clear accumulator
; Add input / 8 to accumulator
; Add input / 16 to accumulator
; Add input / 128 to accumulator
; Add input / 256 to accumulator
; Add input / 2048 to accumulator
; Add input / 4096 to accumulator
; Add input / 32768 to accumulator
; Shift accumulator right (LSb to carry)
; If carry set, increment accumulator
; Move accumulator to result
;

Vilket ger :

12850/8 = 1606
12850/16 = 803
12850/128 = 100
12850/256 = 50
12850/2048 = 6
12850/4096 = 3
12850/32768 = 0

1606 + 803 + 100 + 50 + 6 + 3 + 0 = 2568

Ett shift till höger ger 1284.
Ingen carry, så ingen extra ADD.
Resultatet ligger 1 från rätt värde.

Du kan köra fram andra algoritmer för andra precissioner eller nämnare
här : http://www.piclist.com/techref/piclist/ ... divmul.htm
Där får du även den kompletta koden (kommenterad och allt).
86 instruktioner för den mest komplexa algoritmen ovan.
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Inlägg av jesse »

Binary calculations in AVR Assembler:

http://www.avr-asm-tutorial.net/avr_en/calc/index.html

including:

# Conversion of number formats

* Basics on number formats
* General rules of the assembler routines
* ASCII to binary conversion
* BCD to binary conversion
* Binary multplication by 10
* Binary to ASCII conversion
* Binary to BCD conversion
* Binary to hex conversion
* Hex to binary conversion
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Sidan saknar division med en *konstant*, vilket är ett specialfall som
ofta kan optimeras ganska hårt. Notera att den algoritm som jag
föreslog inte är arkitekturspecifik, den fungerar lika bra för AVR.
Användarvisningsbild
Swech
EF Sponsor
Inlägg: 4750
Blev medlem: 6 november 2006, 21:43:35
Ort: Munkedal, Sverige (Sweden)
Kontakt:

Inlägg av Swech »

Delar godtyckligt 16 bitars tal med ett annat

Swech

Kod: Markera allt


;----------------------------
DIV16:
;       >       R16:=VALUE 1 LO
;       >       R17:=VALUE 1 HI
;       >       R18:=VALUE 2 LO
;       >       R19:=VALUE 2 HI
;       <       R20,21:=VAL1/VAL2
	LDI	R20,0
	LDI	R21,0

	CPI	R18,0
	CPC	R19,S_ZERO	;CHECK IF DIVISOR =0
	BREQ	DIV16_ERR       ; YES ERROR

	CP	R16,R18       	;CHECK IF DIVISOR > NR TO DIVIDE
	CPC	R17,R19
	BRLO	DIV16_ERR  	; YES ERROR

	LDI	R20,0
	LDI	R21,0

	LDI	YL,1
	LDI	YH,0
DIV16_L1:
	CP	R16,R18
	CPC	R17,R19
	BRLO	DIV16_SK1      	;CALC LOWEST DIVISOR
	MUL2W	YL,YH
	MUL2W	R18,R19
	RJMP	DIV16_L1

DIV16_SK1:
	DIV2W	R18,R19
	DIV2W	YL,YH

DIV16_L2:
	CP    	R16,R18
	CPC	R17,R19
	BRLO	DIV16_SK2

	SUB  	R16,R18
	SBC	R17,R19
	ADD	R20,YL
	ADC	R21,YH
DIV16_SK2:
	DIV2W	R18,R19
	DIV2W	YL,YH
	CP	R18,S_ZERO
	BRNE	DIV16_L2
	CP	R19,S_ZERO
	BRNE	DIV16_L2
DIV16_ERR:
	RET



.MACRO	DIV2W		;REG LOW, REG HIGH
	LSR		@1 			;SHIFT HIGH REG
	ROR	  @0  		;SHIFT LOW REG
.ENDMACRO

.MACRO	MUL2W		;REG LOW, REG HIGH
	LSL		@0 			;SHIFT HIGH REG
	ROL	  @1  		;SHIFT LOW REG
.ENDMACRO

sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Jag vet inte vilka krav som Fagge hade på prestanda, men notera att
en rutin för att dividera med ett givet/fast tal ofta är betydligt snabbare
än en som dividerar med ett godtyckligt tal.
Användarvisningsbild
Fagge
Inlägg: 3930
Blev medlem: 27 maj 2003, 13:59:51
Ort: Blekinge

Inlägg av Fagge »

Aha sodjan: Nu trillade polletten ner. :)
Ang. prestandan, några ms hit eller dit har ingen betydelse i mitt fall.
Sen så kommer jag nog att behöva dela med varierande div tal, så det skadar inte att ha en flexibel delningsrutin!.

Jesse: Tackar där fanns en hel del exempel som kan komma till nytta!.
Körde nyss igenom ett av div exemplen där i Avrstudio & det funkade fint!. :tumupp:

Swech: Där har vi ett exempel till ja. :tumupp:
Underbart nu har man kvällen & natten räddad igen!.
Såna här projekt är absolut inte nyttiga för samboförhållandet :eh:
Användarvisningsbild
Swech
EF Sponsor
Inlägg: 4750
Blev medlem: 6 november 2006, 21:43:35
Ort: Munkedal, Sverige (Sweden)
Kontakt:

Inlägg av Swech »

Att underhålla kod där man lägger in specifika konstanter kan
äta upp alla dessa millisekunder som man tjänat och resultera i
många onödiga timmars jobb..
Naturligtvis om det är tidskritiskt.. men då kan man oftast ta sig
runt problemet och helt undvika divisioner...

Swech
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Eller skriva den så att alla divisioner görs med 2, 4, 8, 16 o.s.v.
Att dela med 10 låter som något för att få kommat rätt på en display.
Man kan ju i just det fallet även flytta kommat istället (på själva displayen).
Användarvisningsbild
Fagge
Inlägg: 3930
Blev medlem: 27 maj 2003, 13:59:51
Ort: Blekinge

Inlägg av Fagge »

Jo mycket riktigt sodjan, så är det batteri spänningen som ska mätas & presenteras på en display.
Och då valde jag att multiplicera inkommande 8bitars värde med 196 (C4) . Så ADC-värdet FF blir C33C.
Detta blir direkt anpassat för decimal visning efter att man kört bin2bcd rutinen.

Men värdet måste dubbleras innan visning eftersom jag har satt en späningsdelare på A/D ingången som halverar spänningen , eftersom batteriet kan nå över 8V.
Men att multiplicera ett så högt 16bitarsvärde med 2, blir ju för klumpigt 24bitar.
bin2bcd rutinen kan ju bara hantera 16bitars tal.
Därför tänkte jag dela det inkommande & multiplicerande värdet med 10, för att sedan multiplicera det med 2 & sen köra bcd rutinen.
Men nu när jag fick en fungerande Divisions rutin, så kunde jag lika gärna direkt dividera med 5, & på så sätt slippa den sista multiplikationen.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

> Och då valde jag att multiplicera inkommande 8bitars värde med 196...

> jag har satt en späningsdelare på A/D ingången som halverar spänningen

Här är ett bra exempel på hur man med lite "clever design" kan förenkla
kodningen en hel del. Du skulle kunnat ha valt en spänningsdelning som
gav dig en bättre faktor än 196 ovan. Gärna en jämn potens av 2.
Kanske satt en pott som spänningsdelare och sedan trimmat in rutinen
mot en känd spännining med potten.

Ofta kan mycket beräkningar undvikas genom att man tänker på
det redan från början.
Användarvisningsbild
Fagge
Inlägg: 3930
Blev medlem: 27 maj 2003, 13:59:51
Ort: Blekinge

Re:

Inlägg av Fagge »

Swech skrev: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

Hej Igen.
Jag har nu stött på en liten begränsning i Ovanstående Bin2Bcd rutin, som jag inte reder ut.
Allt funkar perfekt, men ”bara med tal upp till den magiska siffran dec 32768 eller 8000hex.
Å givetvis så behöver jag nu hela spannet som ett 16bitars tal kan ge.
Vågar man be kodskaparen att ta sig en liten titt på problemet?
/Fagge
Användarvisningsbild
exile
EF Sponsor
Inlägg: 496
Blev medlem: 21 oktober 2005, 23:32:07

Re: Söker BIN2BCD rutin till AVR. "Nu BCD2BIN"

Inlägg av exile »

Den använder signed "test", så

Kod: Markera allt

BRLT   DIV_NEXT
ersätt

Kod: Markera allt

BRLO   DIV_NEXT
bör lösa problemet....
Användarvisningsbild
Fagge
Inlägg: 3930
Blev medlem: 27 maj 2003, 13:59:51
Ort: Blekinge

Re: Söker BIN2BCD rutin till AVR. "Nu BCD2BIN"

Inlägg av Fagge »

Där satt den :tumupp:
Tusen tack, nu kan jag lugnt fortsätta slutföra EL-cykelns trippmätare :)
Skriv svar