Automatischer Klangregler         

von Alexander "Electronicfox" Fuchs                       
Elektronik-Labor  Projekte   AVR   T13-Contest

  
 
Wenn man mit I²C-Bus-Geschichten anfängt kann man schwer aufhören. Deshalb etwas mit einem TDA6610-2. Den findet man in vielen älteren TV-Geräten der 90er-Jahre aber auch neu bei Reichelt, Lauermann-Elektronik oder bei diversen TV-Ersatzteile-Shops. Diese Schaltung holt sich gezielt die hohen Töne aus den Ausgängen des TDA6610-2. Dafür sorgt ein Hochpass, welcher aber vorher noch einen Tiefpass zur Filterung der hörbaren Töne, vorgeschalt hat. Die gefilterten Frequenzen werden zuerst verstärkt und einem AC-DC-Konverter zu geführt und nachher im ATTiny13 ausgewertet und der TDA6610-2 über den I²C-Bus genau angepasst. Sind zu viele hohen Frequenzen vorhanden, so werden die tieferen Frequenzen angehoben und dadurch die Höhen abgesenkt. Vom Prinzip her nichts anderes als einen dynamischer Rauschlimiter, nur halt nicht so ausgeprägt.
Auch hier sind viele Freiheiten gegeben. Hier kann man alles noch nach seinen individuellen Wümschen rumprogrammieren und den Klang noch beeinflussen. Gefused wurde der ATTiny13 mit internen 9,8MHz-Oszillator oder DIV8.

Download:  DNR.zip
 
 
.include "tn13def.inc"

.cseg
.org 0

.def temp1 = r16
.def temp2 = r17
.def temp3 = r18
.def temp4 = r19
.def ar0 = r20
.def status = r21
.def i2cdelay= r24 ; Delay loop variable
.def i2cdata = r25 ; I2C data transfer register
.def i2cadr = r26 ; I2C address and direction register
.def i2cstat = r27 ; I2C bus status register
.def speca = r28
.def specb = r28


.equ SCLP = 2 ; SCL für PCD3312C
.equ DENA = 0 ; Taster für zweiten Song
.equ SDAP = 1 ; SDA für PCD3312C
.equ b_dir = 0 ; transfer direction bit in i2cadr
.equ i2crd = 1
.equ i2cwr = 0

ldi temp1, RAMEND
out SPL, temp1


clr temp1


INIT:

cbi DDRB, DENA ; DENA im Ruhezustand immer auf 1.
cbi DDRB, SDAP ; SCL im Ruhezustand immer auf 1.
cbi DDRB, SCLP ; SDA im Ruhezustand immer auf 1.

; ADC initialisieren: ADC0, REF als Referenz, Single Conversion, Vorteiler 128

ldi temp1, (1<<REFS0) | (1<<MUX1) ; Kanal 2, interne Referenzspannung 2,5V
out ADMUX, temp1
ldi temp1, (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)
out ADCSRA, temp1


ldi i2cadr,132 ; TDA6610
rcall i2c_start
rcall TDA_PAUSE
ldi i2cdata,$01
rcall i2c_do_transfer
rcall TDA_PAUSE
ldi i2cdata,55
rcall i2c_do_transfer
rcall i2c_stop
rcall TDA_PAUSE


ldi i2cadr,132 ; TDA6610
rcall i2c_start
rcall TDA_PAUSE
ldi i2cdata,$02
rcall i2c_do_transfer
rcall TDA_PAUSE
ldi i2cdata,55
rcall i2c_do_transfer
rcall i2c_stop
rcall TDA_PAUSE

ldi i2cadr,132 ; TDA6610
rcall i2c_start
rcall TDA_PAUSE
ldi i2cdata,$03
rcall i2c_do_transfer
rcall TDA_PAUSE
ldi i2cdata,31
rcall i2c_do_transfer
rcall i2c_stop
rcall TDA_PAUSE

ldi i2cadr,132 ; TDA6610
rcall i2c_start
rcall TDA_PAUSE
ldi i2cdata,$07
rcall i2c_do_transfer
rcall TDA_PAUSE
ldi i2cdata,196
rcall i2c_do_transfer
rcall i2c_stop
rcall TDA_PAUSE

ldi i2cadr,132 ; TDA6610
rcall i2c_start
rcall TDA_PAUSE
ldi i2cdata,$00
rcall i2c_do_transfer
rcall TDA_PAUSE
ldi i2cdata,194
rcall i2c_do_transfer
rcall i2c_stop
rcall TDA_PAUSE

rjmp RETOUR

RETOUR:



rcall sample_adc

ldi i2cadr,132 ; TDA6610
rcall i2c_start
rcall WARTE
ldi i2cdata,$05
rcall i2c_do_transfer
rcall WARTE
mov i2cdata,speca
rcall i2c_do_transfer
rcall i2c_stop
rcall SEKUNDE

rjmp RETOUR



sample_adc:
sbi ADCSRA, ADSC ; den ADC starten

wait_adc:
sbic ADCSRA, ADSC ; wenn der ADC fertig ist, wird dieses Bit gelöscht
rjmp wait_adc

; ADC einlesen:

in speca, ADCL ; immer zuerst LOW Byte lesen
in specb, ADCH ; danach das mittlerweile gesperrte High Byte
com speca
ldi temp1, 128
add speca, temp1



ret




SEKUNDE:
; =============================
; Warteschleifen-Generator
; 800000 Zyklen:
; -----------------------------
; warte 799995 Zyklen:
ldi temp1, $5F
MSP0a: ldi temp2, $17
MSP1a: ldi temp3, $79
MSP2a: dec temp3
brne MSP2a
dec temp2
brne MSP1a
dec temp1
brne MSP0a
; -----------------------------
; warte 3 Zyklen:
ldi temp1, $01
MSP3a: dec temp1
brne MSP3a
; -----------------------------
; warte 2 Zyklen:
nop
nop
ret
; =============================

TDA_PAUSE:
; =============================
; Warteschleifen-Generator
; 40000 Zyklen:
; -----------------------------
; warte 39999 Zyklen:
ldi temp1, $43
MP0: ldi temp2, $C6
MP1: dec temp2
brne MP1
dec temp1
brne MP0
; -----------------------------
; warte 1 Zyklus:
nop
ret

WARTE:

ldi R20, $63
WGLOOP0: ldi R21, $C9
WGLOOP1: dec R21
brne WGLOOP1
dec R20
brne WGLOOP0
ldi R20, $02
WGLOOP2: dec R20
brne WGLOOP2
ret


; Warteschleifenende

i2c_hp_delay:
ldi i2cdelay,5 ;2

i2c_hp_delay_loop:
dec i2cdelay
brne i2c_hp_delay_loop
ret

i2c_qp_delay:
ldi i2cdelay,1
i2c_qp_delay_loop:
dec i2cdelay
brne i2c_qp_delay_loop
ret


i2c_rep_start:
sbi DDRB,SCLP ; force SCL low
cbi DDRB,SDAP ; release SDA
rcall i2c_hp_delay ; half period delay
cbi DDRB,SCLP ; release SCL
rcall i2c_qp_delay ; quarter period delay

i2c_start:
mov i2cdata,i2cadr ; copy address to transmitt register
sbi DDRB,SDAP ; force SDA low
rcall i2c_qp_delay ; quarter period delay

i2c_write:
sec ; set carry flag
rol i2cdata ; shift in carry and out bit one
rjmp i2c_write_first
i2c_write_bit:
lsl i2cdata ; if transmit register empty

i2c_write_first:
breq i2c_get_ack ; goto get acknowledge
sbi DDRB,SCLP ; force SCL low

brcc i2c_write_low ; if bit high
nop ; (equalize number of cycles)
cbi DDRB,SDAP ; release SDA
rjmp i2c_write_high
i2c_write_low: ; else
sbi DDRB,SDAP ; force SDA low
rjmp i2c_write_high ; (equalize number of cycles)

i2c_write_high:
rcall i2c_hp_delay ; half period delay
cbi DDRB,SCLP ; release SCL
rcall i2c_hp_delay ; half period delay
rjmp i2c_write_bit

i2c_get_ack:

sbi DDRB,SCLP ; force SCL low
cbi DDRB,SDAP ; release SDA
rcall i2c_hp_delay ; half period delay
cbi DDRB,SCLP ; release SCL

i2c_get_ack_wait:
sbis PINB,SCLP ; wait SCL high
;(In case wait states are inserted)
rjmp i2c_get_ack_wait


clc ; clear carry flag
sbic PINB,SDAP ; if SDA is high

sec ; set carry flag
rcall i2c_hp_delay ; half period delay
ret

i2c_do_transfer:
sbrs i2cadr,b_dir ; if dir = write
rjmp i2c_write ; goto write data

i2c_read:
rol i2cstat ; store acknowledge
; (used by i2c_put_ack)
ldi i2cdata,0x01 ; data = 0x01
i2c_read_bit: ; do
sbi DDRB,SCLP ; force SCL low
rcall i2c_hp_delay ; half period delay

cbi DDRB,SCLP ; release SCL
rcall i2c_hp_delay ; half period delay

clc ; clear carry flag
sbic PINB,SDAP ; if SDA is high
sec ; set carry flag

rol i2cdata ; store data bit
brcc i2c_read_bit ; while receive register not full

i2c_put_ack:
sbi DDRB,SCLP ; force SCL low
ror i2cstat ; get status bit
brcc i2c_put_ack_low ; if bit low goto assert low
cbi DDRB,SDAP ; release SDA
rjmp i2c_put_ack_high

i2c_put_ack_low: ; else
sbi DDRB,SDAP ; force SDA low
i2c_put_ack_high:

rcall i2c_hp_delay ; half period delay
cbi DDRB,SCLP ; release SCL
i2c_put_ack_wait:
sbis PINB,SCLP ; wait SCL high
rjmp i2c_put_ack_wait
rcall i2c_hp_delay ; half period delay
ret

i2c_stop:
sbi DDRB,SCLP ; force SCL low
sbi DDRB,SDAP ; force SDA low
rcall i2c_hp_delay ; half period delay
cbi DDRB,SCLP ; release SCL
rcall i2c_qp_delay ; quarter period delay
cbi DDRB,SDAP ; release SDA
rcall i2c_hp_delay ; half period delay
ret

i2c_init:
clr i2cstat ; clear I2C status register (used
; as a temporary register)
out PORTB,i2cstat ; set I2C pins to open colector
out DDRB,i2cstat
ret





Elektronik-Labor  Projekte   AVR   T13-Contest