Viele GerĂ€te werden heutzutage nicht nur mit einer Fernbedienung geliefert, einige besitzen sogar einen Fernbedienungseingang. Der Control-S-Bus von Sony (auch SIRCS genannt) ist ein TTL-kompatibler Bus, d.h. er arbeitet mit Signalen von 0 und 5 Volt. Damit ist er sehr einfach mit einem Computer steuerbar. Dieser Artikel beschreibt das Format eines Control-S-Befehls samt zugehöriger Steuer-Software. Damit lĂ€Ăt sich der ST z.B. als Schnittcomputer oder als komfortabler Timer fĂŒr die nĂ€chsten 100 Jahre benutzen, obwohl das GerĂ€t dann wahrscheinlich veraltet ist.
Kommunikation zwischen Sony und Atari
Dabei ist der Hardware-Aufwand minimal, mit weniger als 10 Mark ist man dabei. NatĂŒrlich ist bei solchen Basteleien entsprechend vorsichtig vorzugehen, und wir können weder garantieren, daĂ das vorgestellte Programm mit Ihrem GerĂ€t zusammenarbeitet, noch ob es Rechner oder Videorekorder beschĂ€digt!
Allerdings lĂ€uft bei mir die Schaltung einwandfrei, und ein Widerstand sorgt dafĂŒr, daĂ z.B. bei KurzschluĂ nach dem Ohmschen Gesetz der Port im ST mit nur
I = U/R = 5V / 5600 Ω = 0.9 mA
belastet wird. Damit gehtâs schon an die Hardware. Auf der Seite des zu steuernden GerĂ€tes braucht man nur einen 3.5mm-Mono-Klinkenstecker, auf der Seite des ST einen 25pol. Sub-D-Stecker, sofern man das GerĂ€t mit dem Centronicsport steuern möchte.
NatĂŒrlich kann man dazu auch irgendeinen anderen Port benutzen, in der Steuer-Software mĂŒssen dann nur die beiden Routinen set_one und set_zero am Ende des Assemblerlistings geĂ€ndert werden.
Als Steuerleitung kann ein beliebiges zweipoliges Kabel benutzt werden, besser ist allerdings ein abgeschirmtes Monokabel. Die Abschirmung wird dabei an eine der Masseleitungen am Sub-D-Stecker gelötet (AnschluĂ 18-25). Der 5.6KΩ-Widerstand wird an Daten-Bit 0 des Drucker-Ports gelegt (AnschluĂ 2). AnschlieĂend muĂ die innere Leitung des Kabels an das andere Ende des Widerstandes gelötet werden. Damit sind die Arbeiten auf der Rechnerseite des Kabels beendet. Am anderen Ende wird das Innere des Kabels an den inneren AnschluĂ des Klinkensteckers gelötet, die Abschirmung kommt an den Ă€uĂeren AnschluĂ des Steckers. Und das warâs.
Dieser Klinkenstecker kommt in die CONTROL-S-IN-Buchse des zu steuernden GerÀtes, NICHT an die CONTROL-S-OUT-Buchse!
Da im GerĂ€t der CONTROL-S-Eingang und der Fernbedienungssensor auf eine Leitung verodert sind, ist die Fernbedienung wirkungslos, wenn der Drucker-Port dauerhaft auf High geschaltet wird! Aber nach dem Reset schaltet der ST den Drucker-Port ohnehin auf Low. Und Schaden wĂŒrde das GerĂ€t dabei sowieso nicht nehmen (hat es bei mir zumindest nicht getan), also keine Bange.
Wie gehtâs?
Hier nun eine Beschreibung des Sony-Control-S-Formats. Keine Angst, wenn nicht alles verstanden wird, das Programm lĂ€uft auch so. Die Routine send_sony() ĂŒbernimmt die ganze Arbeit und kann zu eigenen Programmen dazugebunden werden. In Tabelle 1 stehen die GerĂ€tecodes und in Tabelle 2 die mir bekannten Befehle. Ein Teil dieser Befehle stammt aus den Sony-Control-S-Protocol-Specifications von Scott Coleman, den Rest habe ich mit einem Oszilloskop selbst herausgefunden. Wenn also der eine oder andere Befehl hier nicht aufgefĂŒhrt ist, hilft nur: ausprobieren. Auch kann ein Befehl auf diesem oder jenem GerĂ€t eine andere Bedeutung haben oder ĂŒberhaupt nicht funktionieren. Denn sowohl Scott Coleman als auch ich hatten keine offizielle Sony-Dokumentation zur VerfĂŒgung, alle Angaben beruhen auf experimentell ermittelten Werten.
Aber jetzt zur Sache. Ein Befehlswort besteht dabei aus 12 Bits, wovon die ersten 7 Bits den eigentliche Befehl darstellen und die restlichen 5 Bits das GerĂ€t angeben, fĂŒr das der Befehl bestimmt ist. Damit können 128 Befehle an 32 GerĂ€te verschickt werden, wobei ja Befehlscodes fĂŒr verschiedene GerĂ€te auch unterschiedliche Bedeutung haben können. Nun werden diese 12 Bits aber nicht einfach so verschickt, sondern werden codiert. ZunĂ€chst geht das Signal fĂŒr 2.4 ms auf High, dann fĂŒr 0.6 ms auf Low. Das war sozusagen das âStart-Bitâ. Jetzt werden die 12 Bits des Befehls-/GerĂ€tecodes gesendet, jeweils das niederwertigste Bit zuerst. Ein Bit ist dabei folgendermaĂen codiert:
0: 0.6ms High, 0.6ms Low
1: 1,2ms High, 0.6ms Low
TV 1
VTR1 2 Betamax
VTR2 4 Betamax
VTR2 7 Video 8
VTR3 11 VHS
Tabelle 1: GerÀtecodes
Damit wird sichergestellt, daà der Code gleichstromfrei ist, das Signal muà also nach spÀtestens 1,2ms seinen Zustand Àndern.
Bevor der Befehlscode noch einmal bzw. ein anderer Befehlscode gesendet werden darf, muà zunÀchst gewartet werden, bis 45ms vergangen sind, seit das Signal beim Start-Bit zum ersten Mal auf High gegangen ist.
Damit ein Code vom GerĂ€t erkannt wird, muĂ mindestens 3mal das gleiche Befehlswort erkannt werden, sonst wird von einem Ăbertragungsfehler ausgegangen.
Die Software
Der Routine send_sony() wird nur ĂŒbergeben, welches GerĂ€t welchen Befehl wie oft erhalten soll. Das Beispielprogramm steuert die wichtigsten Funktionen eines Videorekorders und ist als Accessory oder als Programm lauffĂ€hig. Unter Verwendung der Projektdatei und des Turbo-C Compilers 2.0 sollte es kein Problem sein, das Programm zum Laufen zu bringen. Gegebenenfalls muĂ nur die Zeile
#define VTR VTR3
im SONY.C-Listing auf das entsprechende System angepaĂt werden.
Literatur:
Sony-Control-S-Protocol-Specifications by Scott Coleman
 |
 |
0 |
Taste 1 |
1 |
Taste 2 |
2 |
Taste 3 |
3 |
Taste 4 |
4 |
Taste 5 |
5 |
Taste 6 |
6 |
Taste 7 |
7 |
Taste 8 |
8 |
Taste 9 |
9 |
Taste 10/Taste 0 |
10 |
Taste -/â-, Taste 11, Taste * |
11 |
Taste 12, Taste CH/Enter/# |
12 |
Taste 13, Taste 1- |
13 |
Taste 14, Taste 2- |
16 |
Programm + |
17 |
Programm - |
18 |
LautstÀrke + |
19 |
LautstÀrke - |
20 |
Zeitraffer X 2 / X3 |
21 |
Power |
22 |
Eject |
23 |
Audio Monitor (L-CH/R-CH/Stereo) |
24 |
Stop |
25 |
Pause |
26 |
Play |
27 |
Rewind |
28 |
FF |
29 |
Record |
32 |
Freeze Frame > |
34 |
X 1/10 play |
35 |
X 1/5 play |
38 |
High Double Speed ( X7 ~ X15X ) |
40 |
Review (RĂŒcklauf mit Bild) |
41 |
Cue (Vorlauf mit Bild) |
42 |
TV/VTR |
45 |
VTR from TV (ANT-VTR) |
46 |
Power on |
47 |
Power off |
48 |
Einzelbild rĂŒckwĂ€rts/langsame RĂŒckwĂ€rtswiedergabe |
49 |
Einzelbild vorwÀrts/langsame VorwÀrtswiedergabe |
51 |
X 1 |
58 |
ZurĂŒckspulen, dann Play |
60 |
aux |
61 |
Zeitlupe + |
62 |
Zeitlupe - |
64 |
Function Memory |
66 |
Cursor auf |
67 |
Cursor ab |
70 |
ZĂ€hler Reset |
71 |
Counter Memory on/off |
72 |
Index write |
73 |
Index erase |
74 |
Shuttle > |
75 |
Shuttle < |
77 |
Menu |
78 |
TV/VTR (ANT-TV) |
79 |
Input Select |
81 |
Execute |
83 |
index (scan) |
85 |
Edit Monitor |
88 |
Rec Mode (SP/LP) |
89 |
Tape return |
90 |
Data Screen / Display on/off |
91 |
Open/Close |
92 |
Timer on screen |
97 |
Cursor rechts |
98 |
Cursor links |
99 |
Timer löschen |
100 |
Timer check |
104 |
Audio Insert |
105 |
Video Insert |
106 |
edit play (Assemble) |
107 |
mark |
108 |
Start |
110 |
PiP off |
111 |
PiP shift |
116 |
TV scan |
119 |
Picture in picture (PiP) on |
Tabelle 2: Befehlscodes
SONY.PRG
=
TCSTART.O
SONY.C
SEND.S
TCSTDLIB.LXB
TCGEMLIB LIB
#define DIALOG 0 /* TREE */
#define POWER 1 /* OBJECT in TREE #0 */
#define PAUSE 3 /* OBJECT in TREE #0 */
#define PLAY 4 /* OBJECT in TREE #0 */
#define REC 5 /* OBJECT in TREE #0 */
#define REW 6 /* OBJECT in TREE #0 */
#define STOP 7 /* OBJECT in TREE #0 */
#define FF 8 /* OBJECT in TREE #0 */
#define EJECT 9 /* OBJECT in TREE #0 */
#define FERTIG 10 /* OBJECT in TREE #0 */
#define PRGUP 11 /* OBJECT in TREE #0 */
#define PRGDOWN 12 /* OBJECT in TREE #0 */
OBJECT rs_object[] = {
-1, 1, 13, G_BOX, NONE, OUTLINED,
0x2H00L, 0,0, 34,12,
2, -1, -1, G_BUTTON, 0x5, NORMAL,
(long)"POWER", 2,1, 8,1,
3, -1, -1, G_STRING, NONE, NORMAL,
(long)"SIRCS-Fernbedienung", 13,1, 19,1,
4, -1, -1, G_BUTTON, 0x5, NORMAL,
(long)"PAUSE", 3,4, 8,1,
5, -1, -1, G_BUTTON, 0x5, NORMAL,
(long)"PLAY", 13,4, 8,1,
6, -1, -1, G_BUTTON, 0x5, NORMAL,
(long)"REC", 23,4, 8,1,
7, -1, -1, G_BUTTON, TOUCHEXIT, NORMAL,
(long)"REW", 3,6, 8,1,
8, -1, -1, G_BUTTON, 0x5, NORMAL,
(long)"STOP", 13,6, 8,1,
9, -1, -1, G_BUTTON, TOUCHEXIT, NORMAL,
(long)"FF", 23,6, 8,1,
10, -1, -1, G_BUTTON, 0x5, NORMAL,
(long)"EJECT", 3,8, 8,1,
11, -1, -1, G_BUTTON, 0x7, NORMAL,
(long)"FERTIG", 13,10, 8,1,
12, -1, -1, G_BUTTON, TOUCHEXIT, NORMAL,
(long)"+", 29,8. 2,1,
13, -1, -1, G_BUTTON, TOUCHEXXT, NORMAL,
(long)"-", 29,10, 2,1,
0, -1, -1, G_STRING, LASTOB, NORMAL,
(long)"Programm ", 19,8, 9,1);
LONG rs_trindex[] = {
0L};
struct foobar {
WORD dummy;
WORD *image,
} rs_imdope[] = {
0};
#define NUM_OBS 14
#define NUM_TREE 1
* Senden eines Befehls an ein GerÀt mit
* Sony SIRCS (Control-S) AnschluĂ
* von Oliver Scholz
* (c) MAXON Computer 1991
MFP equ $FFFFFA01
INT_ENA_A equ MFP+$6
INT_PEND_A equ MFP+$A
INT_MASK_A equ MFP+$12
TA_CTRL equ MFP+$18
TA_DATA equ MFP+$1E
SOUND equ $FFFF8800
* C-Deklaration:
* VOID send_sony(WORD device, WORD command,
* WORD times)
*
.globl send_sony
send_sony:
move d0,device
move d1,command
move d2,times
movem.l d2-d7/a2-a6,-(sp)
send_loop:
tst times ;entsprechend oft senden
beq done
pea do_send
move #38,-(sp) ;im Supervisormodus
trap #14 ;ausfĂŒhren
addq.l #6,sp
sub #1,times
bra send_loop
done:
movem.l (sp)+,d2-d7/a2-a6
rts
.super
do_send:
move sr,-(sp)
ori #$700,sr ; Interrupts sperren
clr.b TA_CTRL ; Timer anhalten
bclr.b #5,INT_ENA_A ; Interrupt disablen
bclr.b #5,INT_MASK_A ; Interrupt maskieren
bset.b #5,INT_ENA_A ; Interrupt enablen
move (sp)+,sr ; Interrupts frei!
* Sony SIRCS Wort senden
clr counter ; ZeitzÀhler löschen
bsr set_one ; Ausgang auf High
bsr wait0_6 ; 2.4 ms warten
bsr wait0_6
bsr wait0_6
bsr wait0_6
bsr set_zero ; und auf Low:
bsr wait0_6 ; das war der StartschuĂ
move command,d0 ; Kommando senden
move #6,d1 ; 7 Bits
bsr cmd_loop
move device,d0 ; Zielgeratecode
move #4,d1 ; senden (5 Bits)
bsr cmd_loop
fill:
bsr wait0_6
cmp #75,counter ; auffĂŒllen, bis 45ms
blt fill
rts
* Bits an GerÀt senden
* Anzahl der Bits minus 1 in D1.w
* Die Bits selbst in D0.w
* Leitung fĂŒr jedes 1-Bit 1.2ms auf High,
* fĂŒr Null-Bits nur 0.6ms auf High
* danach 0.6ms auf Low
cmd_loop:
bsr set_one ; Leitung auf high
bsr wait0_6
btst #0,d0 ; unterstes Bit
beq cmd_zero ; Bit ist Null
bsr wait0_6
cmd_zero:
bsr set_zero
bsr wait0_6
lsr #1,d0
dbf d1,cmd_loop
rts
* 0.6 ms warten
wait0_6:
move.b #$DF,INT_PEND_A ; Flag löschen
move.b #92,TA_DATA ; Timer setzen
move.b #3,TA_CTRL ; und einschalten
wait:
btst.b #5,INT_PEND_A ; Warten, bis Flag
beq wait ; gesetzt
clr.b TA_CTRL ; Timer wieder stop
add #1,counter ; bisher gewartete
rts ; Intervalle: erhöhen
* Leitung auf eins setzen
set_one:
move.b #$F,SOUND ; Port B schreiben
move.b #1,SOUND+2
rts
set_zero:
move.b #$F,SOUND
move.b #0,SOUND+2
rts
.bss
counter: ds.w 1
device: ds.w 1
command: ds.w 1
times: ds.w 1
/*
* Control-S-Steuerprogramm fĂŒr Videorekorder
* von Oliver Scholz
* (c) 1991 MAXON Computer 1991
*/
#include "portab.h"
#include "aes.h"
#lnclude "stdlib.h"
#include "sony.h"
#include "sony.rsh"
#define TV 1
#define VTR1 2
#define VTR2 7
#define VTR3 11
/* VTR1 fĂŒr Betamax */
/* VTR2 fĂŒr Video 8 */
/* VTR3 fĂŒr VHS */
#define VTR VTR3
#define TRUE 1
#define FALSE 0
VOID send_sony(WORD device, WORD command, WORD times);
OBJECT *get_traddr(WORD tree_index),
VOID do_dialog(OBJECT *dial);
VOID redo_obj(OBJECT *tree, WORD index);
extern _app;
VOID main(VOID)
{
OBJECT *dialog;
WORD i,dummy;
WORD event;
WORD msgbuff[8];
WORD ap_id, mn_id;
/* Programm initialisieren */
ap_id = appl_init();
if(ap_id == -1) exit(-1);
/* Dialogadresse berechnen */
for (i=0; 1<NUM_OBS; i++)
rsrc_obfix(rs_object,i);
dialog=get_traddr(DIALOG);
if(_app)
{
/* Applikation */
graf_mouse(ARROW,0L);
do_dialog(dialog);
}
else
{ /* Accessory */
mn_id = menu_register( ap_id," Fernbedienung" );
/* keine Touchexit Knöpfe.. */
dialog[REW].ob_flags=SELECTABLE|EXIT;
dialog[FF].ob_flags=SELECTABLE|EXIT;
dialog[PRGUP].ob_flags=SELECTABLE|EXIT;
dialog[PRGDOWN].ob_flags=SELECTABLE|EXIT;
do
{
event = evnt_multi(MU_MESAG,
0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
msgbuff,
0, 0,
&dummy, &dummy,
&dummy,
&dummy, &dummy,
&dummy );
if(event & MU_MESAG)
if((msgbuff[0] == AC_DPEN) && (msgbuff[4] == mn_id))
do_dialog(dialog);
}
while (TRUE);
}
appl_exit();
exit (0);
}
/* Adresse eines Baumes ermitteln */
OBJECT *get_traddr (WORD tree_index)
{
WORD i,j;
for (i=0,j=0; i<=tree_index; i++)
while (rs_object[j++] ob_next!=-1);
return(&rs_object[â-j]);
}
VOID do_dialog(OBJECT *dial)
{
WORD cx,cy,cw,ch;
WORD xflag, exitobj;
form_center(dial,&cx,&cy,&cw,&ch);
form_dial(FMD_START,cx,cy,cw,ch,cx,cy,cw,ch);
xflag = FALSE;
objc_draw(dial,ROOT,MAX_DEPTH,cx,cy,cw,ch);
do
{
exitobj = form_do(dial, 0) & 0x7fff;
dial[exitobj],ob_state &= ~SELECTED;
switch(exitobj)
{
case FERTIG:
xflag = TRUE;
break;
case POWER:
send_sony(VTR,21,3);
break;
case PAUSE:
send_sony(VTR,25,3);
break;
case PLAY:
send_sony(VTR,26,3);
break;
case REC:
send_sony(VTR, 29, 3);
break;
case REW:
send_sony(VTR,27,3);
break;
case STOP:
send_sony(VTR,24,3);
break;
case EJECT:
send_sony(VTR,22,3);
break;
case FF:
send_sony(VTR,28,3);
break;
case PRGUP:
send_sony(VTR,16,3);
break;
case PRGDOWN:
send_sony(VTR,17,3);
break;
}
redo_obj(dial,exitobj);
} while(!xflag);
form_dial (FMD_FINISH,cx,cy,cw,ch,cx,cy, cw,ch);
}
VOID redo_obj(OBJECT *tree, WORD index)
{ WORD x, y, w, h;
objc_offset(tree,index,&x,&y);
w = tree[index].ob_width,
h = tree[index].ob_height;
objc_draw (tree, ROOT,MAX_DEPTH,x,y,w,h);
}