Melodiengenerator
von Alexander "Electronicfox" Fuchs
Hier soll gezeigt werden, dass man mit dem ATTiny13 auch
I²C-Projekte bauen kann. Der PCD3312C ist meistens in alten Telefonen
der 80er-Jahre zu finden. Direkt unter der Tastatur, nahe dem µC 80C49.
Der PCD3312C ist bei Tastentelefonen nur ein DTMF-Prozessor, aber ein
Blick ins Datenblatt verrät uns, dass auch ein Modemton- und ein
Tongenerator drinnen ist. Der Tongenerator wird von mir verwendet und
die passende Software im ATTiny13 liefert dazu 2 passende Melodien.
Eine Melodie ist eine Geheimmelodie welche nur durch betätigen eines
Tasters abgespielt werden kann. Allerdings erst wenn man zuerst die
Taste drückt und dann die Betriebsspannung einschaltet. Sobald die
Melodie ertönt kann man den Taster loslassen. Der Quarz kann zwischen 2
bis 5MHz haben, je nach Verfügbarkeit oder gewünschter Tonhöhe. Ich hab
einen mit 4,433MHz verwendet, welchen ich aus einem Videorecorder
ausgebaut habe. Die Geschwindigkeit der Melodie kann man mit den
Fuse-Einstellungen bestimmen. Ich wählte den internen Takt mit 9,8MHz
und einer DIV8. Hier habe ich wegen der kleinen Software sehr viele
externe Freiheiten eingebaut um mich an den Vorgaben halten zu können.
Die
Lautsprecherendstufe ist ein TDA7233. Wenn man andere Endstufen
verwenden will geht das auch. Ich hätte auch einen TBA820M oder LM386N
verwenden können. Wenn man den Generator an Endstufen oder Mischpulten
anschließen möchte kann man das direkt am PCD3312C tun.
Ein Soundcheck ist hier zu sehen und zu hören:
http://www.youtube.com/watch?v=cPBc2Q2RfIw
In
der Datei Klanggenerator.zip befinden sich Assembler-, HEX- und
Projektdateien, u.a. auch das Schaltbild für einen kleinen automatisch
Klangregler.
Download: Klanggenerator.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
.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.
sbic PINB,DENA
rjmp SONGA
rjmp SONGB
SONGA:
rcall SEKUNDE
ldi i2cadr,72 ; PCD3312C
rcall i2c_start
rcall PCD_PAUSE
ldi i2cdata,$38
rcall i2c_do_transfer
rcall i2c_stop
rcall PCD_PAUSE
rcall SEKUNDE
ldi i2cadr,72 ; PCD3312C
rcall i2c_start
rcall PCD_PAUSE
ldi i2cdata,$30 ;
rcall i2c_do_transfer
rcall i2c_stop
rcall PCD_PAUSE
rcall SEKUNDE
rjmp SOUNDOUT
SOUNDOUT:
ldi zh, high(SONG1*2)
ldi zl, low(SONG1*2)
rjmp spkout
spkout:
lpm
mov ar0, r0 ;get lentgh
_ldo1:
adiw ZH:ZL, 1
lpm ;get pattern
ldi i2cadr,72 ; PCD3312C
rcall i2c_start
rcall PCD_PAUSE
mov i2cdata, r0 ;
rcall i2c_do_transfer
rcall i2c_stop
rcall PCD_PAUSE
rcall SEKUNDE
dec ar0 ;count down
brne _ldo1
rjmp SOUNDOUT
SONGB:
ldi zh, high(SONG2*2)
ldi zl, low(SONG2*2)
rjmp spksong
spksong:
lpm
mov ar0, r0 ;get lentgh
_ldo2:
adiw ZH:ZL, 1
lpm ;get pattern
ldi i2cadr,72 ; PCD3312C
rcall i2c_start
rcall PCD_PAUSE
mov i2cdata, r0 ;
rcall i2c_do_transfer
rcall i2c_stop
rcall PCD_PAUSE
rcall SEKUNDE
dec ar0 ;count down
brne _ldo2
rjmp SONGB
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
; =============================
PCD_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
; 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
SONG1:
.db 37, $35
.db $35, $35
.db $35, $35
.db $35, $35
.db $36, $00
.db $00, $35
.db $35, $35
.db $31, $35
.db $35, $35
.db $36, $00
.db $31, $36
.db $36, $36
.db $31, $36
.db $37, $37
.db $38, $00
.db $38, $38
.db $38, $38
.db $39, $39
.db $00, $00
SONG2:
.db 37, $00
.db $30, $31
.db $32, $33
.db $34, $35
.db $34, $34
.db $34, $34
.db $35, $00
.db $34, $34
.db $34, $34
.db $35, $00
.db $34, $34
.db $34, $34
.db $35, $00
.db $32, $32
.db $32, $32
.db $32, $33
.db $30, $30
.db $30, $30
.db $30, $31