Matura - Projektwoche

PC-Fernbedienung per Telefon 2








Nico Edtinger

13.05.2002 - 17.05.2002

1. Übersicht

2. Dokumente

3. Source-Code

4. Hardware

Zeitplan

erster Tag (13.05.2002)

zweiter Tag (14.05.2002)

dritter Tag (15.05.2002)

vierter Tag (16.05.2002)

fünfter Tag (17.05.2002)

Aufgabenstellung

Ein beliebiges Telefon wählt über das Telefonnetz einen Computer an der an das analoge Telefonnetz angeschlossen wird. Der Bildschirm des Computers wird durch einen Projekter ersetzt, der das Bild auf eine Leinwand projeziert.

Eine externe Schaltung übernimmt die Telefonnachbildung, mit derer der PC auch das Klingeln erkenn und abheben bzw. auflegen kann. Ausserdem übernimmt die Schaltung die Dekodierung der Tastentöne (DTMF-Töne) des anwählenden Telefons.

Als Schnittstelle zwischen dem PC und der externen Schaltung dient die serielle Schnittstelle.

Blockentwurf

Module und Schnittstellen

Module

Externe Schaltung

Eingang:
Am Eingang der Schaltung wird die analoge Telefonleitung angeschlossen. Die Spannung beträgt dort 60V und muss ausserdem gegen Überspannungen geschützt werden.
Ausgang:
Der Ausgang ist ein serielles Signal bestehend aus 4 Bit je Datenwort, das die gedrückte Taste angibt. Da der DTMF-Dekoder diese 4 Bit parallel und in TTL-Pegel ausgibt muss dieses Wort serialisiert und mittels eines speziellen Bausteins (MAX232) umgewandelt werden. Zur Serialisierung wird ein Schieberegister verwendet, das vom PC über DTR getaktet wird und das 8 Bit ausgibt, wobei die oberen 4 Bits immer auf Low gesetzt sind. Dieses Signal wird dann dem PC auf einer RS232-Schnittstelle übergeben.
Handshake-Leitungen:
Das Klingelsignal sowie die Halteanforderung werden über die Handshakeleitungen (RTS, CTS) der seriellen Schnittstelle vom und zum PC geleitet. CTS dient zusätzlich dazu dem PC in der Anruf-Phase ein neues Datenwort zu signalisieren. Die Konvertierung der Pegel übernimmt wieder der RS232-Baustein.
Klingelerkennung:
Das Wechselsspannungssignal der Klingel (Sinussignal) wird in ein TTL-Signal umgewandelt (5V/0V-Rechtecksignal). Über den MAX232 wird dieses Signal dann an den PC weitergegeben (an CTS), der das Klingeln erkennen soll.
Gleichrichter:
Am Eingang wird parallel zur Klingelerkennung ein Gleichrichter geschaltet. Somit wird das Klingelsignal für die restlichen Teile der Schaltung ausgefiltert.
Halteschaltung:
Nachdem Gleichrichter befindet sich die Halteschaltung. Sie zieht die Spannung am Eingang kontrolliert hinunter um somit ein Abheben zu signalisieren. Geschaltet wird sie über den PC (mittels RTS). Solange die Halteleitung aktiv ist wird das die Telefonnachbildung seitens des Telefonnetzes als abgehoben erkannt. Nach Ende der Eingaben wird über sie auch wieder aufgelegt (nach einer gewissen Leerzeit).
DTMF-Dekoder:
Der DTMF Dekoder (Mitel MT8870D) ist ein spezieller IC der eigens zur Dekodierung von DTMF-Tastentöne konzipiert ist. Er wird nach der Halteschaltung geschaltet und bekommt somit die Hörer-Signale des Telefons. Er filtert selbsttätig nur die DTMF-Töne aus und gibt ein 4 Bit Wort aus sobald er sie erkannt und dekodiert hat und gibt dies über den Pin StD zu erkennen.
Schieberegister und Pegelanpassung:
In der Phase des Anrufs ist die Leitung StD beim PC an CTS geschaltet (über eine Multiplexer Schaltung die mittels RTS, also der Halteleitung, gesteuert wird) und an den Load-Pin des Schieberegisters. Der PC schickt ein Taktsignal an das Schieberegister über DTR, sobald das 4 Bit Datenwort vom DTMF Dekoder in das Schieberegister geladen wurde (StD bzw. CTS wieder auf Low). Da die serielle Schnittstelle des PCs ein 8 Bit Datenwort benötigt ist auch das Schieberegister 8 Bit breit, wobei die oberen Bits immer auf Low liegen. Dieses Datenwort wird dann über den MAX232 und einem Schieberegister an den PC übergeben.

Software

Eingang:

Der Eingang des PCs ist die RS232-Schnittstelle. An RxD werden die 4 Bit Datenwörter des DTMF-Dekoders eingelesen.
Ausgang:
Ausgang des PCs ist das Bild, das über einen Projekter der an den VGA-Port angeschlossen wird, und das an eine Leinwand geworfen wird.
Handshake:
Zusätzlich zur RxD-Leitung werden an der seriellen Schnittstelle noch die Handshake-Leitungen RTS, CTS und DTR verwendet. Über CTS wird das Klingelsignal eingelesen (Phase: aufgelegt) oder erkannt, dass am DTMF Dekoder ein neues Datenwort anliegt (Phase: abgehoben). Das Klingelsignal, falls dieses sich in einem gewissen Zeitraum oft genug ändert (High auf Low und vice versa), als Anruf gewertet und via RTS wird an der Telefonnachbildung abgehoben. Dadurch wird auch CTS mit dem DTMF-Dekoder verbunden und dient ab nun der Erkennung eines neuen Datenwortes. Bei einem Wechsel von High auf Low ist das neue Datenwort in das Schieberegister geladen worden und DTR wird in 1ms Abständen von Low auf High und vice versa geschaltet womit das Schieberegister getaktet wird und das Datenwort mit ungefähr 30 Baud an den PC übergibt.
Programmoberfläche:
Die Programmoberfläche selbst ist einfach gestaltet. Es wird lediglich eine Bildschirmmarke mit den Tasten des anrufenden Telefons gesteuert. Es soll allerdings einfach zu erweitern sein, weshalb dieser Softwareteil zu dem Teil der sich um die Telefonsteuerung kümmert klar durch eine API getrennt wird.

Software

Die Software untergliedert sich in zwei Teile. Einerseits die Telefonsteuerung, andererseits dem davon unabhängigen Hauptprogramm.

Telefonsteuerung

Phase: Initialisierung

Zuerst müssen für die serielle Schnittstelle die notwendigen Einstellungen gesetzt werden. Dabei wird ein 7 Bit Wort mit einem Stopbit (entspricht 8 Bit) und eine Übertragungsrate von 30 Baud eingestellt.

Phase: Aufgelegt

Solange noch kein Anruf eingegangen ist, wird in regelmäßigen Abständen die Handshake-Leitung zum Klingelsignal abgefragt (CTS). Bei Änderungen wird deren Häufigkeit gezählt und so entschieden ob ein Anruf herein kommt. In diesem Falle wird über die Handshake-Leitung der Halteschaltung abgehoben.

Phase: Abgehoben

Während des Anrufs wird über einen Interrupt-Handler oder pollings die serielle Schnittstelle auf neue Werte abgefragt. In der ersten Variante wird eine Callback-Routine, die das Hauptprogramm registriert hat, aufgerufen. Im zweiten Falle wird einfach vom Hauptprogramm in regelmäßigen Abständen eine Funktion zum Abfragen aufgerufen. Allerdings besteht dabei die Gefahr ein neu ankommendes Signal zu verpassen.

Eingelesen wird in drei Schritten.

  1. Warten auf CTS. Wird CTS High wurde eine neue Taste erkannt. Fällt CTS wieder auf Low ist das Datenwort in das Schieberegister übernommen worden und es kann ausgelesen werden.
  2. DTR wird abwechselnd von Low auf High und vice versa geschaltet mit einem Abstand von 1ms zwischen jedem Wechsel.
  3. Das Datenwort befindet sich nun im Receive-Register der seriellen Schnittstelle und kann von dort ausgelesen werden.

Hauptprogramm

Phase: Initialisierung

In der Initialisierungs-Phase wird der Bildschirm aufgebaut und die Initialisierungsroutine der Telefonsteuerung aufgerufen. Ausserdem muss eventuell der Callback-Handler registriert werden.

Phase: Aufgelegt

Die Kontrolle über das Programm wird an die Telefonsteuerung übergeben die dann ihrerseits auf einen Anruf wartet. Sobald ein Anruf erkannt wurde wird wieder in das Hauptprogramm zurück gesprungen.

Phase: Abgehoben

Während eines Anrufes wird eine Endlos-Schleife durchlaufen. Dabei wird die Bildschirmmarke entsprechend der von der Telefonsteuerung kommenden Daten über den Bildschirm gelenkt. Der Abruf dieser Daten wird entwerder auch in der Endlos-Schleife getätigt (polling) oder über eine Laufzeitvariable die vom Callback-Handler gesetzt wird.

Quellcode erklärt

main.c

Konstanten WIDHT_X, WIDTH_Y
Maximala Koordinaten am Bildschirm
Konstante TIMEOUT_MAX
Wert der Timeout-Zähler Variable bei der mit einem Timeout abgebrochen wird
Globale Variable last_key
Die letzte gedrückte Taste (wird von key_callback() gesetzt)
Globale Variable key_changed
Ob eine neue Taste vorhanden ist (wird von key_callback() gesetzt und in der Hauptschleife rückgesetzt)
Funktion key_callback(key)
key_callback() wird von der Interrupt-Routine in tele*.c mit der erkannten Taste als Parameter aufgerufen. Falls nicht bereits eine neue Taste vorliegt, die noch nicht abgefragt wurde, wird key_changed auf 1 (True) gesetzt und key in last_key abgelegt.
Funktion main()
Zuerst werden die Telefonsteuerung initialisiert und der Callback-Handler registriert.
Danach beginnt die Endlosschleife des Programms. In ihr wird auf einen neuen Anruf gewartet.
Sobald einer auftritt beginnt die zweite Endlosschleife. Es wird geprüft ob der Callback-Handler eine neue Taste abgelegt hat. In diesem Fall wird key_changed auf 0 (False) gesetzt um zu zeigen, dass die Taste verarbeitet wurde, der Timeout-Counter zurück gesetzt und falls die Taste die Escape-Taste war diese Endlosschleife beendet um auf einen neuen Anruf zu warten.
Sonst wird der Timeout-Counter erhöht und im Fall, dass der Timeout-Counter TIMEOUT_MAX überschreitet wie bei einer Escape-Taste aus der Endlosschleife gesprungen.
Es wird noch die Bildschirmmaske je nach gedrückter Taste bewegt.

telephon.c

Konstanten KEY_*
Werte für die Tasten nach oben(2), unten(8), links(4), rechts(), escape (0), sowie die NULL-Taste (= keine Taste)
Konstante COM_INT_NUM
Nummer des Interrupts für die serielle Schnittstelle
Konstanten COM_PORT_*
Die Port-Adressen der seriellen Schnitstelle
Konstanten COM_DIVISOR_LOW, COM_DIVISOR_HIGH
Divisor Wert
Konstante COM_INT_READHSK
Wert für Interrrupt-Enable-Register (Interrupt bei neuem Datenwort oder Änderung der Handshake Leitungen)
Konstanten COM_LINE_*
Werte für Line-Control-Register (Divisor-Latch setzen, Übertragung 7 bit/1 Stoppbit/0 Paritätsbit)
Konstanten COM_MODEM_WRITE_*, COM_MODEM_READ_*
Masken um Handshake Leitungen zu setzen und lesen
Globale Variable key_callback_handler
Adresse des Callback-Handler des Hauptprogramms
Globale Variable old_interrupt
Adresse der alten Interrupt-Routine
Globale Variable got_dataready_signal
Flag um im Interrupt zu prüfen ob nun ein neues Datenwort anliegt oder eine Änderung der Handshake-Leitungen herbei geführt wurde
Funktion telephone_register_callback_handler(callback_handler)
Callback-Handler setzten bzw. mit NULL als Paramter löschen
Funktion telephone_interrupt_handler()
Interrupt Routine für die serielle Schnittstelle. Zuerst wird überprüft (mitels got_dataready_signal) ob nun ein neues Datenwort eingelesen wurde. Ist dies der Fall wird die Callback-Routine mit diesem Datenwort eingelesen.
Falls nicht wurde über die Handshake Leitung signalisiert, dass eine neue Taste erkannt wurde. In diesem Fall wird dem Schieberegister der Telefonsteuerung der Takt zum auslesen geschickt.
Funktion telephone_deinit()
Beendet das Telefongespräch, setzt den Interrupt auf die alte Routine zurück und deaktiviert den Interrupt für die serielle Schnittstelle
Funktion telephone_init()
Initialisiert die serielle Schnittstelle und stellt sicher, dass telephone_deinit() zumindest am Ende des Programms aufgerufen wird.
Funktion telephone_wait_for_call()
Diese Funktion wartet zuerst auf eine bestimmte Anzahl an Änderungen an der Handshake-Leitung für das Klingelsignal. Treten genug auf wird dies als Klingel erkannt und über die Halteleitung abgehoben.
Danach wird noch der Interrupt auf die eigene Routine gesetzt und die Interrupts für die serielle Schnittstelle aktiviert.
Funktion telephone_get_key()
Liefert die letzte gedrückte Taste. Kann auch zum gepollten einlesen verwendet werden.

teletkey.c

teletkey.c hat die gleichen Funktionen und Konstanten wie telephon.c - damit können sie gegeneinander ausgetauscht werden. Der Zweck dieser Datei ist das Testen des Hauptprogramms ohne die externe Schaltung.

Konstanten KEY_*
Werte für die Tasten nach oben(UpArrow), unten(DownArrow), links(LeftArrow), rechts(RightArrow), escape (ESC), sowie die NULL-Taste (= keine Taste)
Konstante KEYBOARD_INT_NUM
Nummer des Interrupts für die Tastatur
Konstante KEYBOARD_PORT_READ
Port-Adresse der Tastatur
Globale Variable key_callback_handler
Adresse des Callback-Handler des Hauptprogramms
Globale Variable old_keyboard_handler
Adresse der alten Interrupt-Routine
Funktion telephone_register_callback_handler(callback_handler)
Callback-Handler setzten bzw. mit NULL als Paramter löschen
Funktion telephone_interrupt_handler()
Wird vom Tastatur-Interrupt aufgerufen. Die neue Taste wird gelesen und der Callback-Handler damit aufgerufen. Danach wird noch der alte Tastatur-Interrupt aufgerufen.
Funktion telephone_deinit()
Setzt die Interrupt Routine auf die alte.
Funktion telephone_init()
Stellt sicher, dass telephone_deinit() zumindest am Ende des Programms aufgerufen wird.
Funktion telephone_wait_for_call()
Wartet auf einen beliebigen Tastendruck. Danach wird die Interrupt-Routine der Tastatur auf die eigene gesetzt
Funktion telephone_get_key()
Liefert die letzte gedrückte Taste. Kann auch zum gepollten einlesen verwendet werden.

Source of source/main.c

/*****************************************************************************
** **
** MATURA - PROJEKTWOCHE - TELEFONSTEUERUNG **
** **
******************************************************************************
** **
** Original Dateiname: main.c **
** Autor: Nico Edtinger **
** Erstversion: 14.05.2002 **
** Letzte Änderung: 15.05.2002 **
** Dateiversion: 0.2 **
** **
******************************************************************************
** **
** Hauptprogramm **
** **
******************************************************************************
** **
** Änderungen: **
** **
** 14.05.2002: Hinweis: statt der Telefonsteuerung wird Testweise die **
** Tastatur abgefragt **
** 15.05.2002: KEY_ Konstanten verwendet **
** Bewegbare Bildschirmmaske **
** Timeout Counter **
** Anmerkung: ausser Sch�nheitsfehlern ist das Hauptprogramm **
** nun vollst�ndig **
** **
*****************************************************************************/


#include "teletkey.c" /* test modul */
#include <stdio.h>
#include <stdlib.h>

#define WIDTH_X 80
#define WIDTH_Y 25

#define TIMEOUT_MAX 2500000

char last_key
;
int key_changed = 0;

void key_callback(char key)
{
if(key_changed) {
return;
}

key_changed = 1;
last_key = key;
}

int main()
{
unsigned long timeout_counter;
unsigned int pos_x = WIDTH_X/2, pos_y = WIDTH_Y/2;

printf("main: telephone_init();\n");
telephone_init();
printf("main: telephone_register_callback_handler();\n");
telephone_register_callback_handler(key_callback);
while(1) {
timeout_counter = 0;
last_key = KEY_NULL;
printf("main: telephone_wait_for_call();\n");
telephone_wait_for_call();
printf("main: got call\n\n");
while(1) { /* main loop */
if(key_changed) {
timeout_counter = 0;
if(last_key == KEY_ESCAPE) {
printf("main: You've chosen escape\n");
break;
}
key_changed = 0;
} else {
timeout_counter++;
if(timeout_counter > TIMEOUT_MAX) {
printf("main: Timeout\n");
break;
}
}
if(!(timeout_counter % (TIMEOUT_MAX / 50))) {
gotoxy(pos_x, pos_y);
printf(" ");
switch(last_key) {
case KEY_UP:
if(pos_y > 1) {
pos_y--;
}
break;
case KEY_DOWN:
if(pos_y < (WIDTH_Y)) {
pos_y++;
}
break;
case KEY_LEFT:
if(pos_x > 1) {
pos_x--;
}
break;
case KEY_RIGHT:
if(pos_x < (WIDTH_X-1)) {
pos_x++;
}
break;
}
gotoxy(pos_x, pos_y);
textcolor(RED);
cprintf("*");
textcolor(WHITE);
}
}
telephone_deinit();
}
}

Source of source/telephon.c

/*****************************************************************************
** **
** MATURA - PROJEKTWOCHE - TELEFONSTEUERUNG **
** **
******************************************************************************
** **
** Original Dateiname: telephone.c **
** Autor: Nico Edtinger **
** Erstversion: 14.05.2002 **
** Letzte Änderung: 17.05.2002 **
** Dateiversion: 0.4 **
** **
******************************************************************************
** **
** Modul zur Telefonsteuerung; kümmert sich um die Kommunikation und **
** Steuerung der Telefonsteuerung über die serielle Schnittstelle **
** **
******************************************************************************
** **
** Änderungen: **
** 15.05.2002: KEY_ Konstanten hinzugefügt **
** 16.05.2002: Init, Deinit, WaitForCall **
** 17.05.2002: KEY_ Konstanten an DTMF Dekoder angepasst **
** **
*****************************************************************************/


#include <dos.h>

#define KEY_UP 0x02
#define KEY_DOWN 0x50
#define KEY_LEFT 0x08
#define KEY_RIGHT 0x06
#define KEY_ESCAPE 0x0a
#define KEY_NULL 0x00

#define COM_INT_NUM 0x0c /* Interruprt-Nummer für COM1 */

#define COM_PORT_READ 0x3f8 /* Rw Daten aus serieller Leitung */
#define COM_PORT_DIV_LOW 0x3f8 /* RW Divisor Latch Lowbyte */
#define COM_PORT_DIV_HIGH 0x3f9 /* RW Divisor Latch Highbyte */
#define COM_PORT_INT_ENABLE 0x3f9 /* RW Interrupt Enable Register */
#define COM_PORT_LINE_CTRL 0x3fb /* RW Line Control */
#define COM_PORT_MODEM_WRITE 0x3fc /* RW Modem Control Register (Handshake)*/
#define COM_PORT_MODEM_READ 0x3fe /* R Modem Status Register (Handshake) */

#define COM_DIVISOR_LOW 0x0e /* ~30 Baud */
#define COM_DIVISOR_HIGH 0x74

#define COM_INT_READHSK 0x09 /* Interrupt nur bei neuem Datenwort oder Handshake */

#define COM_LINE_DIV_ACCESS 0x82 /* Modus zum setzen des Divisor Latch */
#define COM_LINE_7BIT 0x02 /* 7bit, ein Stopbit, keine Parität */

#define COM_MODEM_WRITE_RTS_MASK 0x02 /* Maske um RTS zu setzen */
#define COM_MODEM_WRITE_DTR_MASK 0x02 /* Maske um RTS zu setzen */
#define COM_MODEM_READ_CTS_MASK 0x10 /* Maske um CTS zu lesen */

/*
** Callback-Handler; wird von Interrupt-Handler aufgerufen, wenn ein neues
** Datenwort anliegt
*/

void (*key_callback_handler)(char) = NULL;
/*
** alte Interrupt-Routine
*/

void interrupt (*old_interrupt)() = NULL;

/*
** Callback-Handler für neues Datenwort registrieren. Wenn der Parameter
** NULL gesetzt wird, wird der Handler deaktiviert
*/

int telephone_register_callback_handler(void * callback_handler)
{
key_callback_handler = callback_handler;
return 1;
}


/*
** Ob Interrupt von StD des DTMF-Dekoders oder von Daten kommt
**/

int got_dataready_signal = 0;

/*
** Interrupt-Handler für die serielle Schnittstelle. Wird in der Anruf-Phase
** aufgerufen, wenn an der seriellen Schnittstelle ein neues Datenwort anliegt
** oder der DTMF Dekoder neue Daten signalisiert. Unterschieden wird über das
** got_ready_signal Flag. Bei einem neuem Datenwort wird dem Schieberegister
** der Takt gesendet. Beim nächsten Interrupt wird dieses dann eingelesen und
** mit diesem der Callback-Handler aufgerufen.
*/

interrupt void telephone_interrupt_handler()
{
char key;
int i;
unsigned char mcr;

if(!got_dataready_signal) {
got_dataready_signal = 1;
mcr = (unsigned char)inp(COM_PORT_MODEM_WRITE);
for(i = 0; i < 7; i++) {
outp(COM_PORT_MODEM_WRITE, mcr | COM_PORT_WRITE_RTS_MASK);
delay(1);
outp(COM_PORT_MODEM_WRITE, mcr);
delay(1);
}
outp(COM_PORT_MODEM_WRITE, mcr | COM_PORT_WRITE_RTS_MASK);
delay(1);
outp(COM_PORT_MODEM_WRITE, mcr);
} else {
got_dataready_signal = 0;
key = (char)inp(COM_PORT_READ);
if(key_callback_handler) {
key_callback_handler(key);
}
}
if(old_interrupt) {
old_interrupt();
}
}

/*
** Auflegen und Interrupt-Vektoren wieder zurück setzen
*/

void telephonee_deinit()
{
unsigned char mcr;

mcr = (unsigned char)inp(COM_PORT_MODEM_WRITE);
outp(COM_PORT_MODEM_WRITE, (~COM_MODE_WRITE_RTS_MASK) & mcr);
if(old_interrupt) {
setvect(COM_INT_NUM, old_interrupt);
old_interrupt = NULL;
}
/* Interrupts nur wenn abgeoben wurde */
outp(COM_PORT_INT_ENABLE, 0);
}

/*
** Initialisierung der seriellen Schnittstelle
*/

int telephone_init()
{
/* Interrupts nur wenn abgeoben wurde */
outp(COM_PORT_INT_ENABLE, 0);
/* Divisor Latch setzen */
outp(COM_PORT_LINE_CTRL, COM_LINE_DIV_ACCESS);
outp(COM_PORT_DIV_LOW, COM_DIVISOR_LOW);
outp(COM_PORT_DIV_HIGH, COM_DIVISOR_HIGH);
/* 7Bit, 1 Stopbit, keine Parität */
outp(COM_PORT_LINE_CTRL, COM_LINE_7BIT);

return atexit(telephone_deinit);
}


/*
** Warten auf einen Anruf (Phase aufgelegt)
*/

int telephone_wait_for_call()
{
unsigned char mcr;
unsigned char ring = 0;
unsigned char last_ring = 0;
unsigned char changes = 0;

mcr = (unsigned char)inp(COM_PORT_MODEM_WRITE);
/* warten */
do
{
ring = inp(COM_PORT_MODEM_READ) & COM_MODEM_CTS_MASK;
if(ring != last_ring) {
changes++;
}
}
while (changes < 50);

/* abheben */
outp(COM_PORT_MODEM_WRITE, COM_MODE_WRITE_RTS_MASK | mcr);

/* interrupt handler setzen */
if(!old_keyboard_handler) {
old_keyboard_handler = getvect(KEYBOARD_INT_NUM);
setvect(KEYBOARD_INT_NUM, telephone_interrupt_handler);
}
outp(COM_PORT_INT_ENABLE, COM_INT_READHSK);
return 1;
}

/*
** aktuellen Wert der seriellen Schnittstelle lesen. Für gepollte Abfragen
*/

char telephone_get_key()
{
char key;

key = (char)inp(COM_PORT_READ);
return key;
}

Source of source/teletkey.c

/*****************************************************************************
** **
** MATURA - PROJEKTWOCHE - TELEFONSTEUERUNG **
** **
******************************************************************************
** **
** Original Dateiname: telephone_test_key.c **
** Autor: Nico Edtinger **
** Erstversion: 14.05.2002 **
** Letzte Änderung: 15.05.2002 **
** Dateiversion: 0.2 **
** **
******************************************************************************
** **
** Testmodul - verwendet Keyboard statt Telefonsteuerung **
** **
******************************************************************************
** **
** Änderungen: **
** 15.05.2002: KEY_ Konstanten hinzugef�gt **
** **
*****************************************************************************/


#include <dos.h>
#include <stdio.h>
#include <conio.h>

#define KEY_UP 0x48
#define KEY_DOWN 0x50
#define KEY_LEFT 0x4b
#define KEY_RIGHT 0x4d
#define KEY_ESCAPE 0x01
#define KEY_NULL 0x00

#define KEYBOARD_INT_NUM 0x09
#define KEYBOARD_PORT_READ 0x060

/*
** Callback-Handler; wird von Interrupt-Handler aufgerufen, wenn ein neues
** Datenwort anliegt
*/

void (*key_callback_handler)(char) = NULL;
/*
** alte Interrupt-Routine
*/

void interrupt (*old_keyboard_handler)() = NULL;

/*
** Callback-Handler für neues Datenwort registrieren. Wenn der Parameter
** NULL gesetzt wird, wird der Handler deaktiviert
*/

int telephone_register_callback_handler(void (*callback_handler)(char))
{
key_callback_handler = callback_handler;
return 1;
}

/*
** Interrupt-Handler für die serielle Schnittstelle. Wird in der Anruf-Phase
** aufgerufen, wenn an der seriellen Schnittstelle ein neues Datenwort anliegt
** und ruft dann den Callback-Handler mit der erkannten Taste auf
*/

void interrupt telephone_interrupt_handler()
{
char key;

key = (char)inp(KEYBOARD_PORT_READ);
if(key_callback_handler) {
key_callback_handler(key);
}
if(old_keyboard_handler) {
old_keyboard_handler();
}
}

void telephone_deinit()
{
if(old_keyboard_handler) {
setvect(KEYBOARD_INT_NUM, old_keyboard_handler);
old_keyboard_handler = NULL;
}
}

/*
** Initialisierung der seriellen Schnittstelle
*/

int telephone_init()
{
printf("1, 2, 3, IN THE PLACE TO BE - WHAT MEANS INIT DONE :)\n");
return atexit(telephone_deinit);
}


/*
** Warten auf einen Anruf (Phase aufgelegt)
*/

int telephone_wait_for_call()
{
while(kbhit()) {
getch();
}
printf("WAIT FOR CALL\nHJS: WHERE IS THE ANY KEY? ;)\n\n");
getch();
if(!old_keyboard_handler) {
old_keyboard_handler = getvect(KEYBOARD_INT_NUM);
setvect(KEYBOARD_INT_NUM, telephone_interrupt_handler);
}
return 1;
}

/*
** aktuellen Wert der seriellen Schnittstelle lesen. Für gepollte Abfragen
*/

char telephone_get_key()
{
char key;

key = (char)inp(KEYBOARD_PORT_READ);
return key;
}

Bauteile

Schaltungsname Bauteil Anmerkung
Schaltungsname Bauteil Anmerkung
Widerstände
R1 VDR S10K150 -
R2, R3 10kΩ -
R4 150kΩ -
R5, R13, R14 100kΩ -
R6 39Ω -
R7 20kΩ -
R8 1,2kΩ -
R9 180Ω -
R10 39kΩ -
R11, R12 68Ω -
R15 270kΩ -
Kondensatoren
C1, C2 470nF -
C3 10nF -
C4 10µF/63V -
C5 150nF -
C6 4,7µF/63V -
C7, C11, C12 100nF -
C8, C9, 10 1µF bei MAX232 alle C=1µF
Dioden
D1, D2, D3, D4, D9, D10 LL4148 -
D5, D6, D7, D8 1N4001 -
D11 ZPD6V8 -
Transistoren
T1, T2 BC848 -
T3 BD139 -
andere
OK1 TLP627 -
Q1 Quarz 3,579545 MHz
IC1 Mitel MT8870D
  • 01: VP
  • 02: VN
  • 03: GS
  • 04: VREF
  • 05: INH
  • 07: X1
  • 08: X2
  • 09: Vss
  • 11-14: D0-D3
  • 15: StD
  • 16: EST
  • 17: RT/GT
  • 18: Vdd
IC2 MAX232 -
IC3 74F00 -
IC4 74HC165 -
JP1 - A/B-Leitung an 3/4
SV1 -
  • 1: RxD
  • 2: RTS
  • 3: CTS
  • 4: DTR
  • 5: Masse