Path:
Home =>
AVR-Overview =>
Binary calculations =>
Fixed decimal => 10-bit-conversion
(Diese Seite in Deutsch:
)
10-bit binary to 4-digit fixed decimal conversion in AVR Assembler
; Demonstrates floating point conversion
; in Assembler, (C)2003 www.avr-asm-tutorial.net
;
; The task: You read in a 10-bit result of an
; analogue-digital-converter, number is in the
; range from hex 0000 to 03FF.
; You need to convert this into a floating point
; number in the range from 0.000 to 5.000 Volt
;
; The program scheme:
; 1. Check that the number is smaller than $0400.
; Prevent illegal overflows during the
; following multiplication.
; 2. Multiplication by 320,313 (hex 04E338).
; That step multiplies by 5,000, 65536 and
; divides by 1023 in one step!
; 3. Round the result and cut the last two bytes
; of the result.
; This step divides by 65536 by ignoring the
; last two bytes of the result. Before doing
; that, bit 15 is used to round the result.
; 4. Convert the resulting word to ASCII and set
; the correct decimal point
; The resulting word in the range from 0 to
; 5.000 is displayed in ASCII-characters
;
; The registers used:
; The routines use the registers R10..R1 without
; saving these before. Also required is a multi-
; purpose register called rmp, located in the
; upper half of the registers. Please take care
; that this register doesn't conflict with the
; register use in the rest of your program.
;
; When entering the routine the 10-bit number is
; expected in the register pair R2:R1.
; If the number is greater than $03FF then the
; check routine returns with the carry flag set,
; and the resulting string in R5:R6:R7:R8:R9:R10
; is set to the null-terminated ASCII-string
; "E.EEEE".
; The multiplication uses R6:R5:R4:R3 to hold
; the multiplicator 320.313 (is shifted left
; max. ten times during multiplication)
; The result of the multiplication is calculated
; in the registers R10:R9:R8:R7.
; The result of the so called division by 65536
; by just ignoring R8:R7 in the result, is in
; R10:R9. R10:R9 is rounded, depending on the
; highest bit of R8, and the result is copied to
; R2:R1.
; Conversion to an ASCII-string uses the input
; in R2:R1, the register pair R4:R3 as a divisor
; for conversion, and places the ASCII result
; string to R5:R6:R7:R8:R9:R10 (null-terminated).
;
; Other conventions:
; The conversion uses subroutines and the stack.
; The stack must work fine for the use of three
; levels (six bytes SRAM).
;
; Conversion times:
; The whole routine requires 326 clock cycles
; maximum (converting $03FF), and 111 clock cycles
; minimum (converting $0000). At 4 MHz the times
; are 81.25 microseconds resp. 27.5 microseconds.
;
; Definitions:
; Registers
.DEF rmp = R16 ; used as multi-purpose register
;
; AVR type
; Tested for type AT90S8515, only required for
; stack setting, routines work fine with other
; AT90S-types also
.NOLIST
.INCLUDE "8515def.inc"
.LIST
;
; Start of test program
;
; Just writes a number to R2:R1 and starts the
; conversion routine, for test purposes only
;
.CSEG
.ORG $0000
rjmp main
;
main:
ldi rmp,HIGH(RAMEND) ; Set the stack
out SPH,rmp
ldi rmp,LOW(RAMEND)
out SPL,rmp
ldi rmp,$03 ; Convert $03FF
mov R2,rmp
ldi rmp,$FF
mov R1,rmp
rcall fpconv10 ; call the conversion routine
no_end: ; unlimited loop, when done
rjmp no_end
;
; Conversion routine wrapper, calls the different conversion steps
;
fpconv10:
rcall fpconv10c ; Check the input value in R2:R1
brcs fpconv10e ; if carry set, set "E.EEE"
rcall fpconv10m ; multiplicate by 320,313
rcall fpconv10r ; round and divide by 65536
rcall fpconv10a ; convert to ASCII string
rjmp fpconv10f ; set decimal point and null-termination
fpconv10e:
ldi rmp,'E' ; set error condition to result string
mov R5,rmp
mov R7,rmp
mov R8,rmp
mov R9, rmp
fpconv10f:
ldi rmp,'.' ; set decimal point
mov R6,rmp
clr rmp ; null-terminate ASCII string
mov R10,rmp
ret ; all done
;
; Subroutine inputcheck
;
fpconv10c:
ldi rmp,$03 ; compare MSB with 03
cp rmp,R2 ; if R2>$03, set carry on return
ret
;
; Subroutine multiplication by 320,313
;
; Starting conditions:
; +---+---+
; | R2+ R1| Input number
; +---+---+
; +---+---+---+---+
; | R6| R5| R4| R3| Multiplicant 320.313 = $00 04 E3 38
; | 00| 04| E3| 38|
; +---+---+---+---+
; +---+---+---+---+
; |R10| R9| R8| R7| Result
; | 00| 00| 00| 00|
; +---+---+---+---+
;
fpconv10m:
clr R6 ; set the multiplicant to 320.313
ldi rmp,$04
mov R5,rmp
ldi rmp,$E3
mov R4,rmp
ldi rmp,$38
mov R3,rmp
clr R10 ; clear the result
clr R9
clr R8
clr R7
fpconv10m1:
mov rmp,R1 ; check if the number is clear
or rmp,R2 ; any bit of the word a one?
brne fpconv10m2 ; still one's, go on convert
ret ; ready, return back
fpconv10m2:
lsr R2 ; shift MSB to the right (div by 2)
ror R1 ; rotate LSB to the right and set bit 7
brcc fpconv10m3 ; if the lowest bit was 0, then skip adding
add R7,R3 ; add the number in R6:R5:R4:R3 to the result
adc R8,R4
adc R9,R5
adc R10,R6
fpconv10m3:
lsl R3 ; multiply R6:R5:R4:R3 by 2
rol R4
rol R5
rol R6
rjmp fpconv10m1 ; repeat for next bit
;
; Round the value in R10:R9 with the value in bit 7 of R8
;
fpconv10r:
clr rmp ; put zero to rmp
lsl R8 ; rotate bit 7 to carry
adc R9,rmp ; add LSB with carry
adc R10,rmp ; add MSB with carry
mov R2,R10 ; copy the value to R2:R1 (divide by 65536)
mov R1,R9
ret
;
; Convert the word in R2:R1 to an ASCII string in R5:R6:R7:R8:R9:R10
;
; +---+---+
; + R2| R1| Input value 0..5,000
; +---+---+
; +---+---+
; | R4| R3| Decimal divider value
; +---+---+
; +---+---+---+---+---+---+
; | R5| R6| R7| R8| R9|R10| Resulting ASCII string (for input value 5,000)
; |'5'|'.'|'0'|'0'|'0'|$00| null-terminated
; +---+---+---+---+---+---+
;
fpconv10a:
ldi rmp,HIGH(1000) ; Set the decimal divider value to 1,000
mov R4,rmp
ldi rmp,LOW(1000)
mov R3,rmp
rcall fpconv10d ; get ASCII digit by repeated subtraction
mov R5,rmp ; set thousands string char
clr R4 ; Set the decimal divider value to 100
ldi rmp,100
mov R3,rmp
rcall fpconv10d ; get the next ASCII digit
mov R7,rmp ; set hundreds string char
ldi rmp,10 ; Set the decimal divider value to 10
mov R3,rmp
rcall fpconv10d ; get the next ASCII digit
mov R8,rmp ; set tens string char
ldi rmp,'0' ; convert the rest to an ASCII char
add rmp,R1
mov R9,rmp ; set ones string char
ret
;
; Convert binary word in R2:R1 to a decimal digit by substracting
; the decimal divider value in R4:R3 (1000, 100, 10)
;
fpconv10d:
ldi rmp,'0' ; start with decimal value 0
fpconv10d1:
cp R1,R3 ; Compare word with decimal divider value
cpc R2,R4
brcc fpconv10d2 ; Carry clear, subtract divider value
ret ; done subtraction
fpconv10d2:
sub R1,R3 ; subtract divider value
sbc R2,R4
inc rmp ; up one digit
rjmp fpconv10d1 ; once again
;
; End of floating point conversion routines
;
;
; End of conversion test routine
;
©2003 by http://www.avr-asm-tutorial.net