← ST-Computer 11 / 1991

Sony-GerÀte selbstgesteuert: Der SIRCS-Bus

Hardware

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
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