Melodiengenerator           

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

  
 
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


Elektronik-Labor  Projekte   AVR   T13-Contest