Sony-Geräte selbstgesteuert: Der SIRCS-Bus

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);
}

Oliver Scholz
Aus: ST-Computer 11 / 1991, Seite 158

Links

Copyright-Bestimmungen: siehe Über diese Seite