Převodník z pulsní na tónovou volbu


Kecy úvodem

Je to vina K16! Já v podstatě za nic nemůžu. K16 mi věnoval hromadu bordelu na vyhození, mezi jiným i nádherný stařičký černý bakelitový telefon s rotačním voličem. Dneska už téměř historický unikát.

telefén

Telefon jsem si tedy doma připojil a po domě přes místní ústřednu do vedlejší místnosti s ním telefonoval. Bohužel, VoIP brána, kterou doma máme nepodporuje pulsní volbu, takže jsem se dál nedovolal. Začal jsem tedy hledat řešení. Zjistil jsem, že existují nějaké převodníky, které se vřadí mezi telefon a ústřednu, ale já jsem nechtěl mít zase další krabičku navíc. Chtěl jsem něco pro zabudování přímo do přístroje. A jako správný bastlič jsem dal přednost vlastní výrobě.

K nastudování teorie se můžete podívat třeba do wikipedie jako já.

Našel jsem na webu i návod na takové udělátko, dokonce s mým oblíbeným procesorem PIC16F84A, mělo to ale chybku. Byl tam jako generátor DTMF použit obvod HT9200A. V GESu jsem si jich mohl objednat 1000 kusů po 9 Kč. Byl by to nádherný kreténský čin - sehnat dalších 999 Kreténů a polokreténů s podobnými přístroji... Ne, nakonec se ukázalo, že by to nebylo tak hrozné - ten obvod byl v jiných webšopech k sehnání tak za dvacku. Jenže poštovné by vyšlo na několikanásobek jeho ceny. Navíc by ten můj výtvor už nebyl originál.


HW

Takže jsem se rozhodl to celé udělat pouze s mikroprocesorem. Použil jsem již zmíněný PIC16F84A. Na port B jsem navěsil odporovou síť, která tvoří D/A převodník. Za ní je emitorový sledovač, který DTMF signálem přitěžuje linku. Kondenzátor v bázi tranzistoru vyhlazuje schody na generovaném průběhu.

Obvod se připojí paralelně na přívod telefonní linky do přístroje. Rotační volič se odpojí a připojí přímo na převodník. Místo kontaktu voliče, který je v klidu seplý vložíme do obvodu telefonu odpor několika stovek ohmů. Jeho hodnotu bude asi dobré zjistit experimentálně. Zjistil jsem, že pobočková ústředna, kterou máme doma dává do linku napětí něco málo přes 20 V a když jsem zvednul sluchátko, ten telefon linku zatížil natolik, že napětí spadlo pod 5 V. To bylo příliš málo na napájení PICu. Odpor jsem zvolil tak, aby na lince při zvednutém sluchátku zůstalo zhruba 12 V. Rotační volič je připojený tak, aby během vytáčení byl trvale sepnutý kontakt mezi D0 a D1 a pulsní spínač k nim ještě připínal D2 (viz. schéma). Paralelně ke kontaktům přerušovače jsem ještě vložil kondanzátor 100 nF - občas se stalo, že obvod díky nějakému tomu zákmitu napočítal omylem o puls víc. Tyto chyby jsem potom ještě brutálně ošetřil v kódu.

  • schéma
  • tišťák
  • osazovák
  • Mezi bázi tranzistoru a +5 V napájecího napětí jsem při oživování ještě vložil odpor 22k, abych posunul pracovní bod tranzistoru. Jinak by zhruba čtvrtina průběhu chyběla. V zapojení, ze kterého jsem ten konec obkreslil by tranzistor buzený přímo z výstupu HT9200, který zdaleka nemá takový rozkmit signálu - neklesá pod 0,45 VDD, takže se tranzistor nikdy úplně nezavře. Tenhle odpor už jsem dobastlil ze strany spojů. U tohoto projektu jsem to vůbec nějak přehnal s miniaturizací. Zlatý lampy :-)


    SW

    Činnost obvodu je možné rozdělit do několika kroků. Po přivedení napájecího napětí je vstup zároveň RA0 v logické jedničce. Vstup RA4 slouží jako vstup časovače a počítá pulsy rotačního voliče. Když skončí vytáčení, spadne vstup RA0 do logické nuly. Procesor podle počtu natočených pulsů nastaví odpovídající kmitočty a na portu B začne generovat DTMF signál. V tuto chvíli obvod běží na energii elektrolytu, dokud se nevybije. Při rychlém sledu vytáčení číslic je logickou jedničkou na RA0 generování přerušeno a obvod se vrátí do režimu počítání pulsů.

    Dlouho jsem přemýšlel, jak nejlépe vygenerovat směs dvou tónů. Nakonec jsem dospěl k myšlence, že budu tóny sčítat přímo v procesoru a na výstup posílat už hotovou směs. Procesor běží na kmitočtu 8 MHz, vzorkování mi vyšlo 50 kHz. Použil jsem přitom procesor na 4 MHz - kdyby tak šly na dvojnásobek přetaktovat procesory od Intelu! DTMF používá osm tónů. Pro zvolené vzorkování jsem pro každý tón v tabulkovém procesoru spočítal úrovně jednotlivých vzorků pro jednu periodu, pro nižší tóny v rozsahu hodnot 0 až 127, pro vyšší tóny v rozsahu 0 až 128. Rutina, která generuje DTMF signál v každém vzorku přečte z tabulky hodnotu vyššího a nižšího tónu a ty sečte. Výsledkem je tedy hodnota mezi 0 a 255, kterou zapíše na port B.

    Je jasné, že časování je kritické. Generování výstupního signálu probíhá ve smyčce, která musí mít přesný počet cyklů aby byla dodržena vzorkovací frekvence. Ze začátku jsem měl snahu to dopočítat, ale furt mi to nějak nevycházelo. Tak jsem nakonec vyměknul a doladil to podle osciloskopu. První varianta programu neměla definovanou dobu trvání DTMF tónu. Ta, pokud nebyla předčasně ukončena vytáčením další číslice, byla dána kapacitou elektrolytu na napájecím napětí. Obvod pískal, dokud se kondenzátor nevybil. Tóny byly docela dlouhé. Zrevidoval jsem kód a ve druhé variantě jsem zavedl počítadlo, které definuje dobu trvání na 80 period nižší frekvence, což je přijatelné.

    Jedním z nejtěžších úkolů bylo dosáhnout vysoké spolehlivosti počítání pulsů. Rotační volič už přeci jen nemá kontakty nové a občas se mi objevil nějaký zákmit, který se mi napočítal a obvod vygeneroval špatný signál. Výše zmíněné kondenzátory pomohly vytvarovat hrany, ale to pořád nestačilo. Občas se objevil zákmit, který spustil generování tónu ještě při natahování voliče. Proto jsem zavedl hlídání hran na vstupu RA0 ve smyčkách. Před začátkem počítání pulsů jsem pak ještě přidal časovou prodlevu potřebnou k ustálení napětí na vstupu, které narůstá pomalu - nabíjí se elektrolyt.

    Kdo by měl zájem se trochu víc pošťourat ve vzorkování, tomu přikládám tabulku.


    Zabudování do přístroje

    telefén

    Je potřeba dát pozor na to, aby se propojovací dráty nezapletly do kontaktů voliče.

    No a tady je zdroják napsaný v MPLABu:

    ;********************************************************************************
    ;*	Pulse To DTMF converter							*
    ;*	K15 2008								*
    ;*										*
    ;*	pin assignment:								*
    ;*	PORTB 	- D/A converter output						*
    ;*	RA0	- dial 1 input - HIGH during dialing				*
    ;*	RA4	- dial 2 input - dialing pulses					*
    ;********************************************************************************
    	__config _HS_OSC & _WDT_OFF & _CP_OFF  
    
    	include p16f84A.inc
    
    ;******************************************************************************
    ; variables:
    ;******************************************************************************
    
    LO_C		equ		0x0C					; lower tone sample counter
    HI_C		equ		0x0D					; higher sample counter
    LO_L		equ		0x0E					; lower tone last sample address
    HI_L		equ		0x0F					; higher tone last sample address
    LO_S		equ		0x10					; lower tone number of samples
    HI_S		equ		0x11					; higher tone number of samples
    TMP		equ		0x12					; auxiliary variable
    DUR		equ		0x13					; tone duration counter - LB
    DUR2		equ		0x14					; dial startup delay
    
    ;******************************************************************************
    ; main program:
    ;******************************************************************************
    	org 	0x0000
    
    	bsf			STATUS, RP0			; select bank 1
    	clrf		TRISB					; PORTB is output
    	bsf			OPTION_REG, T0CS		; TMR0 driven by RA4
    	bcf			STATUS, RP0			; select bank 0
    
    INIT:
    	clrf		DUR
    	movlw		D'100'
    	movwf		DUR2	
    
    DIAL_BEGIN:
    	decfsz		DUR, F
    	goto		DIAL_BEGIN	
    	decfsz		DUR2, F
    	goto		DIAL_BEGIN				; eliminate noise
    
    	clrf		TMR0
    
    ; count dialing pulses - wait for RA0 going low:
    DIAL_END:
    	btfsc		PORTA, 0
    	goto		DIAL_END
    	decfsz		DUR, F
    	goto		DIAL_END				; eliminate noise
    
    ; set dtmf tones:
    	clrf		PCLATH
    
    	movf		TMR0, W
    	call		L_BEGIN
    	movwf		LO_L
    	movf		TMR0, W
    	call		L_LEN
    	movwf		LO_S
    	movwf		LO_C
    
    	movf		TMR0, W
    	call		H_BEGIN
    	movwf		HI_L
    	movf		TMR0, W
    	call		H_LEN
    	movwf		HI_S
    	movwf		HI_C
    
    ; set duration counter:
    	movlw		80
    	movwf		DUR
    
    ; update sample counters:
    BEEP:
    	movf		LO_S, W
    	decfsz		LO_C, F
    	goto		BEEPa
    	movwf		LO_C
    BEEPa:
    	movf		HI_S, W
    	decfsz		HI_C, F
    	goto		BEEPb
    	movwf		HI_C		
    
    ; compute and set output level:
    BEEPb:
    	movlw		2
    	movwf		PCLATH
    	movf		LO_C, W
    	subwf		LO_L, W
    	call		L_DATA
    
    	movwf		TMP
    
    	movlw		3
    	movwf		PCLATH
    	movf		HI_C, W
    	subwf		HI_L, W
    	call		H_DATA
    
    	addwf		TMP, W
    	movwf		PORTB		; 15 cycles from BEEP so far... + 6 cycles of calls
    
    	nop
    	nop
    	nop
    	nop
    
    	decfsz		LO_C, W
    	goto		BEEPc
    	decfsz		DUR, F
    BEEPc:
    	goto		BEEP		; 5 cycles
    
    	clrf		PORTB
    
    ; wait for another digit:
    WAIT:
    	btfss		PORTA, 0
    	goto		WAIT
    	decfsz		DUR, F
    	goto		WAIT		; eliminate noise
    
    	goto		INIT
    	
    ;******************************************************************************
    ; sampling 50 kHz vaweform tables :
    ;******************************************************************************
    
    	radix 		dec
    
    L_BEGIN:
    	addwf		PCL, F
    	dt		249, 249, 72, 72, 72, 137, 137, 137, 196, 196, 196, 249, 249, 249	; 72, 137, 196, 249
    
    L_LEN:
    	addwf		PCL, F
    	dt		53, 53, 72, 72, 72, 65, 65, 65, 59, 59, 59, 53, 53, 53 			; 72, 65, 59, 53
    
    H_BEGIN:
    	addwf		PCL, F
    	dt		112, 112, 41, 78, 112, 41, 78, 112, 41, 78, 112, 78, 41, 112		; 143, 143, 143, 143
    	
    H_LEN:
    	addwf		PCL, F
    	dt		34, 34, 41, 37, 34, 41, 37, 34, 41, 37, 34, 37, 41, 34			; 31, 31, 31, 31
    
    
    ;******************************************************************************
    
    ; Low tones:
    	org		0x0200
    L_DATA:
    	addwf		PCL, F
    	dt	64,69,75,80,85,91,96,100,105,109,113,116,119,122,124,125,126,127,127,127,126,125,123,121,119,116,112,108,104
    	dt	100,95,90,85,79,74,68,63,57,52,46,41,36,31,26,22,18,14,11,8,5,3,2,0,0,0,0,1,2,4,6,9,12,15,19,23,28,33,38,43,48,54,59
    	
    	dt	64,70,76,82,88,93,99,104,108,112,116,119,122,124,126,127,127,127,126,125,123,121,118,114,110,106,101,96,90,85
    	dt	79,73,66,60,54,48,42,36,31,26,21,17,13,9,6,4,2,0,0,0,0,1,3,5,8,11,15,19,24,29,34,40,46,52,58
    	
    	dt	64,70,77,84,90,96,102,107,112,116,120,123,125,126,127,127,127,125,123,121,117,113,109,104,98,92,86,79,73,66,59
    	dt	52,46,39,33,27,22,17,12,9,5,3,1,0,0,0,1,3,5,8,12,17,22,27,33,39,45,52,59
    
    	dt	64,71,78,86,93,99,105,111,115,119,123,125,127,127,127,126,124,121,118,113,108,103,96,90,83,75,68,60,53,45,38,32
    	dt	25,19,14,10,6,3,1,0,0,0,2,4,7,11,16,21,27,33,40,48,55
    
    ; High tones:
    	org		0x0300
    H_DATA:
    	addwf		PCL, F
    	dt 64,74,83,92,101,108,115,120,124,127,128,128,126,123,118,113,106,98,89,80,71,61,51,42,33,25,18,12,7,3,1,0,1,3,6,11,17,25,33,42,51,61
    	dt 64,75,85,95,104,112,118,123,126,128,128,126,122,116,110,101,92,82,72,61,50,40,30,22,14,8,4,1,0,1,3,8,13,21,29,39,49,59
    	dt 64,76,87,98,107,115,121,126,128,128,125,121,115,107,97,86,75,63,51,40,29,20,12,6,2,0,0,3,7,14,22,32,42,54
    	dt 64,77,90,101,111,119,124,127,128,126,121,114,104,93,81,68,55,42,30,20,11,5,1,0,1,6,12,21,31,43,56
    
    	end
    
    ;******************************************************************************
    

    Když se tak nad tím zamyslím, tak celý tenhle projekt je cvičením na použití tabulek v assembleru...

    A opravdu už jenom pro úplnost přikládám přeložený kód.


    Pod čarou: Takto upravený přístroj byste neměli připojovat do JTS. Koneckonců, JTS si ani nezaslouží, aby k ní takovýto unikát byl připojen.

    P15/1, K15 - prosinec 2006, naposledy aktualizováno: září 2015