![]() |
Tutorial for learning avr assembler language of AVR-single-chip-processors AT90S, ATtiny, ATmega, ATxmega of ATMEL using practical examples. Simple 8-bit-digital-to-analog-converter using a R/2R-network |
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 it would be 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
; **************************************************************
; * 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
; **************************************************************
; * 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
ldi rdl,delay ; one clock cycle
delay(Up/Down):
dec rdl ; one clock cycle
brne delay ; one clock cycle if zero flag set, two clock cycles if not
This requires the following clock cycles:
out PORTD,rmp ; one clock cycle
; (DelayUp/Dwn) 3 * delay
inc/dec rmp ; one clock cycle
cpi rmp,maxAmp ; in up cycles only, one clock
brcs/brne Loopup/Loopdwn, one clock if condition true, two if false
So the LoopUp requires the following clock cycles:
; **************************************************************
; * 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
; ******************************************************************
; * 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
;