![]() |
Tutorial for learning avr assembler language of AVR-single-chip-processors AT90Sxxxx of ATMEL using practical examples. Simple 8-bit-digital-to-analog-converter using a R/2R-network |
A R/2R-network
is made of resistors like shown in the picture. The bits, either at 0 or the operating
voltage, enter the network via a resistor of a double value than the rest of the network.
Each bit contributes its part to the resulting voltage on the output. Believe it or not,
this works fine. The commercial Digital-to-Analog-Converters have those resistor networks
within their integrated circuits.R2R-network calculation tool, (C)2004 info!at!avr-asm-tutorial.net ------------------------------------------------------------------ N bits resolution: nr=8[bits], Bits=00000000 Voltages: ub=5.000[V], ul=0.000[V], uh=5.000[V] Resistors: R1= 51k0, R2=100k0 Input combinations and output voltages 00000000: 0.000[V] 00000001: 0.019[V] (Delta= 18.69[mV]) 00000010: 0.038[V] (Delta= 19.06[mV]) 00000011: 0.056[V] (Delta= 18.69[mV]) 00000100: 0.076[V] (Delta= 19.62[mV]) 00000101: 0.095[V] (Delta= 18.69[mV]) 00000110: 0.114[V] (Delta= 19.06[mV]) 00000111: 0.132[V] (Delta= 18.69[mV]) 00001000: 0.153[V] (Delta= 20.67[mV]) 00001001: 0.172[V] (Delta= 18.69[mV]) 00001010: 0.191[V] (Delta= 19.06[mV]) 00001011: 0.210[V] (Delta= 18.69[mV]) 00001100: 0.229[V] (Delta= 19.62[mV]) 00001101: 0.248[V] (Delta= 18.69[mV]) 00001110: 0.267[V] (Delta= 19.06[mV]) 00001111: 0.286[V] (Delta= 18.69[mV]) 00010000: 0.308[V] (Delta= 22.72[mV]) 00010001: 0.327[V] (Delta= 18.69[mV]) 00010010: 0.346[V] (Delta= 19.06[mV]) 00010011: 0.365[V] (Delta= 18.69[mV]) 00010100: 0.384[V] (Delta= 19.62[mV]) 00010101: 0.403[V] (Delta= 18.69[mV]) 00010110: 0.422[V] (Delta= 19.06[mV]) 00010111: 0.441[V] (Delta= 18.69[mV]) 00011000: 0.462[V] (Delta= 20.67[mV]) ... 01111110: 2.446[V] (Delta= 19.06[mV]) 01111111: 2.465[V] (Delta= 18.69[mV]) 10000000: 2.517[V] (Delta= 51.72[mV]) 10000001: 2.535[V] (Delta= 18.69[mV]) 10000010: 2.554[V] (Delta= 19.06[mV]) 10000011: 2.573[V] (Delta= 18.69[mV]) ...Note the extraordinary step in voltage when Bit 7 goes from low to high! It jumps up more than two ordinary steps. This might be unacceptable for an 8-bit-network, but its fine for a four-bit-network.
; **************************************************************
; * R/2R-Network produces a sawtooth signal on Port D *
; * (C)2005 by info!at!avr-asm-tutorial.net *
; **************************************************************
;
.INCLUDE "8515def.inc"
;
; Register definitions
;
.DEF rmp = R16 ; Multipurpose register
;
ldi rmp,0xFF; Set all pins of Port D as output
out DDRD,rmp
sawtooth:
out PORTD,rmp
inc rmp
rjmp sawtooth
The result is somewhat surprising. Doesn't look like a sawtooth but like a saw that has
done some efforts in cutting steel.
; **************************************************************
; * R/2R-Network produces a sawtooth signal on Port D *
; * (C)2005 by info!at!avr-asm-tutorial.net *
; **************************************************************
;
.INCLUDE "8515def.inc"
;
; Register definitions
;
.DEF rmp = R16 ; Multipurpose register
;
ldi rmp,0xFF; Set all pins of Port D as output
out DDRD,rmp
sawtooth:
out PORTD,rmp
inc rmp
andi rmp,0x7F ; set bit 7 to zero
rjmp sawtooth
That looks much better now.
Here's the result of exchanging the CA3140 buffer to a cheaper 741 opamp. The 741 is
neither operating near the operating voltage nor does he work near ground. So the
voltages of our R/2R-network would have to be limited from a minimum of approximately 2
Volts to a maximum of 4 Volts.
; **************************************************************
; * R/2R-Network produces a triangle signal on Port D *
; * (C)2005 by info!at!avr-asm-tutorial.net *
; **************************************************************
;
.INCLUDE "8515def.inc"
;
; Register definitions
;
.DEF rmp = R16 ; Multipurpose register
.DEF rdl = R17 ; Delay counter
;
; Constants
;
.EQU maxAmp = 127 ; Maximum amplitude setting
.EQU delay = 1 ; Delay, higher value causes lower frequency
;
; Main program start
;
ldi rmp,0xFF; Set all pins of Port D as output
out DDRD,rmp
triangle:
clr rmp
loopup:
out PORTD,rmp ; to port
ldi rdl,delay
delayup:
dec rdl
brne delayup
inc rmp ; next higher value
cpi rmp,maxAmp
brcs loopup
loopdwn:
out PORTD,rmp
ldi rdl,delay
delaydwn:
dec rdl
brne delaydwn
dec rmp
brne loopdwn
rjmp triangle ; and again forever
To the left, the voltage is limited to 2.5 V, to the right it's the full 5.0 V.
; **************************************************************
; * Produces a sinewave on a R/2R-network connected to PORTD *
; * (C)2005 by avr-asm-tutorial.net *
; **************************************************************
;
.INCLUDE "8515def.inc"
;
; Register definitions
;
.DEF rmp = R16 ; Multipurpose register
;
; Start of program source code
;
ldi rmp,0xFF ; Set all pins of port D to be output
out DDRD,rmp
ldi ZH,HIGH(2*SineTable) ; Point Z to Table in flash
ldi ZL,LOW(2*SineTable)
clr rmp
loop1:
nop
nop
nop
loop2:
lpm ; Read from table
out PORTD,R0 ; Write value to port D
adiw ZL,1 ; point to next value
dec rmp ; End of Table reached
brne loop1
ldi ZH,HIGH(2*SineTable) ; Point Z to Table in flash
ldi ZL,LOW(2*SineTable)
rjmp loop2
;
; End of source code
;
; Include sinewave table
;
.INCLUDE "sine8_25.txt"
;
; End of program
;
;
; Sinewave table for 8 bit D/A
; VCC=5.000V, uLow=0.000V, uHigh=2.500V
; (generated by sinewave.pas)
;
Sinetable:
.DB 64,65,67,68,70,72,73,75
.DB 76,78,79,81,82,84,85,87
.DB 88,90,91,92,94,95,97,98
.DB 99,100,102,103,104,105,107,108
.DB 109,110,111,112,113,114,115,116
.DB 117,118,118,119,120,121,121,122
.DB 123,123,124,124,125,125,126,126
.DB 126,127,127,127,127,127,127,127
.DB 128,127,127,127,127,127,127,127
.DB 126,126,126,125,125,124,124,123
.DB 123,122,121,121,120,119,118,118
.DB 117,116,115,114,113,112,111,110
.DB 109,108,107,105,104,103,102,100
.DB 99,98,97,95,94,92,91,90
.DB 88,87,85,84,82,81,79,78
.DB 76,75,73,72,70,68,67,65
.DB 64,62,61,59,58,56,54,53
.DB 51,50,48,47,45,44,42,41
.DB 39,38,36,35,34,32,31,30
.DB 28,27,26,25,23,22,21,20
.DB 19,18,17,15,14,13,13,12
.DB 11,10,9,8,8,7,6,5
.DB 5,4,4,3,3,2,2,2
.DB 1,1,1,0,0,0,0,0
.DB 0,0,0,0,0,0,1,1
.DB 1,2,2,2,3,3,4,4
.DB 5,5,6,7,8,8,9,10
.DB 11,12,13,13,14,15,17,18
.DB 19,20,21,22,23,25,26,27
.DB 28,30,31,32,34,35,36,38
.DB 39,41,42,44,45,47,48,50
.DB 51,53,54,56,58,59,61,62
That's all. It produces a nice sine. You wouldn't expect here to be a digital AVR
at work but a nice and clean LC-oscillator.
; ******************************************************************
; * Music on an STK200, using a R/2R network for playing tunes *
; * PortD has eight keys (active low), PortB generates the *
; * output for the R/2R-network, plays notes when key is activated *
; * ATMEL AT90S8515 at 4 Mcs/s *
; * (C)2005 by info!at!avr-asm-tutorial.net *
; ******************************************************************
;
.NOLIST
.INCLUDE "8515def.inc"
.LIST
;
; Constants
;
.EQU clock = 4000000 ; processor clock
.EQU cNSine = 32 ; Table length of sine wave
;
.DEF rLen = R1 ; length of delay for the sine wave
.DEF rCnt = R2 ; Counter for length delay
.DEF rmp = R16 ; multi purpose register
.DEF rTab = R17 ; counter for table length
;
ldi rmp,0xFF ; all bits of port B = output => R/2R network
out DDRB,rmp ; set data direction register
wtloop:
in rmp,PIND ; read the keys
cpi rmp,0xFF ; all keys inactive?
breq wtloop ; yes, wait until active
ldi ZH,HIGH(2*MusicTable) ; Point Z to music table
ldi ZL,LOW(2*MusicTable)
tabloop:
rol rmp ; rotate next lowest bit into carry
brcc tabfound ; found that key pressed
adiw ZL,1 ; point Z to next table value
rjmp tabloop ; check next bit
tabfound:
lpm ; read music table value to R0
mov rlen,R0 ; copy to delay, R0 used otherwise
;
; Play a tone until all keys are inactive
;
startsine:
ldi ZH,HIGH(2*SineTable) ; Set Z to sine wave table
ldi ZL,LOW(2*SineTable)
ldi rTab,cNSine ; Length of SineWave table
;
; The following code is optimized for reaching equal runtimes
; for all loop runs, clocks cycles needed are calculated
; for all instructions after their execution,
; clock cycles during table read / clock cycles at table end
;
loopsine:
in rmp,PIND ; 1/- Check if input still active
cpi rmp,0xFF ; 2/-
breq wtloop ; 3/- key not active any more, restart
nop ; 4/- delay to synchronize
loopnext:
lpm ; 7/3 read sinewave value to R0
out PORTB,R0 ; 8/4 copy to R/2R network
mov rCnt,rLen ; 9/5 Set delay counter value
; delay loop, requires 3*rLen-1 clock cycles
loopdelay:
dec rCnt ; (1) next counter value
brne loopdelay ; (2/1) not yet zero
; 3*rLen+8/3*rLen+4 at end of delay loop
adiw ZL,1 ; 3*rLen+10/3*rLen+6 next value in table
dec rTab ; 3*rLen+11/3*rLen+7 table counter
brne loopsine ; 3*rLen+13/3*rLen+8 play next value in table
ldi ZH,HIGH(2*SineTable) ; -/3*rLen+9 reload first table value
ldi ZL,LOW(2*SineTable) ; -/3*rLen+10
ldi rTab,cNSine ; -/3*rLen+11 Length of sine wave table
rjmp loopnext ; -/3*rLen+13 restart loop
;
; Table for delays to generate the 8 different frequencies
;
; frequency = clock / TableLength / ( 3 * rLen + 13 )
; rLen = ( clock /TableLength / frequency - 13 ) / 3
;
MusicTable:
; f=261.6 293.7 329.6 349.2 392.0 440.0 493.9 523.2 (should be)
.DB 155, 138, 122, 115, 102, 90, 80, 75
; f=261.5 292.7 329.8 349.2 391.9 441.7 494.1 525.2 (is really)
; differences caused by rounding and the limited resolution)
;
; Sinewave table for 8 bit D/A
; Table length = 32 values
; VCC=5.000V, uLow=0.000V, uHigh=2.500V
; (generated by sinewave.pas)
;
Sinetable:
.DB 64,76,88,99,109,117,123,126
.DB 128,126,123,117,109,99,88,76
.DB 64,51,39,28,19,11,5,1
.DB 0,1,5,11,19,28,39,51
;
; End of program
;